这次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回溯次数限制绕过某些安全限制的更多相关文章

  1. php preg_match pcre回溯绕过

    原理需要知识:正则NFA回溯原理,php的pcre.backtrack_limit设置. 正则NFA回溯原理正则表达式是一个可以被"有限状态自动机"接受的语言类."有限状 ...

  2. Js杂谈-正则的测试与回溯次数

    例子来源于<精通正则表达式(第三版)>这本书,我贴出来: 这里的NFA是正则的一种引擎,书中介绍了一共三种引擎:NFA,DFA和POSIX NFA.像一般我们常用的.NET,java.ut ...

  3. [原创]C语言利用pcre正则表达式库

    C语言使用正则表达式,可以利用pcre库,这个比较不错的哦. 在使用过程中,利用python进行测试正则表达式是否OK,后发现出现了问题.如下所示: regex.c:11:18: warning: u ...

  4. 在SQL注入中利用MySQL隐形的类型转换绕过WAF检测

    web应用一般采用基于表单的身份验证方式(页面雏形如下图所示),处理逻辑就是将表单中提交的用户名和密码传递到后台数据库去查询,并根据查询结果判断是否通过身份验证.对于LAMP架构的web应用而言,处理 ...

  5. 内存保护机制及绕过方案——利用未启用SafeSEH模块绕过SafeSEH

    前言:之前关于safeSEH保护机制的原理等信息,可在之前的博文(内存保护机制及绕过方案中查看). 利用未启用SafeSEH模块绕过SafeSEH ⑴.  原理分析: 一个不是仅包含中间语言(1L)且 ...

  6. 内存保护机制及绕过方法——利用未启用SafeSEH模块绕过SafeSEH

    利用加载模块之外的地址绕过safeSEH 前言:文章涉及的概念在之前的文章中都有过详细的讲解 ⑴.  原理分析: 当程序加载进内存中后,处理PE文件(exe,dll),还有一些映射文件,safeSEH ...

  7. 在C语言中利用PCRE实现正则表达式

    1. PCRE简介 2. 正则表达式定义 3. PCRE正则表达式的定义 4. PCRE的函数简介 5. 使用PCRE在C语言中实现正则表达式的解析 6. PCRE函数在C语言中的使用小例子 1. P ...

  8. 利用script和scriptlet moniker绕过脚本白名单限制

    没事儿看了一下subtee和enigma0x3今年在BSides Nashville 2017上的演讲,觉得这两个猥琐男简直不能再猥琐了 :-)其中有一个猥琐小技巧,又可以让我们好好hunting一番 ...

  9. 利用shell脚本通过ssh绕过输入密码直接登录主机

    shell #!/usr/bin/expect spawn ssh root@192.168.137.141 expect "*password:" send "lizh ...

随机推荐

  1. springboot使用swagger2创建文档

    一.导入swagger2依赖 <dependency> <groupId>io.springfox</groupId> <artifactId>spri ...

  2. 关于js中iframe 中 location.href的用法

    关于js中"window.location.href"."location.href"."parent.location.href".&qu ...

  3. Python如何用virtualenv搭建虚拟环境

    虚拟环境的搭建 优点 1.使不同应用开发环境相互独立 2.环境升级不影响其他应用,也不会影响全局的python环境 3.防止出现包管理混乱及包版本冲突 windows 安装 # 建议使用pip3安装到 ...

  4. dyld

    一.介绍 在 MacOS 和 iOS 上,可执行程序的启动依赖于 xnu 内核进程运作和动态链接加载器 dyld. dyld 全称 the dynamic link editor,即动态链接器,其本质 ...

  5. 谈谈surging 与多语言混合微服务构思

    1.前言 微服务架构已成为目前互联网架构的趋势,关于微服务的讨论,几乎是各大技术论坛.技术大会的热门话题.而Surging是高性能的模块化微服务引擎,是大家首选微服务引擎架构之一,而针对于框架有个突出 ...

  6. SSM项目依赖,spring5.1.9

    SSM项目依赖,spring5.1.9 简单的SSM项目,未加AOP等等操作 Spring三包bean core content Mybatis 五个mybatis mybatis-spring jd ...

  7. linux下zip/unzip详解

    linux下zip_unzip详解 命令列表:zip    -q (quiet)    -r (recursive)    -0(level0-level9)    -e (encrypt)    - ...

  8. 泛型代码中的 default 关键字

    在泛型类和泛型方法中会出现的一个问题是,如何把缺省值赋给参数化类型,此时无法预先知道以下两点: l        T将是值类型还是引用类型 l        如果T是值类型,那么T将是数值还是结构 对 ...

  9. 微信小程序之界面交互反馈

    交互反馈就是在用户出发某事件之后,给用户一个反馈信息,这要是一个很友好的习惯. 在小程序中是通过一下几种方式实现的: 1.wx.showToast()方法 showToast: function (p ...

  10. HDFS程序开发

    占位