BUUCTF[HCTF 2018]WarmUp 1題解
- 分析
- 解題過程
- 代碼審計
- 主體函數
- CHECK函數:
- 構造payload
- 總結
分析
啟動靶機,進入網址,是一張滑稽的表情包:
程序化F12
查看源碼:
發現注釋內容,訪問
url:/source.php
得到下面的源碼:
<?phphighlight_file(__FILE__);class emmm{public static function checkFile(&$page){$whitelist = ["source"=>"source.php","hint"=>"hint.php"];if (! isset($page) || !is_string($page)) {echo "you can't see it";return false;}if (in_array($page, $whitelist)) {return true;}$_page = mb_substr($page,0,mb_strpos($page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}$_page = urldecode($page);$_page = mb_substr($_page,0,mb_strpos($_page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}echo "you can't see it";return false;}}if (! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file'])) {include $_REQUEST['file'];exit;} else {echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";}
?>
OK,這時候我們可以知道這是一道PHP代碼審計題,接下來就是公式化構造payload時間啦!
解題過程
代碼審計
主體函數
我們來到了代碼審計階段,先從主體開始:
if (! empty($_REQUEST['file'])&& is_string($_REQUEST['file'])&& emmm::checkFile($_REQUEST['file'])) {include $_REQUEST['file'];exit;} else {echo "<br><img src=\"https://i.loli.net/2018/11/01/5bdb0d93dc794.jpg\" />";}
首先:
$_REQUEST
是一個關聯數組,包含 $_GET
、 $_POST
和 $_COOKIE
的內容
$_REQUEST['file']
表示從用戶請求中獲取名為 “file” 的參數值
用途:獲取表單數據、URL 參數、Cookie 值等用戶輸入
if
判斷了以下幾個條件:
- file的值是否不為空
- file的值是否為字符串
- emmm::checkFile(file)的回顯是否為1
當上面的所有答案都為是,執行:
include $_REQUEST['file'];
反之,輸出滑稽表情包。
接下來我們的任務就是解析emmm::checkFile()函數,以繞過waf條件,訪問文件。
CHECK函數:
class emmm{public static function checkFile(&$page){$whitelist = ["source"=>"source.php","hint"=>"hint.php"];if (! isset($page) || !is_string($page)) {echo "you can't see it";return false;}if (in_array($page, $whitelist)) {return true;}$_page = mb_substr($page,0,mb_strpos($page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}$_page = urldecode($page);$_page = mb_substr($_page,0,mb_strpos($_page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}echo "you can't see it";return false;}}
一步一步分析:
$whitelist = ["source"=>"source.php","hint"=>"hint.php"];
這是一個關聯數組,關聯數組的主要特點是鍵值對數組。
鍵(key):“source” 和 “hint” - 這些是白名單標識符;
值(value):“source.php” 和 “hint.php” - 實際允許訪問的文件名;
這里我們發現了另一個文件hint.php,是一個提示,訪問網址顯示:
hint.php
提示了flag的文件名。
我們接著分析代碼:
if (! isset($page) || !is_string($page)) {echo "you can't see it";return false;}
上面這段代碼這段代碼判斷參數是否為NULL,是否為字符串。
if (in_array($page, $whitelist)) {return true;}
上面這段代碼判斷參數是否在白名單中(即判斷參數的值和數組的值是否相同)。
$_page = mb_substr($page,0,mb_strpos($page . '?', '?'));if (in_array($_page, $whitelist)) {return true;}
mb_strpos($page . '?', '?')
:
若$page
包含 ?
:返回第一個?
的位置;
若$page
不含?
:返回字符串長度。
mb_substr($page, 0, mb_strpos($page . '?', '?'))
:
$page
:原始輸入;
0
:起始位置;
mb_strpos($page . '?', '?')
:從 mb_strpos()
獲取的長度。
這個函數截取了參數從最開始直到第一個?
之間的子串,?之后的子串被丟棄。
請注意這段代碼,我們考慮是否能夠利用函數的特性,在?之后構造一段代碼
之后的代碼是:解碼url,再次判斷參數是否在白名單中。
構造payload
/?file=hint.php?/../../../../ffffllllaaaagggg
或
/?file=source.php?/../../../../ffffllllaaaagggg
構造思想主要是在?
后加入/
,../
表示上一級目錄,然后一級一級的搜索
所以實際上,payload是一層一層實驗出來的。
比如最開始是:
/?file=hint.php?/../ffffllllaaaagggg
發現網頁為空,說明該目錄中沒有目標文件,那么就再嘗試上一級目錄。
順便提一下:在原有的payload基礎上在加幾層都不會影響文件顯示,說明這個文件在網站的根目錄下
最后的flag是:flag{966bef86-ebda-43d8-8703-8e6a57b211de}
總結
本題考察的是PHP代碼閱讀能力和常見的目錄遍歷能力。
本題涉及到的PHP函數整理如下:
mb_strpos($page . '?', '?')
: 返回第一次出現?的數組下標
mb_substr($page, 0, mb_strpos($page . '?', '?'))
:截取第一個字符到?出現之前的字符(不包括?)
這道題的提示還是比較明顯的,難度不是很大,但是對于最后?/../../
的構造原理是否有普適性,筆者不解,總的來說也是一道適合入門的題目。