关于转义符 \ 在php正则中的匹配问题
今天做题遇到一个很经典的问题,记录一下,先看一段代码
<?php
$str,=,"\\";
$pattern,=,"/\\/";
if(preg_match($partern,$str,$arr))
{
,,,,echo,"success";
,,,,print_r($arr);
}else{
,,,,echo,"false";
}
看到这段代码的师傅们,思考一下,会输出success还是false
输出false,正则没有被匹配到,为什么呢?
php对转义符的解析
php解析正则时分为了两个步骤,一个是php对字符串的解析,之后才是对正则的解析,那么php在解析字符串时什么时候才会将\
解析为转义呢?只有在某一字符会对这一语句产生混淆时,php才会将\
解析为转义。
分析一个正则匹配
首先php对字符串进行解析:
在这种情况下可以看到str中\
并没有被当成转义符
而在pattern中,由于有多个\
并且在正则表达式中存在/
,会混淆正则表达式的边界,因此这四个转义符的作用分别是:
-
第一个转义符转义第二个转义符
-
第三个转义符转义第四个转义符,第五个转义符转义
/
因此php最终解析出的str为,\/
,pattern为,\\/
到preg_match时,进行正则解析(正则解析只解析正则表达式):
-
将pattern中的,
\\/
,解析为\/
,(第一个转义符转义了第二个转义符)
经过php和正则的解析后,我们可以发现str与pattern是一样的字符串了,所以应该会输出success,并且匹配到的部分为\/
验证成功
这里提出一个问题,如果在pattern中,我的正则内容中不想使用\
来转义/
,并且还想输出success,那应该怎么修改正则内容呢?
我们刚才提到,转义是为了防止语句中的字符产生混淆,/
与正则边界产生了混淆,所以我们用其他的字符作为边界就好了,比如#
总结:在一般情况下,只有字符串中的某一字符会对该语句产生混淆,这时该符号前的\
才具有转义作用。
【----帮助网安学习,以下所有学习资料免费领!加vx:dctintin,备注 “博客园” 获取!】
① 网安学习成长路径思维导图
② 60+网安经典常用工具包
③ 100+SRC漏洞分析报告
④ 150+网安攻防实战技术电子书
⑤ 最权威CISSP 认证考试指南+题库
⑥ 超1800页CTF实战技巧手册
⑦ 最新网安大厂面试题合集(含答案)
⑧ APP客户端安全检测指南(安卓+IOS)
这里我在做测试有一个小坑
首先php的字符串解析:可以看到由于字符串中并没有可能会产生混淆语句的字符,因此\
都没有转义作用。
正则进行解析(只解析正则表达式,不解析其他字符串):pattern中的\/
被解析成了/
,
因此最终的正则匹配是在字符串\/
中匹配/
,因此输出了/
这里我一开始以为str中的\
也发挥了转义作用,其实并不是。
回到最初的问题,为什么输出了false
<?php
$str,=,"\\";,
$pattern,=,"/\\/";,,
if(preg_match($partern,$str,$arr))
{
,,,,echo,"success";
,,,,print_r($arr);
}else{
,,,,echo,"false";
}
按照上面的流程分析,
首先php进行字符串解析:
-
str被解析为
\
,pattern被解析为\
进行正则表达式解析:
-
pattern中含有转义符
\
,现在正则需要这个转义符去发挥转义作用,但在正则表达式中已经没有其他字符去转义了,导致了正则表达式的解析错误,pattern最终被解析成了什么我们也不知道
所以最终在进行正则匹配时会输出false
那么我们应该怎么让它输出success呢?
php正则如何正确匹配\
刚才我们提到在正则解析时只剩下了一个\
,导致了解析的错误,那么如果我们在正则解析这步剩下两个\
是不是就可以在正则解析中保留下一个\
呢?再往前推,如果想要正则解析这步里保留两个\
,那么在定义partern字符串的时候我们是不是要写四个\
才可以?
具体的解析过程我就不讲了,跟上面是完全一样的。
总结:php在正则中匹配\
时需要在正则表达式中写入四个\
一道ctf题的分析
题目来源:[安洵杯,2019]easy_web,wp移步主页查找,如果没有就是还没写完。
if,(preg_match("/ls|bash|tac|nl|more|less|head|wget|tail|vi|cat|od|grep|sed|bzmore|bzless|pcre|paste|diff|file|echo|sh|\'|\"|\`|;|,|\*|\?|\\|\\\\|\n|\t|\r|\xA0|\{|\}|\(|\)|\&[^\d]|@|\||\\$|\[|\]|{|}|\(|\)|-|<|>/i",,$cmd)),{
,,,,echo("forbid,~");
在这一段代码中对传入的cmd命令进行了过滤,并且可以看到其中有四个反斜杠,对\
做出了过滤,但最后仍然可以用反斜线逃逸,ca\t,l\s
执行命令,这是为什么呢?
按照我们上面所说的进行分析,首先php对字符串进行解析:
-
\\
被解析为\
-
\\\\
,被解析为\\
经过字符串解析,原本的|\\|\\\\|
,变成了|\|\\|
正则表达式解析:
-
第一个
\|
被解析为|
-
\\
被解析为\
经过两次解析后,最终的正则表达式变成了||\|
,所以实际上是对|\
进行了过滤,所以就可以使用\
进行绕过了。
因此解决的办法是在正则过滤中不要添加\\
这一项,会导致整个正则表达式直接变味。
这里跟着原帖看发现原帖说的有点问题,自己思考了一下做出了一些猜想,发现是正确的。
还有原帖中提到的一个问题,这里为什么随便一个字符串甚至是空都可以匹配成功,因为在|\\\\|
的左右两边没有东西,为空,所以随便匹配都可以匹配到。
解决方法就是两边加上东西就可以了。
自己的小感想
这道题在网上的wp基本都是直接用\
去执行命令,但很少有人能去讨论为什么可以这么绕过,后端代码已经做出了过滤,为什么还是会被绕过,我很幸运能够看到更深的分析,这也是我第一次自己有独立的想法去不断的调试代码,虽然每一次看到其他大佬wp里不合理的地方感觉很迷茫,但是还找不到理由,但是经过不断的调试发现有些其他大佬的东西也不一定就都是对的,而且自己不断调试后找到问题有一种说不出来的成就感,总结起来就是看问题要深入,有耐心。引用原帖的一句话就是
更多网安技能的在线实操练习,请点击这里>>
热门相关:人族镇守使 万妖帝主 纣临 不负荣光,不负你 神医娘亲:腹黑萌宝赖上门