关于转义符 \ 在php正则中的匹配问题

今天做题遇到一个很经典的问题,记录一下,先看一段代码

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,那应该怎么修改正则内容呢?

我们刚才提到,转义是为了防止语句中的字符产生混淆,/与正则边界产生了混淆,所以我们用其他的字符作为边界就好了,比如#

总结:在一般情况下,只有字符串中的某一字符会对该语句产生混淆,这时该符号前的\才具有转义作用。

帮助网安学习,全套资料S信领取:

① 网安学习成长路径思维导图

② 60+网安经典常用工具包

③ 100+SRC漏洞分析报告

④ 150+网安攻防实战技术电子书

⑤ 最权威CISSP 认证考试指南+题库

⑥ 超1800页CTF实战技巧手册

⑦ 最新网安大厂面试题合集(含答案)

⑧ APP客户端安全检测指南(安卓+IOS)

这里我在做测试有一个小坑

首先php的字符串解析:可以看到由于字符串中并没有可能会产生混淆语句的字符,因此\都没有转义作用。

正则进行解析(只解析正则表达式,不解析其他字符串):pattern中的\/被解析成了/

因此最终的正则匹配是在字符串\/中匹配/,因此输出了/

这里我一开始以为str中的\也发挥了转义作用,其实并不是。

回到最初的问题,为什么输出了false

php 复制代码
<?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移步主页查找,如果没有就是还没写完。

swift 复制代码
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里不合理的地方感觉很迷茫,但是还找不到理由,但是经过不断的调试发现有些其他大佬的东西也不一定就都是对的,而且自己不断调试后找到问题有一种说不出来的成就感,总结起来就是看问题要深入,有耐心。引用原帖的一句话就是

相关推荐
ServBay10 小时前
告别面条代码,PSL 5.0 重构 PHP 性能与安全天花板
后端·php
JaguarJack3 天前
FrankenPHP 原生支持 Windows 了
后端·php·服务端
BingoGo3 天前
FrankenPHP 原生支持 Windows 了
后端·php
JaguarJack4 天前
PHP 的异步编程 该怎么选择
后端·php·服务端
BingoGo4 天前
PHP 的异步编程 该怎么选择
后端·php
JaguarJack4 天前
为什么 PHP 闭包要加 static?
后端·php·服务端
ServBay5 天前
垃圾堆里编码?真的不要怪 PHP 不行
后端·php
用户962377954485 天前
CTF 伪协议
php
合天网安实验室6 天前
H2O-3反序列化漏洞分析(CVE-2025-6507&CVE-2025-6544)
前端·黑客
BingoGo8 天前
当你的 PHP 应用的 API 没有限流时会发生什么?
后端·php