sql盲注漏洞
2019年某电商平台被拖库,注漏安全团队复盘时发现入口不在WAF规则覆盖的注漏常规注入点,而是注漏一个商品筛选接口的sort参数。攻击者提交sort=price desc时页面正常,注漏改成sort=price asc,注漏(select 1 from dual)就返回500错误。没有回显、注漏没有报错信息、注漏没有数据泄露——但布尔值的注漏真假变化已经足够把数据库里的用户表逐字符拆出来。
这就是注漏盲注的本质:应用不给你看查询结果,也不给你看错误信息,注漏但它的注漏行为会因为你注入的条件成立与否产生细微差异。攻击者要做的注漏只是把“数据库里第3个字符是不是'a'”这种问题,转化成应用能回答的注漏“是”或“否”。
三种盲注,注漏三种问法
布尔盲注是注漏最经典的一种。页面不会返回数据,但“正常显示”和“异常显示”本身就是答案。比如id=1 and ascii(substr(database(),1,1))>100,如果页面正常,说明数据库名首字符ASCII值大于100。二分法逼近,一个字符7次请求就能确定。听起来慢,但自动化工具跑起来,一个8位数据库名也就几十秒。
时间盲注更隐蔽。页面无论真假都返回一样的内容,攻击者只能靠sleep()制造延迟来判断。MySQL里id=1 and if(ascii(substr(database(),1,1))>100,sleep(3),0),如果请求卡了3秒,条件成立。这种手法在网络抖动大的环境下容易误判,但一旦稳定,几乎无法从响应内容上防御。某次应急响应中,我看到日志里大量请求耗时精确落在5.02秒、5.03秒,那就是时间盲注在跑。
报错盲注依赖数据库的报错回显。应用屏蔽了错误信息,但某些函数如extractvalue()、updatexml()会把查询结果拼进报错消息里。攻击者构造id=1 and extractvalue(1,concat(0x7e,(select user()),0x7e)),如果错误日志或调试页面暴露了XPATH syntax error: '~root@localhost~',数据就直接出来了。这种利用方式在PHP+MySQL的老系统里尤其常见。
为什么盲注比显注更危险
显注被WAF拦住的概率高,因为union select、information_schema这些特征太明显。盲注的payload往往只是and 1=1、and sleep(3),甚至用benchmark()替代sleep(),规则库很难在不误伤正常业务的前提下全部覆盖。更麻烦的是,盲注不需要应用返回数据,只要应用“有反应”就行。一个登录接口,账号不存在返回“用户不存在”,密码错误返回“密码错误”,这种差异就是布尔盲注的温床。
我见过一个案例:某OA系统的密码找回接口,token校验逻辑是select * from users where token='输入值'。输入不存在的token返回“链接失效”,输入存在的token但已过期返回“链接已过期”。攻击者用token=' or 1=1-- 让查询返回所有行,页面返回“链接已过期”,说明注入生效。接着用token=' or (select ascii(substr(password,1,1)) from users limit 1)>100-- 逐位猜出管理员密码哈希。整个过程没有一条union,没有一次报错,WAF的SQL注入规则一条都没触发。
防御的难点不在技术
参数化查询能根治盲注,这个没有争议。但现实是,很多老系统用字符串拼接,改造成本高,业务方不愿意动。WAF可以拦一部分,但盲注的payload太像正常请求,误报和漏报很难平衡。真正有效的做法是限制数据库账号权限、关闭不必要的报错回显、对敏感接口做频率限制。一个接口如果每分钟只允许10次请求,盲注猜一个32位哈希需要几万次请求,时间成本直接劝退大部分攻击者。
还有一点常被忽略:盲注的“盲”是相对的。应用不返回数据,但数据库的负载、响应时间、日志量都会变化。部署了RASP的系统能直接看到SQL语句被拼接的过程,不管有没有回显。所以别只盯着WAF规则,运行时防护在盲注场景下往往更管用。
最后说个观察:很多开发觉得“我页面不报错、不返回数据,就安全了”。恰恰相反,不报错只是让显注变盲注,攻击者该拿的数据一个不少,只是多花点时间。真正的安全不是让攻击者看不见,而是让注入根本执行不了。









