| Tested on | Kali GNU/Linux Rolling 2026.2 (kali-rolling) |
|---|---|
| Package | pypdf 6.9.2 (venv)pdfid 0.2.10-0kali1pdf-parser 0.7.14-0kali1libimage-exiftool-perl (ExifTool 13.55)qpdf 12.4.0python3-venv |
| Applies to | Kali Linux and other Debian-based hosts with Python 3 and Didier Stevens PDF tools |
| Lab environment | Pentest lab Isolated VM; create only the harmless samples in this guide |
| Privilege | Normal user for creation and analysis; sudo only to install system packages |
| Cert prep | CompTIA PenTest+ PT0-003 - suspicious file analysis and phishing artifacts |
| Scope | Create harmless PDF examples containing JavaScript, an embedded attachment, and a URI action; inspect how those features appear inside the PDF; then analyze them with PDFiD, pdf-parser, ExifTool, and qpdf. Includes historical EvilPDF context but does not create executable malware, reverse shells, AV evasion, or delivery workflows. |
| Related guides | Browser-in-the-browser phishing Python for pentesters Volatility memory dump analysis |
In this tutorial we will first create several harmless PDF payload examples to understand how active content is stored inside a PDF. We will build a normal PDF, a PDF containing harmless JavaScript, and a PDF containing JavaScript, an embedded text attachment, and a test URL.
We will then analyze those files on Kali with pdfid, pdf-parser, ExifTool, and qpdf. This lets you see both sides of the same structure: how a PDF feature is embedded and how an analyst finds it later.
The lab does not create a reverse shell, executable malware, AV bypass, or delivery mechanism. The JavaScript displays only an alert, the embedded .cmd file contains plain text, and the URL uses the reserved .invalid domain. Keep the work inside an authorized lab and write the scope into your rules of engagement before you touch client mail attachments.
What Is a PDF Payload?
In this tutorial, PDF payload means content or an action stored inside a PDF beyond ordinary page text and images. Examples include JavaScript, an embedded file, or a URI action.
A payload is not automatically malicious. A legitimate form may use JavaScript, and a PDF may legitimately carry an attachment. The security question is what the payload does, when it runs, and whether the user expects it.
Older write-ups for this slug focused on EvilPDF and reverse-shell delivery. A short historical note appears near the end. The hands-on work here stays on harmless structure creation and static analysis.
What Can a PDF Payload Contain?
A PDF is a structured document format, not a simple picture of text. It can hold pages, fonts, forms, annotations, JavaScript, embedded files, and actions that run when a reader opens the file or clicks a link.
Internally, a PDF is made from numbered objects. Objects can describe pages, fonts, links, actions, attachments, and other document features. Some objects contain streams, which hold data such as compressed content or an embedded file. The document catalog is the top-level object that points to important structures and actions.
Names beginning with /, such as /JavaScript and /EmbeddedFile, are PDF dictionary names defined by the PDF format. They are not Linux filesystem paths.
The same PDF mechanisms can be used legitimately or abused. JavaScript may validate a form, an embedded file may provide supporting documentation, and a URI action may open a company website. They become security concerns when the behavior is unexpected, deceptive, or used to deliver harmful content.
The same capabilities can also be abused:
- Social-engineering lures that look like invoices, resumes, or shipping notices
- Embedded executables or scripts that ask the user to “open” or “enable” something
- JavaScript that launches further actions inside a supporting reader
- Links or redirects to phishing or malware download sites
- Occasional reader exploits that target unpatched PDF software
Most real-world PDF incidents still start with phishing. Treat every unexpected PDF attachment as untrusted until you understand what it contains.
PDF Payload Types: JavaScript, Attachments, and Reader Exploits
| Type | How it appears in this lab | Security significance |
|---|---|---|
JavaScript (/JS, /JavaScript) |
Harmless app.alert() message via PdfWriter.add_js() |
May run when a supporting reader opens the document; investigate script behavior before trusting it |
Embedded file (/EmbeddedFile, /Filespec) |
Plain-text bytes named invoice_update.cmd via add_attachment() |
Filename can look dangerous while bytes differ; extract and identify the content offline |
URI action (/URI) |
Reserved test URL http://malicious.example.invalid/payload via add_uri() |
Clickable links can send users elsewhere; record and investigate URLs separately |
| Reader exploit | Not created in this lab | Malicious behavior comes from a vulnerability in the PDF-reading software itself, not only from a normal PDF feature |
We will create examples of normal PDF features that can be abused. We will not create a memory-corruption exploit or target a vulnerable reader. Reader exploits depend on specific software vulnerabilities and are outside this lab.
A non-zero /JS count is a signal to investigate, not automatic proof of malware. Features such as /AcroForm, /XFA, and JavaScript also appear in legitimate interactive forms; their presence increases analysis scope rather than proving maliciousness.
Build a Safe PDF Payload Lab
We will create the PDFs ourselves instead of downloading malware. Use a dedicated Kali VM, keep samples in one folder, and do not open unknown PDFs in a full GUI reader until static checks are done.
Install the analysis packages and the Python venv support with the apt command:
sudo apt install pdfid pdf-parser libimage-exiftool-perl qpdf python3-venvPackage installs need elevated rights; see the sudo command guide if your lab user is not configured yet. Confirm the tools:
pdfid --versionpdfid 0.2.10Confirm pdf-parser as well:
pdf-parser --versionpdf-parser 0.7.14On Kali Rolling 2026.2, pdf-parser may print a short advisory about the host Python version before the tool version line. Parsing still worked in this lab with Python 3.14.6.
Create a working directory and an isolated Python environment for pypdf. Kali protects the system Python, so install the library in a venv rather than with system-wide pip:
mkdir -p ~/pdf-analysis-lab && cd ~/pdf-analysis-labCreate the virtual environment next:
python3 -m venv ~/pdf-lab-venvvenv creates the directory and exits quietly on success. Activate it in the current shell:
source ~/pdf-lab-venv/bin/activateYour prompt should now show (pdf-lab-venv). Install the pinned pypdf release used in this lab:
python -m pip install 'pypdf==6.9.2'pip prints package progress and finishes with a successful install of pypdf 6.9.2. Keep the venv activated for every Python creation step below. When you are finished with the lab, run deactivate.
Create a Basic PDF
Build a baseline PDF with one blank page and metadata, but no JavaScript, attachment, or URI action:
python - <<'PY'
from pypdf import PdfWriter
writer = PdfWriter()
writer.add_blank_page(width=612, height=792)
writer.add_metadata({
"/Title": "Lab Invoice Template",
"/Author": "GoLinuxCloud Lab",
})
writer.write("lab-clean.pdf")
print("Wrote lab-clean.pdf")
PYWrote lab-clean.pdfConfirm the file type, then triage it with PDFiD:
file lab-clean.pdflab-clean.pdf: PDF document, version 1.3, 1 page(s)Triage the baseline with PDFiD, hiding zero counts so the quiet result is easy to read:
pdfid -n lab-clean.pdfPDFiD 0.2.10 lab-clean.pdf
PDF Header: %PDF-1.3
obj 4
endobj 4
xref 1
trailer 1
startxref 1
/Page 1No /JS, /EmbeddedFile, or /OpenAction lines appear. That quiet baseline is what a normal empty document looks like in PDFiD.
Add JavaScript to a PDF
Use the public PdfWriter.add_js() API. Current pypdf documentation states that this adds JavaScript that launches when the PDF is opened:
python - <<'PY'
from pypdf import PdfWriter
writer = PdfWriter()
writer.add_blank_page(width=612, height=792)
writer.add_js(
'app.alert("GoLinuxCloud lab sample: JavaScript executed in the reader.");'
)
writer.write("lab-js.pdf")
print("Wrote lab-js.pdf")
PYWrote lab-js.pdfadd_js() places JavaScript into the PDF's document structure. Our payload only calls app.alert(), so a supporting viewer may display a message box. Nothing is downloaded, executed from the operating system, or sent over the network.
Verify that PDFiD now sees JavaScript names:
pdfid -n lab-js.pdfPDFiD 0.2.10 lab-js.pdf
PDF Header: %PDF-1.3
obj 5
endobj 5
xref 1
trailer 1
startxref 1
/Page 1
/JS 1
/JavaScript 2/JS and /JavaScript are greater than zero. In this fixture, pypdf stores the script using its JavaScript name tree and an /Action object. You may also encounter PDFs that launch JavaScript through /OpenAction. The structures are different, but both can cause JavaScript to run when a supporting reader opens the document.
Embed a Harmless File in a PDF
PdfWriter.add_attachment(filename, data) embeds a file inside the PDF. To demonstrate what a suspicious attachment looks like during analysis without creating anything harmful, give a plain-text attachment a .cmd filename:
python - <<'PY'
from pypdf import PdfWriter
writer = PdfWriter()
writer.add_blank_page(width=612, height=792)
attachment = (
b"This is a harmless lab attachment used only "
b"for PDF structure analysis.\n"
)
writer.add_attachment("invoice_update.cmd", attachment)
writer.write("lab-attachment.pdf")
print("Wrote lab-attachment.pdf")
PYWrote lab-attachment.pdfThe filename ends in .cmd so it looks suspicious during analysis, but its contents are only plain text. We are using the filename to demonstrate how an analyst discovers an unexpected attachment without creating an executable payload.
At the PDF level, the attachment involves two related concepts:
/EmbeddedFilepoints to the stream containing the attachment bytes/Filespecdescribes the embedded file and associates a filename with those bytes
Confirm the keyword hit:
pdfid -n lab-attachment.pdfPDFiD 0.2.10 lab-attachment.pdf
PDF Header: %PDF-1.3
obj 8
endobj 8
stream 1
endstream 1
xref 1
trailer 1
startxref 1
/Page 1
/EmbeddedFile 1/EmbeddedFile is present. Later analysis will recover both the stream bytes and the displayed invoice_update.cmd name.
Add a Test URL to a PDF
PdfWriter.add_uri() creates a clickable rectangular link on a page. Point it at a reserved example domain:
python - <<'PY'
from pypdf import PdfWriter
writer = PdfWriter()
writer.add_blank_page(width=612, height=792)
writer.add_uri(
page_number=0,
uri="http://malicious.example.invalid/payload",
rect=[72, 700, 300, 720],
)
writer.write("lab-uri.pdf")
print("Wrote lab-uri.pdf")
PYWrote lab-uri.pdfThis creates a clickable link region in the PDF. .invalid is a reserved top-level domain intended for names that should not resolve normally, so the lab does not point at a real malicious server.
PDFiD's default keyword list does not always surface every URI. Confirm the link with pdf-parser:
pdf-parser -s /URI lab-uri.pdfobj 5 0
Type: /Annot
Referencing: 4 0 R
<<
/Type /Annot
/Subtype /Link
/P 4 0 R
/Rect [ 72 700 300 720 ]
/H /I
/Border [ 2 2 2 ]
/A
<<
/S /URI
/URI '(http\\072\\057\\057malicious\\056example\\056invalid\\057payload)'
>>
>>The annotation holds a /URI action for http://malicious.example.invalid/payload.
Combine the PDF Payload Features
Now combine those harmless features into one sample. This gives us a single suspicious-looking PDF to analyze in the rest of the article:
python - <<'PY'
from pypdf import PdfWriter
writer = PdfWriter()
writer.add_blank_page(width=612, height=792)
writer.add_metadata({
"/Title": "Urgent: Review Attached Invoice",
"/Author": "accounts@example.invalid",
})
writer.add_js('app.alert("Lab sample");')
writer.add_attachment(
"invoice_update.cmd",
b"This is a harmless lab attachment used only for PDF structure analysis.\n",
)
writer.add_uri(
page_number=0,
uri="http://malicious.example.invalid/payload",
rect=[72, 700, 300, 720],
)
writer.write("lab-suspicious-combo.pdf")
print("Wrote lab-suspicious-combo.pdf")
PYWrote lab-suspicious-combo.pdfCalculate a SHA-256 hash before deeper analysis. The hash gives the file a stable identifier, so you can show which exact PDF your notes and extracted objects came from even if another file has the same filename:
sha256sum lab-suspicious-combo.pdf5a322ef6f692e37b65a8e404ddaa5d497eb2cf85723bebcfc7aba689e2284d9e lab-suspicious-combo.pdfYour hash will differ if you change the builder script. Record whatever sha256sum prints for your copy.
Verify the PDFs You Created
Quickly confirm each artifact before the deeper analysis half:
file lab-clean.pdf lab-js.pdf lab-attachment.pdf lab-uri.pdf lab-suspicious-combo.pdflab-clean.pdf: PDF document, version 1.3, 1 page(s)
lab-js.pdf: PDF document, version 1.3, 1 page(s)
lab-attachment.pdf: PDF document, version 1.3, 1 page(s)
lab-uri.pdf: PDF document, version 1.3, 1 page(s)
lab-suspicious-combo.pdf: PDF document, version 1.3, 1 page(s)Triage the combo sample the same way you will triage an unknown attachment:
pdfid -n lab-suspicious-combo.pdfPDFiD 0.2.10 lab-suspicious-combo.pdf
PDF Header: %PDF-1.3
obj 9
endobj 9
stream 1
endstream 1
xref 1
trailer 1
startxref 1
/Page 1
/JS 1
/JavaScript 2
/EmbeddedFile 1The combo sample shows JavaScript and an embedded file in one pass. The URI still needs pdf-parser, which is the next half of the lab.
Inspect PDF Metadata and Objects
Start with identity and metadata. ExifTool surfaces title, author, producer, and page count without executing JavaScript:
exiftool -a -G1 lab-suspicious-combo.pdf[ExifTool] ExifTool Version Number : 13.55
[File] File Type : PDF
[File] MIME Type : application/pdf
[PDF] PDF Version : 1.3
[PDF] Page Count : 1
[PDF] Producer : pypdf
[PDF] Title : Urgent: Review Attached Invoice
[PDF] Author : accounts@example.invalidThe urgent title and finance-themed author are context worth recording, but PDF metadata is user-controlled and easy to fake. Treat it as supporting evidence, not proof of maliciousness.
PDFiD is a triage tool. It scans for PDF names commonly associated with active or suspicious features and reports how often they appear. It does not tell you whether those features are malicious; it tells you where to investigate next. You already used -n during creation. For first-pass triage of an unknown file, pay particular attention to:
/JSand/JavaScript— JavaScript is present/OpenActionand/AA— actions may run automatically/Launch— a launch action is present/EmbeddedFile— the PDF contains an embedded file/URI— PDFiD's standard keyword list does not include/URI, so usepdf-parserwhen checking for URL actions/ObjStm— other PDF objects may be stored inside an object stream, so a simple keyword scan may not reveal everything contained there
A clean-looking PDFiD result is not a guarantee that the PDF is safe. Interesting objects can be compressed or packed into object streams, which is one reason we follow PDFiD with pdf-parser.
Now move from keyword counting to PDF structure. pdf-parser -a summarizes the document's indirect objects and shows which object numbers contain interesting types or keywords:
pdf-parser -a lab-suspicious-combo.pdfComment: 3
XREF: 1
Trailer: 1
StartXref: 1
Indirect object: 9
Indirect objects with a stream: 6
2: 1, 8
/Action 1: 5
/Annot 1: 9
/Catalog 1: 3
/EmbeddedFile 1: 6
/Filespec 1: 7
/Page 1: 4
/Pages 1: 2
Search keywords:
/JS 1: 5
/JavaScript 2: 3, 5
/EmbeddedFile 1: 6
/URI 1: 9PDF objects are identified by an object number and generation number. For example, 5 0 means object 5, generation 0. In this lab we normally refer to it simply as object 5.
We now know exactly which objects to inspect: object 5 for JavaScript, object 6 for the attachment data, object 7 for the filename, and object 9 for the URL.
For unknown PDFs, you can include -O during analysis so objects stored inside /ObjStm containers are also parsed:
pdf-parser -O -a lab-suspicious-combo.pdf-O tells pdf-parser to parse objects stored inside /ObjStm containers as regular PDF objects. If the PDF has no object streams, -O does not materially change the analysis.
If suspicious content is inside a compressed stream, pdf-parser -f can pass the stream through supported filters such as FlateDecode before displaying it.
Find Embedded JavaScript
Search for /JS in the simple JavaScript fixture:
pdf-parser -s /JS lab-js.pdfobj 5 0
Type: /Action
Referencing:
<<
/Type /Action
/S /JavaScript
/JS '(app\\056alert\\050\\042GoLinuxCloud lab sample\\072 JavaScript executed in the reader\\056\\042\\051\\073)'
>>pdf-parser often octal-escapes characters inside strings (\056 is .). The readable form is app.alert("GoLinuxCloud lab sample: JavaScript executed in the reader.");. That matches the payload we embedded with add_js().
The combo sample uses the same public API. Object 5 holds app.alert("Lab sample");:
pdf-parser -o 5 lab-suspicious-combo.pdfobj 5 0
Type: /Action
Referencing:
<<
/Type /Action
/S /JavaScript
/JS '(app\\056alert\\050\\042Lab sample\\042\\051\\073)'
>>In other malicious or legitimate PDFs you may instead see /OpenAction on the catalog pointing at /JavaScript. That is another common launch path. Treat add_js() and /OpenAction as two ways JavaScript can be wired for document-open behavior, not as identical object trees.
Find Embedded Files and Suspicious URLs
List the embedded-file stream we created earlier:
pdf-parser -t /EmbeddedFile lab-suspicious-combo.pdfobj 6 0
Type: /EmbeddedFile
Referencing:
Contains stream
<<
/Type /EmbeddedFile
/Length 72
>>Dump the stream to disk, then check the extracted bytes outside the PDF container:
pdf-parser -o 6 -d extracted-attachment.bin lab-suspicious-combo.pdfpdf-parser prints the object header when the dump succeeds. Confirm the extracted type next:
file extracted-attachment.binextracted-attachment.bin: ASCII textBecause file identified this lab attachment as ASCII text, it is safe to print here. For an unknown executable or binary object, do not cat it into the terminal; hash it, identify its type, and inspect it with an appropriate static-analysis tool instead:
cat extracted-attachment.binThis is a harmless lab attachment used only for PDF structure analysis.Those are the same bytes we passed to add_attachment(). Check the file specification for the name presented to the user:
pdf-parser -o 7 lab-suspicious-combo.pdfobj 7 0
Type: /Filespec
Referencing: 6 0 R
<<
/Type /Filespec
/F '(invoice\\137update\\056cmd)'
/EF
<<
/F 6 0 R
>>
>>Decoded, /F is invoice_update.cmd. An attachment named invoice_update.cmd does not match what a recipient would normally expect inside an invoice PDF, so it deserves immediate investigation. The extension alone does not tell us what the bytes really are, which is why we used file on the extracted content before drawing a conclusion.
Search for the URI action next:
pdf-parser -s /URI lab-suspicious-combo.pdfobj 9 0
Type: /Annot
Referencing: 4 0 R
<<
/Type /Annot
/Subtype /Link
/P 4 0 R
/Rect [ 72 700 300 720 ]
/H /I
/Border [ 2 2 2 ]
/A
<<
/S /URI
/URI '(http\\072\\057\\057malicious\\056example\\056invalid\\057payload)'
>>
>>The link points at http://malicious.example.invalid/payload. Record the URL in your notes. URLs, domains, IP addresses, filenames, and file hashes are often called observables because analysts can search for the same values in other logs and security tools. Do not visit the URL from the analysis host.
qpdf --qdf rewrites a copy of the PDF into a more readable form. It does not decide whether the file is malicious; it simply makes the structure easier for a human to search with tools such as the grep command:
qpdf --qdf --object-streams=disable --compress-streams=n lab-suspicious-combo.pdf combo.qdfSearch the readable QDF dump for the same high-signal operators:
grep -E '/JS|/JavaScript|/EmbeddedFile|/URI|/Launch|/Action' combo.qdf /JavaScript <<
/JS (app.alert\("Lab sample"\);)
/S /JavaScript
/Type /Action
/Type /EmbeddedFile
/S /URI
/URI (http://malicious.example.invalid/payload)The QDF copy is also easier for a beginner to read because the JavaScript and URI appear without most of pdf-parser's escaped-string notation.
Disarm JavaScript in a Copy
PDFiD also has a disarm feature that rewrites names such as /JavaScript and /OpenAction so normal readers no longer recognize those specific actions. This can be useful for controlled handling of simple PDFs, but it is not a general PDF sanitizer and does not make an unknown document safe to open. Copy the sample first so the original stays intact:
cp lab-js.pdf lab-js-disarm-test.pdfDisarm the copy. pdfid -d writes lab-js-disarm-test.disarmed.pdf beside it:
pdfid -d lab-js-disarm-test.pdfConfirm that PDFiD no longer recognizes the original JavaScript/action names in the disarmed copy:
pdfid -n lab-js-disarm-test.disarmed.pdfPDFiD 0.2.10 lab-js-disarm-test.disarmed.pdf
PDF Header: %PDF-1.3
obj 5
endobj 5
xref 1
trailer 1
startxref 1
/Page 1This only confirms the keyword rewrite performed by -d. It is not proof that every possible active feature has been removed. Didier Stevens notes that the technique does not handle actions hidden inside object streams and can have problems with encrypted PDFs. Keep the original sample unchanged and continue to treat the disarmed copy as untrusted.
When Static Analysis Is Not Enough
Prefer static analysis first. In this lab I did not open the JavaScript samples in Adobe Reader or another full GUI viewer on the analysis host. Creating the features ourselves, then confirming them with PDFiD and pdf-parser, was enough.
Dynamic analysis means opening the document in an isolated environment to see what happens at runtime. If that is required by your procedure:
- Use a disposable VM snapshot with networking disabled or tightly filtered
- Keep the original evidence copy offline and work from a duplicate
- If you are reproducing a real incident, use the same PDF reader/version as the affected system only when your investigation requires it
- Never detonate samples on a domain-joined or production laptop
If you later open a sample dynamically, monitor what the PDF reader starts, what files it creates, and what network connections it attempts. Tools vary by lab, so this article keeps that stage out of scope.
If you need to inspect rendered page content, use a disposable analysis environment and work from a copy. Do not treat PDFiD disarming as a replacement for isolation.
For phishing emails that delivered a PDF, continue the case in your mail-analysis workflow such as ThePhish with TheHive and Cortex.
Indicators of a Malicious PDF
Use a checklist, not a single keyword:
- Unexpected sender or mismatched display name delivering an “urgent invoice” PDF
pdfidhits for/JS,/JavaScript,/OpenAction,/AA,/Launch,/EmbeddedFile,/RichMedia, or/XFA- Embedded names that look like updates, invoices, or
.exe/.js/.cmd/.vbspayloads - Extracted attachment content that is a PE, script, or Office document rather than the claimed invoice
- URI or submit-form actions aimed at unfamiliar domains
- Heavy obfuscation, unusual encoded streams, or unexpected content after the final
%%EOF - Auto-action behavior that tries to export or launch embedded data when opened in a supporting reader
No single row proves malice. Combine technical hits with business context.
Detection and Mitigation
Defenders reduce PDF risk with layered controls:
- Keep PDF readers and Office viewers patched
- Disable or restrict JavaScript and unauthorized attachments in enterprise reader policies where business allows
- Scan or sandbox PDF email attachments at the mail gateway; where policy allows, remove dangerous embedded content or block PDFs with prohibited active features
- Detonate unknown attachments in an isolated sandbox before user delivery when your stack supports it
- Train users not to open unexpected embedded attachments, follow suspicious PDF links, run “updates,” or bypass security prompts
- For incident response, hash the original, analyze offline, and preserve chain of custody
Endpoint monitoring can also flag unusual behavior after a PDF is opened—for example, a PDF reader spawning command interpreters or unexpected programs, or making suspicious outbound connections.
Legacy Example: What EvilPDF Attempted to Do
EvilPDF-style tools tried to turn a PDF into a delivery wrapper. Typical flow:
- Start from a believable decoy PDF
- Attach or stage an executable or download lure with a tempting name
- Rely on the user confirming a prompt or running the nested file
- Point a listener at an attacker-controlled host and wait for a callback
That is social engineering plus an embedded or downloaded program. It is not a reliable Adobe Reader remote exploit by itself, and it is not the creation workflow this article teaches.
For historical reporting, describe EvilPDF as a social-engineering delivery technique rather than assuming the PDF itself silently exploited the reader.
Summary
PDF payloads are built from ordinary PDF features such as JavaScript actions, embedded-file streams, file specifications, annotations, and URI actions. In this lab we created harmless examples of those structures rather than using real malware: an alert-only JavaScript payload, a plain-text attachment presented as invoice_update.cmd, and a test URL under the reserved .invalid domain.
We then looked at the same files from an analyst's perspective. PDFiD exposed the high-signal names, pdf-parser showed which objects contained them, ExifTool provided document metadata, and qpdf produced a more readable copy of the PDF structure. The important lesson is the connection between the two sides: once you understand how a PDF feature is embedded, its analysis output becomes much easier to interpret.
Keep hands-on creation harmless and isolated. Reader exploits, executable payload delivery, AV evasion, and phishing real recipients are outside this lab.
References
- pypdf documentation -
PdfWriter.add_js(),add_attachment(), andadd_uri()APIs used in this lab - Didier Stevens – PDFiD - PDF keyword triage tool
- Didier Stevens – pdf-parser - PDF object inspection and stream extraction
- ExifTool - metadata extraction for PDF and other formats
- QPDF - PDF transformation and inspection utilities
- Adobe PDF Reference - PDF 1.7 ISO reference for operators and actions
Frequently Asked Questions
Can opening a PDF execute malware automatically?
It can, depending on the PDF content and the reader. A vulnerable reader may be exploited without the same interaction required by a simple embedded-file lure, while other malicious PDFs rely on the recipient clicking a link or opening an attachment. Never assume a PDF is inert just because it is not an .exe.
Can PDFs contain JavaScript?
Yes. PDF supports JavaScript actions such as /JS and /JavaScript, often tied to document-open behavior or other triggers. Legitimate forms may use it; malware may abuse it. Inspect the script before you trust the document.
Can PDFs contain executable attachments?
Yes. PDFs can embed other files through /EmbeddedFile and /Filespec entries. The embedded name may look like an invoice while the bytes are a script or executable. Extract and inspect those streams offline.
Does Adobe Reader block malicious PDFs?
Modern PDF viewers have added controls such as attachment warnings, JavaScript restrictions, sandboxing, and exploit mitigations, but behavior varies by product and version. No viewer replaces patching, mail filtering, and careful handling. Analysis still starts with static triage on a safe host.
How can I inspect a suspicious PDF safely?
Work on an isolated analysis system. Hash the file, review metadata with ExifTool, triage keywords with pdfid, inspect objects with pdf-parser, and extract embedded streams for separate review. Avoid opening the original in a production GUI reader until those checks are done, and do not visit extracted URLs from the analysis host.

