0×00 背景
近期在研究學習變量覆蓋漏洞的問題,於是就把之前學習的和近期看到的CTF題目中有關變量覆蓋的題目結合下進一步研究。
通常將可以用自定義的參數值替換原有變量值的情況稱為變量覆蓋漏洞。經常導致變量覆蓋漏洞場景有:$$使用不當,extract()函數使用不當,parse_str()函數使用不當,import_request_variables()使用不當,開啟了全局變量注冊等。
本篇收集了幾個CTF中的題目作為例子,對$$,extract(),parse_str()的問題進行總結。
0×01 $$導致的變量覆蓋問題
$$ 導致的變量覆蓋問題在CTF代碼審計題目中經常在foreach中出現,如以下的示例代碼,使用foreach來遍歷數組中的值,然后再將獲取到的數組鍵名作為變量,數組中的鍵值作為變量的值。因此就產生了變量覆蓋漏洞。請求?name=test 會將$name的值覆蓋,變為test。
<?php //?name=test //output:string(4) “name” string(4) “test” string(4) “test” test $name=’thinking’; foreach ($_GET as $key => $value) { $$key = $value; } var_dump($key); var_dump($value); var_dump($$key); echo $name; ?>
CTF中$$導致的變量覆蓋問題的例題1:
題目源碼:
<?php include “flag.php”; $_403 = “Access Denied”; $_200 = “Welcome Admin”; if ($_SERVER["REQUEST_METHOD"] != “POST”) { die(“BugsBunnyCTF is here :p…”); } if ( !isset($_POST["flag"]) ) { die($_403); } foreach ($_GET as $key => $value) { $$key = $$value; } foreach ($_POST as $key => $value) { $$key = $value; } if ( $_POST["flag"] !== $flag ) { die($_403); } echo “This is your flag : “. $flag . “\n”; die($_200); ?>
題目分析:
源碼包含了flag.php文件,並且需要滿足3個if里的條件才能獲取flag,題目中使用了兩個foreach並且也使用了$$.兩個foreach中對 $$key的處理是不一樣的,滿足條件后會將$flag里面的值打印出來,所以$flag是在flag.php文件文件中的。
但是由於第7,11-14行間的代碼會將$flag的值給覆蓋掉了,所以需要先將$flag的值賦給$_200或$_403變量,然后利用die($_200)或 die($_403)將flag打印出來。
解題方法:
由於第7,11-14行間的代碼會將$flag的值給覆蓋掉,所以只能利用第一個foreach先將$flag的值賦給$_200,然后利用die($_200)將原本的flag值打印出來。
最終PAYLOAD:
本地復現,所以flag與原題不一樣
GET DATA:?_200=flag
POST DATA:flag=aaaaaaaaaaaaaaaaaaaaa
0×02 extract()函數導致的變量覆蓋問題
extract() 該函數使用數組鍵名作為變量名,使用數組鍵值作為變量值。針對數組中的每個元素,將在當前符號表中創建對應的一個變量。
extract()的用法參考:http://www.runoob.com/php/func-array-extract.html
語法: extract(array,extract_rules,prefix)
CTF中extract()導致的變量覆蓋問題的例題1:
題目源碼:
<?php $flag = ‘xxx’; extract($_GET); if (isset($gift)) { $content = trim(file_get_contents($flag)); if ($gift == $content) { echo ‘hctf{…}’; } else { echo ‘Oh..’; } } ?>
題目分析:
題目使用了extract($_GET)接收了GET請求中的數據,並將鍵名和鍵值轉換為變量名和變量的值,然后再進行兩個if 的條件判斷,所以可以使用GET提交參數和值,利用extract()對變量進行覆蓋,從而滿足各個條件。
解題方法:
GET請求 ?flag=&gift=,extract()會將$flag和$gift的值覆蓋了,將變量的值設置為空或者不存在的文件就滿足$gift == $content。
最終PAYLOAD:
GET DATA: ?flag=&gift=
CTF中extract()導致的變量覆蓋問題的例題2:
題目源碼:
<?php if ($_SERVER["REQUEST_METHOD"] == “POST”) { extract($_POST); if ($pass == $thepassword_123) { <div class=”alert alert-success”> <code><?php echo $theflag; ?></code> </div> } } ?>
題目分析:
題目要求使用POST提交數據,extract($_POST)會將POST的數據中的鍵名和鍵值轉換為相應的變量名和變量值,利用這個覆蓋$pass和$thepassword_123變量的值,從而滿足$pass == $thepassword_123這個條件。
解題方法:
使用POST請求提交pass=&thepassword_123=, 然后extract()會將接收到的數據將$pass和$thepassword_123變量的值覆蓋為空,便滿足條件了。
最終PAYLOAD:
POST DATA:pass=&thepassword_123=
0×03 parse_str函數導致的變量覆蓋問題
parse_str() 函數用於把查詢字符串解析到變量中,如果沒有array 參數,則由該函數設置的變量將覆蓋已存在的同名變量。
語法:parse_str(string,array)
parse_str() 用法參考:http://php.net/parse_str
CTF中parse_str()導致的變量覆蓋問題的例題1:
題目源碼:
<?php error_reporting(0); if (empty($_GET['id'])) { show_source(__FILE__); die(); } else { include (‘flag.php’); $a = “www.OPENCTF.com $id = $_GET['id']; @parse_str($id); if ($a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)) { echo $flag; } else { exit(‘其實很簡單其實並不難!’); } } ?>
題目分析:
首先要求使用GET提交id參數,然后parse_str($id)對id參數的數據進行處理,再使用判斷$a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)的結果是否為真,為真就返回flag,md5(‘QNKCDZO’)的結果是0e830400451993494058024219903391由於此次要滿足$a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)所以要利用php弱語言特性,0e123會被當做科學計數法,0 * 10 x 123。所以需要找到一個字符串md5后的結果是0e開頭后面都是數字的,如,240610708,s878926199a
PHP處理0e開頭md5哈希字符串缺陷/bug 參考:http://www.cnblogs.com/Primzahl/p/6018158.html
解題方法:
使用GET請求id=a[0]=240610708,這樣會將a[0]的值覆蓋為240610708,然后經過md5后得到0e462097431906509019562988736854與md5(‘QNKCDZO’)的結果0e830400451993494058024219903391比較都是0 所以相等,滿足條件,得打flag。
最終PAYLOAD:
GET DATA:
?id=a[0]=s878926199a
or
?id=a[0]=240610708
0×04 小總結
變量覆蓋漏洞在PHP代碼審計中會以比較隱晦的方式存在,所以需要更加仔細的閱讀源碼找出漏洞的點,在CTF里面經常是以比較直接方式展示,所以可以先通過學習CTF各種變量覆蓋的題目,然后掌握后再去審計cms,這樣可以更加通透的理解掌握和挖掘變量覆蓋漏洞。
---恢復內容結束---