Loading

关于PHP正则回溯次数绕过

写在最前面

刚好写题遇到了有关PHP正则回溯的知识,在此系统的学一下并写笔记。参考了如下这些师傅的文章:

关于PHP的正则回溯

关于PHP的正则回溯绕过对于PHP中所有采用正则匹配的函数均是适用的,根据PHP官方文档有如下这些函数;

PHP中正则匹配在匹配时遵守着具体的匹配限制,如果在匹配超出限制时(即匹配发生错误)结果会返回False,如preg_match函数:

如preg_match函数通常会被用于作为关键字检测,只有未被检测时才能正常执行,这就意味着如果我们可控,我们可以调整PHP设置中关于正则匹配的限制,同时构造特定的字符串使正则匹配超出限制,从而使preg_match函数返回False,这样在对preg_match函数返回值未使用强等于(===)判断的情况下我们便绕过了关键字检测。这就意味着在一些依托正则匹配设置WAF的情况下,如果可控正则匹配的限制,我们便可以利用正则回溯绕过,使用出题人不想让我们使用的相关方法,做到出其不意。 

 (PHP中关于正则匹配的设置)

其中pcre.backtrack_limit是正则匹配时的回溯次数,pcre.recursion_limit是正则匹配时的递归空间,pcre.jit是正则匹配启用jit编译。

我们这里讲述的使得正则匹配超出限制的方法就是回溯超限,先来看下PHP中正则匹配在什么时候会触发回溯,接着再来利用。

贪婪匹配

贪婪匹配回溯出现于贪婪匹配过多的情况。如匹配串为aaab,匹配子串为.*b,匹配过程如下:

 

可以见到在第二次匹配时.*匹配到了整个原字符串aaab,已匹配的字符串变成aaab;但在第三次匹配时b未匹配到字符,故回溯一次,已匹配的字符串变成aaa;第四次匹配时b匹配到了原字符串末尾的b,已匹配字符串变成aaab,此时匹配字串匹配完成。

在此过程中.*贪婪匹配过多而匹配了整个字符串就导致了随后的b无法匹配到字符,这样为了确保已经匹配到的字符串中的b字符不被漏匹配便进行了回溯直到匹配上或匹配失败。

(以下是匹配失败(即b不存在时)的情况,可见回溯次数大大增多了)

非贪婪匹配

和贪婪匹配区别在于,贪婪匹配注重宁可不匹配也不匹配多的原则,所以匹配时会先对非贪婪匹配随后的字符匹配,匹配不上再回溯进行非贪婪匹配。如匹配串为aaab,匹配字串为.*?b,匹配过程如下:

从第一次匹配开始,按照非贪婪匹配的原则先采用b匹配,b匹配不上再回溯对.*?进行非贪婪匹配,故第一个a将会被匹配上;第二次匹配时,已匹配的字符串变成a,此时匹配仍先采用b匹配,匹配不上再回溯对.*?进行非贪婪匹配,故第二个a将会被匹配上;直到第六次匹配时,b成功匹配,整个匹配结束共进行了三次回溯。

实际中的PHP正则回溯

然而实际上PHP中的正则回溯的次数是比我们上面统计的要多的,这与PHP的函数逻辑有关(Orz,太菜了底层代码一点都看不懂)。例如:

按照此前的统计应该是3次回溯,但实际上确需要4次回溯。 

还有jit编辑器,当jit编辑器(默认开启)关闭时某些原本不需要回溯的正则匹配会变得需要回溯。例如:

这就表示在贪婪模式和非贪婪模式之外采用字符域[]也能明确触发回溯。

如何使用

对于我们来说可控大概是可以上传.htaccess文件(不用重启服务器是真的方便),在.htaccess中重写正则相关的限制(既然是要靠回溯超限触发,那就干脆把超限限制设置到最低咯,并且实际上除了上文所述的以外仍有其他PHP正则匹配式可以触发回溯(博主太菜没得出个通用的触发回溯的规律Orz))。

 

 

 接着再正常使用那些会被WAF检测的语句就好了,如果符合条件WAF就会被我们绕过了。

写在最后面

在写这篇文章的时候对正则的相关设置经行了一些手工测试,在测试过程中发现得到的结果并不所有的都是准确的或可以解释的(如同一套正则对字符a和字符b得不到同样的匹配结果???;在PHP代码中对pcre.jit的开启和关闭并不是及时的,往往等上个十几秒再刷新才会有反应......),写在文中的是选取的多次测试仍不变的结果(相对来说准确)。如果文章有不对之处或不足之处,欢迎各位师傅斧正。

posted @ 2021-07-14 22:51  Article_kelp  阅读(591)  评论(3编辑  收藏  举报