文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的。上传的文件可以是木马,病毒,恶意脚本等等。“文件上传”本身并没有任何问题,但是文件上传后,服务器怎么处理、解释文件就成了一个问题。如果服务器的处理逻辑做得不够安全,则会导致严重的后果。
常见的文件上传之后容易导致的安全问题:
利用上传漏洞,通常需要结合Web容器(IIS、Nginx、Apache、Tomcat)的解析漏洞来让上传的漏洞得到实现。
IIS全称是互联网信息服务,包括FTP/FTPS、NNTP、HTTP/HTTPS、SMTP等服务。IIS是架设Web服务器用来提供网页游览服务的,属于系统环境。一般用ASP.NET开发软件,然后靠IIS对公网提供服务。
IIS 6.0 在处理文件时,出现过一些漏洞,比如当文件名为 abc.asp;xx.jpg
时,IIS 6.0会将此文件解析为 abc.asp
,引文文件名被分号截断了,从而导致脚本被执行。比如:
http://www.target.com/path/xyz.asp;abc.jpg
这会执行 xyz.asp
,而abc.jpg
则被忽略了。
除此之外,IIS 6.0 还出现过一个漏洞,因为处理文件夹扩展名出错,导致将/*.asp/目录下的所有文件都作为ASP文件进行解析。比如:
http://www.target.com/path/xyz.asp/abc.jpg
这里的 abc.jpg
,会被当做ASP文件进行解析。
但是有一点就是,这些漏洞需要在服务器的本地硬盘上确实存在才可以触发,若只是通过Web应用映射出来的URL,则是无法触发的。
Apache解析文件的规则时从右到左开始判断,如果后缀名为不可识别文件解析,则会继续向左判断,直至可以正确识别。
xxx.php.owf.zip 其中.owf和.zip文件后缀Apache不识别,直至判断.php才会按照PHP解析文件
而对于Apache所识别的文件名的类型定义,保存在Apach的mime.types
文件中。
黑名单过滤是一种不安全的方式,黑名单定义了一系列不安全的扩展名,服务器端在接收文件后,与黑名单扩展名对比,如果发现文件扩展名与黑名单里的扩展名匹配,则认为文件不合法。
白名单的过滤方式与黑名单恰恰相反,黑名单是定义不允许上传的文件扩展名,而白名单则是定义允许上传的扩展名,白名单拥有比黑名单更好的防御机制。如:
$WhiteList=array(rar’,jpg’,png,bmpy,gif,jpg;doc);
在获取到文件扩展名后对 WhiteList
数组里的扩展名迭代判断,如果文件扩展名被命中,程序将认为文件是合法的,否则不允许上传。
MIME类型用来设定某种扩展名文件的打开方式,当具有该扩展名的文件被访问时,浏览器会自动使用指定的应用程序来打开。如GIF图片MIME为image/gif,CSS文件MIME类型为text/ess。
在文件上传时,程序通常允许用户将文件放到指定的目录中,然而有些Web开发人员为了让代码更“健壮”,通常会做一个操作,如果指定的目录存在,就将文件写入目录中,不存在则先建立目录,然后写入。
在针对上传文件的检查中,很多应用都是通过判断文件名后缀的方法来验证文件的安全性的。但是在某些时候,如果攻击者手动修改了上传过程中的POST包,在文件名后添加一个%00字节,则可以截断某些函数对文件名的判断。比如对于一个后缀为xxx.php[\0].JPG
的文件名,其中[\0]是十六进制的0x00字符,对于服务器端来说,此文件因为0x00字符截断的关系,最终会变成xxx.php
。这样就可以截断某些函数对文件名的判断。
还有一些时候,会通过判断上传文件的文件头来验证文件的类型。通常通过前10个字节,可以判断出文件的真实类型。
因此常见的攻击技巧就是伪造一个合法的文件头,将真实的PHP等脚本代码附在合法的开头之后。
那么在实际中,如何做到防范文件上传漏洞呢?
常见的几种方法:
文件上传的目录设置为不可执行
只要Web容器无法解析该目录下面的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响,因此这一点至关重要。在实际应用种,很多大型网站的上传应用,在文件被上传之后会放到独立的存储上,做静态文件处理。这一方面方便使用缓存加速,降低性能损耗;另一方面也杜绝了脚本执行的可能。
判断文件类型
在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单方式,黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。
实际中有很多人是将一些包含了HTML的文件(比如一张图片)上传到目标网站,然后通过这个传播这个URL进行钓鱼。这其中就是通过比如伪造一个文件头绕过上传类型检查,然后在接下来的一段脚本里控制浏览器跳向指定的网站,即所谓的钓鱼网站。
使用随机数改写文件名和文件路径
文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用了随机数改写了文件名和路径,将极大地增加攻击的成本。再来就是像shell.php.rar.rar和crossdomain.xml这种文件,都将因为重命名而无法攻击。
单独设置文件服务器的域名
由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传crossdomain.xml、上传包含Javascript的XSS利用等问题将得到解决。
限制上传文件大小
限制上传文件的大小,防止由于内存、磁盘耗尽造成的拒绝服务。
可以配置Web server允许的最大Post大小。
可以在代码层面获取上传文件的大小,根据文件类型的不同进行进一步的过滤。
确保上传文件被访问正确返回
1.将文件上传目录设置为静态资源目录,防止被解析为脚本执行。
2.使用代理页面隐藏文件真实路径。
3.使用上述方法时,确保Content-Type与实际文件类型一致。
4.如果文件不允许在页面展示,仅允许下载,请设置Content-disposition:attachment。
http://teamxlc.sinaapp.com/web5/21232f297a57a5a743894a0e4a801fc3/index.html
这是一道文件上传的题,我们先上传一张图片看一下效果。
我们上传的是一个.png格式的图片,它提示必须上传后缀为php的文件,那我们通过直接改后缀的方式尝试一下。
然而我们得到了这个,这是用黑名单记载了不允许上传的类型,但是它需要上传的是php后缀。
我们回到开始的报错位置。
我们可以发现它是通过basename返回上传的文件名,那么我们想到了截断上传的方式。(basename() 函数返回路径中的文件名部分 )
找到对应的/uploads/。在后面加上比如1.php。然后找到对应十六进制码的位置,加上0x00。
再上传,就成功绕过了检测,拿到flag。