前言
本文將深入研究 preg_replace /e 模式下的代碼執(zhí)行問題,其中包括 preg_replace 函數(shù)的執(zhí)行過程分析、正則表達(dá)式分析、漏洞觸發(fā)分析,當(dāng)中的坑非常多,相信看完本文,你一定會有所收獲。下面是 七月火 和 l1nk3r 的分析結(jié)果。
案例
下面先看一個案例,思考如何利用此處的 preg_replace /e 模式,執(zhí)行代碼(可以先不看下文分析,自己思考出 payload 試試)。
這個案例實(shí)際上很簡單,就是 preg_replace 使用了 /e 模式,導(dǎo)致可以代碼執(zhí)行,而且該函數(shù)的第一個和第三個參數(shù)都是我們可以控制的。我們都知道, preg_replace 函數(shù)在匹配到符號正則的字符串時,會將替換字符串(也就是上圖 preg_replace 函數(shù)的第二個參數(shù))當(dāng)做代碼來執(zhí)行,然而這里的第二個參數(shù)卻固定為 'strtolower("\\1")' 字符串,那這樣要如何執(zhí)行代碼呢?
爬坑1
上面的命令執(zhí)行,相當(dāng)于 eval('strtolower("\\1");')
結(jié)果,當(dāng)中的 \\1 實(shí)際上就是 \1 ,而 \1 在正則表達(dá)式中有自己的含義。我們來看看 W3Cschool 中對其的描述:
反向引用
對一個正則表達(dá)式模式或部分模式 兩邊添加圓括號 將導(dǎo)致相關(guān) 匹配存儲到一個臨時緩沖區(qū) 中,所捕獲的每個子匹配都按照在正則表達(dá)式模式中從左到右出現(xiàn)的順序存儲。緩沖區(qū)編號從 1 開始,最多可存儲 99 個捕獲的子表達(dá)式。每個緩沖區(qū)都可以使用 '\n' 訪問,其中 n 為一個標(biāo)識特定緩沖區(qū)的一位或兩位十進(jìn)制數(shù)。
所以這里的 \1 實(shí)際上指定的是第一個子匹配項(xiàng),我們拿 ripstech 官方給的 payload 進(jìn)行分析,方便大家理解。官方 payload 為: /?.*={${phpinfo()}}
,即 GET 方式傳入的參數(shù)名為 /?.* ,值為 {${phpinfo()}}
。
原先的語句: preg_replace('/(' . $regex . ')/ei', 'strtolower("\\1")', $value);
變成了語句: preg_replace('/(.*)/ei', 'strtolower("\\1")', {${phpinfo()}});
爬坑2
上面的 preg_replace 語句如果直接寫在程序里面,當(dāng)然可以成功執(zhí)行 phpinfo() ,然而我們的 .* 是通過 GET 方式傳入,你會發(fā)現(xiàn)無法執(zhí)行 phpinfo 函數(shù),如下圖:
我們 var_dump 一下 $_GET 數(shù)組,會發(fā)現(xiàn)我們傳上去的 .* 變成了 _* ,如下圖所示:
這是由于在PHP中,對于傳入的非法的 $_GET 數(shù)組參數(shù)名,會將其轉(zhuǎn)換成下劃線,這就導(dǎo)致我們正則匹配失效。我們可以 fuzz 一下PHP會將哪些符號替換成下劃線,發(fā)現(xiàn)有:(這是非法字符不為首字母的情況)
當(dāng)非法字符為首字母時,只有點(diǎn)號會被替換成下劃線:
所以我們要做的就是換一個正則表達(dá)式,讓其匹配到 {${phpinfo()}}
即可執(zhí)行 phpinfo 函數(shù)。這里我提供一個 payload : \S*=${phpinfo()}
執(zhí)行結(jié)果如下:
爬坑3
下面再說說我們?yōu)槭裁匆ヅ涞?{${phpinfo()}}
或者 ${phpinfo()}
,才能執(zhí)行 phpinfo 函數(shù),這是一個小坑。這實(shí)際上是PHP可變變量 的原因。在PHP中雙引號包裹的字符串中可以解析變量,而單引號則不行。 ${phpinfo()}
中的 phpinfo() 會被當(dāng)做變量先執(zhí)行,執(zhí)行后,即變成 ${1} (phpinfo()
成功執(zhí)行返回true)。如果這個理解了,你就能明白下面這個問題:
var_dump(phpinfo()); // 結(jié)果:布爾 true
var_dump(strtolower(phpinfo()));// 結(jié)果:字符串 '1'
var_dump(preg_replace('/(.*)/ie','1','{${phpinfo()}}'));// 結(jié)果:字符串'11'
var_dump(preg_replace('/(.*)/ie','strtolower("\\1")','{${phpinfo()}}'));// 結(jié)果:空字符串''
var_dump(preg_replace('/(.*)/ie','strtolower("{${phpinfo()}}")','{${phpinfo()}}'));// 結(jié)果:空字符串''
這里的'strtolower("{${phpinfo()}}")'執(zhí)行后相當(dāng)于 strtolower("{${1}}") 又相當(dāng)于 strtolower("{null}") 又相當(dāng)于 '' 空字符串
總結(jié)
這個問題是我們在做 PHP-Audit-Labs 項(xiàng)目的時候發(fā)現(xiàn)的,我們盡可能地將每一篇文章的漏洞理解透徹,分析清楚,這對自身也是一種提高。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
您可能感興趣的文章:- PHP preg_replace() 正則替換所有符合條件的字符串
- PHP 字符串正則替換函數(shù)preg_replace使用說明
- PHP正則替換函數(shù)preg_replace和preg_replace_callback使用總結(jié)
- php正則之函數(shù) preg_replace()參數(shù)說明
- 詳解PHP正則表達(dá)式替換實(shí)現(xiàn)(PHP preg_replace,PHP preg_replace)
- php中preg_replace正則替換用法分析【一次替換多個值】
- php preg_replace替換實(shí)例講解
- PHP 正則表達(dá)式之正則處理函數(shù)小結(jié)(preg_match,preg_match_all,preg_replace,preg_split)
- php中preg_replace_callback函數(shù)簡單用法示例
- php中使用preg_replace函數(shù)匹配圖片并加上鏈接的方法