PDF file structure, attack vectors — embedded JavaScript, launch actions, embedded files — and a practical triage workflow using pdfid.py and pdf-parser.py to extract and analyse malicious content.
Sign in to mark this article as read and track your progress.
PDF is a complex format. A single PDF file can contain JavaScript, embedded files, launch actions, form fields, and multimedia objects. Attackers weaponise PDFs by:
PDF attacks were dominant from 2008–2015 when Adobe Reader vulnerabilities were plentiful. They remain relevant today — unpatched corporate endpoints running legacy Acrobat versions are still targeted.
┌─────────────────────────────────┐
│ Header (%PDF-1.7) │ Identifies PDF version
├─────────────────────────────────┤
│ Body │ Numbered objects: text, images,
│ (objects: text, JS, files...) │ JavaScript, embedded files, actions
├─────────────────────────────────┤
│ Cross-reference table (xref) │ Random access to objects by number
├─────────────────────────────────┤
│ Trailer │ Points to root object (catalog)
└─────────────────────────────────┘
Objects are the building blocks. Every JavaScript block, every embedded file, every action is an object with a number. The xref table maps object numbers to byte offsets.
| Vector | Mechanism | Key Indicator |
|---|---|---|
| Embedded JavaScript | /JS or /JavaScript action; triggers on /OpenAction (auto-open) | /JS, /JavaScript, /OpenAction in pdfid output |
| Launch action | /Launch executes external command — user must click through a warning | /Launch keyword present |
| Embedded file | Executable attached via /EmbeddedFile; user prompted to extract | /EmbeddedFile with suspicious extension |
| URI action | /URI opens a URL in browser on click; leads to phishing or EK | /URI pointing to unknown domain |
| Form field scripts | JavaScript on /OnFocus, /OnBlur, /OnChange events | /AcroForm with /AA (additional actions) |
pdfid.py counts suspicious keywords in a PDF. It is the fastest first-pass tool.
pdfid suspicious.pdf
Example output for a malicious PDF:
PDF Header: %PDF-1.6
obj 12
endobj 12
stream 4
/Page 1
/JS 1 ← JavaScript present
/JavaScript 1 ← JavaScript action
/OpenAction 1 ← Executes on document open
/EmbeddedFile 0
/Launch 0
High-risk indicator combinations:
/JS + /JavaScript + /OpenAction — JavaScript executes automatically on open/Launch alone — attempts to execute a shell command/EmbeddedFile — may contain an executable payload/JBIG2Decode — associated with historic heap overflow exploits/RichMedia — can embed Flash (many known CVEs)After pdfid identifies suspicious keywords, extract the relevant object:
# Find which object contains the JavaScript
pdf-parser.py --search /JS suspicious.pdf
# Extract and display that object
pdf-parser.py --object 8 suspicious.pdf
# Extract with stream decompression (FlateDecode etc.)
pdf-parser.py --object 8 --filter suspicious.pdf
# Dump raw stream bytes
pdf-parser.py --object 8 --filter --raw suspicious.pdf > extracted.js
PDF streams are often compressed or encoded. The /Filter entry names the encoding:
| Filter | Encoding | Decode Tool |
|---|---|---|
/FlateDecode | Zlib/Deflate compressed | pdf-parser --filter handles automatically |
/ASCIIHexDecode | Hexadecimal ASCII | CyberChef: From Hex |
/ASCII85Decode | Base-85 encoding | CyberChef: From Base85 |
/LZWDecode | LZW compression | pdf-parser --filter handles automatically |
Multiple filters can be chained: /Filter [/ASCII85Decode /FlateDecode]. pdf-parser --filter applies them in order.
After extracting the JavaScript stream, apply the de-obfuscation techniques from Topic 2:
# If the JS contains eval() patterns, open in browser DevTools
# or use Node.js to execute safely:
node -e "
eval = function(s) { console.log('[EVAL]:', s); };
// paste extracted JS here
"
Look for:
\u9090\u9090)var nop = unescape('%u9090%u9090').repeat(10000))app.alert() calls that probe for specific Acrobat versionspeepdf provides an interactive shell for PDF analysis and can emulate JavaScript using SpiderMonkey:
peepdf -i suspicious.pdf
PPDF> tree # show object tree
PPDF> stream 8 # display stream 8 content
PPDF> js # execute embedded JavaScript in emulator
PPDF> extract js # extract all JavaScript to file
pdfid shows /JS + /OpenAction?
→ YES: Extract JS with pdf-parser, de-obfuscate, find shellcode/URL
→ NO:
pdfid shows /Launch?
→ YES: Extract Launch action object, read the command string
→ NO:
pdfid shows /EmbeddedFile?
→ YES: Extract embedded object, hash it, submit to VirusTotal
→ NO: Low risk from PDF-specific vectors (may still be phishing)