文件上传漏洞通常是由于对上传文件的类型、内容没有进行严格的过滤、检查,使得攻击者可以通过上传木马获取服务器的webshell权限,因此文件上传漏洞带来的危害常常是毁灭性的。
关于docker的安装:
https://www.docker.com/
通过Docker一键搭建
docker run --rm -it -p 80:80 vulnerables/web-dvwa
搭建完成后,访问http://localhost
即可进入
在前端上传文件的form
表单中,<form>
标签的属性值enctype
属性会规定发送到服务器之前对表单的数据进行何种编码
它的常见值如下:
application/x-www-form-urlencoded: 在发送前编码所有字符(默认)
multipart/form-data: 不对字符编码。在使用包含文件上传控件的表单时,必须使用该值。
text/plain: 空格转换为 "+" 加号,但不对特殊字符编码。
PHP会用$_FILES
数组接收参数
$_FILES
的内容为:
[name] => feng.jpeg 文件的名称
[type] => image/jpeg 文件的MIME类型
[tmp_name] => C:\Users\Administrator\AppData\Local\Temp\php2007.tmp 文件的临时位置
[error] => 0 文件的错误信息 0 ok 1234 error
[size] => 2859 文件的大小,单位:byte 1M=1024KB 1KB=128Byte
<?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' ] );
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
$html .= '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
?>
代码流程:
文件上传文件后,文件直接保存,保存路径为hackable/uploads/
,文件上传成功后,返回succesfully
。
函数basename()
:返回路径中的文件名部分
函数
move_uploaded_file ( string $filename , string $destination )
:将文件移动到指定地址
防御措施:无
本关没有任何保护措施,上传成功后会返回文件路径地址,可直接上传恶意脚本文件进行攻击
<?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_type = $_FILES[ 'uploaded' ][ 'type' ];
$uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
// Is it an image?
if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
( $uploaded_size < 100000 ) ) {
// Can we move the file to the upload folder?
if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
// No
$html .= '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
代码流程:
当有提交上传文件时,后端先用变量从$_POST['uploaded']
取出文件路径、文件名、文件类型、文件大小;然后对文件MIME类型进行判断,如果文件类型为image/jpeg
或者image/png
并且文件大小小于100000byte,则将临时文件移动到指定目录,上传成功,则回显文件路径
防御措施:
上传我们准备好的恶意脚本文件,通过Burp Suite
抓包拦截
将Content-Type
的值修改为image/png
,即可绕过检测,成功上传,访问该文件即可执行恶意脚本
<?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
$html .= '<pre>Your image was not uploaded.</pre>';
}
else {
// Yes!
$html .= "<pre>{$target_path} succesfully uploaded!</pre>";
}
}
else {
// Invalid file
$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
?>
代码流程:
当有提交上传文件时,后端先用变量从$_POST['uploaded']
取出文件路径、文件名、文件扩展名、文件大小,临时文件名称;然后进行判断,如果文件扩展名为jpg
,png
,jpeg
,且文件大小小于100000byte,并且能获取到图像信息,则将临时文件移动到指定目录,上传成功,则回显文件路径
函数strtolower():将所有字母转换为小写
函数getimagesize():获取图像信息
防御措施:
常见的图片格式的文件头标识如下:
JPEG/JPG - 文件头标识 (2 bytes): FF D8 (SOI) (JPEG 文件标识) - 文件结束标识 (2 bytes): FF D9 (EOI)
PNG - 文件头标识 (8 bytes) 89 50 4E 47 0D 0A 1A 0A
GIF - 文件头标识 (6 bytes) 47 49 46 38 39(37) 61 |GIF89(7)a
文件头欺骗:伪造文件头,使文件头标识一样,其它部分我们修改为一句话木马,也就成了我们常说的图片一句话。
思路:
绕过扩展名检测和getimagesize()
图像信息检测,通过制作图片马可以绕过图像信息检测,通过00截断可以绕过扩展名检测(适用于 php 小于 5.3.4 版本),通常图片马需要配合文件包含才能发挥作用
制作图片马:
(windows的copy命令) 准备好恶意脚本文件(x.php)和图片文件(y.png),在cmd下执行如下命令
copy y.png/b+x.php/a z.png
即可生成图片马
%00截断上传:
由于我搭建的环境是PHP7.2,%00截断在此不适用
文件上传+文件包含组合利用:
通过我们刚刚上传的图片一句话木马,借助High Security Level
的文件包含漏洞来进行组合利用:
http://your-dvwa-ip/vulnerabilities/fi/?page=file:///var/www/dvwa/hackable/uploads/c.jpg
访问如上地址即可成功执行我们的恶意脚本(这里用phpinfo()
代替)
<?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!
$html .= "<pre><a href='${target_path}${target_file}'>${target_file}</ succesfully uploaded!</pre>";
}
else {
// No
$html .= '<pre>Your image was not uploaded.</pre>';
}
// Delete any temp files
if( file_exists( $temp_file ) )
unlink( $temp_file );
}
else {
// Invalid file
$html .= '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
代码流程:
当有提交上传文件时,后端先用变量从$_POST['uploaded']
取出文件路径、文件名、文件扩展名、文件大小,临时文件名称;通过uniqid()
生成唯一的ID拼接文件名进行md5加密再拼接上扩展名形成新的文件名,对临时文件也进行此操作,如果扩展名为jpg,jpeg,png且文件大小小于100000byte,且MIME类型为image/png或者image/jpeg并且能取得图像信息,则重新生成图像进行上传
函数uniqid():基于以微秒计的当前时间,生成一个唯一的 ID。
函数ini_get():函数返回相应选项的值
函数sys_get_temp_dir(): 返回用于临时文件的目录
函数imagecreatefromjpeg(filename):函数返回图片文件的图像标识,失败返回false
函数imagejpeg(image,filename,quality):从image图像以filename为文件名创建一个JPEG图像,可选参数quality,范围从 0(最差质量,文件更小)到 100(最佳质量,文件最大)。
函数imagedestroy():函数销毁图像资源
函数rename():重命名文件或目录
函数getcwd():获取当前工作目录
防御措施:
无解
## 参考资料:
1. DVWA_File Upload 文件上传:https://www.cnblogs.com/huangming-zzz/p/9900435.html
2. DVWA File Upload 通关教程:http://www.storysec.com/dvwa-file-upload.html
3. 《DVWA漏洞测试平台分析》
4. 通过文件头标识判断图片格式:http://www.cnblogs.com/Wendy_Yu/archive/2011/12/27/2303118.html
5. windows中copy命令详解:https://www.cnblogs.com/andr01la/p/5146422.html