Server-Side Request Forgery (SSRF)
- https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/19-Testing_for_Server-Side_Request_Forgery
- https://hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/
- https://hacktricks.wiki/en/pentesting-web/xss-cross-site-scripting/pdf-injection.html
- https://namratha-gm.medium.com/ssrf-to-local-file-read-through-html-injection-in-pdf-file-53711847cb2f
SSRF forces the server to make HTTP (or other protocol) requests on the attacker’s behalf. The server becomes a proxy — useful for accessing internal services, cloud metadata endpoints, or local files.
Identification: Look for any parameter that fetches a remote resource — ?url=, ?image=, ?server=, ?dest=, PDF export features, webhooks, icon fetchers.
| Variant | Description |
|---|---|
| Basic SSRF | Parameter takes a URL; redirect it to internal target |
| Blind SSRF | Server makes the request but returns no output — detect via out-of-band callback |
| SSRF → LFI | Server (or its PDF renderer) reads file:// URIs |
| SSRF → RCE | Chain into internal services (Redis, Elasticsearch, etc.) |
Basic SSRF
Point the vulnerable parameter at an internal resource or metadata endpoint:
Blind SSRF Detection
If the response gives nothing back, confirm the request fires via a callback:
Use Burp Collaborator or interactsh for DNS-based OOB confirmation.
SSRF → LFI via HTML Injection in PDF
When an app generates PDFs from user input using a headless browser (wkhtmltopdf, Puppeteer, Chrome headless), injecting JavaScript into the input causes the renderer to execute it. The script can read file:// URIs and exfiltrate content back to the attacker.
Identification: Submit a field and download the resulting PDF — if your input renders as HTML (not escaped), the renderer is likely vulnerable.
Inject into the vulnerable input field (e.g., a tracker or name field that ends up in the PDF):
Automate with curl
URL-encode the payload and POST it directly:
NOTE: Some PDF renderers block file:// access by default. If the payload fires but returns empty, try /proc/self/environ, /etc/hosts, or app config files as alternative targets.