今天做題遇到一個很經典的問題,記錄一下,先看一段程式碼
<?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裡不合理的地方感覺很迷茫,但是還找不到理由,但是經過不斷的除錯發現有些其他大佬的東西也不一定就都是對的,而且自己不斷除錯後找到問題有一種說不出來的成就感,總結起來就是看問題要深入,有耐心。引用原帖的一句話就是
更多網安技能的線上實操練習,請點選這裡>>