【DVWA】Command Injection
Command Injection即命令注入,应用程序有时调用一些执行系统命令的函数,如在PHP中,使用system、exec、shell_exec、passthru、popen、proc_popen等函数可以执行系统命令。当黑客能控制这些函数中的参数时,就可以将恶意的系统命令拼接到正常命令中,从而造成命令执行攻击,这就是命令注入漏洞。
一、low级别
1、漏洞测试
如图所示,ipconfig执行成功,当然在Linux下也可以通过cat查看一些敏感文件,如/etc/password或者shadow文件。文件详解
关于符号的使用:
windows与Linux:
“|”:command1 | command2; 直接执行后面的命令;
“||”:command1 || command2; 只有前面为假(执行错误)才会执行后面的命令。ping 2 || ipcongfig
“&”:command1 & command2; 无论前面真假,都会执行后面的语句。
“&&”:command1 && command2; 只有前面为真,才会执行后面的语句。Ping 127.0.0.1 && whoami
另Linux中:
“;”:command1 ; command2; 执行完前面的语句再执行后面的语句。
2、源码分析
<?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>";
}
?>
函数详解:
php_uname('mode')
:其中mode为单个字符
'a'
:此为默认。包含序列"s n r v m"
里的所有模式。's'
:操作系统名称。例如:FreeBSD
。'n'
:主机名。例如:localhost.example.com
。'r'
:版本名称,例如:5.1.2-RELEASE
。'v'
:版本信息。操作系统之间有很大的不同。'm'
:机器类型。例如:i386
。
shell_exec('cmd')
:其中cmd为要执行的系统命令
stristr()
函数:搜索字符串在另一个字符串中的第一次出现,并返回该字符串以及剩余部分是二进制安全的,不区分大小写。如需区分大小写则应使用strstr()函数
2、medium级别
1、漏洞测试
输入222.24.28.118 && hostname 出错:
输入 8 | hostname 成功执行:
2、源码分析
<?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>";
}
?>
如上面源代码,采用的是黑名单过滤机制,将用户传入的参数中的&& 和 ;替换为空从而试图防御命令注入,但可以使用其他符号,如|, ||, &。
也可以使用拼凑法:222.24.28.118 &;& ipconfig
三、high级别
1、漏洞测试
输入222.24.28.118 || ipconfig
成功执行:
输入222.24.28.118 |whoami
成功执行:
2、源码分析
<?php
if( isset( $_POST[ 'Submit' ] ) ) {
// Get input
$target = trim($_REQUEST[ 'ip' ]); //trim函数去除开头和结尾的空格,以及换行符\n,Tab键\t等
// 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级别,high级别完善了黑名单机制,但是仔细看发现它过滤的是|加空格,所以直接打|就可绕过。
而且222.24.28.118 || ipconfig
也可绕过,原因是过滤机制是按照数组中的顺序过滤的,先过滤|空格,然后剩下222.24.28.118 |ipconfig
也可执行成功。
四、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 ); //把字符串打散为数组,这里是把ip用点区分为四个数组元素
// Check IF each octet is an integer //检查每个元素是否是一个数字,并且该数组有4个元素
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];
//将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>";
}
else {
// Ops. Let the user name theres a mistake
echo '<pre>ERROR: You have entered an invalid IP.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
从上述代码可以看出,他是先将传入的字符串以‘.’为分隔,打散成一个数组,并且判断该数组中每个元素是否为数字,该数组是否一共有4个元素。比较严格的做了格式限制,绕过比较困难。
五、漏洞修复建议
1、不使用
尽量不要使用命令执行函数。
对于php语言来讲,不能完全控制的危险函数最好不要使用。
2、若非要使用,在参数传入前一定做过滤。
客户端提交的变量在进入执行命令函数前要做好过滤和检测。
在使用动态函数之前,确保使用的函数是指定的函数之一。