The Click You Never Made: Finding /OpenAction in a Malicious PDF
The challenge
Finance forwarded a supplier invoice and the mail gateway quarantined it. The usual first move is to check whether the extension is lying about the file, and this time it is not: the signature is honest, this really is a PDF. The problem sits further in. The catalog dictionary at the top of the file names two things that no plain invoice has any business carrying. One of them merely declares that a script is attached, and a careful reader would still prompt you before running it. The other is the key that hands control to that script the instant the document is opened, with nobody clicking anything. Read the ASCII column and name that second key.
What you'll learn
- Read a PDF header and confirm that a signature is genuine
- Locate the catalog dictionary and its keys in a raw hex view
- Tell /OpenAction apart from /JavaScript and explain why only one is the finding
- Explain why an honest file signature proves nothing about a document's behaviour
- Name the sibling keys that produce the same effect (/AA and /Launch)
Skills tested
Prerequisites
- Reading a hex dump with an ASCII column
- Basic idea of a file signature
How it works
A PDF is a set of numbered objects. The one the viewer loads first is the catalog, marked /Type /Catalog, and it describes what the document is and what should happen to it.
Among the keys a catalog may carry is /OpenAction. It points at an action the viewer performs automatically the moment the document is opened. It exists for benign reasons: jump to a named destination, set the zoom level, play a sound. It also accepts an action whose type is /S /JavaScript.
That is the whole trick. /JavaScript describes what the payload is. /OpenAction decides when it runs. A file that carries only the first needs a human to click something. A file that carries both needs nothing at all.
The signature is irrelevant to this. %PDF- at offset 0 is correct here, which is exactly why signature checking alone is a weak control: the file is not pretending to be something else, it is honestly a PDF that honestly asks to run code.
Common mistakes
- Answering /JavaScript. It is genuinely suspicious, but on its own it only means a script is attached. Interactive forms ship script that fires on a click and never touches anything. It is the trigger, not the payload, that makes this automatic.
- Hunting for a forged signature. Eight of the first bytes look like every other file-type puzzle, so the instinct is to compare them against the reference list and stop. Here they match, and matching is the point.
- Skimming the hex pane instead of the ASCII column. The whole object dictionary is printable text. Reading the right-hand column is faster than decoding bytes by hand.
- Assuming a viewer will always prompt. Some readers ask before running document script, many older and embedded ones do not, and a mail preview pane is the worst case.
How to defend against it
The fix is not to block PDFs. It is to stop treating a valid signature as a verdict.
- Parse, do not sniff. Score attachments on their object structure.
/OpenAction,/AA,/Launch,/EmbeddedFileand/RichMediain an inbound invoice are each worth a quarantine on their own. - Disable document script in the reader you deploy, and turn off the preview pane for external mail so opening is always a deliberate act.
- Flatten on ingest. Rendering an inbound PDF to images, or rebuilding it through a sanitiser, drops every action key and keeps the invoice readable.
- Alert on the combination. A single script key is noisy on its own;
/OpenActionresolving to a/JavaScriptaction is a high-confidence signal worth paging on.