前言
根據紅日安全寫的文章,學習PHP程式碼審計的第五節內容,題目均來自PHP SECURITY CALENDAR 2017,講完題目會用一道CTF的題目和例項來加深鞏固。這是之前寫的,有興趣可以去看看:
PHP程式碼審計01之in_array()函式缺陷
PHP程式碼審計02之filter_var()函式缺陷
PHP程式碼審計03之例項化任意物件漏洞
PHP程式碼審計04之strpos函式使用不當
漏洞分析
下面看題目,程式碼如下:
題目漏洞是正則使用不嚴謹導致任意檔案刪除的漏洞,現在來具體分析,引起漏洞的地方在上面程式碼的21行,這裡用到了preg_replace()函式,我們開啟PHP手冊來看看對這個函式的定義如下:
瞭解了函式的用法,看上面程式碼,[^a-z.-_] 表示匹配除了 a 字元到 z 字元和. 字元到 _ 字元之間的所有字元,但是沒有考慮到目錄路徑字元。這就直接可以任意刪除檔案,例如構造如下引數:
action=delete&data=../../config.php
將刪除config.php檔案。
CTF練習
通過上面的講解,來用一道CTF題目來練習一下,也是關於正則的問題,先看程式碼:
//index.php
<?php
include 'flag.php';
if ("POST" == $_SERVER['REQUEST_METHOD'])
{
$password = $_POST['password'];
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
{
echo 'Wrong Format';
exit;
}
while (TRUE)
{
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
$c = 0;
$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
if ("42" == $password) echo $flag;
else echo 'Wrong password';
exit;
}
}
highlight_file(__FILE__);
?>
//flag.php
<?php $flag = "HRCTF{Pr3g_R3plac3_1s_Int3r3sting}";?>
這道題目考察了是否熟悉PHP正則表達的字元類,大體是下面這個表格:
alnum | 字母和數字 |
---|---|
alpha | 字母 |
ascii | 0 - 127的ascii字元 |
blank | 空格和水平製表符 |
cntrl | 控制字元 |
digit | 十進位制數(same as \d) |
graph | 列印字元, 不包括空格 |
lower | 小寫字母 |
列印字元,包含空格 | |
punct | 列印字元, 不包括字母和數字 |
space | 空白字元 (比\s多垂直製表符) |
upper | 大寫字母 |
word | 單詞字元(same as \w) |
xdigit | 十六進位制數字 |
想要更加詳細的瞭解,建議翻閱PHP手冊,瞭解了字元類,下面來分析程式碼,上面一共三處正則表達,第一處如下:
if (0 >= preg_match('/^[[:graph:]]{12,}$/', $password))
它表示的含義是匹配到可列印字元12往上包含12,^表示必須某類字元開頭,$表示必須某類字元結尾。
第二處正則如下:
$reg = '/([[:punct:]]+|[[:digit:]]+|[[:upper:]]+|[[:lower:]]+)/';
if (6 > preg_match_all($reg, $password, $arr))
break;
它表示的含義是,把連續的字元,數字,大寫,小寫作為一段,最少分成六段,比如Test+0He 會分為T est + 0 H e六段。
下面看第三處正則:
$ps = array('punct', 'digit', 'upper', 'lower');
foreach ($ps as $pt)
{
if (preg_match("/[[:$pt:]]+/", $password))
$c += 1;
}
if ($c < 3) break;
if ("42" == $password) echo $flag;
這裡的含義是輸入的字元必須包含字元,數字,大寫,小寫其中的三種往上。最後與42進行弱型別比較,都符合就輸出flag,現在都解讀清楚了,讓我們們構造payload結果如下:
例項分析
通過例題和CTF題目的講解,是不是感覺棒棒的,現在我們們來分析例項吧,例項是LvyeCMS3.1,是基於ThinkPHP3.2.3框架。這個例項存在的漏洞也是函式使用不規範被繞過,導致任意檔案刪除。下面來具體分析:
先檢視入口檔案index.php
可以看到公共目錄,應用目錄等一些資訊。接下來再看看目錄結構:
而漏洞在Application/Template/Controller/StyleController.class.php
檔案中,具體如下:
看程式碼第117行,這裡是獲取目錄路徑,引數也是我們可以控制的,再向後看,用到了str_replace()函式,它是個字串替換函式,具體說明如下:
再這裡起到的作用就是將'..\', '../', './', '.\'替換為空。但是這裡是可以繞過的,如果我們輸入.....///呢,會發生什麼?是不是正好構造成了../,舉個小例子會更清楚,如下:
構造出../我們就可以穿越目錄了,現在訪問install.php檔案會提示已安裝,如下圖:
然後嘗試刪除lvyecms/Application/Install/目錄下的 install.lock 檔案,構造payload如下:
http://www.xxx.com/index.php?g=Template&m=Style&a=delete&dir=.....///Application/Install/&file=install.lock
現在訪問install.php,發現確實刪除了,如下圖:
小結
通過這篇文章的學習與講解,是不是對PHP的正則瞭解的更多了呢,下一篇文章會對parse_str函式缺陷進行學習和講解。一起加油吧!