文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。
文件上传功能本身是一个正常业务需求,对于网站来说,很多时候也确实需要用户将文件上传到服务器。所以“文件上传”本身没有问题,但有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。
文件上传后导致的常见安全问题一般有:
- 上传文件是Web脚本语言,服务器的Web容器解释并执行了用户上传的脚本,导致代码执行
- 上传文件是Flash的策略文件crossdomain.xml,黑客用以控制Flash在该域下的行为(其他通过类似方式控制策略文件的情况类似)
- 上传文件是病毒、木马文件,黑客用以诱骗用户或管理员下载执行
- 上传文件是钓鱼图片或为包含了脚本的图片,在某些版本的浏览器中会被作为脚本执行,被用于钓鱼和诈骗
在大多数情况下,文件上传漏洞一般是指“上传Web脚本能够被服务器解析”的问题,也就是通常所说的webshell的问题。要完成这个攻击,要满足如下几个条件:
- 首先,上传的文件能够被Web容器解释执行。所以文件上传后所在的目录要是Web容器所覆盖的路径。
- 其次,用户能够从Web上访问这个文件。如果文件上传了,但用户无法通过Web访问,或者无法使得Web容器解释这个脚本,那么也不能称之为漏洞。
- 最后,用户上传的文件若被安全检查、格式化、图片压缩等功能改变了内容,则也可能导致攻击不成功。
绕过文件上传检查功能
在文件上传文件的检查中,很多应用都是通过判断文件名后缀的方法来验证文件的安全性的。但是在某些时候,如果攻击者手动修改了上传过程的POST包,在文件名后添加一个%00字节,则可以截断某些函数对文件名的判断。因为在许多语言的函数中,比如在C、PHP等语言的常用字符串处理中,0x00被认为是终止符。受此影响的环境有Web应用和一些服务器。比如应用原本只允许上传JPG图片,那么可以构造文件名(需要修改POST包)为xxx.php[\0].JPG, 其中[\0]为十六进制的0x00字符,.JPG绕过了应用的上传文件类型判断;但对于服务器端来说,此文件因为0字节截断的关系,最终却变成xxx.php。
除了常见的检查文件名后缀的方法外,有的应用还会通过判断上传文件的文件头来验证文件的类型。在正常情况下,通过判断前10个字节,基本上就能判断出一个文件的真实类型。浏览器的MIME Sniff功能实际上也是通过读取文件的前256个字节,来判断文件的类型。因此,为了绕过应用中类似MIME Sniff的功能,常见的攻击技巧是伪造一个合法的文件头,而将真实的PHP等脚本代码附在合法的文件头后,但仍需要通过PHP来解释此图片文件才行(若上传文件的后缀是.JPG,则Web Server很可能会将此文件当做静态文件解析,而不会调用PHP解释器,攻击的条件无法满足)。
文件解析问题
Apache文件解析问题
Apache对于文件名的解析是从后往前解析的,直到遇见一个Apache认识的文件类型为止。比如:
phpshell.php.rar.rar.rar.rar
因为Apache不认识rar这个文件类型(在Apache的mime.types文件中定义了它所认识的文件类型),所以会一直遍历后缀到.php,然后认为这是一个PHP类型的文件。
Apache的这个特性,很多工程师在写应用的时候并不知道,导致写出的安全检查功能可能会存在缺陷。比如.rar是一个合法的上传需求,在应用里只判断文件的后缀是否是.rar,最终用户上传的文件是phpshell.php.rar.rar.rar.rar,从而导致脚本被执行。
IIS文件解析问题
IIS 6 在处理文件解析时,也出过一些漏洞。前面提到的0x00字符截断文件名,在IIS和Windows环境下曾经出现过非常类似的漏洞,不过截断的字符变成了分号“;”。
当文件名为abc.asp;xyz.jpg时,IIS 6 会将此文件解析为abc.asp,文件名被截断了,从而导致脚本被执行,比如:
http://www.target.com/path/abc.asp;xyz.jpg
会执行abc.asp,而不会管xx.jpg。
在IIS 6中还曾经出现过一个漏洞,因为处理文件夹拓展名出错,导致将/*.asp/目录下的所有文件都作为ASP文件进行解析,比如:
http://www.target.com/path/abc.asp/xyz.jpg
这个xyz.jpg会被当做ASP文件进行解析。
设计安全的文件上传功能
文件上传功能本身并没错,只是在一些条件下会被攻击者利用,从而成为漏洞。那么如何才能设计出安全的、没有缺陷的文件上传功能呢?根据攻击的原理,总结了一下几点:
1. 文件上传的目录设置为不可执行
只要Web容器无法解析目录下的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响,因此这点至关重要。很多大型的网站的上传应用,文件上传后会放到独立的存储上,做静态文件处理,一方面方便使用缓存加速,降低性能损耗;另一方面也杜绝了脚本执行的可能。
2. 判断文件类型
在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单的方式,黑名单的方式已经无数次证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。
3. 使用随机数改写文件名和文件路径
文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用使用随机数改写了文件名和路径,将极大地增加攻击的成本。
4. 单独设置文件服务器的域名
由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传cossdomain.xml、上传包含JavaScript的XSS利用等问题将得到解决。但能否如此设置,还需要看具体的业务环境。