Loading... # 1、目标 ![图片.png](http://47.117.131.13/usr/uploads/2022/04/1393603564.png) # 2、级别 Low 关键代码: ```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>"; } ?> ``` 关键点: * 接收并通过 shell_exec 执行指令,具体为 ping ip 参数值 * 命令结果输出到页面中 php_uname 函数通过不同的参数控制,返回相应信息,其中 's' 参数返回系统平台信息。测试在 Windows 系统部署的 DVWA 网站,php_uname('s') 返回值为 "Windows NT": ![图片.png](http://47.117.131.13/usr/uploads/2022/04/4155363301.png) 后续拼接 ping + ip参数值,通过 shell_exec 执行指令。一次正常的命令执行过程,进程链如下。可见 shell_exec 函数是通过 cmd.exe 进程来完成命令的: ![图片.png](http://47.117.131.13/usr/uploads/2022/04/3481112813.png) 结果页面: ![图片.png](http://47.117.131.13/usr/uploads/2022/04/3257437116.png) 然而有一个问题,Windows 平台 cmd.exe 中的 shell 命令可以通过 `&、&&、|、||` 等连接符同时执行多个指令: ``` & :连接两条命令,且无论前面的命令是否成功,都执行后面的命令 && :连接两条命令,且只有前面的命令执行成功,才执行后面的命令 | :管道符,将前一条命令的输出当作后一条命令的参数 || :&& 的反向选项,连接两条命令且只有前面的命令执行失败,才执行后面的命令 ``` 因此我们构造输入查看系统信息。但有一点值得研究的,响应的数据中,中文都乱码了,查看十六进制数据,根本就不是相应中文字符的编码值,而是一些错误重复的不可打印字符,尚待研究: ``` 192.168.222.138 & systeminfo ``` ![图片.png](http://47.117.131.13/usr/uploads/2022/04/3144857052.png) 构造输入查看进程列表: ``` 192.168.222.138 & tasklist ``` ![图片.png](http://47.117.131.13/usr/uploads/2022/04/1524438153.png) 构造输入通过防火墙配置: ``` 192.168.222.138 & netsh firewall show config ``` ![图片.png](http://47.117.131.13/usr/uploads/2022/04/3354764350.png) 由此,实际上所有的 cmd 命令都能执行,这是一个利用条件十分低的远程代码执行漏洞。 同时,因为命令执行结果还会输出到页面中,所以这里还存在反射型 XSS 漏洞。构造命令为: ``` 192.168.222.138 & echo "<script>alert(document.cookie)</script>" ``` 页面成功弹出 cookie: ![图片.png](http://47.117.131.13/usr/uploads/2022/04/1368558582.png) # 3、级别 Medium 关键代码: ```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>"; } ?> ``` 相较于级别 Low 改动为: * 设置黑名单,过滤 && 及 ; 子串 然而上面说到我们至少可以通过四种连接符连接 cmd 命令,在级别 Low 中我们使用了 & 连接符,而这里只过滤了 &&,根本无用。 # 4、级别 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>"; } ?> ``` 相较于级别 Medium 改动为: * 扩展黑名单列表 这里代码首先将 ip 参数首尾的空字符移除,然后使用扩展的黑名单进行过滤,思路还是在黑名单这里。看起来似乎我们在级别 Low 中列出的四个连接符都在黑名单中了,然而细心的话可以发现,黑名单中有一项是 `| ` ,这里 | 后面还跟着一个空格。 str_replace 对子串的搜索是严格进行的,所以它这里必定是将 `| ` 子串给过滤。然而在 cmd 的语法中,| 前后不一定要加空格,因此我们可以构造如下攻击,成功绕过: ``` 192.168.222.138 |ipconfig ``` ![图片.png](http://47.117.131.13/usr/uploads/2022/04/852346227.png) 另一个角度,除了空格,tab 制表键也可以充当空白字符,因此我们还可以构造如下攻击: ``` 192.168.222.138 | ipconfig ``` 观察后台 ip 参数,| 后面跟的是 \t 制表键: ![图片.png](http://47.117.131.13/usr/uploads/2022/04/515667453.png) 同样绕过成功。 # 5、级别 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(); ?> ``` 相较于级别 High 改动为: * 使用 explode() 将 ip 参数以 . 为分隔符分割 * 使用 is_numeric() 检查分割的每一部分是否为数字,且检验分割后的数组长度 这里根据具体的业务逻辑,即 ping ip参数,来严格校验 ip 参数的是否为指定的数字串形式,不存在继续绕过的可能。 # 6、总结 从级别 Low 到级别 Impossible,对抗命令执行的思路总结为: * 设置黑名单,并确保黑名单的完整性 * 根据具体的业务逻辑,对输入数据进行相应的校验,如格式、长度、数量等 最后修改:2022 年 04 月 29 日 01 : 16 AM © 允许规范转载