file-upload

star 72

文件上传多策略综合检测 — 上传链路未知时先走通用测试流程,再按"绕过技巧 + 效果验证"组合子检测,输出可复现 PoC 与修复建议。

Q16G By Q16G schedule Updated 6/7/2026

name: file-upload description: >- 文件上传多策略综合检测——覆盖扩展名 / MIME / 魔术数 / 解析漏洞 / 上传链组合(LFI / Zip Slip / SVG XSS / SVG XXE)。 流量含 multipart/form-data 上传、响应回显落盘路径、上传后文件可被 GET 解析、上传字段含 filename / path 可控时使用。 when-to-use: 当目标存在文件上传功能、上传链路与防护未知,需要综合绕过与效果验证测试时 allowed-tools: bash,read_file,list_files,rg user-invocable: false

文件上传:多策略综合检测(黑盒)

1. 触发线索 / 适用信号

以下是已知的文件上传触发线索,作为基线起点而非必检硬清单。结合流量与响应特征动态调整:

  • 适用且已完成 → [x] done
  • 明确不适用 → [-] n/a (原因)(原因要具体到响应特征)
  • 基线未列出但实际发现 → [+] added (来源)

响应特征命中信号(漏洞-specific):

  • 响应里返回完整 URL / 相对路径 / 对象存储 Key(/uploads/<hash>.<ext> / /static/<userid>/<filename> / S3 预签名 URL)
  • 上传后可直接 GET 该 URL 拿到文件
  • 文件类型校验错误信息(Invalid file type / File extension not allowed / Magic number mismatch
  • 响应里出现框架库名(multer / formidable / commons-fileupload / Spring MultipartFile

入口类型粗筛(仅作类似场景示例 不限于此):

  • Content-Type: multipart/form-data 端点(典型 file part)
  • JSON body 含 base64 文件字段(fileContent / data / base64
  • 富文本编辑器内嵌上传组件(CKEditor / TinyMCE / UEditor 的 upload 子端点)
  • 头像 / 资料 / 附件 / 简历 / 工单 / 聊天 / bulk import / template upload / 备份恢复
  • 路径可控字段:上传请求体含 filename / path / dir / subPath / targetDir
  • 落点位于 web 可执行目录(直接 URL 可访问 + 服务端会解析脚本),或进入二次处理链(OCR / 缩略图 / 转码 / 解压 / 文档导入),或对象存储(CDN / S3 / OSS)

业务命名(如 avatar / attachment / import)只作粗筛——sink 语义相同就属此范围。JSON body 任意 base64 / file 字段都是候选;具体参数位置由 HAR 实际请求推导,不在此预设清单。

2. 造成原因

source 是用户上传的整套文件元素:文件内容字节流、文件名(含扩展名)、Content-Type、multipart part header、JSON 中的路径 / 文件名字段。sink 是文件存储 + 后续消费链:磁盘 / 对象存储的写入位置、web 服务器对该路径的解析行为、业务后续二次解析(OCR / 缩略图 / 转码 / 解压 / 文档导入 / 预览渲染)、跨身份回读通道。

文件类型 / 魔术数 / 扩展名 / 落点路径 / 解析器配置任一缺失校验,即构成文件上传漏洞——攻击者可让服务端把"用户提交的字节流"识别为可执行脚本或可越权读取的资源。仅扩展名白名单不够(Content-Type / 文件名双扩展 / 解析器多扩展回退都可绕过),仅魔术数校验不够(SVG / PDF / Office 文档都是合法格式但内嵌可执行内容),仅落盘成功不构成漏洞(必须看后续如何消费)。

中间件解析行为是放大器:Apache 旧版 mod_mime 按文件名后缀链解析、Nginx %00 截断历史漏洞、IIS 短文件名 / 分号解析、Tomcat PUT / JSP 解析差异——同一份上传产物在不同中间件下的危害天差地别。

3. 响应信号映射

列出文件上传黑盒可观测的响应通道集合(observation-channel)——攻击效果可被黑盒探测的侧信道。输入位置(multipart 文件字节流 / filename / Content-Type / JSON base64 / 路径字段 / archive entry / 嵌入式 payload 等本漏洞典型 source)从 HAR 实际请求推导,本节不预设清单。

响应观察通道集合(observation-channel):

  • path-echo:响应直接回显落盘路径 / URL / 对象存储 Key
  • direct-parse:GET 上传后的 URL,响应被服务端解析为脚本(如 PHP 输出 / JSP 输出)
  • oob-callback:上传文件被解析后触发带外回连(DNS / HTTP)
  • secondary-trigger:业务预览 / OCR / 缩略图 / 转码 / 解压链触发后产生可观测效果
  • cross-identity-read:未登录 / 其他用户能 GET 到上传文件
  • overwrite-diff:路径可控时覆盖已有文件,前后回读差异可见
  • error-fingerprint:错误响应回显文件类型校验逻辑(库名 / 行号 / 校验链)

4. 常见类型

类型 触发条件 响应特征 适用环境
扩展名绕过 扩展名白名单校验不严 上传 .phtml / .phar / .pht / .php5 / .shtml / .jspx / .cer / .cdx 通过 Apache / Tomcat / IIS
双扩展名 多扩展回退解析 shell.php.jpg 落盘后被 Apache 解析为 PHP Apache 旧版 mod_mime
MIME 类型绕过 仅校验 Content-Type 文件内容 PHP,但 part header Content-Type: image/jpeg 通过 全部
魔术数 + payload 拼接 仅校验文件头 GIF89a 头 + PHP payload 双格式文件 全部(依赖落点解析)
.htaccess 上传 上传目录允许 .htaccess 生效 上传 .htaccess 改写 AddType 让任意扩展解析为 PHP Apache
.user.ini 上传 PHP-FPM 启用 user.ini auto_prepend_file 包含已上传图片马 Nginx + PHP-FPM
web.config 上传 IIS 上传目录覆盖 handler web.config 改写 handler 让 .jpg 解析为 ASPX IIS
Nginx %00 截断 Nginx + PHP 旧版本 shell.jpg%00.php 解析为 PHP Nginx < 0.8.41 历史
IIS 短文件名 / 分号 IIS 解析差异 shell.asp;.jpg 被解析为 ASP IIS 6
SVG XSS SVG 直接挂 web 预览 SVG 内 <script> 触发存储型 XSS 全部(依赖渲染)
SVG XXE SVG 进 XML 解析器 SVG 内含外部实体读 /etc/passwd 服务端 XML 解析未禁 DTD
Office / Excel XXE docx / xlsx 进 XML 解析 XLSX [Content_Types].xml 含外部实体 POI / 老 OpenXML
xlsm 宏 / xlsx 公式注入 业务下载 Excel 给用户 `=cmd '/C calc'!A1` 公式 / VBA 宏
Zip Slip 解压时不校验 entry 路径 zip 内 entry 名 ../../../webroot/shell.php 全部含解压链
解压炸弹 解压不限大小 10KB zip 解压 4GB 全部含解压链
路径穿越覆盖 filename / path 字段不规范化 filename=../../webroot/index.jsp 覆盖 全部
上传 + LFI 组合 上传可达但目录不解析 + 存在 LFI sink 上传图片马 + LFI include 该图片 LFI 链存在时

5. 侦察输入

按以下方式从侦察输出中筛文件上传候选:

从 HAR / 端点账本

  • Content-Type: multipart/form-data 端点(直接定位 file part)
  • 参数名含 file / upload / attach / avatar / cover / image / photo / doc / resume / import / backup / template
  • 端点路径含 /upload / /file / /attachment / /import / /avatar / /media
  • JSON body 含 base64 大字段(>10KB 的字符串字段是候选)
  • 响应里出现落盘路径回显(/uploads/<hash>.<ext> / https://*.oss-cn-*.aliyuncs.com/<key> / s3.amazonaws.com/<bucket>/<key>
  • 富文本编辑器(CKEditor / TinyMCE / UEditor)的 /ueditor/controller / /ckeditor/upload 等子端点

从业务场景(由 page-analysis 输出):

下列业务场景仅作类似场景示例 不限于此;以 page-analysis 实际输出为准。

  • 电商:商品图 / 用户头像 / 评价附图 / 退货凭证 / 店铺资质
  • SaaS:租户 Logo / 团队头像 / 知识库附件 / 数据导入(CSV / Excel)
  • 内容 / 论坛:富文本图片 / 视频上传 / 用户简历 / 文章封面
  • HR / 招聘:简历上传(PDF / docx)/ 证件照
  • 金融 / 政企:合同附件 / 票据 / 资质证明(高敏感落点)
  • 运维 / 管理后台:备份包导入 / 模板包 / 升级包(最高危,常被服务端二次执行)

从身份切换 HAR 对比

  • 管理员端的"导入模板""恢复备份""上传补丁"端点高优先级测(落点常进可执行链)
  • 跨子系统的同名上传端点(多端口 / 多子域)每个独立测,不假设"主站测过子站也安全"

业务命名只作粗筛——sink 语义相同就是候选,JSON body 任一 base64 字段 / 任一含 filename 子字段都是候选

6. 中间件 / 解析器响应指纹

通过响应(Server / X-Powered-By / 错误回显 / 落盘 URL 格式)推断后端中间件 / 解析器 / 存储链路,优化 payload 选择。

中间件解析行为指纹

响应特征 推断中间件 payload 选择
Server: Apache/2.x + X-Powered-By: PHP/... Apache + PHP 优先 shell.php.jpg(mod_mime 多扩展)/ .phtml / .phar / .htaccess 上传
Server: nginx + X-Powered-By: PHP/... Nginx + PHP-FPM .user.ini 上传 / 老版本 %00 截断 / shell.jpg/x.php 路径解析
Server: nginx + Set-Cookie: JSESSIONID Nginx 反代 Tomcat .jsp / .jspx / .jspf / .war 上传
Server: Microsoft-IIS/... + X-AspNet-Version IIS + ASP.NET .aspx / .cer / .cdx / web.config 上传 / 短文件名 shell.asp;.jpg
Server: Apache-Coyote / Server: Apache Tomcat Tomcat .jsp / .jspx / 大小写 .JSP / PUT 方法
Server: gunicorn + Set-Cookie: sessionid Django / Flask 通常 RCE 链弱,关注 SVG XSS / XXE / Zip Slip

文件处理库 / 框架指纹

响应特征 推断库 优化方向
错误回显 MulterError / LIMIT_FILE_SIZE Node.js multer 检查 fileFilter 是否仅校验扩展名 / mimetype(皆可伪造)
错误回显 Formidable / 路径含 /tmp/upload_xxx Node.js formidable 检查 keepExtensions 与 hash filename
错误回显 FileUploadException / commons-fileupload Java commons-fileupload 关注历史 CVE(CVE-2023-24998 拒绝服务等)
错误回显 MultipartException / MaxUploadSizeExceeded Spring MultipartFile 检查 @RequestParam 后是否仅看 getOriginalFilename() 后缀
错误回显 werkzeug.formparser Flask 检查 secure_filename 是否调用
错误回显 django.core.files Django 检查 FileSystemStorage 的 path / name 是否规范化

存储路径回显指纹

落盘路径格式 推断存储链 风险点
/uploads/<uuid>.<ext> / /static/<hash>.<ext> 本地磁盘 + UUID 重命名 重命名 ≠ 安全,看目录是否解析脚本
/uploads/<userid>/<original_filename> 本地磁盘 + 保留原名 原名保留即扩展名可控 + 路径穿越候选
https://<bucket>.oss-cn-*.aliyuncs.com/<key> 阿里云 OSS 关注公开读 / 跨租户 key 猜测 / 预签名 URL 泄露
https://<bucket>.s3.amazonaws.com/<key> / s3.<region>.amazonaws.com AWS S3 同上 + 关注 bucket policy
/files/download?id=<num> DB 存储 + 下载端点 关注 IDOR + 跨身份回读

文件类型校验机制响应特征

响应文案 推断校验层 绕过方向
Invalid file extension / File type not allowed 仅扩展名白名单 双扩展 / 大写 / .phtml / .phar 等冷门后缀
Invalid MIME type / Content-Type not supported 仅 Content-Type 校验 改 part header Content-Type: image/jpeg
Magic number mismatch / Invalid file header 魔术数校验 文件头 GIF89a / \xFF\xD8\xFF + payload 拼接
Image processing failed / ImageMagick error 真实解析校验 polyglot 文件 / ImageTragick CVE / SVG XXE
无任何文案,直接落盘成功 仅检查文件存在 任意扩展尝试,按中间件解析行为命中

指纹仅作辅助判断——真实的 sink 验证仍需在 §9 闭环要求章节定义的可观测效果证据。

7. 思考检查点

加载本 skill 时按这些问题思考:

  • 文件落盘后通过什么链路再消费?直接 URL 访问被解析?业务预览触发 payload?OCR / 解压链二次执行?跨身份回读?仅落盘不是 sink
  • 校验在哪一层做:前端、扩展名、Content-Type、魔术数、还是真实解析?哪一层最薄弱?
  • 文件名 / 路径字段会不会进 filepath.Join 决定落点?能不能跨目录覆盖已有文件?
  • 中间件是 Apache / Nginx / IIS / Tomcat 中哪个?解析行为差异如何利用(双扩展 / .htaccess / web.config / 分号截断)?
  • 同一类上传入口(头像 / 附件 / 简历 / 导入 / 备份)是否每个独立测了?是否被假定"主站测过子站也安全"?
  • 解析链(zip 解压 / SVG XML / PDF JS / OCR / 转码)有没有显式做"嵌入式 payload"测试?
  • 落点是本地磁盘还是对象存储?对象存储场景关注点切换到"跨租户 key 猜测 + 公开读 + 覆盖",不是 RCE。

8. 检测方法论 / 决策树

全局约束(默认保守预算)

  • 单上传点最多 25 次请求(含基线、变体、失败重试);同一轮只改 1 个变量(扩展名 / MIME / 魔术数 / 文件名 / 路径之一)
  • 解压炸弹 / 极端嵌套样本禁止直接投递,先用小样本(如 3 层 1MB)验证防护边界
  • 命中真实可执行链路立即停止扩测,进入证据固化
  • 并发建议 1(多并发可能触发上传限流误判)

Step 0:基线采集

  1. 上传合法白名单文件(baseline.jpg 含真实 JPEG 头),记录:响应结构 / 落盘 URL / 命名规则 / 是否触发异步处理(缩略图 / OCR / 转码)
  2. 直接 GET 落盘 URL,记录 Content-Type / 内容 / 是否被服务端处理
  3. 收集约束:前端 accept 属性、后端报错文案、大小限制

Step 1:中间件 / 框架指纹判断

按 §6 响应指纹表识别中间件 + 文件处理库 + 存储链 + 校验机制。结论标注置信度(high / medium / low)。

Step 2:策略决策树

落点是本地磁盘 + web 可执行目录?
  → 优先:扩展名绕过 + 中间件解析(按指纹选 .htaccess / .user.ini / web.config / 双扩展)
落点是本地磁盘 + 静态目录(不解析脚本)?
  → 优先:SVG XSS / 路径穿越覆盖 / 上传 + LFI 组合
落点是对象存储(OSS / S3)?
  → 优先:跨租户 key 猜测 + 公开读 + 预签名 URL 泄露 + 覆盖
入口含解压链(zip / tar / rar)?
  → 必测:Zip Slip / 解压炸弹(小样本) / 同名覆盖 / 软链穿越
入口是 Office / Excel 导入?
  → 必测:XXE(docx / xlsx [Content_Types].xml 含外部实体) / 公式注入
入口含 filename / path 控制字段?
  → 必测:路径穿越覆盖

Step 3:变体探测(按"每轮只改 1 个变量")

维度 payload 范式
扩展名 fuzzing .php .phtml .pht .phar .php5 .php7 .php8 .jsp .jspx .jspf .asp .aspx .cer .cdx .shtml
大小写 .PHP / .PhP / .JSP / .AsPx
双扩展 shell.php.jpg / shell.jpg.php / shell.php.xxx
尾随字符 shell.php. / shell.php / shell.php/.
空字节 / URL 编码 shell.php%00.jpg / shell%2ephp / shell.ph%70
MIME 伪造 文件内容 PHP + part header Content-Type: image/jpeg
part header 不一致 multipart filename 与外层 form 字段 filename 不一致
魔术数 + payload 前缀 GIF89a / \xFF\xD8\xFF + PHP / JSP payload
Polyglot 同时是有效 JPEG + 有效 PHP(用 jhead / exiftool 注释段)
.htaccess AddType application/x-httpd-php .jpg(Apache)
.user.ini auto_prepend_file=shell.jpg(PHP-FPM)
web.config 改写 IIS handler 让 .jpg 解析为 ASPX
SVG XSS SVG 内 <script>alert('sastx_sentinel')</script>
SVG XXE SVG 内 <!ENTITY xxe SYSTEM "file:///etc/hostname">
Office XXE docx / xlsx 解包改 [Content_Types].xml 加 DTD
Zip Slip zip entry 名 ../../sastx_sentinel.txt../../webroot/sastx.jsp
路径控制字段 JSON {"filename":"../../sastx_sentinel.txt"} / {"path":"/tmp/sastx_test/"}

Step 4:效果验证(必做——仅"能上传"不算闭环)

按落点选择验证通道:

  • 直接访问:GET 落盘 URL,看 Content-Type 与响应内容是否为脚本执行结果
  • 业务预览 / 解析:触发预览 / OCR / 缩略图 / 转码后看是否产生跨链效果(如 SVG XSS 在预览页面命中、SVG XXE 带外回连)
  • 跨身份访问:未登录 / 其他用户能否 GET 上传文件,是否泄露租户数据
  • 覆盖写入:路径可控时验证是否覆盖已有文件(前后回读对比)
  • 解压落点:上传含 ../ 的 zip 后检查解压目标是否越界(找哨兵文件落点)
  • OOB 回连:SVG XXE / xlsx XXE 等场景配 DNS / HTTP 带外通道,看是否触发

Payload 范式与编码绕过

  • WAF 关键字过滤(拦 php / jsp)→ 改大小写 / 冷门扩展(.phtml / .jspx)/ part header 注入
  • 严格扩展名白名单 → 切换中间件配置文件思路(.htaccess / .user.ini / web.config
  • 真实解析校验拦合成 polyglot → 切 SVG / Office / PDF 嵌入路径(合法格式 + 内嵌 payload)

基线检查项(按 §4.3 三态标注)

  • 每个上传入口独立测过 Step 0-3(不复用结论跨入口推广)
  • 中间件 / 框架 / 存储链 指纹结论已落
  • 解析链场景显式测过嵌入式 payload(SVG / Office / 解压)
  • 路径控制字段独立测过穿越 / 覆盖
  • 跨身份回读测过未登录 / 其他用户 / 其他租户

9. 闭环要求(必须遵守)

闭环判定(confirmed / suspected / not_vulnerable)以 common/closure-verification.md 为准。下面只列本漏洞特有的可观测信号。

confirmed(必须挂可观测效果证据)

  • 可执行:上传文件 GET 后被服务端解析为脚本(PHP / JSP / ASPX 输出可控字符串,如 echo 时间戳 / md5),重复 3 次稳定
  • OOB 回连:上传文件被解析(SVG XXE / xlsx XXE / 中间件配置触发)后带外通道收到明确归属本上传的请求,含时间戳与唯一 token
  • 存储型 XSS:SVG / HTML 上传后在业务预览页面触发 <script>,截图或 DOM 证据可复核
  • 路径穿越覆盖:哨兵文件 sastx_sentinel_<ts>.txt 落在越界目录,且前后回读对比可见
  • Zip Slip:解压后哨兵文件出现在 zip 解压目标目录之外(如父目录 / web 根),路径可证
  • 跨身份越权读:未登录 / 其他用户 / 其他租户 GET 上传文件成功且返回真实业务内容
  • 上传 + LFI 链:上传图片马 + LFI include 该图片,LFI 端点返回图片马 payload 执行结果

suspected(落 status=needs_review

  • 上传成功 + 落盘 URL 回显,但 GET 该 URL 返回原始字节流(未被解析)
  • 中间件配置文件(.htaccess / web.config)上传成功但未观察到改写后的解析行为
  • 文件类型校验绕过成功(如 .phtml 通过),但落点目录不解析脚本
  • 解压成功但未确认 entry 落点越界
  • SVG 上传成功但未确认是否在业务预览渲染

not_vulnerable(落 status=not_vulnerable

  • 校验四层叠加全过:扩展名白名单 + Content-Type + 魔术数 + 真实解析,且服务端重命名 + 落点目录禁解析脚本
  • 该端点不接触文件流(不是真实上传入口,是 metadata API 误判)

禁止仅凭"上传成功 + 返回 URL / 对象存储 key / 落盘"判 confirmed——这些只到 suspected,必须叠加后续消费链的可观测效果证据。

反例义务(必须遵守)

why:反例义务属于交付契约——"该子系统无文件上传风险"或"已防护"结论是覆盖完整性的产物声明,缺失反向验证清单会让下游误信"该维度全站安全"。文件上传入口分散(头像 / 附件 / 简历 / 富文本 / 导入 / 备份 / 解压),逐入口失败漏报最高发。

写"未发现上传风险"或"已防护"前,产物必须包含:

  • 测过的上传入口完整清单(按 sink 语义枚举:头像 / 附件 / 简历 / 富文本 / 导入 / 备份恢复 / 媒体处理链 / 解压类 / 路径可控字段;多子系统按子系统独立分组结账)
  • 每个入口测过的变体维度(扩展名 / MIME / 魔术数 / polyglot / SVG XML / Office XML / 路径控制 / 解压穿越 / 中间件配置)
  • 每个入口的响应证据(上传响应 + 落盘 URL + 直接访问验证 + 触发链行为)
  • 解析链场景的"嵌入式 payload"测试结果(OCR / 预览 / 转码 / 解压落点)

清单不完整 → 结论降级为 partial-coverage 并显式声明未覆盖范围。

10. 具象化反例库

FP(看似命中实际不构成)

反例 1:上传成功 + 落盘 URL 回显,但 GET 返回原始字节流

  • 抽象规则:落盘 ≠ 解析 ≠ 漏洞
  • 具体场景:上传 shell.php 成功,响应回显 /uploads/abc.php,GET 该 URL 返回 200 + 原 PHP 源码
  • 关键识别特征:响应 Content-Type: text/plainapplication/octet-stream,body 是 <?php ...?> 原文
  • 排除方法:上传目录配了 php_admin_flag engine off 或被静态文件服务器代理,不解析 PHP;判 suspected,深挖能否通过 .htaccess / LFI 绕过

反例 2:上传 .phtml / .phar 通过校验,但落点目录不解析任何脚本

  • 抽象规则:扩展名白名单绕过 ≠ RCE
  • 具体场景:shell.phtml 上传成功,GET 返回原文
  • 关键识别特征:响应 200 + 原内容;尝试 .htaccess 上传也无效(被拒)
  • 排除方法:落点是 CDN / 对象存储 / 静态域,无 PHP handler;本入口最多 suspected,关注其他入口

反例 3:响应 500 但无解析迹象

  • 抽象规则:500 ≠ 上传漏洞
  • 具体场景:上传含 <?php 的图片,响应 500 通用错误页
  • 关键识别特征:响应里无库名 / 无落盘 URL / 无脚本输出
  • 排除方法:可能是图片库解析图片头失败(ImageMagick 拒绝非真实图片),不是 PHP 执行;判 suspected

FN(看似不命中实际是真洞)

反例 4:上传成功无回显路径,但路径可猜

  • 抽象规则:响应不回显 URL ≠ 文件不可访问
  • 具体场景:响应仅 {"success": true},但落点是 /uploads/<userid>/<original_filename> 这类可推断格式
  • 关键识别特征:业务里其他地方(用户主页 / 资料页)会显示已上传文件的 URL,可反推命名规则
  • 确认方法:换个端点(如 /profile 详情接口)看是否返回上传文件 URL,再回头 GET 验证

反例 5:SVG 上传到 CDN 不解析 PHP,但业务预览渲染触发 XSS

  • 抽象规则:CDN 落点 ≠ 安全
  • 具体场景:SVG 落点是阿里云 OSS,OSS 不解析脚本;但业务 /preview?file=<key> 直接把 SVG 内嵌入 <object> 渲染
  • 关键识别特征:业务有预览端点 / 富文本编辑器会内嵌渲染上传内容
  • 确认方法:SVG 含 <script>alert('sastx_sentinel')</script>,在预览页面看是否触发

反例 6:JSON body base64 字段被忽略的候选

  • 抽象规则:上传不一定是 multipart——JSON body 含 base64 字段同样是上传 source
  • 具体场景:POST /api/avatar body {"image": "<base64>", "filename": "x.png"},后端 base64 解码后落盘
  • 关键识别特征:HAR 里非 multipart 但 body 含 >10KB 的 base64 字符串字段
  • 确认方法:把 base64 内容换成 PHP / SVG 内容,filename 字段含 ../.php,按上传变体测

反例 7:跨子系统隐式推广(漏报高发模式)

  • 抽象规则:在主站头像测过 ≠ admin 后台导入安全
  • 具体场景:测过 user-portal 头像上传未发现 RCE,admin-portal 的"导入备份包"未测就推断安全
  • 关键识别特征:admin 端的导入 / 模板 / 备份 / 恢复类入口落点通常进可执行链,远比头像危险
  • 确认方法:每个上传入口独立做反例义务自检,admin 入口优先级最高

易混淆案例

反例 8:路径穿越 filename 字段,但服务端做了 filepath.Clean 规范化

  • 抽象规则:filename 含 ../ ≠ 路径穿越生效
  • 具体场景:上传 {"filename": "../../etc/passwd"},响应回显 /uploads/etc/passwd.. 被规范化掉)
  • 关键识别特征:落盘路径只剩文件名,前缀 ../ 被吃掉
  • 确认方法:尝试编码绕过(%2e%2e%2f / ..%2f / ..%5c)+ 看是否覆盖已有同名文件

11. 测试安全边界

破坏性 / 不可逆动作的闭环边界以 common/closure-verification.md《破坏性 / 不可逆动作的闭环边界》节为准。下面只列文件上传特有的破坏点。

禁止对真实业务环境执行以下动作来"挂可观测效果":

  • 上传真实可破坏的 webshell 到生产环境(如带 system($_GET['cmd']) / Runtime.exec 的 shell)
  • 用路径穿越覆盖业务真实文件(index.php / web.xml / 真实业务模板)
  • 上传 .htaccess / web.config 改写生产解析规则后未还原
  • 解压炸弹直投生产(先小样本验证防护边界)
  • 跨租户覆盖对象存储真实业务 key

允许的非破坏验证手段:

  • 哨兵自证:所有上传哨兵文件名前缀 sastx_sentinel_<ts>_,payload 只做无害自证:
    • PHP / JSP / ASPX 哨兵:<?php echo 'sastx_sentinel_' . time(); ?> / <% out.println("sastx_sentinel_" + System.currentTimeMillis()); %> —— 仅 echo 时间戳,不执行命令
    • SVG XSS 哨兵:<svg><script>document.title='sastx_sentinel_'+Date.now()</script></svg> —— 仅改 title
    • SVG XXE 哨兵:实体指向哨兵 OOB 域名(sastx-sentinel-<token>.dnslog.cn)+ 读无害资源(如 file:///etc/hostname 而非 /etc/shadow
  • 路径穿越哨兵:filename 设 ../../sastx_sentinel_<ts>.txt,内容是无害文本;越界后清理
  • Zip Slip 哨兵:zip entry 名 ../../sastx_sentinel_<ts>.txt,内容无害;解压后回收
  • 覆盖前快照:路径可控覆盖类测试前,先 GET 目标 URL 拿原始内容做 backup,确认后还原
  • OOB 通道:DNS / HTTP OOB 用一次性子域 + 唯一 token,不修改目标状态
  • 测试库 / 测试租户:跨身份越权类测试在隔离测试租户内完成,不碰真实租户数据

测试结束必须主动清理:删除所有 sastx_sentinel_* 哨兵文件、回滚覆盖、清理 .htaccess / .user.ini / web.config 等持久化配置。

12. 修复建议

源头治理(首选)

  • 四层叠加校验,缺一不可
    1. 扩展名白名单(仅放行业务真实需要的扩展,不要默认黑名单)
    2. Content-Type 校验(与扩展名一致性)
    3. 魔术数 / 文件头校验(实际读字节验证)
    4. 真实解析校验(图片走 ImageMagick / vips 转码、文档走真实解析器、只接受能成功重新编码的)
  • 服务端重命名:UUID / hash + 受控扩展名,禁用用户提供的 filename / path 字段决定落点
  • 落点与 web 可执行目录隔离:上传走独立域名 / 静态文件服务器(如 Nginx 上传目录配 location ~ \.(php|jsp|asp|aspx)$ { deny all; }),或落对象存储

中间件配置防御

  • 上传目录 Apache 配 php_admin_flag engine off<FilesMatch> 禁脚本解析
  • 上传目录 Nginx 配 location 禁所有动态 handler
  • 上传目录禁 .htaccess / .user.ini / web.config 生效(AllowOverride None / user_ini.filename=""
  • 静态域配 Content-Disposition: attachment 强制下载,避免内联渲染

解析链防御

  • SVG / HTML / XML:XML parser 禁 DTD(setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)),或转栅格图后存储
  • Office 文档:用沙箱解析,禁外部实体;处理前调用 disallow-doctype-decl
  • PDF:去除 JavaScript / 文件嵌入层(用 pdftk / qpdf 清理)
  • ZIP / TAR 解压:每个 entry 路径用 filepath.Rel(dst, target) 校验在目标目录内;限制解压大小(如 100MB)、文件数(如 10000)、嵌套层数(如 3)
  • OCR / 转码链:用受限沙箱(容器 / seccomp),禁网络出口

路径 / 字段防御

  • 路径参数禁绝对路径、..、空字节、URL 编码绕过
  • 服务端用白名单 enum 决定子目录,不接受用户路径
  • filename 字段服务端忽略,落盘用 UUID

边界访问控制

  • 上传文件访问按业务身份校验(不要靠 URL 不可猜来防越权)
  • 对象存储 bucket 禁公开读,业务下载走签名 URL + 鉴权后端
  • 跨租户隔离落到 key 前缀 + 服务端鉴权

兜底拒绝

  • 大小 / 数量 / 频次限流(防解压炸弹 / 上传炸库)
  • 错误响应不暴露库名 / 行号 / 校验链细节
  • 上传日志含上传者身份 + 文件 hash + 落盘路径,便于事后审计

参考

Install via CLI
npx skills add https://github.com/Q16G/aster --skill file-upload
Repository Details
star Stars 72
call_split Forks 6
navigation Branch main
article Path SKILL.md
More from Creator