SSRF Filter Bypass in a PDF Invoice Fetcher
A SaaS invoice platform lets users import invoices from a URL. The fetcher service runs in a Kubernetes pod with AWS IMDSv1 enabled at http://169.254.169.254. It blocks URLs whose hostname matches ^(127\.|10\.|192\.168\.|169\.254\.) using a regex on the raw string, follows up to five redirects, and renders the fetched response into a PDF. You have a trial account. The internal admin API at http://admin.internal:8080 is reachable only from the pod network and returns JSON. The fetcher sets Content-Type: text/html when rendering. You control attacker.test with an HTTP server. You need cloud credentials to reach a customer S3 bucket. The PDF download is the only output channel; no raw response is shown.
1. Describe two SSRF filter bypasses and give exact URLs. Which do you try first and why?
Read the worked answer
The regex checks the initial hostname only, so a redirect is the deterministic bypass. Host https://attacker.test/r returns 302 Location: http://169.254.169.254/latest/meta-data/iam/security-credentials/. Submit that URL to the importer; the fetcher validates attacker.test, follows the redirect, and retrieves metadata. If redirects are re-validated, use DNS rebinding: http://rebind.attacker.test resolves first to your public server to pass validation, then to 169.254.169.254 on the second lookup. A third option is decimal or hex IP encoding: http://2852039166/latest/meta-data/ or http://0xA9FEA9FE/; the regex does not match those strings.
Try the redirect first because it is repeatable and does not depend on DNS TTL or resolver behavior. Keep the chain to one hop to avoid a fetcher redirect limit. Verify the bypass with a harmless internal endpoint such as http://admin.internal:8080/health before touching metadata, so you can distinguish a filter bypass from a rendering failure.
2. The PDF is the only output. How do you recover credentials and pivot to S3?
Read the worked answer
Fetch the role list first, then the role name: .../latest/meta-data/iam/security-credentials/ returns a role name, and .../security-credentials/<role> returns JSON containing AccessKeyId, SecretAccessKey, and Token. Because the fetcher renders text/html, the JSON is rendered as text inside the PDF. Download the PDF, run pdftotext invoice.pdf -, and copy the JSON. If the output truncates, use the same SSRF to call a shorter internal endpoint or request only one credential path at a time.
Configure AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN, then run aws sts get-caller-identity to confirm the role. Enumerate with aws s3 ls and access only the bucket named in scope. Report the SSRF, the redirect-following behavior, the regex-on-initial-host flaw, and the IMDSv1 exposure. Remediation should include IMDSv2, an egress allowlist, resolving and validating every redirect target, and blocking link-local and metadata addresses after DNS resolution, not by string matching.