關於轉義符 \ 在php正則中的匹配問題

今天做題遇到一個很經典的問題,記錄一下,先看一段代碼

<?php
$str="\\";
$pattern="/\\/";
if(preg_match($partern,$str,$arr))
{
,,,,echo,"success";
,,,,print_r($arr);
}else{
,,,,echo,"false";
}

看到這段代碼的師傅們,思考一下,會輸出success還是false

輸出false,正則沒有被匹配到,爲什麼呢?

image-20220820220805568

php對轉義符的解析

php解析正則時分爲了兩個步驟,一個是php對字符串的解析,之後纔是對正則的解析,那麼php在解析字符串時什麼時候纔會將\解析爲轉義呢?只有在某一字符會對這一語句產生混淆時,php纔會將\解析爲轉義。

分析一個正則匹配

image-20220820233147371

首先php對字符串進行解析:

在這種情況下可以看到str中\並沒有被當成轉義符

而在pattern中,由於有多個\並且在正則表達式中存在/,會混淆正則表達式的邊界,因此這四個轉義符的作用分別是:

  • 第一個轉義符轉義第二個轉義符

  • 第三個轉義符轉義第四個轉義符,第五個轉義符轉義/

因此php最終解析出的str爲,\/,pattern爲,\\/

到preg_match時,進行正則解析(正則解析只解析正則表達式):

  • 將pattern中的,\\/,解析爲\/,(第一個轉義符轉義了第二個轉義符)

經過php和正則的解析後,我們可以發現str與pattern是一樣的字符串了,所以應該會輸出success,並且匹配到的部分爲\/

驗證成功

image-20220820233913117

這裏提出一個問題,如果在pattern中,我的正則內容中不想使用\來轉義/,並且還想輸出success,那應該怎麼修改正則內容呢?

我們剛纔提到,轉義是爲了防止語句中的字符產生混淆,/與正則邊界產生了混淆,所以我們用其他的字符作爲邊界就好了,比如#

image-20220820234345677

總結:在一般情況下,只有字符串中的某一字符會對該語句產生混淆,這時該符號前的\才具有轉義作用。

【---- 幫助網安學習,以下所有學習資料免費領!領取資料加 we~@x:dctintin,備註 “開源中國” 獲取!】

① 網安學習成長路徑思維導圖
② 60 + 網安經典常用工具包
③ 100+SRC 漏洞分析報告
④ 150 + 網安攻防實戰技術電子書
⑤ 最權威 CISSP 認證考試指南 + 題庫
⑥ 超 1800 頁 CTF 實戰技巧手冊
⑦ 最新網安大廠面試題合集(含答案)
⑧ APP 客戶端安全檢測指南(安卓 + IOS)

這裏我在做測試有一個小坑

image-20220820234819307

首先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字符串的時候我們是不是要寫四個\纔可以?

image-20220821001642109

具體的解析過程我就不講了,跟上面是完全一樣的。

總結: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對字符串進行解析:

  • \\被解析爲\

  • \\\\,被解析爲\\

經過字符串解析,原本的|\\|\\\\|,變成了|\|\\|

正則表達式解析:

  • 第一個\|被解析爲|

  • \\被解析爲\

經過兩次解析後,最終的正則表達式變成了||\|,所以實際上是對|\進行了過濾,所以就可以使用\進行繞過了。

image-20220821004532200

因此解決的辦法是在正則過濾中不要添加\\這一項,會導致整個正則表達式直接變味。

這裏跟着原帖看發現原帖說的有點問題,自己思考了一下做出了一些猜想,發現是正確的。

image-20220821004815392

還有原帖中提到的一個問題,這裏爲什麼隨便一個字符串甚至是空都可以匹配成功,因爲在|\\\\|的左右兩邊沒有東西,爲空,所以隨便匹配都可以匹配到。

image-20220821004919804

解決方法就是兩邊加上東西就可以了。

image-20220821005133509

自己的小感想

這道題在網上的wp基本都是直接用\去執行命令,但很少有人能去討論爲什麼可以這麼繞過,後端代碼已經做出了過濾,爲什麼還是會被繞過,我很幸運能夠看到更深的分析,這也是我第一次自己有獨立的想法去不斷的調試代碼,雖然每一次看到其他大佬wp裏不合理的地方感覺很迷茫,但是還找不到理由,但是經過不斷的調試發現有些其他大佬的東西也不一定就都是對的,而且自己不斷調試後找到問題有一種說不出來的成就感,總結起來就是看問題要深入,有耐心。引用原帖的一句話就是

image-20220821005705584

  

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章