CTF之php變量覆蓋漏洞

什么是變量覆蓋漏洞

自定義的參數值替換原有變量值的情況稱為變量覆蓋漏洞

經常導致變量覆蓋漏洞場景有:$$使用不當,extract()函數使用不當,parse_str()函數使用不當,import_request_variables()使用不當,開啟了全局變量注冊等。


$$導致的變量覆蓋問題

1.$$介紹
$$這種寫法稱為可變變量
一個可變變量獲取了一個普通變量的值作為這個可變變量的變量名。

<?php
$a = "hello";
echo "$a";              //輸出hello
$a="world";
echo "$a";              //輸出hello
echo "$$a";            //輸出word
echo "$a ${$a}";    //輸出hello world
echo "$a $hello";  //輸出hello world
?>

2.漏洞產生
使用foreach來遍歷數組中的值,然后再將獲取到的數組鍵名作為變量,數組中的鍵值作為變量的值。因此就產生了變量覆蓋漏洞。
舉例

<?php
foreach ($_GET as $key => $value) {
${$key} = $value;
}
echo $a;
?>

get得到的數據$key和$value,關鍵第3行,${$key}用get傳進來的$key做為新的變量,將get傳進來的$value賦值給它。
get ?a=1 第3行回解析為$a=1。就造成了變量覆蓋。
3.漏洞重現

include “flag.php”;                                                //包含并運行指定文件
$_403 = “Access Denied”;
$_200 = “Welcome Admin”;
if ($_SERVER["REQUEST_METHOD"] != “POST”)      //返回post的值
**die**(“BugsBunnyCTF is here :p…”);
if ( !isset($_POST["flag"]) )              //檢查是否設置
**die**($_403);
//echo "$_GET";
foreach ($_GET as $key => $value)
**$$key = $$value;                      //后面分析**
//echo "$key";
//echo "$$key  $_200";
//echo "$value";
//echo "$$value  $flag";
foreach ($_POST as $key => $value)
**$$key = $value;**
**//echo "$key";**
//echo "$$key";
//echo "$value";
if ( $_POST["flag"] !== $flag )
**die**($_403);
else
{
echo “This is your flag : “. $flag . “\n”;
die($_200);
}
**?>**

題目分析
需要滿足三個if 才能夠輸出flag,很明顯$flag在flag.php文件里。
兩個foreach 都使用了$$ 修改了$flag變量 我們沒辦法直接通過echo “This is your flag : “. $flag . “\n”;得到flag。所以利用die($_200),在$_200種儲存$flag的值,從而得到flag
解題思路
get ?_200=flag post flag=aaaaaaaaaaaaaa
1.)foreach ($_GET as $key => $value) $$key = $$value;
將_200=flag 變為了 $_200=$flag 此時這個$flag=我們想要的flag
2.) foreach ($_POST as $key => $value)
$$key = $value;
將post過去的flag=aaaaaaaaaaa變為$flag=aaaaaaaaaaaaaaaa


flag=b~


extract()函數使用不當

1.extract()函數介紹
extract() 函數從數組中將變量導入到當前的符號表。
該函數使用數組鍵名作為變量名,使用數組鍵值作為變量值。針對數組中的每個元素,將在當前符號表中創建對應的一個變量。
該函數返回成功設置的變量數目。
2.語法
extract(array,extract_rules,prefix)
參數 描述
array必需。 規定要使用的數組。
extract_rules可選。 extract() 函數將檢查每個鍵名是否為合法的變量名,同時也檢查和符號表中已存在的變量名是否沖突。對不合法和沖突的鍵名的處理將根據此參數決定。
可能的值:
EXTR_OVERWRITE - 默認。如果有沖突,則覆蓋已有的變量。
EXTR_SKIP - 如果有沖突,不覆蓋已有的變量。
EXTR_PREFIX_SAME - 如果有沖突,在變量名前加上前綴 prefix。
EXTR_PREFIX_ALL - 給所有變量名加上前綴 prefix。
EXTR_PREFIX_INVALID -僅在不合法或數字變量名前加上前綴 prefix。
EXTR_IF_EXISTS - 僅在當前符號表中已有同名變量時,覆蓋它們的值。其它的都不處理。
EXTR_PREFIX_IF_EXISTS - 僅在當前符號表中已有同名變量時,建立附加了前綴的變量名,其它的都不處理。
EXTR_REFS - 將變量作為引用提取。導入的變量仍然引用了數組參數的值。

prefix可選。 如果 extract_rules 參數的值是 EXTR_PREFIX_SAME、EXTR_PREFIX_ALL、 EXTR_PREFIX_INVALID 或 EXTR_PREFIX_IF_EXISTS,則 prefix 是必需的。
該參數規定了前綴。前綴和數組鍵名之間會自動加上一個下劃線。

從以上說明我們可以看到第一個參數是必須的,會不會導致變量覆蓋漏洞由第二個參數決定,該函數有三種情況會覆蓋已有變量。
舉例

<?php
$a = 1;    //原變量值為1
$b = array('a' => '3');
extract($b);    //經過extract()函數對$b處理后
echo $a;    //輸出結果為3
?>

3.漏洞重現

"extract($_GET);
if(isset($bdctf))
{
$content=trim(file_get_contents($flag));//file_get_contents—將整個文件讀入一個字符串
if($bdctf==$content)                             //trim—去除字符串首尾處的空白字符(或者其他字符)
{ echo'bdctf{**********}'; }
else
{ echo'這不是藍盾的密碼啊'; }
}"

題目分析
題目使用了extract($_GET)接收了GET請求中的數據,并將鍵名和鍵值轉換為變量名和變量的值,然后再進行兩個if 的條件判斷,所以可以使用GET提交參數和值,利用extract()對變量進行覆蓋,從而滿足各個條件。

解題思路
if($bdctf==$content) 輸出flag
利用extract($_GET)漏洞,使$bdctf與$content都為空或者不存在就滿足 $bdctf==$content
get ?flag=&bdctf= 得到flag


parse_str()函數使用不當

1.parse_str()函數介紹
parse_str() 函數把查詢字符串解析到變量中。
注釋:如果未設置 array 參數,由該函數設置的變量將覆蓋已存在的同名變量。
注釋:php.ini 文件中的 magic_quotes_gpc 設置影響該函數的輸出。如果已啟用,那么在 parse_str() 解析之前,變量會被 addslashes() 轉換。
parse_str函數的作用就是解析字符串并注冊成變量,在注冊變量之前不會驗證當前變量是否存在,所以直接覆蓋掉已有變量
2.語法
parse_str(string,array)**
參數 描述
string必需。 規定要解析的字符串。
array可選。 規定存儲變量的數組名稱。該參數指示變量存儲到數組中。
舉例

<?php
$a = 1;                  //原變量值為1
parse_str('a=2');   //經過parse_str()函數后注冊變量$a,重新賦值
print_r($b);          //輸出結果為2
?>

漏洞重現

<?php
error_reporting(0);
if(
empty($_GET['id'])) {                    //empty()檢查是否為空
show_source(__FILE__);            //highlight_file—語法高亮一個文件
die();                                          //等同于exit—輸出一個消息并且退出當前腳本
} 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(‘其實很簡單其實并不難!’);
}
}
?>

題目分析

**$a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)**
**PHP Hash比較存在缺陷**
**md5(‘QNKCDZO’)**的結果是**0e830400451993494058024219903391**

PHP在處理哈希字符串時,會利用”!=”或”==”來對哈希值進行比較,它把每一個以”0E”開頭的哈希值都解釋為0,所以如果兩個不同的密碼經過哈希以后,其哈希值都是以”0E”開頭的,那么PHP將會認為他們相同,都是0。詳細參照 http://www.freebuf.com/news/67007.html
解題思路
md5(s878926199a)=0e545993274517709034328855841020 php解析為0
php處理哈希字符串 http://www.cnblogs.com/Primzahl/p/6018158.html
使用get請求?id=a[0]=s878926199a 得到flag


import_request_variables()使用不當

1.import_request_variables()函數介紹
import_request_variables—將 GET/POST/Cookie 變量導入到全局作用域中
import_request_variables()函數就是把GET、POST、COOKIE的參數注冊成變量,用在register_globals被禁止的時候

2.語法
bool import_request_variables(string$types[,string$prefix] )
$type代表要注冊的變量,G代表GET,P代表POST,C代表COOKIE,第二個參數為要注冊變量的前綴
舉例

<?php
$auth='0';
import_request_variables('G');
if($auth== 1){
echo"private!";
}else{
echo"public!";
}
?>

get auth=1時,網頁上會輸出private!
import_request_variables('G')指定導入GET請求中的變量,從而導致變量覆蓋

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容