一个单引号就能让登录框吐出整个用户表。注证这不是入漏电影桥段,是洞验我上周在测试环境里亲手复现的事。
目标站点有个搜索接口,注证URL长这样:
/search?入漏keyword=手机。我在后面加了个单引号,洞验页面直接返回500,注证错误信息里明晃晃写着You have 入漏an error in your SQL syntax near ''手机''。MySQL的洞验报错,连数据库类型都省得猜了。注证把单引号换成
%27再试,入漏照样报错。洞验换成手机' AND '1'='1,注证页面正常返回结果;换成手机' AND '1'='2,入漏结果为空。洞验布尔盲注的经典特征,两个请求的响应差异肉眼可见。这说明拼接进SQL的输入没有经过任何转义处理。手工验证到这一步已经够了,但我想看看能走多远。用
ORDER BY试列数:手机' ORDER BY 1-- -正常,ORDER BY 5-- -正常,ORDER BY 6-- -报错。查询结果有5列。接着用UNION SELECT定位回显位:手机' UNION SELECT 1,2,3,4,5-- -,页面上第3个位置显示了数字“3”,回显位找到了。把3替换成
database(),页面吐出当前库名。替换成group_concat(table_name)配合information_schema.tables,所有表名一次性列出来。用户表叫t_user,字段有u_name、u_pass、u_phone。再查group_concat(u_name,0x7c,u_pass),十几条用户名和密码明文躺在页面上。密码是MD5,但手机号是明文,撞库成本几乎为零。整个过程不到二十分钟。没有用sqlmap,纯手工。工具当然快,但手工走一遍能看清每个环节的成因。这个接口的问题出在Java层用
String.format拼SQL,而不是PreparedStatement。开发图省事,把用户输入直接塞进查询串里。有人觉得预编译万能,其实未必。如果
ORDER BY后面的参数是拼接的,预编译也救不了。表名、列名、排序方向这些位置没法用占位符,只能白名单校验。我见过一个系统,WHERE子句用了预编译,但ORDER BY字段名直接拼,照样能注入。攻击者用CASE WHEN配合布尔条件,把数据一位一位盲注出来,慢是慢,但能拿到。验证漏洞时有个细节值得记:目标返回的报错信息里带了完整SQL语句。这等于把数据库结构送给攻击者。生产环境应该关掉详细报错,返回统一错误页。但很多内部系统没做这个,觉得“反正外网访问不到”。内网横向移动的案例里,这种疏忽往往是跳板的第一块砖。
再说回那个搜索接口。我测完提交了报告,开发回了一句“参数化了,已修复”。我第二天复测,
手机' AND '1'='1和手机' AND '1'='2返回结果依然不同。追问才知道,他只把keyword参数做了过滤,另一个sort参数忘了。那个参数藏在请求头里,前端没暴露,但Burp抓包能看到。SQL注入的验证逻辑其实不复杂:找到输入点,构造闭合,观察响应差异。难的是覆盖所有输入点,包括那些藏在Cookie、Header、JSON深层字段里的参数。自动化扫描器能发现一部分,但逻辑漏洞和二阶注入往往漏掉。手工测的时候,我习惯把每个参数都单独拎出来,加单引号、加注释符、加
AND 1=1,看响应码、响应长度、响应时间三个维度的变化。最后提一句,别在未授权的系统上做这些测试。我这次能动手,是因为甲方给了测试授权书,并且约定了时间窗口和IP白名单。没有授权,一个单引号就可能让你从技术爱好者变成犯罪嫌疑人。
顶: 4982踩: 69684






评论专区