PHP利用PCRE回溯次数限制绕过某些安全限制
副标题[/!--empirenews.page--]
这次 Code-Breaking Puzzles 中我出了一道看似很简单的题目pcrewaf,将其代码简化如下:
大意是判断一下用户输入的内容有没有 PHP 代码,如果没有,则写入文件。这种时候,如何绕过 is_php() 函数来写入 webshell 呢? 这道题看似简单,深究其原理,还是值得写一篇文章的。 一、正则表达式是什么 正则表达式是一个可以被「有限状态自动机」接受的语言类。 「有限状态自动机」,其拥有有限数量的状态,每个状态可以迁移到零个或多个状态,输入字串决定执行哪个状态的迁移。 而常见的正则引擎,又被细分为 DFA(确定性有限状态自动机)与 NFA(非确定性有限状态自动机)。他们匹配输入的过程分别是:
由于 NFA 的执行过程存在回溯,所以其性能会劣于 DFA,但它支持更多功能。大多数程序语言都使用了 NFA 作为正则引擎,其中也包括 PHP 使用的 PCRE 库。 二、回溯的过程是怎样的 所以,我们题目中的正则 <?.*[(`;?>].*,假设匹配的输入是 见上图,可见第 4 步的时候,因为第一个 .* 可以匹配任何字符,所以最终匹配到了输入串的结尾,也就是 //aaaaa。但此时显然是不对的,因为正则显示.*后面还应该有一个字符 [(`;?>]。 所以 NFA 就开始回溯,先吐出一个 a,输入变成第 5 步显示的 //aaaa,但仍然匹配不上正则,继续吐出 a,变成 //aaa,仍然匹配不上…… 最终直到吐出;,输入变成第 12 步显示的 ] ,这个结果满足正则表达式的要求,于是不再回溯。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 如下:
四、PCRE 另一种错误的用法 延伸一下,很多基于 PHP 的 WAF,如:
均存在上述问题,通过大量回溯可以进行绕过。 另外,我遇到更常见的一种 WAF 是:
(编辑:威海站长网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |