一、什么是文件上传漏洞
大多数网站都有文件上传的接口,如果没有对上传的文件类型做严格的限制,会导致攻击者可以上传恶意文件。(例如Webshell)
利用这些恶意文件,攻击者可能获取到执行服务器端命令的能力。
二、漏洞分析
我们从DVWA网站的代码来理解文件上传漏洞。
网站上传界面:
(1)low 等级
前端代码:
前端通过POST方法,将文件传给php处理:
PHP通过$_FILES
方法接收文件属性:
$_FILES['uploaded']['name']
,获取客户端文件的原名称;
$_FILES['uploaded']['tmp_name'
], 获取文件被上传后在服务端存储的临时文件名。
可以看到,这里并未对上面两个参数做任何处理,就通过move_uploaded_file()
函数完成了上传,因此存在文件上传漏洞,因为我们可以在此处上传任意类型的文件。
(2)Medium级别
Medium等级通过$_FILES ['uploaded']['type']
获取了文件的MIME类型,通过$_FILES['uploaded']['size']
获取了文件的大小。
通过设置白名单的方式,只允许上传jpeg和png类型的文件,并且上传的文件大小需要小于100K。
这种方法也很容易绕过,我们依然可以上传一个任意后缀的文件,使用Burp进行抓包,修改“Content-Type”字段为为“image/jpeg”即可。
//将application/octet-stream修改为image/jpeg
(3)high级别
high级别新增了一段代码用于提取文件的后缀名:
$uploaded_ext = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
首先利用strrpos() 函数查找“.”
在变量$uploaded_name
中出现的位置,然后将得到的数值加1,最后利用substr()函数从变量$uploaded_name
的指定位置截取部分字符串。
再通过白名单的方式单独检查文件后缀名是否合规:
if (($uploaded_ext == "jpg" || $uploaded_ext == "JPG" || $uploaded_ext == "jpeg" || $uploaded_ext == "JPEG") && ($uploaded_size < 100000))
getimagesize( $uploaded_tmp )
会检查文件内容是否是图片格式的。
该级别可以通过%00截断的方式绕过,或者直接上传一个图片马,尝试配合文件包含漏洞来进行绕过了,这两种方法在下面进行讲解。
从上面三个例子我们可以看出,对文件格式做严格的校验可以一定程度上防范文件上传漏洞。
三、常见的绕过方法
(1)修改文件后缀
如果文件类型检测是在前端代码中,如JS代码:
我们可以将我们构造的恶意脚本后缀修改为图片类型,进行上传:
然后使用burp进行抓包,将后缀名改回来即可:
(2)修改Content-Type类型
如果后端服务器只对$_FILES['uploaded']['type']
进行校验的话,那么我们只需要抓包修改报文的Content-Type类型即可,因为$_FILES['uploaded']['type']
读取的就是报头的Content-Type字段,修改该字段也并不会对我们上传的文件有任何影响。
(3)%00截断
有时候,我们在文件名后面加一个%00,就可以截断后面的内容,因为%00会被判断为终止符。
如果通过GET的方法传输文件,我们直接抓包添加%00即可:
如果通过POST方法进行传输,与GET方法不同,POST方法不会对%00进行解码,我们需要选中%00,通过ctrl+shift+u快捷键进行转换才行。
但是%00截断仅存在于php < 5.3.4的版本中,且需要关闭magicquotesgpc,因为该开关会对%00等进行转义。而在5.3.4及以上版本中,存在另一个函数addslashes()进行转义。
(4)伪造合法的文件头
从DVWA High级别的文件上传页面可以看出,有时候在后端严格的检测下,我们只能够上传后缀为图片格式的文件。
这种情况下,如果能找到文件包含、文件解析这样的漏洞,我们也可以先将恶意脚本的后缀改成图片格式(如.jpg)上传到服务器中去,再通过其他漏洞来执行。
但是服务器端为了规避这种情况,使用getimagesize()这类函数来检查文件内容是否是图片格式的,这样我们伪造的恶意脚本就无法上传了。
通过伪造合法的文件头可以绕过这种检测。通常检测函数根据文件的前10个字节,就可以判断出文件的类型,而图片的文件头一般都是固定的。
因此,我们可以在我们的恶意脚本前加上一段文件头即可,如GIF89a(gif文件头)。又或者使用010 editor等编辑工具打开一张图片,在图片中加上webshell脚本:
这样我们就可以成功上传图片马了,关于怎么利用文件包含漏洞执行图片马,我将在文件包含知识总结专题中进行讲解。
四、文件上传漏洞的防护
1、前端JS代码过滤
前端通过JS代码做第一次过滤,可以起到一定的防范作用。虽然可以被burp人工绕过,但仍然可以减少一些攻击行为,以及过滤掉正常用户的误上传操作,减轻后台服务器的压力。
2、后端设置黑白名单
通过设置黑名单或者白名单的方式,对上传文件的后缀做严格的检查。但黑名单可能出现遗漏的情况,也可能被大小写等方式绕过,因此白名单一般更安全。
3、限制上传文件大小
避免攻击者上传过大的恶意脚本,防止由于内存、磁盘耗尽而造成的拒绝服务攻击。
4、将文件上传的目录设置为不可执行
只要Web容器无法解析该目录下的文件,即使攻击者上传了恶意脚本文件,服务器本身也不会受到影响。当前一些网站,在文件上传之后,会将文件放到独立的存储上,做静态文件处理,一方面方便使用缓存加速,降低性能损耗,另一方面也杜绝了脚本被执行的可能性。
5、采用随机数改写文件名
只要攻击者无法获取他们上传的脚本的文件名,他们也就无法访问到上传的恶意脚本了。