【DVWA】File Upload
一、low级别
1、测试流程
上传一张图片:
如图所示,上传成功!
上传冰蝎木马:
如图所示,上传成功!
成功getshell.
2、源码分析
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/"; //设置文件上传的路径
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] ); //在上传的目录下加上uploaded传来的文件名
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) { //移动文件到指定路径
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
?>
从上面的源码可以看出,文件在上传时并没有对文件的类型、大小、内容等做过滤和限制,存在明显的文件上传漏洞。
二、Medium级别
1、测试流程
- 上传一张图片
上传成功!
尝试上传冰蝎的大马shell.php
上传失败
-
尝试更改数据包content-type字段内容
上传成功!
然后用冰蝎链接即可getshell.
2、源码分析
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// File information
$uploaded_name = $_FILES[ 'uploaded' ][ 'name' ]; //获取上传文件的name
$uploaded_type = $_FILES[ 'uploaded' ][ 'type' ]; //获取上传文件的类型,通过MIME判断
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ]; //获取上传文件的大小
// Is it an image?
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {
//采用白名单过滤的方式,只接受.jpeg,.png的图片文件,并且文件大小设置在100000以内
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
代码审计可以发现,后端对上传文件的类型的大小做了限制,但文件类型是通过MIME获取的,所以用burp抓包后直接更改文件类型(content-type字段)即可绕过。
三、high级别
1、测试流程
上传一张图片
上传成功!
更改数据包的content-type字段,仍然上传失败。
于是制作图片木马:
尝试上传:
上传成功!
但由于文件是图片格式,无法直接执行,需要借助其他漏洞。
漏洞利用思路:
1、利用命令注入漏洞将muma.png的后缀改为php,再用菜刀连接
2、利用文件包含漏洞包含muma.png,再用菜刀连接。
2、源码分析
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Where are we going to be writing to?
$target_path = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
$target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
// File information
$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' ];
// Is it an image?
if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) &&
( $uploaded_size < 100000 ) &&
getimagesize( $uploaded_tmp ) ) {
// Can we move the file to the upload folder?
if( !move_uploaded_file( $uploaded_tmp, $target_path ) ) {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
echo "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
strrpos()
函数:函数查找字符串在另一字符串中最后一次出现的位置。
substr() 函数
:返回字符串的一部分。substr(string,start,length) length为可选
strtolower()
函数:把字符串转换为小写。
getimagesize()
函数:用于获取图像大小及相关信息,成功返回一个数组,失败则返回 FALSE 并产生一条 E_WARNING 级的错误信息。
由此可知,后端代码对上传的文件后缀进行白名单过滤,用getimagesize()函数二进制检测图片头部信息,然后对文件大小也进行了限制。绕过方法:制作图片木马,借助其他漏洞getshell
四、impossible级别
源码分析
<?php
if( isset( $_POST[ 'Upload' ] ) ) {
// Check Anti-CSRF token
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
// File information
$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' ];
// Where are we going to be writing to?
$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;
//创建文件位置
// Is it an image?
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 ) ) {
// Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)
if( $uploaded_type == 'image/jpeg' ) {
$img = imagecreatefromjpeg( $uploaded_tmp );
imagejpeg( $img, $temp_file, 100); //重新创建图片文件
}
else {
$img = imagecreatefrompng( $uploaded_tmp );
imagepng( $img, $temp_file, 9);
}
imagedestroy( $img );
// Can we move the file to the web root from the temp folder?
if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
// Yes! //更改文件路径
echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
}
else {
// No
echo '<pre>Your image was not uploaded.</pre>';
}
// Delete any temp files
if( file_exists( $temp_file ) ) //删除其他的临时文件
unlink( $temp_file );
}
else {
// Invalid file
echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken(); //预防CSRF攻击
?>
uniqid()
函数:基于以微秒计的当前时间,生成一个唯一的 ID。
ini_get(varname)
函数:获取一个配置选项的值,成功是返回配置选项值的字符串,null 的值则返回空字符串。如果配置选项不存在,将会返回 false。注: upload_tmp_dir 的这个参数为上传文件的临时目录。
sys_get_temp_dir()
函数:返回默认情况下PHP将临时文件存储在其中的目录的路径。
imagecreatefromjpeg(filename)
函数: 返回一图像标识符,代表了从给定的文件名取得的图像。
jmagejpeg(image, filename, quality)
函数:从image图像以filename为文件名创建一个JPEG图像,可选参数quality,范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大)。
imagedestroy(resource $image)
函数:在图像的所有资源使用完毕后,通常需要释放图像处理所占用的内存。在 PHP 中可以通过 imagedestroy() 函数来释放图像资源,$image 为要释放的图像资源。
rename(oldname,newname,context)
函数:重命名文件或目录。context可选,规定文件句柄的环境。
getcwd()
函数:获取当前工作目录。
file_exists(文件指定路径)
函数:检查文件或目录是否存在,如果指定的文件或目录存在则返回 true,否则返回 false。
unlink(文件指定路径)
函数:删除文件。若成功,则返回 true,失败则返回 false。
可以看到,Impossible级别的代码对上传文件进行了重命名(为md5值,导致%00截断无法绕过过滤规则),加入Anti-CSRF token防护CSRF攻击,同时上传成功后作不显示文件上传的路径,导致攻击者无法连接上含有恶意脚本的图片文件。而且似乎将图片重做了,在上传的图片文件中也找不到恶意脚本了。