DVWA練習二、Command Injection
Command Injection,即命令注入,是指通過提交惡意構造的引數破壞命令語句結構,從而達到執行惡意命令的目的。PHP命令注入攻擊漏洞是PHP應用程式中常見的指令碼漏洞之一,國內著名的Web應用程式Discuz!、DedeCMS等都曾經存在過該型別漏洞
php中常用的系統函式如:system()、exec()、shell_exec、passthru、popen、proc_popen等
命令注入判斷流程:
一、是否呼叫系統命令
二、函式或函式的引數是否可控
三、是否拼接注入命令
首先要知道命令注入的流程,確定可控欄位
確定命令語句、windows下如何連線兩條命令
- &,&&,|,||命令拼接符
A&B:簡單的拼接,AB之間無制約關係,A無論是夠成功都會執行B
A&&B:A執行成功然後才會執行B
A|B:A的輸出作為B的輸入
A||B:A執行失敗,然後才會執行B
常見漏洞:
bash破殼漏洞(CVE-2014-6271)如果我們能夠控制執行的bash的環境變數,就可以通過破殼漏洞來執行任意程式碼
呼叫第三方元件存在程式碼執行漏洞:
可以看到,Impossible級別的程式碼加入了Anti-CSRF token,同時對引數ip進行了嚴格的限制,只有諸如“數字.數字.數字.數字”的輸入才會被接收執行,因此不存在命令注入漏洞。
很經典的就是WordPress中,可以選擇使用ImageMagick這個常用的圖片處理元件,對使用者上傳的圖片進行處理(預設是ImageMagick庫),造成命令執行。
另外java中的命令執行漏洞:struts2/ElasticsearchGroovy等
漏洞危害:
繼承web服務程式的許可權,執行系統命令
繼承web服務程式的許可權,讀寫檔案
反彈shell
控制整個網站甚至伺服器
可以進一步內網滲透
下面對不同級別的程式碼進行分析。
Low
伺服器端核心程式碼
<?php if( isset( $_POST[ 'Submit' ] ) ) { // Get input $target = $_REQUEST[ 'ip' ]; // Determine OS and execute the ping command. if( stristr( php_uname( 's' ), 'Windows NT' ) ) { // Windows $cmd = shell_exec( 'ping ' . $target ); } else { // *nix $cmd = shell_exec( 'ping -c 4 ' . $target ); } // Feedback for the end user echo "<pre>{$cmd}</pre>"; } ?>
相關函式介紹
stristr(string,search,before_search)
stristr函式搜尋字串在另一字串中的第一次出現,返回字串的剩餘部分(從匹配點),如果未找到所搜尋的字串,則返回FALSE。引數string規定被搜尋的字串,引數search規定要搜尋的字串(如果該引數是數字,則搜尋匹配該數字對應的ASCII值的字元),可選引數before_true為布林型,預設為“false”,如果設定為“true”,函式將返回search引數第一次出現之前的字串部分。
php_uname函式
(PHP 4>= 4.0.2, PHP 5, PHP 7)
php_uname — 返回執行 PHP 的系統的有關資訊
stringphp_uname ([ string$mode
="a" ] )
php_uname()返回了執行 PHP 的作業系統的描述。這和 最頂端上輸出的是同一個字串。如果僅僅要獲取作業系統的名稱。可以考慮使用常量PHP_OS
,不過要注意該常量會包含 PHP 構建(built)時的作業系統名。
在一些舊的 UNIX 平臺,它有可能無法檢測到當前系統的資訊,然後會還原顯示成構建 PHP 時的系統資訊。這僅僅在你的 uname() 函式庫不存在或無法執行時發生。
引數 ¶
mode
mode
是單個字元,用於定義要返回什麼資訊:
o 'a':此為預設。包含序列"s n r v m"裡的所有模式。
o 's':作業系統名稱。例如:FreeBSD。
o 'n':主機名。例如:localhost.example.com。
o 'r':版本名稱,例如:5.1.2-RELEASE。
o 'v':版本資訊。作業系統之間有很大的不同。
o 'm':機器型別。例如:i386。
o 漏洞利用
o window和linux系統都可以用&&來執行多條命令
o 127.0.0.1&&netuser
Linux下輸入127.0.0.1&&cat /etc/shadow甚至可以讀取shadow檔案,可見危害之大。
Medium
伺服器端核心程式碼
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = $_REQUEST[ 'ip' ];
// Set blacklist
$substitutions = array(
'&&' => '',
';' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
可以看到,相比Low級別的程式碼,伺服器端對ip引數做了一定過濾,即把”&&”、”;”刪除,本質上採用的是黑名單機制,因此依舊存在安全問題。
漏洞利用
1、127.0.0.1&netuser
因為被過濾的只有”&&”與”;”,所以”&”不會受影響。
2、由於使用的是str_replace把”&&”、”;”替換為空字元,因此可以採用以下方式繞過:
127.0.0.1&;&ipconfig
這是因為”127.0.0.1&;&ipconfig”中的”;”會被替換為空字元,這樣一來就變成了”127.0.0.1&&ipconfig”,會成功執行。
High
伺服器端核心程式碼
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = trim($_REQUEST[ 'ip' ]);
// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);
// Remove any of the charactars in the array (blacklist).
$target = str_replace( array_keys( $substitutions ), $substitutions, $target );
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
?>
相比Medium級別的程式碼,High級別的程式碼進一步完善了黑名單,但由於黑名單機制的侷限性,我們依然可以繞過。
漏洞利用
黑名單看似過濾了所有的非法字元,但仔細觀察到是把”| ”(注意這裡|後有一個空格)替換為空字元,於是”|”成了“漏網之魚”。
127.0.0.1|net user
Impossible
伺服器端核心程式碼
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// Get input
$target = $_REQUEST[ 'ip' ];
$target = stripslashes( $target );
// Split the IP into 4 octects
$octet = explode( ".", $target );
// Check IF each octet is an integer
if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
// If all 4 octets are int's put the IP back together.
$target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
// Determine OS and execute the ping command.
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
// Windows
$cmd = shell_exec( 'ping ' . $target );
}
else {
// *nix
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
// Feedback for the end user
echo "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
相關函式介紹
stripslashes(string)
stripslashes函式會刪除字串string中的反斜槓,返回已剝離反斜槓的字串。
explode(separator,string,limit)
把字串打散為陣列,返回字串的陣列。引數separator規定在哪裡分割字串,引數string是要分割的字串,可選引數limit規定所返回的陣列元素的數目。
is_numeric(string)
檢測string是否為數字或數字字串,如果是返回TRUE,否則返回FALSE。
可以看到,Impossible級別的程式碼加入了Anti-CSRF token,同時對引數ip進行了嚴格的限制,只有諸如“數字.數字.數字.數字”的輸入才會被接收執行,因此不存在命令注入漏洞。