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 #
源码 #
<?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
$html .= "<pre>{$cmd}</pre>";
}
?>
函数简介 #
php_uname():返回运行PHP的操作系统的描述,当参数为s时表示指定只返回操作系统名称。
stristr(string,search,before_search):这是PHP中的字符串查找函数,用于查找字符串中第一次出现的位置,不分大小写。
shell_exec():这是PHP中用于执行系统命令的函数,并返回命令的完整输出。
漏洞点 #
在源码中,$target直接拼接到shell_exec之中,这是我们用上面讲的命令连接符即可执行系统命令,获取敏感信息。
等级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
$html .= "<pre>{$cmd}</pre>";
}
?>
与low的区别 #
与low相比,medium做了一点简单的防护,这里使用了黑名单:
$substitutions = array(
'&&' => '',
';' => '',
);
ban掉了&&和“;”,但是上面所讲的命令连接符依然有可以利用的。
等级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
$html .= "<pre>{$cmd}</pre>";
}
?>
防御手法 #
这里的防御措施依旧是使用黑名单,这里的黑名单看起来是很全面的,但是这里的'| '是在|后面加上空格的,我们可以用' |'来绕过。当然,我在注入的时候习惯性的会两边加上空格。
等级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
$html .= "<pre>{$cmd}</pre>";
}
else {
// Ops. Let the user name theres a mistake
$html .= '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
关键函数解析 #
explode()
#
这是PHP中用于将字符串分割成数组的函数。
基本语法 #
explode(string $separator, string $string, int $limit = PHP_INT_MAX): array
$separator:分隔符,用于指定在哪里分割字符串
$string:要分割的原始字符串
$limit(可选):限制返回数组的最大元素个数
防御逻辑 #
使用explode()来把我们传入的参数进行分割,然后用is_numeric判断是否为纯数字,并且判断数组大小是不是4,然后进行ip的重构。当我们使用命令连接符时,在纯数字的验证时会出错,从而阻止执行其他命令的可能。