sql正则绕过/正则回溯preg_match()

正则表达式

正则表达式是一个可以被「有限状态自动机」接受的语言类。

「有限状态自动机」,其拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。

而常见的正则引擎,又被细分为 DFA(确定性有限状态自动机)与 NFA(非确定性有限状态自动机)。他们匹配输入的过程分别是:

DFA: 从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入

NFA:从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态

由于 NFA 的执行过程存在回溯,所以其性能会劣于 DFA,但它支持更多功能。大多数程序语言都使用了 NFA 作为正则引擎,其中也包括 PHP 使用的 PCRE 库。

PHP 的preg_match、preg_replace底层是 PCRE 库( NFA 非确定有限状态机) ,会发生回溯。NFA 引擎回溯漏洞属于 ReDOS (正则拒绝服务),大量回溯会消耗服务器 CPU。因此有了最大回溯次数为100万次(查看官方文档)

核心特点:回溯 (backtrack)

正则符号规则

符号 功能详解(通俗版) 示例 匹配说明 / 控制对象 可匹配 不可匹配
^ 匹配行首/字符串开头 ^abc 匹配以abc开头的内容 abc123 123abc
$ 匹配行尾/字符串结尾 abc$ 匹配以abc结尾的内容 123abc abc123
. 匹配任意单个字符(除换行\n) a.b 中间必须有1个任意字符 aab、acb、a1b ab、a\nb
\[\] 匹配括号内任意一个字符 [abc] 只匹配 a / b / c 其中一个 a、b、c d、e、f
\^ 匹配除括号内的任意字符(取反) 123[^45] 禁止后面是4、5,其他都可以 1236、1237 1234、1235
- 匹配递增区间内任意字符 [0-9] 匹配任意一位数字 0~9任意数字 字母、符号
? 控制左边字符:出现0次或1次(最多1次) colou?r ? 专门控制左边的 u:u可以没有、可以有1个,不能多 color(无u)、colour(1个u) colouur(2个u)
+ 控制左边字符:出现1次或多次(至少1次) sa-6+ + 专门控制左边的 6:6必须至少有1个 sa-6、sa-666 sa-(没有6)
* 控制左边字符:出现0次或多次(有无都行) co*l * 专门控制左边的 o:o可以没有、可以无数个 cl、col、cool、coool 几乎无限制
() 分组,把括号内容当成一个整体 max(tri)? ? 控制整体 tri 单元 max、maxtri maxtritri
{n} 左边字符精准匹配n次 [0-9]{3} 必须刚好3个数字 123、456 12、1234
{n,} 左边字符最少n次,无上限 [0-9]{2,} 至少2位数字 12、123、9999 1
{n,m} 左边字符最少n次、最多m次 [0-9]{2,5} 2~5位数字 12、12345 1、123456
| 或逻辑,匹配左右任意一项 `ab(c d)` 匹配 c 或者 d abc、abd

贪婪:尽可能多的去匹配(大前提:匹配成功)

a*: 0-任意多

a?:0-1

a+:1-任意

a{n,m}: n-m

1./<\?.*(\`;?\>.*/

在了解了正则相关规则后查看以下一个代码中的正则

复制代码
<?php
function is_php($data){  
    return preg_match('/<\?.*[(`;?>].*/is', $data);  
}
<?php eval()

if(!is_php($input)) {
     fwrite($f, $input); ...
}

测试样例文本:<?php eval($_GETcmd);?>aaaaaaaaa//

步骤 1:<\?匹配

引擎从字符串开头,匹配字面<紧接着?。

成功匹配<?,指针移动到p(<?之后)。

步骤 2:第一个.*贪婪吞噬

(关键,回溯的源头).代表任意一个字符,而*代表当前左边字符能出现一次或任意次

.*是贪婪量词,它的本能:能拿多少拿多少。直接把从p一直到整个字符串末尾全部吃掉。现在指针直接跳到字符串最后面。

当前状态:.*把后面所有字符全部占完了;此时后面还有模式:(;?\>需要匹配一个字符,但是已经没有字符剩下。 (;?\>拿不到任何字符,匹配失败。

为什么要回溯:

NFA 不会直接宣告整体匹配失败。 它认为是.*吃太多了,霸占了本该留给后面(;?\>的那一个字符。 于是触发回溯.*吐出 (归还)最后一个字符 ,把指针往回退一位,再交给(;?\>尝试匹配。

步骤 3:循环回溯

吐出 1 个字符 交给(;?\>` 测试,不是集合里的字符 → 继续回溯;再吐出一个字符,继续测试......

不断往回退,直到退到字符 > 的位置。此时(属于(;?\>` 集合,匹配成功!停止回溯。

现在:.*占有php eval,让出了>,指针停在(这里。

步骤 4:

匹配(;?\>** 命中>,匹配成功。指针向后移动。

步骤 5:最后的.*

剩余的交给末尾.*贪婪全部吃掉。整个正则全部子模式全部匹配,返回匹配成功。

PHP 为了防止正则表达式的拒绝服务攻击(reDOS),给 pcre 设定了一个回溯次数上限 pcre.backtrack_limit。查阅文档可得知为100万次。

如何绕过?

通过POST方式发送超长字符串的方式,使正则执行失败,最后绕过目标对 PHP 语言的限制。 preg_match 函数返回 false 表示此次执行失败了

同样的含.+的正则也可以通过这样的方式实现绕过

另一种.+?

复制代码
<?php
if(preg_match('/UNION.+?SELECT/is', $input)) {
    die('SQL Injection');
}

测试正则: UNION/aaaaa/SELECT

1)匹配 UNION

引擎从文本开头,依次匹配 U‑N‑I‑O‑N。

UNION匹配成功。

指针移动到 UNION 的后面,指向第一个字符:/

2)进入 .+?(非贪婪)

规则:先拿最少 1 个字符,马上尝试后面的SELECT。

第1轮:.+?吃掉第 1 个字符 /。

现在指针走到a。立刻尝试匹配后面是否是SELECT。

当前位置是a,不是 S,匹配失败。

回溯(向前多吞一个字符)

第2轮:.+?再多吃下一个a,指针移到下一个a。

尝试匹配SELECT,这里还是a,失败。👉继续多吃字符。

重复循环:

不断吃字符:a →a→a→a→a→/

每吃一个字符,就停下来试一次SELECT,全部失败。

直到:.+?吃完 /aaaaa/,指针停到字符 S 的位置。

尝试匹配SELECT

当前指针正好指向S,依次匹配 S‑E‑L‑E‑C‑T,全部匹配成功。

整个正则全部片段完成匹配。

整体匹配成功。

匹配捕获的完整字符串:UNION/aaaaa/SELECT

php preg_match 返回值:int(1)

绕过方法

回溯次数随着 a 的数量增加而增加。所以,我们仍然可以通过发送大量 a,来使回溯次数超出 pcre.backtrack_limit 限制,进而绕过 WAF

新的sql绕过思想

使用百万次回溯绕过正则过滤的select等关键字

防御思路

  • 不使用贪婪匹配 .*

  • 正则执行时间限制

  • 请求体大小限制

  • 不使用单层检测,容易绕过

  • 使用===号判断

思考如何绕过

die() 等价于 exit(),输出字符串并直接终止整个 PHP 脚本,后面代码不再执行

preg_match------执行匹配正则表达式

strpos()

function strpos(string haystack, [string](https://www.php.net/manual/zh/language.types.string.php "string") needle, int $offset = 0): int|false

回 needle 在 haystack 中首次出现的数字位置。

haystack

在该字符串中进行查找。

needle

要搜索的字符串

isset ()--- 检测变量是否已声明并且其值不为空

使用传入数组 绕过

PHP 特性: preg_match 传入数组参数,直接返回 null ,不会报错

strpos接收数组,返回null

null !== false → true,条件成立,输出 flag

正则回溯绕过

is_array():检测变量是否是数组,是则返回ture

相关推荐
迪康Defender5 小时前
从静态存储到动态流转:终端透明加密两种模式实战解析
运维·服务器·网络·数据库·其他
2601_965798475 小时前
Is Piroll WordPress Theme Worth It for Freelancers? Full Review
数据库·php
冷凝娇5 小时前
【MySQL】2026总结(二)
数据库·mysql
aaa小葵6 小时前
LangGraph
数据库·人工智能
wWYy.7 小时前
Mysql:Read View
数据库·mysql
—Miss. Z—7 小时前
存储过程、用户定义函数、触发器和游标
数据库·mysql
小码农 - 初8 小时前
MySQL 的约束
mysql
文人sec8 小时前
MySQL事务与索引做了什么?
android·笔记·mysql
Dear~yxy9 小时前
MySQL企业级实战
数据库·mysql
晚安code9 小时前
MySQL 开发规范清单:建表、索引、SQL、ORM 四块一次讲透
数据库·sql·mysql