前言:
文件上传漏洞刚上手并不难以理解,可他的知识点比较杂乱,往往需要花费较长的时间去搜索资料并理解,笔者也是浏览了不少他人的文章有所感触,所以打算综合一下形成文档,以期对文件上传漏洞能有个系统性的认识并且便于回顾
思维导图
文件上传漏洞简介:
在Web程序中,经常需要用到文件上传的功能,比如上传头像,上传附件等。如果没有限制上传类型或者限制不严格被绕过,就有可能造成文件上传漏洞。
文件上传漏洞危害:
- 内部信息被窃取
- 网站被控制甚至服务器沦陷
- ······
文件上传漏洞产生原因:
本地文件上传限制被绕过
服务器过滤不严格被绕过
文件路径被截断
存在文件解析漏洞导致文件执行
……
文件上传漏洞常见类型:
基于文件校验的漏洞:
一、客户端检测:
1)js前端验证
源码
1 | function checkFileExt() |
问题
- 无法阻止恶意用户修改js
- hacker可以通过抓包软件先通过js前端再更改文件名
入侵手段
- 直接禁用js
- 利用burp等工具,先通过js验证,然后再修改拓展名
2)Flash AS脚本检测
原理
提交上传请求前,触发检测用AS脚本进行检测
入侵手段
bp抓包改包
原理
检测写在APP客户端代码或者所调用的HTML页面中
二、服务器端检测:
1)服务器端MIME检测
MIME补充知识:
概述:
MIME即多用途互联网邮件扩展类型。是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。
常见类型:
超文本标记语言文本 .html,.html text/html
普通文本 .txt text/plain
RTF文本 .rtf application/rtf
GIF图形 .gif image/gif
JPEG图形 .jpeg,.jpg image/jpeg
au声音文件 .au audio/basic
MIDI音乐文件 mid,.midi audio/midi,audio/x-midi
RealAudio音乐文件 .ra, .ram audio/x-pn-realaudio
MPEG文件 .mpg,.mpeg video/mpeg
AVI文件 .avi video/x-msvideo
GZIP文件 .gz application/x-gzip
TAR文件 .tar application/x-tar
典型代码:
1 | if ((($_FILE["file"]["type"] == "image/gif") ||$_FILE["file"]["type"] == "image/jpeg") ||$_FILE["file"]["type"] == "image/pjpeg") |
源码
1 | $is_upload = false; |
问题
由于是校验请求头content-type字段的,所以会被hacker抓包修改
入侵手段
通过抓包来修改Http头部的content-type,使其满足源码所给出的类型,即可绕过
2)服务器拓展名检测
黑名单检测
原理
我个人的理解认为黑名单就是程序员根据经常遇见可能存在漏洞的文件名创建的一个禁止上传目录
可以比喻成一个自动监控系统,里面存有过去一些犯人的面部照片,如果符合就会产生预警禁止入内,但是如果有些罪犯并没有被录入或者化了妆,那么就有混进去的危险
源码
1 | $is_upload = false; |
问题
- 不能保证黑名单中存在所有恶意文件后缀
- 如果没有区分大小写,可能会因为系统问题导致被绕过
- 可能会被特殊文件名绕过
入侵思路
- 利用文件名大小写绕过
- 后缀名双写绕过 eg:pphphp
- 解析漏洞绕过
- 利用操作系统命名规则绕过
- .htaccess文件攻击
- 00截断绕过:由于00代表结束符,所以会把00之后的字符删除
白名单检测(较黑名单更为安全)
原理
根据黑名单的类比,白名单规定了只允许特定的人进入,相当于发请帖的模式,如若对象没有在邀请名单中就无法入内
源码
1 | $is_upload = false; |
问题
并不能完全防御上传漏洞,如当文件名为*.asp;1.jpg时,白名单过滤机制识别文件扩展名为.jpg,即验证通过可以上传。
入侵手段
- 00截断绕过:由于00代表结束符,所以会把00之后的字符删除
- 解析漏洞绕过
3)文件头检查文件
文件头补充知识
概述:
文件头就是为了描述一个文件的一些重要的属性,比如图片的长度、宽度、像素尺寸等,当程序打开文件时读取这些属性对文件进行处理。
源码
1 | function getReailFileType($filename){ |
问题
无法防御入侵者在文件头后插入恶意代码的情况
入侵手段
- 将文件的前面加通过16进制编辑器添加文件头。如:GIF89a
- 将一个正常的图片文件添加到一个php文件前面,通过命令行copy。(图片马)
4)目录验证
目录验证知识补充
概述:
在文件上传时,允许用户将文件放到指定的目录中,如果不存在指定目录,就会先创建目录再将文件放入。
入侵手段
HTML代码中有一个隐藏标签<input type="hidden" name="Extension" value="up"/>
,这是文件上传默认的文件夹,我们可以将参数value的值修改就可以达到目的。结合IIS解析漏洞(当建立*.asa
、*.asp
格式的文件夹时,其目录下的任意文件都将被IIS当做asp文件来解析),就可以将恶意代码写入。
5)恶意文件内容检测
原理
系统检测提交内容是否包含有webshell等数据
问题
由于是非人工检测,有些过于隐蔽的内容难以检测出来
绕过手段
考虑使用强混淆的weevely[^1]进行尝试,成功连接后即可进行操作
基于文件处理的漏洞:
一、条件竞争
原理
很多上传场景里会先将文件上传到服务器,然后通过rename修改名称再将原文件删除,因此可以通过条件竞争的方式在文件删除之前多线程访问webshell。一般使用intruder同时发两个包:上传文件+访问webshell地址
入侵思路
利用上传文件与服务器检测到webshell并删除的时间差,不断的放包并写入生成木马的文件,达成目的。
二、二次渲染
原理
二次渲染概述:
这是一种较高端的处理手段,当我们把包含恶意代码的图片上传,后台会对图片内容进行了二次渲染。相当于把原本属于图像数据的部分抓了出来,然后删除其他部分
源码
1 | $is_upload = false; |
问题
不能保证处理了所有的数据,仍残留一些位置没有处理,所以可能存在隐患
入侵思路
将上传后经过处理的图片马与原来的图片马进行对比,找到没有被修改处理过的地方,在该处写入webshell
基于文件解析的漏洞:
IIS篇
1);截断
概述
当文件名为abc.asp;xx.jpg时,IIS6会将此文件解析成abc.asp,文件名被截断了,从而导致脚本被执行。
2)处理文件夹拓展名出错
概述
在IIS6.0的网站目录中创建有*.asp形式的目录,该文件夹下的所有文件都会以asp脚本格式进行解析
3)WebDav漏洞
概述
WebDav是一种基于HTTP1.1协议的通信协议。在GET、POST、HEAD等HTTP标准方法之外扩展了新方法。
攻击者可以通过PUT方法向服务器上传危险脚本。
Nginx篇
1)php配置错误导致的解析漏洞
测试准备:
/test.jpg/test.php (test.jpg的内容为一句话木马)
详细解析:
在浏览器中访问 http://127.0.0.1/test.jpg Nginx拿到文URL/test.jpg/test.php后,一看后缀是.php,便认为该文件是php文件,转交给php去处理。php一看/test.jpg/test.php不存在,便删去最后的/test.php,又看/test.jpg存在,便把/test.jpg当成要执行的文件了
这其中涉及到php的一个选项:cgi.fix_pathinfo,该值默认为1,表示开启。开启这一选项有什么用呢?看名字就知道是对文件路径进行“修理”。何谓“修理”?举个例子,当php遇到文件路径“/aaa.xxx/bbb.yyy/ccc.zzz”时,若“/aaa.xxx/bbb.yyy/ccc.zzz”不存在,则会去掉最后的“/ccc.zzz”,然后判断“/aaa.xxx/bbb.yyy”是否存在,若存在,则把“/aaa.xxx/bbb.yyy”当做文件“/aaa.xxx/bbb.yyy/ccc.zzz”,若“/aaa.xxx/bbb.yyy”仍不存在,则继续去掉“/bbb.yyy”,以此类推。
目前我们还没能成功执行代码,因为新版本的php引入了“security.limit_extensions”,限制了可执行文件的后缀,默认只允许执行.php文件。我们可以考虑修改该文件中的“security.limit_extensions”,添加上.jpg,这样php就认为.jpg也是合法的php文件后缀了。
漏洞利用
白名单和黑名单都可以使用这个方法,我们可以在需要的文件后加入一段不存在的但符合要求的格式,这样就可以成功上传,并且解析的时候是我们上传的一句话木马
Apache篇
1)多后缀名
Apache认为一个文件可以有多个后缀,举个栗子:example.php.small.wolf,Apache会从右往左辨别后缀,一开始看到这个wolf,不认识所以继续下一个small,又不认识,于是读到了php, 啊,这下认识了。所以他就把我们输入的这个文件当作了php文件,也就不继续往下读了。
漏洞利用
主要是针对的黑名单使用,因为白名单的文件后缀Apache应该都认识:laughing:。具体操作很简单,可以仔细想一想哇。
2)罕见后缀
hxd,你可能知道茴香豆有多少种写法,可我打赌你不知道php文件有多少种后缀,php,phtml,php3,pht·······这些都是Apache和php认可的php程序后缀,那么写黑名单滴格格会不会有遗漏哇,嘿嘿,这就给了我们绕过的机会
3)换行解析漏洞
原理
此漏洞的出现是由于apache在修复第一个后缀名解析漏洞时,用正则来匹配后缀。在解析php时xxx.php\x0A将被按照php后缀进行解析,导致绕过一些服务器的安全策略。
漏洞利用
用burp抓包后在例如1.php后插入以一个\x0A绕过黑名单过滤
访问1.php%0A,即可看到文件被当作php解析,那么就可以绕过一些黑名单过滤啦
基于文件包含的漏洞:
概述
服务器执行PHP文件时,可以通过文件包含函数加载另一个文件中的PHP代码,并且当做PHP来执行,这会为开发者节省大量的时间。这意味着可以创建供所有网页引用的标准页眉或菜单文件。当页眉需要更新时,只更新一个包含文件就可以了,或者当向网站添加一张新页面时,仅仅需要修改一下菜单文件(而不是更新所有网页中的链接)。
常见的文件包含函数
- require(),找不到被包含的文件时会产生致命错误,并停止脚本运行。
- include(),找不到被包含的文件时只会产生警告,脚本将继续运行。
- include_once()与include()类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含。
- require_once()与require()类似,唯一区别是如果该文件中的代码已经被包含,则不会再次包含。
分类
本地文件包含:在网站服务器本身存在恶意文件,然后利用本地文件包含使用
远程文件包含:远程文件包含就是调用其他网站的恶意文件进行打开
入侵思路
先往网站上传一个图片马,然后更改到存在文件上传漏洞的地址中,那么就会将我们上传的图片马当作php文件来解析,此时我们的木马就是生效啦。
部分bypass手段解释:
一、00截断
原理:
当一个字符串中存在空字符的时候,在被解析的时候会导致空字符后面的字符被丢弃。例如我们往1.php.jpg中插入空字符变成1.php0x00.jpg,解析后就只会剩下1.php(空字符插入需要利用burp抓包后在HEX中找到空格对于的16进制编码将它改成00)
限制条件:
php版本要小于5.3.4,magic_quotes_gpc需要为OFF状态。
正确用法
时机:数据包中必须含有上传后文件的目录情况才可以用,比如数据包中存在path:uploads/,那么攻击者可以修改path的值来构造path:uploads/aa.php%00
0x00与%00
一开始接触00截断被这俩困扰了好久,后来了解了一下,它们最终的结果都是一样的,都代表着chr(0),即空字符,只不过使用的位置不同,0x00代表16进制的空字符00,需要在HEX中改为00,进行截断,而%00是URL解码之前的字符,它被解码成16进制ASCII码之后实际上也是0x00,所以它们最终都对应的是空字符,这里%00可以用在URL中如xx.php?filename=test.php%00.txt,也可以直接插在Burp包中的路径中,如path=shell.jsp%00.txt
二、.htaccess文件攻击
概述:
.htaccess文件是Apache服务器中的一个配置文件,它负责相关目录下的网页配置.通过htaccess文件,可以实现:网页301重定向、自定义404页面、改变文件扩展名、允许/阻止特定的用户或者目录的访问、禁止目录列表、配置默认文档等功能。
入侵思路
创建一个htaccess文件,内容为SetHandler application/x-httpd-php
此文件可以将该目录下的所有文件解析成php文件来执行
我们接下来上传一个一句话木马后缀为jpg格式则依然能连接正常处理
三、windows文件流绕过
概述
NTFS交换数据流(ADS),ADS是NTFS磁盘格式的一个特性,在NTFS文件系统下,每个文件都可以存在多个数据流。通俗的理解,就是其它文件可以“寄宿”在某个文件身上,而在资源管理器中却只能看到宿主文件,找不到寄宿文件
背景知识
:NTFS卷下完整文件名为
所以sample.txt文件名实际上是sample.txt::$DATA的缩写,sample.txt为文件名,$DATA为文件流类型,故sample.php::$DATA等价于sample.php,这样我们就可能可以绕过黑名单过滤
四、操作系统命名规则绕过
概述
不同的操作系统有文件不同的命名规则,利用这样规则我们可能可以绕过黑名单
例子
- 上传不符合windows文件命名规则的文件名
test.asp (空格)test.asp.
test.php:1.jpg
…….
会被windows系统自动去掉不符合规则符号后面的内容。 - linux下后缀名大小写
在linux下,如果上传php不被解析,可以试试上传pHp后缀的文件名。
五、利用.user.ini进行攻击
.user.ini比.htaccess用的更广,不管是nginx/apache/IIS,只要是以fastcgi运行的php都可以用这个方法。不像.htaccess只能运用在apache上。
需要先补充php.ini的知识,php.ini是php默认的配置文件,其中包含了很多PHP的配置,这些配置中,又分为几种:PHP_INI_SYSTEM
、PHP_INI_PERDIR
、PHP_INI_ALL
、PHP_INI_USER
。
模式 | 含义 |
---|---|
PHP_INI_USER | 可在用户脚本(例如ini_set())或Windows注册表以及.user.ini中设定 |
PHP_INI_PERDIR | 可在php.ini,.htaccess或httpd.conf设定 |
PHP_INI_SYSTEM | 可在php.ini或httpd.conf中设定 |
PHP_INI_ALL | 可在任何地方设定 |
除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录($_SERVER['DOCUMENT_ROOT']
所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。
在 .user.ini
风格的 INI 文件中只有具有 PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置可被识别。
.user.ini实际上就是一个可以由用户自定义的php.ini,我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。
php配置项中有两个比较有意思的项。
auto_prepend_file
指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。
auto_append_file
指定一个文件,自动包含在要执行的文件后,类似于在文件后调用了require()函数。
使用方法很简单,直接写在.user.ini中即可,格式如:
1 | auto_prepend_file=01.gif \\01.gif就是我们要包含的文件 |
六、各种另类绕过方法(笔者了解不多)
多个分号绕过
概述
文件进行解析的时候,遇到多个分号可能会解析不到文件名,导致绕过。
Content-Disposition: form-data;name=”file_x”;;;filename=”test.php”
多个Content-Disposition绕过
原理
在IIS的环境下,上传文件时如果存在多个Content-Disposition的话,IIS会取第一个Content-Disposition中的值作为接收参数,而如果waf只是取最后一个的话便会被绕过
利用大小上限绕过
原理
WAF对校验的用户数据设置大小上限,此时可以构造一个大文件的木马,前面都是填充的垃圾内容
针对fliename绕过
尝试多加一个filenam或者将filename换位置
删除实体里面的Conten-Type字段
第一种是删除Content整行,第二种是删除C后面的字符。删除掉ontent-Type: image/jpeg只留下c,将.php加c后面即可,但是要注意,双引号要跟着c.php
特殊的长文件名绕过
文件名使用非字母数字,比如中文等最大程度的拉长,不行的话再结合一下其他的特性进行测试:
shell.asp;王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王王.jpg文件重命名绕过
如果web程序会将filename除了扩展名的那段重命名的话,那么还可以构造更多的点、符号等等。
Boundary边界不一致
原理
每次文件上传时的Boundary边界都是一致的,但如果容器在处理的过程中并没有严格要求一致的话可能会导致一个问题,两段Boundary不一致使得waf认为这段数据是无意义的。
文件名处回车
文件上传漏洞防御思路:
引用自美创安全实验室
一、系统运行时的防御:
- 文件上传的目录设置为不可执行。只要web容器无法解析该目录下面的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响,因此这一点至关重要。
- 判断文件类型。在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文
件类型检查中,强烈推荐白名单方式,黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者resize函数,在处理图片的同时破坏图片中可能包含的HTML代码。 - 使用随机数改写文件名和文件路径。文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用了随机数改写了文件名和路径,将极大地增加攻击的成本。再来就是像shell.php.rar.rar和crossdomain.xml这种文件,都将因为重命名而无法攻击。
- 单独设置文件服务器的域名。由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传crossdomain.xml、上传包含Javascript的XSS利用等问题将得到解决。
- 使用安全设备防御。文件上传攻击的本质就是将恶意文件或者脚本上传到服务器,专业的安全设备防御此类漏洞主要是通过对漏洞的上传利用行为和恶意文件的上传过程进行检测。恶意文件千变万化,隐藏手法也不断推陈出新,对普通的系统管理员来说可以通过部署安全设备来帮助防御。
二、系统开发阶段的防御
- 系统开发人员应有较强的安全意识,尤其是采用PHP语言开发系统。在系统开发阶段应充分考虑系统的安全性。
- 对文件上传漏洞来说,最好能在客户端和服务器端对用户上传的文件名和文件路径等项目分别进行严格的检查。客户端的检查虽然对技术较好的攻击者来说可以借助工具绕过,但是这也可以阻挡一些基本的试探。服务器端的检查最好使用白名单过滤的方法,这样能防止大小写等方式的绕过,同时还需对%00截断符进行检测,对HTTP包头的content-type也和上传文件的大小也需要进行检查。
三、系统维护阶段的防御
- 系统上线后运维人员应有较强的安全意思,积极使用多个安全检测工具对系统进行安全扫描,及时发现潜在漏洞并修复。
- 定时查看系统日志,web服务器日志以发现入侵痕迹。定时关注系统所使用到的第三方插件的更新情况,如有新版本发布建议及时更新,如果第三方插件被爆有安全漏洞更应立即进行修补。
- 对于整个网站都是使用的开源代码或者使用网上的框架搭建的网站来说,尤其要注意漏洞的自查和软件版本及补丁的更新,上传功能非必选可以直接删除。除对系统自生的维护外,服务器应进行合理配置,非必选一般的目录都应去掉执行权限,上传目录可配置为只读。
四、防护代码参考
引自DVWA中impossible源码
1 |
|
- strtolower()函数,对字符串进行小写操作,防止了用大小写绕过
- uniqid()函数,基于以微秒计的当前时间,生成一个唯一的ID
- $target_file = md5( uniqid() . $uploaded_name ) . ‘.’ . $uploaded_ext;:对上传的文件进行了重命名,为md5值,导致00截断无法绕过过滤规则
- 通过imagecreatefromjpeg()和imagecreatefrompng()函数将上传的图片文件重新写入到一个新的图片文件中,这两个函数会自动将图片中的有害元数据抹除,因此即使黑客上传了一张图片马也会被这个函数过滤成一个纯正的图片。
- imagedestroy( $img )将用户上传的源文件删除
- unlink( $temp_file )删除过滤过程中产生的任何临时文件
[^1]: Weevely工具是Python语言编写的,是一种隐蔽的类终端的PHP webshell,其生成的webshell内容是经过特殊方式混淆处理的,详细使用可以参考的博客