前言:
這周來學習下命令執行漏洞;命令執行漏洞是用戶通過瀏覽器在遠程服務器上執行任意系統命令,與代碼執行漏洞沒有太大的區別,不過我們在學習時還是要區分不同的概念。關於代碼執行漏洞會在下篇博客中詳述。
一、什麼是命令執行漏洞:
應用 有時需要調用一些執行系統命令的函數,如PHP中的system
、exec
、shell_exec
、passthru
、popen
、proc_popen
等,當用戶能控制這些函數中的參數時,就可以將惡意系統命令拼接到正常命令中,從而造成命令執行攻擊,這就是命令執行漏洞。
二、命令執行漏洞產生原因:
- 開發人員沒有對特殊函數入口做過濾,導致用戶可以提交惡意代碼並提交服務端執行。
- Web服務器沒有過濾危險函數導致命令執行漏洞攻擊成功。
三、PHP中的危險函數:
system
:成功則返回命令輸出的最後一行,失敗則返回FALSE。
exec
:命令執行結果的最後一行內容。
shell_exec
:命令執行的輸出。如果執行過程中發生錯誤或者進程不產生輸出,則返回NULL。
passthru
:執行外部程序並且顯示原始輸出。
eval
:將輸入的字符串參數當做PHP程序代碼來執行。
assert
preg_replace
call_user_func
四、命令執行漏洞的危害:
- 任意執行系統命令
- 惡意木馬被種植
- 掛馬、釣魚
- 敏感信息泄露
常用的命令連接符:
在windows和linux都支持,如果程序沒有進行過濾,那麼我們可以通過連接符來執行多條命令。
A&&B 先執行A,執行成功後執行B,否則不執行B
A|B 只執行B
A&B 先執行A,不管是否成功,都會執行B
A;B 先執行A,再執行B
A&B 簡單拼接,A B之間無制約關係
瞭解了這些知識後,下面就在本地搭建的DVWA中練習下。
DVWA—Command Injection(命令注入)
先從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>";
}
?>
可以看到,low級別的代碼接收了用戶輸入的ip,服務器通過判斷操作系統執行不同ping命令。但是這裏對用戶輸入的ip並沒有進行任何的過濾,所以存在可利用的命令執行漏洞。
用&&
來執行多條命令,構造payload:127.0.0.1&&net user
執行成功;(如果存在亂碼問題,把DVWA\dvwa\includes
目錄下的dvwaPage.inc.php
文件中所有的”charset=utf-8”
,全部替換修改爲”charset=gb2312”
即可)
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>";
}
?>
可以看到,medium級別相較於low級別的代碼 只增加了對 &&
和 ;
的過濾,繞過也很簡單,不用 &&
,直接用 &
不就好起來了嗎!
&&
和&
的區別在於&&
是執行完前面的命令然後執行後面的命令,&
是不管前面的命令是否值執行,後面的都執行。
構造poyload: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>";
}
?>
相比於前面兩個等級的,high等級的黑名單更完善了,但是由我們可以看出來他只是過濾掉了“| ”
,如果用|
後不跟空格就可以繞過過濾。
構造payload:127.0.0.1|dir
繞過成功。
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)
: 該函數會刪除字符串string中的反斜槓,返回已剝離反斜槓的字符串。
explode(separator,string,limit)
: 該函數把字符串打散爲數組,返回字符串的數組。參數separator規定在哪裏分割字符串,參數string是要分割的字符串,可選參數limit規定所返回的數組元素的數目。
is_numeric(string)
: 該檢測string是否爲數字或數字字符串,如果是返回TRUE,否則返回FALSE。
可以看到 Impossible級別的代碼加入了Anti-CSRF token
,至於什麼是Anti-CSRF token
請點擊 鏈接 查看;同時對參數ip進行了嚴格的限制,只有諸如“數字.數字.數字.數字”的輸入纔會被接收執行,因此不存在命令注入漏洞。
好了,關於命令執行漏洞這次就先總結到這,下篇會更代碼執行漏洞的知識。(●’◡’●)
參考博客:
https://blog.csdn.net/qq_36119192/article/details/82917250
https://blog.csdn.net/jpygx123/article/details/83276533