CTF——PHP审计——变量覆盖
一,變量覆蓋漏洞
參考:https://www.cnblogs.com/xiaozi/p/7768580.html
通常將可以用自定義的參數值替換原有變量值的情況稱為變量覆蓋漏洞。經常導致變量覆蓋漏洞場景有:
1,$$使用不當,
2,extract()函數使用不當,
3,parse_str()函數使用不當,
4,import_request_variables()使用不當,
5,開啟了全局變量注冊等。?
?
1,$$使用不當
$key='text'; $$key =200? 的意思是? 將 $key 的值作為變量? 并賦值為 200
$$?導致的變量覆蓋問題在CTF代碼審計題目中經常在foreach中出現,如以下的示例代碼,使用foreach來遍歷數組中的值,然后再將獲取到的數組鍵名作為變量,數組中的鍵值作為變量的值。因此就產生了變量覆蓋漏洞。請求?name=test?會將$name的值覆蓋,變為test。
<?php$name=’thinking’;foreach ($_GET as $key => $value)$$key = $value;var_dump($key);var_dump($value);var_dump($$key);echo $name;?> //?name=test //output:string(4) “name” string(4) “test” string(4) “test” test例題一:
<?phpinclude “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);15.echo “This is your flag : “. $flag . “\n”;16.die($_200);17.?>題目分析:?
源碼包含了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
2,extract()函數使用不當
extract(array,extract_rules,prefix)定義和用法
extract() 函數從數組中將變量導入到當前的符號表。
該函數使用數組鍵名作為變量名,使用數組鍵值作為變量值。針對數組中的每個元素,將在當前符號表中創建對應的一個變量。
第二個參數?type?用于指定當某個變量已經存在,而數組中又有同名元素時,extract() 函數如何對待這樣的沖突。
該函數返回成功導入到符號表中的變量數目。
<?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 $flag='xxx'; extract($_GET); if(isset($shiyan)) { $content=trim(file_get_contents($flag)); if($shiyan==$content) { echo'flag{xxx}'; }else { echo'Oh.no'; } } ?>題目分析:URL 通過 get 的方式傳參,傳輸的數據以數組的形式被封裝在$_GET 中;extract()函數從數組中將變量導入到當前的符號表,該函數使用數組鍵名作為變量名,使用數組鍵值作為變量值;isset()函數判斷是否存在變量$shiyan;trim()函數移除字符串兩側的空白字符或其他預定義字符 ,這里是移除字符串兩側的空格;file_get_contents()函數將整個文件讀入一個字符串;假如$shiyan的值等于文件的內容($content)時,就打印出flag
同時傳入 shiyan 和 flag 了使? 讀取文件錯誤 就會試 $content 的值為 布爾的0
?shiyan=&flag=?
例題二:
<?php if ($_SERVER["REQUEST_METHOD"] == “POST”) { ?><?phpextract($_POST);if ($pass == $thepassword_123) { ?><div class=”alert alert-success”><code><?php echo $theflag; ?></code></div><?php } ?><?php } ?>題目分析:?
題目要求使用POST提交數據,extract($_POST)會將POST的數據中的鍵名和鍵值轉換為相應的變量名和變量值,利用這個覆蓋$pass和$thepassword_123變量的值,從而滿足$pass == $thepassword_123這個條件。
解題方法:?
使用POST請求提交pass=&thepassword_123=, 然后extract()會將接收到的數據將$pass和$thepassword_123變量的值覆蓋為空,便滿足條件了。
最終PAYLOAD:?
POST DATA:pass=&thepassword_123=
3,parse_str()函數使用不當
parse_str() 函數把查詢字符串解析到變量中。
注釋:如果未設置?array?參數,則由該函數設置的變量將覆蓋已存在的同名變量。
parse_str(string,array)| string | 必需。規定要解析的字符串。 |
| array | 可選。規定存儲變量的數組的名稱。該參數指示變量將被存儲到數組中。 |
例題一:
<?phperror_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
4,import_request_variables()使用不當
import_request_variables()?函數將 get/post/cookie 變量導入到全局作用域中。該函數在最新版本的 php 中已經不支持
import_request_variables()?函數將 get/post/cookie 變量導入到全局作用域中。如果你禁止了 register_globals,但又想用到一些全局變量,那么此函數就很有用。
bool import_request_variables ( string $types [, string $prefix ] )-
$types:指定需要導入的變量,可以〖can〗用字母 g、p 和 c 分別表示 get、post 和 cookie,這些字母不區分大小寫,所以你可以〖can〗使用 g 、 p 和 c 的任何組合。post 包含了通過 post 方法上傳的文件信息。注意這些字母的順序,當使用 gp 時,post 變量將使用相同的名字覆蓋 get 變量。任何 gpc 以外的字母都將被忽略。
-
$prefix: 變量名的前綴,置于所有〖all〗被導入到全局作用域的變量之前。所以如果你有個名為 userid 的 get 變量,同時提供了 pref_ 作為前綴,那么你將獲得一個名為 $pref_userid 的全局變量。雖然 prefix 參數是可選的,但如果不指定前綴,或者指定一個空字符串作為前綴,你將獲得一個 e_notice 級別的錯誤。
例題一:
<?php $auth='0'; import_request_variables('G'); if($auth== 1){ echo"private!"; }else{ echo"public!"; } ?>get auth=1時,網頁上會輸出private!?
?
例題二:
<?php $yml = "happy"; echo "out0:".$yml; echo "<br>"; import_request_variables('P'); echo "out1:".$yml; ?>?
5,全局變量注冊開關問題
register_globals的意思就是注冊為全局變量,所以當On的時候,傳遞過來的值會被直接的注冊為全局變量直接使用,而Off的時候,我們需要到特定的數組里去得到它。
代碼示例1:
<?php //?id=1 echo "Register_globals: ".(int)ini_get("register_globals")."<br/>"; echo '$_GET["id"] :'.$_GET['id']."<br/>"; echo '$id :'.$id; ?>當register_globals=Off的時候,下一個程序接收的時候應該用$_GET['id']來接受傳遞過來的值;
當register_globals=On的時候,下一個程序可以直接使用$id來接受值,也可以用$_GET['id']來接受傳遞過來的值。
tips:如果上面的代碼中,已經對變量$id賦了初始值,比如$id=0,那么即使在URL中有/test.php?id=1,也不會將變量覆蓋,id值為0
代碼示例2:
<?php echo "Register_globals: ".(int)ini_get("register_globals")."<br/>"; if (ini_get('register_globals')) foreach($_REQUEST as $k=>$v) unset(${$k}); print $a."<br/>"; print $_GET[b]; ?>在register_globals=ON時,
提交/test.php?a=1&b=2 ,?變量$a未初始化,$_GET[b]=2
提交/test.php??GLOBALS[a]=1&b=2,$a=1,$_GET[b]=2
tips: 從 PHP ? 4.2.0 版開始配置文件中 PHP 指令 register_globals 的默認值從 on 改為 off 了,自 PHP 5.3.0 起廢棄并將自 PHP 5.4.0 起移除。
?
?
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的CTF——PHP审计——变量覆盖的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bugku- web -login3
- 下一篇: 动态规划算法php,php算法学习之动态