做了一道关于文件上传的webCTF题,就想着系统地把文件上传漏洞的知识点整理一下,方便日后查阅。
文件上传包括了上传头像,上传相册,上传附件,添加新闻图片,自定义主题背景,新闻投稿等等,一个很简单的栗子就是微信或者QQ的头像上传功能,或者上传到百度云这种
文件上传漏洞是指由于程序员未对上传的文件进行严格的验证和过滤,而导致的用户可以越过其本身权限向服务器上上传可执行的动态脚本文件。这里上传的文件可以是木马,病毒,恶意脚本或者WebShell等。这种攻击方式是最为直接和有效的,“文件上传”本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。
开发者由于对安全意识不足,或者编写代码时对上传文件的合法校验存在缺陷,导致上传漏洞的产生。
什么是web容器?
web容器是一种服务程序,在服务器一个端口就有一个提供相应服务的程序,而这个程序就是处理从客户端发出的请求,如JAVA中的Tomcat容器,ASP的IIS或PWS都是这样的容器。一个服务器可以有多个容器。
公共网关接口(Common Gateway Interface,CGI)是Web 服务器运行时外部程序的规范,按CGI 编写的程序可以扩展服务器功能。CGI 应用程序能与浏览器进行交互,还可通过数据API与数据库服务器等外部数据源进行通信,从数据库服务器中获取数据。格式化为HTML文档后,发送给浏览器,也可以将从浏览器获得的数据放到数据库中。
可以看下面的例子加以理解
攻击者在利用上传漏洞时,通常会与Web容器的解析漏洞配合在一起。所以我们首先来了解一下解析漏洞,这样才能更深入地了解上传漏洞,并加以防范。 常见的Web容器有ⅡS、Apache、Nginx、Tomcat等,下面主要讲IIS、Apache容器。
什么是IIS? IIS全称是互联网信息服务,包括FTP/FTPS、NNTP、HTTP/HTTPS、SMTP等服务。
.net Framework是基础类库,是程序运行的底层框架。
IIS是架设Web服务器用来提供网页游览服务的,属于系统环境。
一般用ASP.NET开发软件,然后靠IIS对公网提供服务。
什么是文件解析?
当服务器接收到一个HTTP请求的时候,IIS首先需要决定如何去处理这个请求(服务器处理.aspx和.html肯定是不一样的),根据的是文件的后缀名。
服务器获取所请求的页面(也可以是文件)的后缀名后接下来会在服务器端寻找可以处理这类后缀名的应用程序,如果IIS找不到可以处理此类文件的应用程序,那么IIS将直接把这个文件返还给客户端。
IIS 6.0在解析文件时存在以下两个解析漏洞。
当建立*.asa、*.asp格式的文件夹时,其目录下的任意文件都将被IIS当做asp文件来解析。
例如:建立文件夹parsing.asp,在parsing.asp文件夹内新建一个文本文档test.txt,其内容为<%=NOW()%>,然后在浏览器内访问。 “NOWO”是ASP提供获取当前时间的函数,TXT是文本文档格式,IIS是不会去解析此类文件的,应该会直接显示其内容,而在parsing.asp文件夹中,却被当作ASP脚本来解析。
当文件为*.asp;1.jpg时,IIS6.0同样会以ASP脚本来执行,如:新建文件test.asp;1.jpg,内容为<%=NOW()%>。
Apache是从右到左开始判断解析,如果为不可识别解析,就再往左判断,如xxx.php.owf.rar ,”.owf”和”.rar”这两种后缀是apache解析不了的,apache就会把xxx.php.owf.rar解析成php。
怎么去判断是不是合法的后缀就是这个漏洞利用关键,测试时把常见的后缀都写上,去测试是不是合法,任意不识别的后缀,逐级向上识别。
有些程序开发人员在上传文件时,判断文件名是否是PHP、ASP、ASPX、ASA、CER、ASPX等脚本扩展名,如果是,则不允许上传,这时攻击者就有可能上传1.php.rar等扩展名来绕过程序检测,并配合解析漏洞,获取到WebShell。
绕过上传漏洞分以下两种。
客户端使用JavaScript检测,在文件未上传时,就对文件进行验证;
服务器端检测:服务端脚本一般会检测文件的MIME类型,检测文件扩展名是否合法, 甚至有些程序员检测文件中是否嵌入恶意代码。
在研究上传漏洞之前,首先来看两个小工具:中国菜刀和一句话图片木马。 “中国菜刀”这款软件是用来管理网站文件的,非常小巧灵活,它仅仅需要一段简短的代码 就可以方便地管理网站。中国菜刀现在已经成为安全研究者手中必备的利器,其官方网站为:http://www.maicaidao.com。
该软件提供的服务器端文件仅有一行代码。目前支持的服务器端脚本包括:PHP、ASP、ASP.NET、JSP等,并且支持HTTPS安全连接的网站。常见的代码如下:
Asp一句话:<%eval request(“xxx”)%> Php 一句话:<%php @eval($_POST[xxx]);?> Aspx一句话:<%@ Page Languag=”xxx”%><%eval(Request.Item[“xxx”])%>
正因为代码短小精悍,所以被黑客称为一句话木马(一句话后门)。 具体实例可以看看这一篇博客:攻防世界 | upload1 WP
“图片一句话”则是将一句话木马插入在图片文件中,而且并不损坏图片文件,这一方法可以躲过少许的防火墙检测。制作图片一句话木马的方法非常多,目前已经有安全研究人员设计出了专业的制作软件:Edjpgcom。Edjpgcom的使用方法非常简单:把一张正常的图片拖到Edjpgcom.exe程序中,填写相应的一句话代码,就可以制作图片一句话木马。 在插入一句话木马之后,以文本的方式打开图片,就可以看到一句话木马代码就在里面,而且不影响图片正常预览。 还有将一句话木马与正常图片通过CMD命令结合起来。
注:如果直接以文本的方式打开图片插入一句话,可能会造成文件损坏。
知道了程序员是如何防护上传漏洞及一句话图片木马后,下面深入研究攻击者是如何绕过程序员的防护思维来上传一句话木马文件的。
客户端检测
使用FileBug浏览器插件
FireBug是一款开源的浏览器插件,它支持Firefox、Chrome等浏览器。它可以让Web开发者轻松地调试HTML、JavaScript、AJAX、CSS等前端脚本代码,属于Web开发人员的必备武器。正由于FireBug功能强大,所以也被黑客认为是必备利器。
中间人攻击
中间人攻击这种方式与FireBug完全不同,FireBug是删除客户端的JavaScript验证,而使用Burp Suite则是按照正常的流程通过JavaScript验证,然后在传输中的HTTP层做手脚。 首先把木马文件扩展名改为一张正常图片的扩展名,比如JPG扩展名,在上传时使用Burp Suite拦截上传数据,再将其中的扩展名JPG修改为PHP,就可以绕过客户端验。
白名单与黑名单验证
(1)黑名单过滤方式 黑名单过滤是一种不安全的方式,黑名单定义了一系列不安全的扩展名,服务器端在接收文件后,与黑名单扩展名对比,如果发现文件扩展名与黑名单里的扩展名匹配,则认为文件不合法。
白名单过滤方式 白名单的过滤方式与黑名单恰恰相反,黑名单是定义不允许上传的文件扩展名,而白名单则是定义允许上传的扩展名,白名单拥有比黑名单更好的防御机制。如:$WhiteList= array(rar’,jpg’,png,bmpy,gif,jpg;doc);在获取到文件扩展名后对 WhiteList数组里的扩展名迭代判断,如果文件扩展名被命中,程序将认为文件是合法的,否则不允许上传。
MIME验证
MIME类型用来设定某种扩展名文件的打开方式,当具有该扩展名的文件被访问时,浏览器会自动使用指定的应用程序来打开。如GIF图片MIME为image/gif,CSS文件MIME类型为text/ess。
目录验证
在文件上传时,程序通常允许用户将文件放到指定的目录中,然而有些Web开发人员为了让代码更“健壮”,通常会做一个操作,如果指定的目录存在,就将文件写入目录中,不存在则先建立目录,然后写入。
截断上传攻击
文件名后缀有一个%00字节,可以截断某些函数对文件名的判断。在许多语言函数中,处理字符串的函数中0x00被认为是终止符eg. 网站上传函数处理xxx.asp%00.jpg时,首先后缀名是合法的jpg格式,可以上传,在保存文件时,遇到%00字符丢弃后面的 .jpg,文件后缀最终保存的后缀 名为xxx.asp
当我们遇到一个网站的上传拦截只是使用了JavaScript来校验的时候,下一步我们要做的就是判断绕过JavaScript上传是否可行
1. 由于是JavaScript前端校验,判断上传非法文件那个提示时,有无http包发出
2. 查看HTML代码中是否有相关JavaScript代码
利用的话,我们可以直接暴力一点,删除相关的JavaScript代码,或者修改我们的上传文件的后缀为允许上传的文件类型
由于上传文件的合法性校验使用的是黑名单的方式判断上传文件后缀,因为有些黑名单不全,就存在被攻击者绕过导致的上传漏洞
要判断一个黑名单是否可以绕过,我们可以用试错法,如
上传一个现实中不存在的后缀名文件(file.hatsune),观察服务器的返回情况
如果服务器上传成功,说明这个服务器用的就是黑名单
那还是说说我们如何绕过
绕过也很简单,我们可以使用如下的变换手段:
Asp->Asa or cer or cdx php->php3 or php4 or php5 jsp->jspx,etc.
如:asp->asP,php->pHp,etc.
比如 test.test. or test.test_(Windows文件名特性)
这个绕过方法呢是利用一些php函数的解析文件后缀名的规律
比如我们这里有个这样的代码
name=getname(request); type=gettype(name); If(type==jpg){ SaveFileToPath(UploadPath.name, name); }在这里,我们用gettype()函数<从后往前>判断后缀名
假设我们上传一个php文件叫test.php,在上传的时候用BurpSuite截包了,将文件名改为了test.php .jpg
其中test.php与.jpg之间是一个空格
然后我们用BurpSuite自带的Hex格式打开(这里我随便截了一个包演示一下)
我们将这个文件改为
然后我们用Hex格式打开(我们注意一点,这里的Hex不是指ASCII编码,而是URL编码)
我们找到20的地方(%20是空格的URL编码)
把他修改成为00
然后这时候我们就完成了这个0x00截断,服务器因为存在00,所以就会忽略后面的内容,于是我们的文件就绕过了校验上传上去了
什么是MIME,这里有个解释
MIME(Multipurpose Internet Mail Extensions)是描述消息内容类型的因特网标准
MIME消息能包含文本、图像、音频、视频以及其他应用程序专用的数据
然后这里有个按照内容类型排列的MIME类型列表
类型/子类型
扩展名
application/envoy
evy
application/fractals
fif
application/futuresplash
spl
application/hta
hta
application/internet-property-stream
acx
application/mac-binhex40
hqx
application/msword
doc
application/msword
dot
比如这里有一个代码是这样的
if(($_FILES["file"]["type"] == "image/jpeg")){ do_something(); } else{ echo "Only jpg file can upload"; }上面这个代码就是使用了MIME的类型检测,那我们怎么绕过这个上传点呢?
我们可以这样
在上传比如一个php文件的时候,我们截包,将上传流量里面的Content-Type: text/php修改为Content-Type: image/jpeg
这个利用就比较暴力了,如果是你自己发现的某个Web中间件存在这种漏洞,那基本都是0day类型的
这个漏洞的成因也很多,基本就是,Web中间件的漏洞或者Web服务器与cgi处理的差异,运气最好的就是运帷人员配置不当
那我们用几个历史上真实发生过的栗子来说说利用方法吧,很著名的有这么几个
我们可以构造上传文件名为webshell.asp;test.jpg
因为这时候IIS只会解析到后面的jpg文件类型而没有前面的asp
所以就允许上传了,但是上传的文件里面其实还包含了一个webshell.asp
之后webshell.asp就绕过了上传限制上传到了服务器上了,且这个webshell.asp不会被重命名
(这种漏洞现在基本不会见到了)
比如我们构造上传文件名为webshell.asp/test.jpg
这个解析漏洞是呢在服务器上可以建立任意名称的文件夹,也就是我们前面的webshell.asp那是一个<文件夹>的名字
然后同时也可以在该文件夹下上传其他文件或创建其他文件夹
这个漏洞和上面的也差不多
这个漏洞的成因和我们上期讲的逻辑漏洞是一样的
首先我们构造一个文件名为webshell.php.jpg
Apache呢会先从后往前尝试解析,然后解析到了一个jpg
因为这个jpg是Apache默认不解析的文件名,所以Apache会跳过这个jpg继续往前解析,直到解析到了我们的php
比如我们一个php文件叫webshell.php
我们点击上传,然后BurpSuite截包,将文件名改为
webshell.php.jpg
OK,上传成功,之后我们在服务器执行
http://x.x.x.x/webshell.php.jpg
这里是假设上传路径就是网站的根目录,如果不是就自行改成其他目录
之后php代码就会被Apache执行了
这个解析漏洞的一个好处就是文件上传后不会对文件名进行重命名
漏洞版本包含:apache 2.0.x <= 2.0.59,apache 2.2.x <= 2.2.17,apache 2.2.2 <= 2.2.8
这个漏洞呢是Nginx本身代码编写时候就存在的,Nginx与php-cgi处理方式存在差异造成了这个漏洞
我们可以这样首先在本地有个webshell.php文件
之后我们上传,然后将这个上传的php文件名截包,并改为webshell.jpg%00.php绕过上传限制
之后当服务器中设置了cgi.fix_pathinfo = 1的时候,php就会以'/'为分割符从<最后一个文件>开始<向前>找<存在的文件>去执行
然后我们执行(还是假设上传目录在网站根目录)http://x.x.x.x/webshell.jpg/abc.php
我们上面已经大概说了一下原理
php会以'/'为分隔符取找存在的文件取执行
这里abc.php是乱写的,故意让服务器找不到这个文件,肯定是不存在的
之后Nginx找不到这个文件,就会去试着去前面执行我们的webshell.jpg
然后我们的的webshell里面写的php代码就被Nginx执行了
漏洞包含的版本:nginx 0.5.*,nginx 0.6.*,nginx 0.7 <= 0.7.65,nginx 0.8 <= 0.8.37
首先,上传的文件能够被Web容器解释执行。所以文件上传后所在的目录要是Web容器所覆盖到的路径。 其次,用户能够从Web上访问这个文件。如果文件上传了,但用户无法通过Web访问,或者无法得到Web容器解释这个脚本,那么也不能称之为漏洞。 最后,用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。 防范文件上传漏洞常见的几种方法:
只要web容器无法解析该目录下面的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响,因此这一点至关重要。
在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单方式,黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。
文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用了随机数改写了文件名和路径,将极大地增加攻击的成本。再来就是像shell.php.rar.rar和crossdomain.xml这种文件,都将因为重命名而无法攻击。
由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传crossdomain.xml、上传包含Javascript的XSS利用等问题将得到解决。
限制上传文件的大小,防止由于内存、磁盘耗尽造成的拒绝服务。 可以配置web server允许的最大Post大小。 可以在代码层面获取上传文件的大小,根据文件类型的不同进行进一步的过滤。
1.将文件上传目录设置为静态资源目录,防止被解析为脚本执行。 2.使用代理页面隐藏文件真实路径。 3.使用上述方法时,确保Content-Type与实际文件类型一致。 4.如果文件不允许在页面展示,仅允许下载,请设置Content-disposition:attachment。