前言
變量覆蓋指的是用我們自定義的參數值替換程序原有的變量值,一般變量覆蓋漏洞需要結合程序的其它功能來實現完整的攻擊。
參考文章
經常導致變量覆蓋漏洞場景有:$$,extract()函數,parse_str()函數等.
正文
**$$**變量覆蓋問題
簡介
在PHP中$$表示的是一個可變變量獲取了一個普通變量的值作爲這個可變變量的變量名。
<?php
$c='hello';
$$c='world';
echo $c;
echo $$c;
?>
在這個例子中,c當成了當成了變量名的一個變量,上面的輸出結果是:
helloworld //$c=hello $$c=world 這樣看起來更好一點
漏洞產生
使用foreach來遍歷數組中的值,然後再將獲取到的數組鍵名作爲變量,數組中的鍵值作爲變量的值。因此就產生了變量覆蓋漏洞。
<?php
$ary=array('a','b','c','c','e');
foreach($ary as $key=>$value){ //$ary的鍵名賦給$key,鍵值賦給$value
$$key=$value; //把鍵值賦給$$key
}
print_r($key); //輸出4
print_r($value); //輸出e
print_r($$key); //輸出e
?>
換成$_GET和$_POST是一樣的
<?php
$id=5;
foreach ($_GET as $key => $value) {
$$key = $value;
}
echo $a;
?>
如果get傳的是?id=1,那麼,經過**$$key = id=1,覆蓋掉原來的
$id=5`.
漏洞復現
<?php
include "flag.php";
$_403 = "AccessDenied";
$_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);
else
{
echo "This is your flag : ". $flag . "\n";
die($_200);
}
?>
從源碼文件中可以看出,要想獲得flag,你必須知道原來的flag,那是不可能的,
這個時候我們就可以利用變量覆蓋漏洞。
foreach ($_GET as $key => $value) $$key = $$value
當get上傳_200=flag,經過value的處理後,就會變爲flag,這樣就會將原來的flag的值。
foreach ($_POST as $key => $value)
$$key = $value;
這裏的KaTeX parse error: Can't use function '$' in math mode at position 7: key = $̲value,將數組的值當做這個key的值。例如post方法傳入flag=abc,則處理後變成$flag=abc。這樣就造成將我們需要獲取的flag的值給覆蓋掉了。不過,當通過get傳入_200=flag之後,flag的值將會賦給_200,這時候隨便post什麼都可以,反正flag已經在_200=flag裏面了。
所以payload:
get:_200=flag
post:flag=aaaaa
復現成功。
extract()函數
定義和用法
extract() 函數從數組中將變量導入到當前的符號表。該函數使用數組鍵名作爲變量名,使用數組鍵值作爲變量值。針對數組中的每個元素,將在當前符號表中創建對應的一個變量。該函數返回成功設置的變量數目。具體信息
語法
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 = "Original";
$my_array = array("a" => "Cat","b" => "Dog", "c" => "Horse");
extract($my_array);
echo "\$a = $a; \$b = $b; \$c = $c";
?>
輸出結果爲:
$a = Cat; $b = Dog; $c = Horse
這樣就可以實現變量覆蓋。
漏洞復現
<?php
include('flag.php')
$test='*************';
extract($_GET);
if(isset($gift)){
$content=trim($test);
if($gift==$content){
echo 'flag is',$flag;
}
echo 'error';
}
變量content的值是通過讀取變量test的值獲取到的。如果兩個變量相等輸出flag。如果不相等,輸出錯誤。但是我們並不知道test的值是什麼?所以我們使用變量覆蓋漏洞,重新給test賦值。
例如:test=’a’,有與之同名的變量KaTeX parse error: Expected 'EOF', got '\*' at position 9: test = '\̲*̲\*\*\*\*\*\*';,…gift=$content。就可以獲得flag。
構造我們的payload:
Get方法傳值:?gift=a&test=a.
今天就寫這兩個把,肚子疼的厲害。
Parse_str()
分析
Parse_str()–將字符串解析成多個變量
註釋:
parse_str(string,array)
如果未設置 array 參數,由該函數設置的變量將覆蓋已存在的同名變量。
php.ini 文件中的 magic_quotes_gpc 設置影響該函數的輸出。如果已啓用,那麼在 parse_str() 解析之前,變量會被 addslashes() 轉換。
例子:
<?php
parse_str("name=Bill&age=60");
echo $name."<br>";
echo $age;
?>
輸入:
Bill
60
漏洞復現
<?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);//把查詢字符串解析到變量中,沒有使用array選項,若有同名變量,將原來的覆蓋。這裏明顯將原來的$a的值給覆蓋掉。
if($a[0]!='QNKCDZO'&&md5($a[0])==md5('QNKCDZO')){
//判斷$a[0]的值不是QNKCDZO並且$a[0]的MD5值要和QNKCDZO的MD5值相同,很難找出這樣的字符串。
echo $flag;
}else{
exit('其實很簡單,其實並不難');
}
}
這裏最重要的就是處理if($a[0]!='QNKCDZO'&&md5($a[0])==md5('QNKCDZO'))
這個問題,我們要找到一個字符串值和QNKCDZO不同,但是字符串的MD5值和QNKCDZO的MD5值相同。
這裏要利用PHP處理0e開頭的MD5哈希字符串的漏洞,PHP在處理哈希字符串時,會利用”!=”或”==”來對哈希值進行比較,它把每一個以”0E”開頭的哈希值都解釋爲0,這樣的話兩個不同字符串也就可以相等了,都等於0就行。以0e開頭的這樣的
而QNKCDZO的MD5加密後的值是0e830400451993494058024219903391
,所以再找一個字符串,值和QNKCDZO不要,但MD5加密後的值是0e開頭的字符串就行
構造payload:
?id=a[]=
s1836677006a`