DVWA代码审计之命令注入 原理 在操作系统中,&,&&,|,||都可以作为命令连接符使用,用户通过浏览器提交执行命令,由于服务器端没有对执行函数进行过滤,从而造成可以执行危险命令。
PHP的主要命令执行函数有:system、exec、passthru、shell_exec。反引号同样能够执行命令,但不是函数。
连接符的使用 cmd1 & cmd2:不管cmd1执行成功与否,都执行cmd2,即cmd1和cmd2都执行。
cmd1 && cmd2:先执行cmd1,成功后再执行cmd2,若cmd1执行失败,cmd2也不执行。
cmd1 | cmd2:把cmd1的输出作为cmd2的输入。
cmd1 || cmd2:若cmd1执行失败,再执行cmd2;反之,cmd2不执行。
等级low 源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?php if ( isset ( $_POST [ 'Submit' ] ) ) { $target = $_REQUEST [ 'ip' ]; if ( stristr ( php_uname ( 's' ), 'Windows NT' ) ) { $cmd = shell_exec ( 'ping ' . $target ); } else { $cmd = shell_exec ( 'ping -c 4 ' . $target ); } $html .= "<pre>{$cmd} </pre>" ; } ?>
函数简介 php_uname():返回运行PHP的操作系统的描述,当参数为s时表示指定只返回操作系统名称。
stristr(string,search,before_search):这是PHP中的字符串查找函数,用于查找字符串中第一次出现的位置,不分大小写。
shell_exec():这是PHP中用于执行系统命令的函数,并返回命令的完整输出。
漏洞点 在源码中,$target直接拼接到shell_exec之中,这是我们用上面讲的命令连接符即可执行系统命令,获取敏感信息。
等级medium 源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 <?php if ( isset ( $_POST [ 'Submit' ] ) ) { $target = $_REQUEST [ 'ip' ]; $substitutions = array ( '&&' => '' , ';' => '' , ); $target = str_replace ( array_keys ( $substitutions ), $substitutions , $target ); if ( stristr ( php_uname ( 's' ), 'Windows NT' ) ) { $cmd = shell_exec ( 'ping ' . $target ); } else { $cmd = shell_exec ( 'ping -c 4 ' . $target ); } $html .= "<pre>{$cmd} </pre>" ; } ?>
与low的区别 与low相比,medium做了一点简单的防护,这里使用了黑名单:
1 2 3 4 $substitutions = array ( '&&' => '' , ';' => '' , );
ban掉了&&和“;”,但是上面所讲的命令连接符依然有可以利用的。
等级high 源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 <?php if ( isset ( $_POST [ 'Submit' ] ) ) { $target = trim ($_REQUEST [ 'ip' ]); $substitutions = array ( '&' => '' , ';' => '' , '| ' => '' , '-' => '' , '$' => '' , '(' => '' , ')' => '' , '`' => '' , '||' => '' , ); $target = str_replace ( array_keys ( $substitutions ), $substitutions , $target ); if ( stristr ( php_uname ( 's' ), 'Windows NT' ) ) { $cmd = shell_exec ( 'ping ' . $target ); } else { $cmd = shell_exec ( 'ping -c 4 ' . $target ); } $html .= "<pre>{$cmd} </pre>" ; } ?>
防御手法 这里的防御措施依旧是使用黑名单,这里的黑名单看起来是很全面的,但是这里的'| '是在|后面加上空格的,我们可以用' |'来绕过。当然,我在注入的时候习惯性的会两边加上空格。
等级impossible 源码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 <?php if ( isset ( $_POST [ 'Submit' ] ) ) { checkToken ( $_REQUEST [ 'user_token' ], $_SESSION [ 'session_token' ], 'index.php' ); $target = $_REQUEST [ 'ip' ]; $target = stripslashes ( $target ); $octet = explode ( "." , $target ); if ( ( is_numeric ( $octet [0 ] ) ) && ( is_numeric ( $octet [1 ] ) ) && ( is_numeric ( $octet [2 ] ) ) && ( is_numeric ( $octet [3 ] ) ) && ( sizeof ( $octet ) == 4 ) ) { $target = $octet [0 ] . '.' . $octet [1 ] . '.' . $octet [2 ] . '.' . $octet [3 ]; if ( stristr ( php_uname ( 's' ), 'Windows NT' ) ) { $cmd = shell_exec ( 'ping ' . $target ); } else { $cmd = shell_exec ( 'ping -c 4 ' . $target ); } $html .= "<pre>{$cmd} </pre>" ; } else { $html .= '<pre>ERROR: You have entered an invalid IP.</pre>' ; } } generateSessionToken ();?>
关键函数解析 explode()这是PHP中用于将字符串分割成数组的函数。
基本语法 1 explode (string $separator , string $string , int $limit = PHP_INT_MAX): array
$separator:分隔符,用于指定在哪里分割字符串
$string:要分割的原始字符串
$limit(可选):限制返回数组的最大元素个数
防御逻辑 使用explode()来把我们传入的参数进行分割,然后用is_numeric判断是否为纯数字,并且判断数组大小是不是4,然后进行ip的重构。当我们使用命令连接符时,在纯数字的验证时会出错,从而阻止执行其他命令的可能。