domains / security / ssrf-hunt / hunt-ssrf
Both copies share the same core procedure (OOB gate, crown-jewel targets, SharePoint lesson), but the sickn33 copy adds material constraints and steps the uphiago copy lacks: a mandatory authorization gate, a warning that Burp Collaborator keys interactions by payload ID rather than subdomain, a requirement to generate a fresh payload per candidate parameter, and a negative-control step. The uphiago copy adds a detailed attack-surface signal list (URL patterns, JS patterns, response headers, tech stack signals) that the sickn33 copy does not include. Crowning one would hide the other's unique steps.
| Repository | Path |
|---|---|
| uphiago/recon-skills | redteam/hunt-ssrf/SKILL.md |
| sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills-claude/skills/hunt-ssrf/SKILL.md |
| Role | Repository | Path |
|---|---|---|
| peer | uphiago/recon-skills | redteam/hunt-ssrf/SKILL.md |
| peer | sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills-claude/skills/hunt-ssrf/SKILL.md |
| adapter | sickn33/agentic-awesome-skills | plugins/agentic-awesome-skills/skills/hunt-ssrf/SKILL.md |
| adapter | sickn33/agentic-awesome-skills | skills/hunt-ssrf/SKILL.md |
---
name: hunt-ssrf
description: Hunting skill for SSRF vulnerabilities. Use when hunting SSRF on any target. OOB Collaborator confirmation mandatory for blind cases.
category: security
risk: offensive
license: MIT
---
## Authorization Gate (Read First)
Before running any command that probes, exploits, changes, persists on, extracts data from, or attempts credential access against a target:
1. Ask the user to state the exact target URL, IP, account, or resource.
2. Ask the user to confirm written authorization and the permitted scope.
3. Show the exact command(s) and explain their expected effect.
4. Wait for explicit confirmation in the current conversation.
Without that confirmation, remain read-only and provide defensive guidance only. Prefer a sandbox, disposable VM, or controlled lab.
## When to Use
Use when the target has any feature that fetches a URL, imports data from a remote source, generates previews/screenshots, processes webhooks, or renders documents. SSRF allows an attacker to make the server itself perform network requests — reaching internal services, cloud metadata endpoints, and otherwise inaccessible infrastructure. Every link-preview, file-import, image-proxy, webhook-registration, PDF-generation, and redirect-following endpoint is a candidate. Highest-value targets: cloud-hosted SaaS, Kubernetes/orchestration platforms, CI/CD systems, and URL-fetching features.
## Crown Jewel Targets
SSRF is highest-value when the target runs on cloud infrastructure (AWS, GCP, Azure) where metadata services expose credentials, or when the server sits inside a complex internal network (Kubernetes clusters, microservice meshes, internal APIs). Priority targets:
- **Cloud-hosted SaaS products** (GCP metadata at `169.254.169.254` or `metadata.google.internal`, AWS IMDSv1)
- **Kubernetes/orchestration platforms** — aggregated API servers, metrics-server, kubelet endpoints expose privileged cluster operations
- **Internal developer tooling** — CI/CD, workflow orchestration (Flyte, Argo), admin panels not exposed externally
- **Link preview / URL fetching features** — Reddit-style preview APIs, Slack-style unfurling, media processors
- **Dataset/file import pipelines** — anything that fetches remote URLs on behalf of a user
- **Enterprise self-hosted software** (GitHub Enterprise, GitLab) — SSRF frequently chains to RCE via internal services
Payouts are highest when SSRF reaches: cloud credentials → account takeover, internal admin APIs → data exfil, or chains to RCE.
## OOB-Or-It-Didn't-Happen Gate
**Claims of blind SSRF require an out-of-band (OOB) confirmation. Always. No exceptions.**
OOB means: a Burp Collaborator domain, an `interactsh-client` listener, a canarytoken, or any DNS+HTTP receiver you control that confirms the server actually made an outbound network connection on your behalf.
### What is NOT confirmation of SSRF
- The server **echoing your URL back in an error message**. Example: `"The Web application at http://evil.example.com/x could not be found"` — this is the server formatting your input into an error string, NOT making an outbound HTTP request.
- The server returning a different status code for an external URL vs `localhost`. Different error responses can come from URL-scheme validators, not from actual fetching.
- A delayed response when the URL is sent. Delay can come from DNS resolution attempts within the parser, not from completed HTTP fetches.
### What IS confirmation of SSRF
- A DNS lookup for your unique Collaborator subdomain appears in the OOB listener.
- An HTTP request to your Collaborator HTTP endpoint with the server's source IP and User-Agent.
- For SSRF in JavaScript-execution contexts (PDF renderers, headless browsers), a fetch from the server to your callback URL.
### Default workflow
1. **Plant the Collaborator payload first.** Sub-tagging (`dlsrcurl.<collab>`, `import.<collab>`) only works if your listener actually reports the queried subdomain back to you — **verify that before relying on it.** Burp's `get_collaborator_interactions` keys results by **payload ID, not by subdomain**, so several sub-tags generated from one payload are indistinguishable in the output. When that is the case, **generate a fresh payload per candidate parameter** and send exactly one request per payload.
2. **Send the request** to the target endpoint.
3. **Wait 30–120 seconds**, then poll the OOB listener.
4. **Only after a confirmed callback** do you claim SSRF.
5. If zero callbacks across all sub-tagged sinks: SSRF claims must be retracted, even if error messages echo URLs.
**Lesson from an authorized engagement:** SharePoint's `/_layouts/15/download.aspx?SourceUrl=` returned 500 with the title `"The Web application at <attacker-URL> could not be found"`. Initial scan flagged this as SSRF (server clearly processed the URL). 38 Collaborator-tagged payloads across 12+ URL-accepting parameters yielded **zero DNS or HTTP interactions**. The "echo" was client-side error-string formatting; the server never made an outbound HTTP request. The path is actually an SP-internal `SPFile`/`SPWebApplication` resolver, not a generic URL fetcher. Reporting this as SSRF would have been N/A'd at triage.
### Attribute the callback to ONE parameter before reporting
A callback proves the server made a request. It does **not** tell you which parameter caused it, and the fix depends entirely on that.
```
BAD — four candidate fields, one payload, fired in one batch
-> callbacks arrive, attribution impossible, retest required
GOOD — fresh payload per field, one request each, poll between
url -> callbacks <- this is the sink
apiUrl -> none
endpoint -> none
target -> none
```
**Run the negative control.** A parameter that produces no callback confirms it is not the sink.
## Attack Surface Signals
### URL Patterns to Hunt
```
/api/*/preview
/api/*/fetch
/api/*/import
/api/*/webhook
/api/*/proxy
/api/*/render
/api/*/link
/api/*/screenshot
/api/*/export
/api/*/validate
?url=
?uri=
?endpoint=
?redirect=
?src=
?source=
?feed=
?host=
?target=
?dest=
?file=
?path=
?callback=
?image=
?load=
?fetch=
```
### JS Patterns (in client-side code)
```javascript
// Look for these in JS bundles
fetch(userInput)
axios.get(params.url)
XMLHttpRequest + variable URL
url: req.body.url
src: params.source
href: query.endpoint
```
### Response Header Signals
```
X-Forwarded-For headers echoed back
Server: internal-service
Via: 1.1 internal-proxy
X-Cache headers revealing internal hostnames
```
### Tech Stack Signals
- **Kubernetes** — any public-facing aggregated API, metrics endpoints
- **GCP** — any service fetching URLs that runs on Compute Engine/GKE
- **Node.js/Python** with URL-fetching libraries (`requests`, `node-fetch`, `axios`)
- **Headless browsers** (Puppeteer, PhantomJS) used for screenshots/PDF — extremely high value
- **XML/DSPL/CSV import features**