这次在Code-BreakingPuzzles中,我想出了一个看似简单的题目pcrewaf,并将其代码简化如下:].*/is',$data);}if(!is_php($input)){//fwrite($f,$input);...}一般思路是判断用户输入的内容中是否有PHP代码,如果没有则写入文件,此时如何绕过is_php()函数写入webshel??l呢,值得写一、什么是正则表达式正则表达式是“有限状态自动机”可以接受的语言类,“有限状态自动机”的状态是有限的,每个状态可以迁移到零个或多个状态,而输入字符串决定了在哪个状态下进行迁移,常见的正则引擎又细分为DFA(确定性有限状态自动机)和NFA(非确定性有限状态自动机)的过程他们的匹配输入的s是:DFA:从初始状态开始,逐个字符地读取输入串,根据正则逐步确定下一个过渡状态,直到没有找到匹配或者整个输入NFA完成:从初始状态开始,逐个字符读取输入字符串,并与正则表达式进行匹配。如果匹配失败,则回溯并尝试其他状态。由于NFA在执行过程中存在回溯,因此性能会较差。它基于DFA,但支持更多的功能。大多数编程语言都使用NFA作为正则引擎,包括PHP使用的PCRE库。二、回溯的过程是怎样的?所以,常规的<\?.*[(`;?>].*,假设匹配输入如上图所示,可以看出在第4步中,因为第一个.*可以匹配任意字符,所以最终匹配到结尾输入字符串,即//aaaaa。但是此时显然是错误的,因为正常显示.*后面应该是一个字符[(`;?>]。所以NFA开始回溯,先吐出一个a,并且在第5步输入变成/显示/aaaa,但是还是匹配不上正则表达式,继续吐出a,变成//aaa,还是匹配不上...最后直到吐出;,输入变成]在第12步显示,这个结果符合正则表达式的要求,所以不再回溯。第13步开始向后匹配;,第14步匹配.*,第2步.*匹配到字符串末尾,最后结束匹配。在调试正则表达式的时候,我们可以查看当前的回溯次数:这里我们回溯了8次。3、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如下:importrequestsfromioimportBytesIOfiles={'file':BytesIO(b'aaa].*/is',$data);}if(is_php($input)===0){//fwrite($f,$input);...}这样即使正则执行失败返回false也不进入if语句。
