文件上传漏洞原理和进阶利用
文件上传漏洞原理和进阶利用
原理
文件上传漏洞是指当一个应用程序允许用户上传文件时,没有对文件类型、文件内容和上传位置进行适当的验证和处理,从而导致恶意用户能够上传并执行恶意文件(如 Web Shell、木马病毒等)。
靶场练习
靶场地址:https://github.com/c0ny1/upload-labs
pass01-前端校验
观察页面源代码,其实本关是仅依赖前端js进行过滤,可以直接禁用js就能完成绕过。

在火狐浏览器中,更改浏览器配置
网页栏输入about:config, 搜索javascript, 将javascript.enabled的值设置为false, 即表示浏览器禁止执行javascript代码

成功上传,并获得文件路径http://upload-labs/upload/upload.php

访问文件路径为空白,说明成功解析
pass02-MIME绕过
检查源代码,发现是MIME检测,这里的$_FILES['upload_file']['type']就是从HTTP请求头中获取的MIME类型。系统只允许上传JPEG、PNG和GIF这三种图片格式的文件。

那么可以通过抓包来更改HTTP请求头中的Content-Type字段,实现绕过

将application/octet-stream改为image/jpeg即可

pass03-上传可解析后缀
查看源代码,发现对文件强制转小写,过滤了.asp,.aspx,.php,.jsp后缀文件

那么可以尝试后缀php1,php2,php3,php4.php5,phtmI, pht

pass04 -(.htaccess)
先看源代码,发现都给禁用了
1 | |

但是.htaccess还是没有过滤,可以重写文件解析规则绕过,上传一个.htaccess,这个文件内容的意思是告诉apache当遇到qianxun.jpg文件时,按照php去解析,文件内容如下:
1 | |
分别将.htaccess文件和xxx.jpg文件上传,z最后成功解析

pass05 -(黑名单验证,.user.ini.)
本关和上一关差不多,但是htaccess也被禁用了

黑名单未过滤.user.ini文件(PHP 的用户级配置文件),为绕过留下入口。
可以使用.user.ini+ 图片马的组合实现绕过
上传.user.ini:配置 “自动包含图片马”
创建.user.ini文件,内容仅一行:
1 | |
告诉 PHP——只要执行当前目录下的任何 PHP 文件,都必须先自动包含666.jpg文件(无论666.jpg的后缀是什么,都会被当作 PHP 代码解析);
随后上传xxx.jpg
1 | |
随后访问合法的PHP文件即可

pass06-大小写绕过
这关把能禁的后缀都禁了,但是发现没有使用strtolower()函数
strtolower() 函数把字符串转换为小写

直接上传web.Php即可

pass07-空格绕过

这关并没有用trim()去除空格,可以使用空格绕过黑名单
绕过逻辑:“空格伪装”+“系统自动去空格”
- 源码缺陷:未用
trim()删除文件名末尾空格,导致php(带空格)后缀绕开黑名单; - 系统特性:Windows 自动剔除末尾空格,将
shell.php还原为可解析的shell.php;

pass08-点号绕过
没有使用deldot()过滤文件名末尾的点,可以使用文件名后加.进行绕过

源码缺陷:修复了空格漏洞(加
trim()),但遗漏了点号漏洞(缺deldot()),导致php.(带点)后缀绕开黑名单;系统特性:Windows 自动剔除文件名末尾的点号,将
shell.php.还原为可解析的shell.php;

pass09-特殊字符::$DATA绕过
本关卡未对::$DATA进行过滤

利用 “源码未过滤::$DATA + Windows 自动剥离::$DATA” 的叠加效应:
构造文件名
shell.php::$DATA→ 源码未过滤,直接保存到 Windows 服务器;Windows 自动剥离
::$DATA→ 实际文件名变为shell.php;

连接蚁剑,不能使用::$DATA

pass10-点空格点绕过
这关对文件后缀处理其实很全面
小知识:deldot()函数从后向前检测,当检测到末尾的第一个点时会继续它的检测,但是遇到空格会停下来

可利用「点 + 空格 + 点」的组合绕过,原理如下:
构造文件名:通过 Burp 修改为
shell.php. .(点 + 空格 + 点);源码处理:
deldot()→ 删末尾点 →shell.php.;trim()→ 删首尾空格 →shell.php..;Windows 处理:自动删最后一个点 →
shell.php;


pass11-双写绕过
这里的防御逻辑是会将黑名单的后缀替换为空

str_ireplace() 函数不区分大小写,大小写绕过不适用,可以采用双写绕过

连接蚁剑成功,php文件成功被解析

pass12-白名单绕过,00截断GET型
只能上传jpg,png,gif图片的方式
用GET的方式传参,要求是:随机数+时间参数 +.+可控的类型,保证传入的参数可控

采用前提
- PHP 版本:需低于 5.3.4(5.3.4 及以上版本修复了 %00 截断漏洞);
- 配置要求:
magic_quotes_gpc = Off(若开启,%00 会被转义为\%00,无法解码为 \0,截断失效); - 传参方式:GET(Pass-12)——GET 参数会被 Apache/PHP 自动 URL 解码,POST 参数需手动解码(对应 Pass-13)
原理:php的一些函数的底层是C语言,而move_uploaded_file就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00

注意:连接蚁剑要将中间的字母数字都混杂内容清理掉,然后便可成功连接

pass13-白名单绕过,post 00截断
和前面一关差不多,但是post提交不会自动进行url解码

在请求包中修改URL,进行解码即可

访问后发现,php文件能够被浏览器解析

pass14-图片马上传
这关会读取判断上传文件的前两个字节,判断上传文件类型,并且后端会根据判断得到的文件类型重命名上传文件

使用 图片马 + 文件包含 绕过
制作图片马copy 1.png/b + xxx.php pass14.png

然后这关要使用文件包含才能解析木马的执行

因为上传图片马之后会被重命名图片所以下面的payload的图片名字可以在上传之后复制图片链接就可以了
构造的URL为include.php?file=upload/3420210320172751.png
然后用蚁剑连接

pass15-getimagesize图片马
这里和上一关差不多,通过使用getimagesize()检查是否为图片文件,所以还是可以用第十四关的图片马绕过,并使用文件包含漏洞解析图片马

pass16-exif_imagetype图片马
exif_imagetype()读取一个图像的第一个字节并检查其后缀名。
返回值与getimage()函数返回的索引2相同,但是速度比getimage快得多。需要开启php_exif模块。

pass17-二次渲染绕过
- 文件头校验:通过
imagecreatefromxxx()函数校验 —— 只有合法的 JPG/PNG/GIF 文件头(如 JPG 的FF D8 FF、PNG 的89 50 4E 47)才能被函数解析,普通 PHP 文件会直接报错; - 二次渲染:服务器会读取原始图片的像素数据,重新生成一张全新的图片并保存 —— 这会清除图片中所有非像素数据的恶意代码(比如普通 “图片马” 的 PHP 代码会被完全抹除)。

可以找到图片二次渲染后 “不会被修改的字节区域”,将恶意 PHP 代码写入该区域,使渲染后的图片仍包含可执行代码。

用010Editorr打开,尝试将一句话插到此位置

然后蚁剑连接

pass18-条件竞争绕过
代码的核心防御逻辑是「先上传文件→校验文件合法性→合法则保留 / 非法则删除」,攻击者利用 “文件上传后→删除前的时间窗口”,通过高频请求访问该文件,实现恶意代码执行(即使文件最终被删除,只要请求命中时间窗口就会生效)。
1 | |

1、创建shell.php,内容为 “写文件脚本”(目的是在服务器生成永久木马,避免依赖原文件):
1 | |
2、构造条件竞争请求
1 | |
请求成功后,可访问写入的木马

pass19-竞争条件
| 维度 | Pass-18 | Pass-19 |
|---|---|---|
| 文件名规则 | 保留原文件名 | 按 time() 重命名(可预判) |
| 竞争窗口 | 上传后 → 校验前 | 重命名后 → 校验前 |
| 路径预判 | 原文件名(易) | 时间戳(需实时计算) |
| 核心依赖 | 先传后验 | 先传 + 重命名 + 后验 |
pass20-文件名绕过
系统没有对上传文件的内容进行验证,而是仅对用户输入的文件名进行了检查。具体来说,系统使用文件后缀名的黑名单进行过滤,但由于上传的文件名是用户可控的,这为绕过机制提供了可能性。此外,move_uploaded_file()函数还有一个特性,即它会忽略文件名末尾的 /.,这可以被利用来绕过后缀名的黑名单检查,从而上传恶意文件。
在upload-19.png改为upload-19.php/.,修改完直接放包

最后成功连接

文件上传漏洞核心防护方法
一、核心校验:白名单 + 内容验证(杜绝伪装绕过)
后缀白名单(替代黑名单)
- 仅放行业务必需后缀(如
.jpg/.png/.pdf/.doc),拒绝所有未明确允许的类型; - 统一处理文件名:去空格、删末尾点号、转小写,避免
php「空格」、php.「点号」、Php「大小写」绕过。
- 仅放行业务必需后缀(如
文件内容 / 魔数校验(防图片马)
- 校验文件头特征字节(魔数):JPG(
FF D8 FF)、PNG(89 50 4E 47)、GIF(47 49 46 38); - 图片类文件二次渲染:重新生成图片(清除非像素区恶意代码),文本类文件扫描是否含
<?php/<script>等执行代码。
- 校验文件头特征字节(魔数):JPG(
MIME 类型辅助校验
验证Content-Type(如image/jpeg),避免攻击者篡改后缀但未改 MIME 的绕过。
二、流程管控:先验后传 + 随机重命名(防条件竞争 / 路径预判)
先验后传(核心)
先在临时目录完成所有校验(后缀 / 内容 / 大小),校验通过再移动到上传目录;禁止 “先保存文件→再校验→非法删除” 的逻辑(避免条件竞争)。
强制随机重命名放弃原文件名,用md5(time().rand(1000,9999))+合法后缀
命名,杜绝文件名预判(如 Pass-19 的时间戳预判)。
限制文件大小 / 权限
- 按业务场景限制单文件大小(如图片≤5MB);
- 上传目录仅赋予「读 / 写」权限,禁止「执行」权限(Linux:
chmod 644 目录)。
三、服务器 / 环境加固(兜底阻断解析)
禁止上传目录解析脚本
- Apache:上传目录
.htaccess配置php_flag engine off,禁用 PHP 解析; - Nginx:配置上传目录
location ~ \.php$ { deny all; },拒绝所有脚本解析。
- Apache:上传目录
上传目录隔离
将上传文件存放在「非 Web 根目录」(如/data/upload),而非/var/www/html/upload,即使解析规则失效也无法 Web 访问。
环境配置加固
- 升级 PHP 至 7.4+(修复 %00 截断、数组绕过等漏洞);
- 禁用危险函数:
eval/exec/system/passthru,关闭allow_url_fopen; - 禁止
.htaccess/.user.ini生效(阻断解析规则篡改)。
四、前端 / 参数防护(减少攻击面)
前端基础过滤
JS 校验后缀、文件大小,拦截普通误操作(仅辅助,需后端兜底);
参数类型校验
禁止数组传参(如filename[]),强制文件名为字符串类型,避免数组绕过(Pass-20)。
五、运维监控(长期保障)
- 日志审计:记录所有上传请求(文件名、IP、时间),审计高频上传、特殊字符文件名;
- 定期扫描:检测上传目录是否存在
.php/.asp等危险后缀文件; - 漏洞测试:定期用图片马、%00 截断、数组传参等方式验证防护有效性。