[转]PHP利用PCRE回溯次数限制绕过某些安全限制
这次Code-Breaking Puzzles中我出了一道看似很简单的题目pcrewaf,将其代码简化如下:
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
} if(!is_php($input)) {
// fwrite($f, $input); ...
}
大意是判断一下用户输入的内容有没有PHP代码,如果没有,则写入文件。这种时候,如何绕过is_php()
函数来写入webshell呢?
这道题看似简单,深究其原理,还是值得写一篇文章的。
0x01 正则表达式是什么
正则表达式是一个可以被“有限状态自动机”接受的语言类。
“有限状态自动机”,其拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。
而常见的正则引擎,又被细分为DFA(确定性有限状态自动机)与NFA(非确定性有限状态自动机)。他们匹配输入的过程分别是:
- DFA: 从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入
- NFA:从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态
由于NFA的执行过程存在回溯,所以其性能会劣于DFA,但它支持更多功能。大多数程序语言都使用了NFA作为正则引擎,其中也包括PHP使用的PCRE库。
0x02 回溯的过程是怎样的
所以,我们题目中的正则<\?.*[(`;?>].*
,假设匹配的输入是<?php phpinfo();//aaaaa
,实际执行流程是这样的:
见上图,可见第4步的时候,因为第一个.*
可以匹配任何字符,所以最终匹配到了输入串的结尾,也就是//aaaaa
。但此时显然是不对的,因为正则显示.*
后面还应该有一个字符[(`;?>]
。
所以NFA就开始回溯,先吐出一个a
,输入变成第5步显示的//aaaa
,但仍然匹配不上正则,继续吐出a
,变成//aaa
,仍然匹配不上……
最终直到吐出;
,输入变成第12步显示的<?php phpinfo()
,此时,.*
匹配的是php phpinfo()
,而后面的;
则匹配上[(`;?>]
,这个结果满足正则表达式的要求,于是不再回溯。13步开始向后匹配;
,14步匹配.*
,第二个.*
匹配到了字符串末尾,最后结束匹配。
在调试正则表达式的时候,我们可以查看当前回溯的次数:
这里回溯了8次。
0x03 PHP的pcre.backtrack_limit
限制利用
PHP为了防止正则表达式的拒绝服务攻击(reDOS),给pcre设定了一个回溯次数上限pcre.backtrack_limit
。我们可以通过var_dump(ini_get('pcre.backtrack_limit'));
的方式查看当前环境下的上限:
这里有个有趣的事情,就是PHP文档中,中英文版本的数值是不一样的:
我们应该以英文版为参考。
可见,回溯次数上限默认是100万。那么,假设我们的回溯次数超过了100万,会出现什么现象呢?比如:
可见,preg_match
返回的非1和0,而是false。
preg_match
函数返回false表示此次执行失败了,我们可以调用var_dump(preg_last_error() === PREG_BACKTRACK_LIMIT_ERROR);
,发现失败的原因的确是回溯次数超出了限制:
所以,这道题的答案就呼之欲出了。我们通过发送超长字符串的方式,使正则执行失败,最后绕过目标对PHP语言的限制。
对应的POC如下:
import requests
from io import BytesIO files = {
'file': BytesIO(b'aaa<?php eval($_POST[txt]);//' + b'a' * 1000000)
} res = requests.post('http://51.158.75.42:8088/index.php', files=files, allow_redirects=False)
print(res.headers)
0x04 PCRE另一种错误的用法
延伸一下,很多基于PHP的WAF,如:
<?php
if(preg_match('/SELECT.+FROM.+/is', $input)) {
die('SQL Injection');
}
均存在上述问题,通过大量回溯可以进行绕过。
另外,我遇到更常见的一种WAF是
<?php
if(preg_match('/UNION.+?SELECT/is', $input)) {
die('SQL Injection');
}
这里涉及到了正则表达式的“非贪婪模式”。在NFA中,如果我输入UNION/*aaaaa*/SELECT
,这个正则表达式执行流程如下:
.+?
匹配到/
- 因为非贪婪模式,所以
.+?
停止匹配,而由S
匹配*
S
匹配*
失败,回溯,再由.+?
匹配*
- 因为非贪婪模式,所以
.+?
停止匹配,而由S
匹配a
S
匹配a
失败,回溯,再由.+?
匹配a
- ...
回溯次数随着a的数量增加而增加。所以,我们仍然可以通过发送大量a,来使回溯次数超出pcre.backtrack_limit
限制,进而绕过WAF:
0x05 修复方法
那么,如何修复这个问题呢?
其实如果我们仔细观察PHP文档,是可以看到preg_match
函数下面的警告的:
如果用preg_match
对字符串进行匹配,一定要使用===
全等号来判断返回值,如:
<?php
function is_php($data){
return preg_match('/<\?.*[(`;?>].*/is', $data);
} if(is_php($input) === 0) {
// fwrite($f, $input); ...
}
这样,即使正则执行失败返回false,也不会进入if语句
来自p神文章:
https://www.leavesongs.com/PENETRATION/use-pcre-backtrack-limit-to-bypass-restrict.html?page=1#reply-list
[转]PHP利用PCRE回溯次数限制绕过某些安全限制的更多相关文章
- php preg_match pcre回溯绕过
原理需要知识:正则NFA回溯原理,php的pcre.backtrack_limit设置. 正则NFA回溯原理正则表达式是一个可以被"有限状态自动机"接受的语言类."有限状 ...
- Js杂谈-正则的测试与回溯次数
例子来源于<精通正则表达式(第三版)>这本书,我贴出来: 这里的NFA是正则的一种引擎,书中介绍了一共三种引擎:NFA,DFA和POSIX NFA.像一般我们常用的.NET,java.ut ...
- [原创]C语言利用pcre正则表达式库
C语言使用正则表达式,可以利用pcre库,这个比较不错的哦. 在使用过程中,利用python进行测试正则表达式是否OK,后发现出现了问题.如下所示: regex.c:11:18: warning: u ...
- 在SQL注入中利用MySQL隐形的类型转换绕过WAF检测
web应用一般采用基于表单的身份验证方式(页面雏形如下图所示),处理逻辑就是将表单中提交的用户名和密码传递到后台数据库去查询,并根据查询结果判断是否通过身份验证.对于LAMP架构的web应用而言,处理 ...
- 内存保护机制及绕过方案——利用未启用SafeSEH模块绕过SafeSEH
前言:之前关于safeSEH保护机制的原理等信息,可在之前的博文(内存保护机制及绕过方案中查看). 利用未启用SafeSEH模块绕过SafeSEH ⑴. 原理分析: 一个不是仅包含中间语言(1L)且 ...
- 内存保护机制及绕过方法——利用未启用SafeSEH模块绕过SafeSEH
利用加载模块之外的地址绕过safeSEH 前言:文章涉及的概念在之前的文章中都有过详细的讲解 ⑴. 原理分析: 当程序加载进内存中后,处理PE文件(exe,dll),还有一些映射文件,safeSEH ...
- 在C语言中利用PCRE实现正则表达式
1. PCRE简介 2. 正则表达式定义 3. PCRE正则表达式的定义 4. PCRE的函数简介 5. 使用PCRE在C语言中实现正则表达式的解析 6. PCRE函数在C语言中的使用小例子 1. P ...
- 利用script和scriptlet moniker绕过脚本白名单限制
没事儿看了一下subtee和enigma0x3今年在BSides Nashville 2017上的演讲,觉得这两个猥琐男简直不能再猥琐了 :-)其中有一个猥琐小技巧,又可以让我们好好hunting一番 ...
- 利用shell脚本通过ssh绕过输入密码直接登录主机
shell #!/usr/bin/expect spawn ssh root@192.168.137.141 expect "*password:" send "lizh ...
随机推荐
- 机器学习算法系列:FM分解机
在线性回归中,是假设每个特征之间独立的,也即是线性回归模型是无法捕获特征之间的关系.为了捕捉特征之间的关系,便有了FM分解机的出现了.FM分解机是在线性回归的基础上加上了交叉特征,通过学习交叉特征的权 ...
- 记录一个不同的流媒体网站实现方法,和用Python爬虫爬它的坑
今天找到一片电影,想把它下载下来. 先开Networks工具分析一下: 初步分析发现,视频加载时会拉取TS格式的文件,推测这是一个m3u8的索引,记录着几百段TS文件,这样方便快进时加载. 但是实际分 ...
- coding++:Java 获取request中的参数
第一种: private Map<String,Object> mapParameters(HttpServletRequest request) { //封装查询条件参数 Map< ...
- coding++:Spring Boot 全局事务解释及使用(二)
什么是全局事务: Spring Boot(Spring) 事务是通过 aop(aop相关术语:通知(Advice).连接点(Joinpoint).切入点(Pointcut).切面(Aspect).目标 ...
- iOS 应用签名
一.密码学简介 1.1 base64 Base64 是一种通过查表的编码方法,不能用于加密,即使使用自定义的编码表也不行. Base64 适用于小段内容的编码,比如数字证书签名.Cookie 的内容等 ...
- LLVM 编码规范 - 中文翻译
LLVM 编码规范 导论 语言.库和标准 C++ 标准版本 C++ 标准库 Go 代码准则 机械的代码问题 代码格式化 注释 头文件 类概述 method information 注释格式化 使用Do ...
- JS烟花案例
html代码部分 <!DOCTYPE html> <!-- * @Descripttion: * @version: * @Author: 小小荧 * @Date: 2020-03- ...
- RbbitMQ详解
高性能消息队列RabbitMQ 1.为什么要使用mq 主要解决应用解耦,流量削峰,异步消息,实现高性能,可升缩,最终一致性的架构. 2.activeMq的通讯模式 基于队列(点对点)与发布订阅(有多个 ...
- IO 流
------------恢复内容开始------------ 一.流的简介 输入流:把硬盘中的数据读取到内存中 输出流:把内存中的数据读取到硬盘中 1字符=2字节 1字节=8位 1.2顶级父类 二. ...
- SSM项目启动的三种方式
SSM整合Maven项目的三种启动方式 项目部署如图: 1.从父工程的的tomcat插件中直接启动 2.从web子工程的tomcat插件中启动,(需要先执行父工程的install) 如果没有执行父工程 ...