malicious-pdf is a free, open source threat detection & response project written in Python and released under BSD-2-Clause. It has 4,449 GitHub stars, 588 forks and 4 open issues, and was last pushed 1 months ago. On this registry it ranks #22 of 48 tracked projects in Threat Detection & Response, with 5 head-to-head comparisons available.

What is malicious-pdf?

malicious-pdf is a Python generator of 67 malicious PDF test files for penetration testers, bug bounty hunters, and red teams who need to probe how PDF viewers, converters, and web applications handle hostile document content.

What it is

malicious-pdf is a command-line Python tool that emits a collection of 67 malicious PDF test files, each embedding links and payloads designed to reveal how a target handles untrusted documents. The generated files are written to the output/ directory as test1.pdf, test2.pdf, test3.pdf, and so on, with an optional --no-credit flag to suppress embedded attribution metadata. The tool sits in the security-testing ecosystem and is built to work alongside out-of-band interaction servers such as Burp Collaborator and Interact.sh, into which you pass your callback URL as the script's argument.

The concrete problem it solves is that testing whether a PDF-consuming system leaks data or makes outbound requests normally requires hand-crafting many distinct PDF attack variants. Upload endpoints, PDF-to-image converters, server-side processing libraries such as PDFBox and iText, PDF readers, and static analysis tools each expose different behaviours, so a tester needs a broad set of samples covering phone-home callbacks, SSRF, XSS, XXE, NTLM credential theft, and data exfiltration. This tool replaces that manual assembly with a single invocation that produces the whole batch at once.

Key capabilities

  • Generates 67 distinct malicious PDF samples in one run against a supplied callback URL, covering phone-home callbacks, SSRF, XSS, XXE, NTLM credential theft, and data exfiltration.
  • Supports four obfuscation levels through --obfuscate LEVEL: level 1 applies PDF name hex encoding plus string octal/hex encoding, level 2 adds JS bracket notation and `` URI case/whitespace obfuscation, level 3 adds FlateDecode stream compression, and level 4 wraps JS payloads in a base64 decoder stub so the original API calls never appear as literal substrings.
  • Produces staged JavaScript payloads, including form-field /V and base64 decoder techniques, which defeat naΓ―ve /JS regex scanners in PDF static analysis tools.
  • Writes output to a configurable directory via --output-dir DIR (default output/) and offers --no-credit to omit credit and attribution metadata from the generated PDFs.
  • Targets server-side PDF processing libraries by name, including PDFBox and iText, whose XXE history (CVE-2016-2175 and CVE-2017-9096) and related flaws are among the references it builds on.
  • Integrates directly with Burp Collaborator and Interact.sh as the callback receiver for blind interaction detection.
  • Ships under a BSD-2-Clause licence and carries an OpenSSF Best Practices badge.

Who uses it and how

  • Penetration testers feed a Burp Collaborator URL to python3 malicious-pdf.py and upload the resulting output/ files to web pages and services that accept PDF input.
  • Bug bounty hunters use the samples to look for SSRF, XXE, blind callbacks, and NTLM leaks in file upload endpoints, PDF-to-image converters, and document processing pipelines on programs that accept PDF input.
  • Red teams evaluate security products and PDF readers by observing which generated files trigger outbound interactions.
  • Defenders and QA engineers test server-side PDF processing libraries such as PDFBox and iText, and validate PDF static analysis tools against staged payloads.

Getting started

Install the dependencies with pip install -r requirements.txt, then run python3 malicious-pdf.py burp-collaborator-url to write the generated PDFs into the output/ directory.

How it compares

Among the related work it credits, Bad-PDF and Burp Suite UploadScanner address overlapping ground, but this project is specifically a standalone batch generator rather than a Burp extension, so it pairs with a collaborator URL instead of living inside an intercepting proxy. That keeps it usable from a plain Python environment on any target where you can upload the resulting files.

When to use it β€” and when not

A self-hoster needs only Python and an out-of-band listener such as Burp Collaborator or Interact.sh to receive callbacks, since the tool itself generates files rather than serving them. It is not a scanner or an exploit: it will not find vulnerabilities on its own, and it should not be used outside authorized testing, as the README states the project is for educational and professional purposes only. The documentation is also somewhat inconsistent β€” the options block describes --obfuscate LEVEL as 0–3 while the examples demonstrate level 4 β€” so the exact range should be confirmed before relying on a specific level.

project readme (upstream, from github) β€” read inline

malicious-pdf.png

made-with-python OpenSSF Best Practices

Malicious PDF Generator ☠️

Generate 67 malicious PDF test files for testing phone-home callbacks, SSRF, XSS, XXE, NTLM credential theft, and data exfiltration in PDF viewers, converters, and web applications. Can be used with Burp Collaborator or Interact.sh

Used for penetration testing, bug bounty hunting, and/or red-teaming etc. I created this tool because I needed a tool to generate a bunch of PDF files with various links. Educational and professional purposes only.

Usage

pip install -r requirements.txt
python3 malicious-pdf.py burp-collaborator-url

Output will be written to the output/ directory as: test1.pdf, test2.pdf, test3.pdf etc.

Options

--output-dir DIR    Directory to save generated PDF files (default: output/)
--no-credit         Do not embed credit/attribution metadata in generated PDFs
--obfuscate LEVEL   Obfuscation level (0-3):
                      0 = None (default)
                      1 = PDF name hex encoding + string octal/hex encoding
                      2 = Level 1 + JS bracket notation + javascript: URI case/whitespace obfuscation
                      3 = Level 2 + FlateDecode stream compression

Example with obfuscation:

python3 malicious-pdf.py https://your-interact-sh-url --obfuscate 2

Maximum obfuscation (Level 4 wraps JS payloads in a base64 decoder stub so the original API calls never appear as literal substrings):

python3 malicious-pdf.py https://your-interact-sh-url --obfuscate 4

Purpose

  • Test web pages/services accepting PDF files
  • Test security products
  • Test PDF readers
  • Test PDF converters
  • Test server-side PDF processing libraries (PDFBox, iText, etc.)
  • Test PDF static analysis tools β€” staged JS payloads (form-field /V, base64 decoder) defeat naΓ―ve /JS regex scanners
  • Bug bounty hunting β€” useful for finding SSRF, XXE, blind callbacks, and NTLM leaks in file upload endpoints, PDF-to-image converters, and document processing pipelines on programs that accept PDF input

Credits

In Media

Complete Test Matrix

Click to expand all 70 test cases
Test File Function CVE/Reference Attack Vector Method Impact
test1.pdf create_malpdf() CVE-2018-4993 External file access /GoToE action with UNC path Network callback via file system
test1_1.pdf create_malpdf() CVE-2018-4993 External file access /GoToE action with HTTPS URL Network callback via HTTPS
test2.pdf create_malpdf2() XFA form submission Form data exfiltration XDP form with submit event Automatic form submission
test3.pdf create_malpdf3() JavaScript injection Code execution /OpenAction with app.openDoc() External document loading
test4.pdf create_malpdf4() CVE-2019-7089 XSLT injection XFA with external XSLT stylesheet UNC path callback
test5.pdf create_malpdf5() PDF101 research URI action /URI action type DNS prefetching/HTTP request
test6.pdf create_malpdf6() PDF101 research Launch action /Launch with external URL External resource execution
test7.pdf create_malpdf7() PDF101 research Remote PDF /GoToR action Remote PDF loading
test8.pdf create_malpdf8() PDF101 research Form submission /SubmitForm with HTML flags Form data submission
test9.pdf create_malpdf9() PDF101 research Data import /ImportData action External data import
test10.pdf create_malpdf10() CVE-2017-10951 JavaScript execution Foxit this.getURL() callback Network callback via Foxit Reader
test11.pdf create_malpdf11() EICAR test AV detection Embedded EICAR string Anti-virus testing
test12.pdf create_malpdf12() CVE-2014-8453 FormCalc data exfiltration XFA FormCalc Post() function Same-origin data exfiltration with cookies
test13.pdf create_malpdf13() Request injection CRLF header injection XFA submit textEncoding CRLF HTTP header manipulation
test14.svg create_malpdf14() ImageMagick shell injection Shell injection via SVG/MSL SVG-MSL polyglot authenticate attribute Remote code execution via ImageMagick
test15.pdf create_malpdf15() PDF specification FormCalc header injection XFA FormCalc Post() with custom headers Arbitrary HTTP header injection
test16.pdf create_malpdf16() PDF specification JavaScript via GotoE /GoToE with `` URI Browser XSS when PDF embedded via /
test17.pdf create_malpdf17() CVE-2014-8452 XXE injection XMLData.parse() external entity XML external entity resolution
test18.pdf create_malpdf18() PortSwigger research Annotation URI injection Unescaped parens inject JS action via duplicate /A key XSS via PDF-Lib/jsPDF output
test19.pdf create_malpdf19() PortSwigger research PV auto-execution /AA /PV Screen annotation fires JS on page visible Automatic code execution (Acrobat)
test20.pdf create_malpdf20() PortSwigger research PC close trigger /AA /PC annotation fires JS on page close Code execution on close (Acrobat)
test21.pdf create_malpdf21() PortSwigger research SubmitForm SubmitPDF /SubmitForm with Flags 256 sends entire PDF Full PDF content exfiltration
test22.pdf create_malpdf22() PortSwigger research JS submitForm() this.submitForm() with cSubmitAs: "PDF" PDF content submission (Acrobat)
test23.pdf create_malpdf23() PortSwigger research Widget button injection Invisible /Btn widget covering page, JS on click Code execution (Chrome/PDFium)
test24.pdf create_malpdf24() PortSwigger research Text field SSRF Widget /Tx field with submitForm() POST Blind SSRF via form data
test25.pdf create_malpdf25() PortSwigger research Content extraction getPageNthWord() reads all text and exfiltrates Rendered text exfiltration
test26.pdf create_malpdf26() PortSwigger research Mouseover trigger /AA /E annotation fires JS on mouse enter Code execution on hover (PDFium)
test27 β€” β€” Removed Duplicate of test3 (Acrobat OpenAction JS) + test23 (Chrome Widget Btn) β€”
test28.pdf create_malpdf28() PortSwigger research URL hijacking Unescaped parens inject new /URI action Click redirection via PDF-Lib/jsPDF
test29.pdf create_malpdf29() CVE-2024-4367 FontMatrix injection Type1 font FontMatrix string breaks out of c.transform() Arbitrary JS execution in PDF.js (Firefox >` forces remote fetch
test33_1.pdf create_malpdf33_1() PDF101 research JS: this.submitForm() Acrobat JS form submission callback Acrobat Reader
test33_2.pdf create_malpdf33_2() PDF101 research JS: this.getURL() Acrobat JS URL fetch Acrobat Reader
test33_3.pdf create_malpdf33_3() PDF101 research JS: app.launchURL() Acrobat JS launch URL Acrobat Reader
test33_4.pdf create_malpdf33_4() PDF101 research JS: app.media.getURLData() Acrobat JS media fetch Acrobat Reader
test33_5.pdf create_malpdf33_5() PDF101 research JS: SOAP.connect() Acrobat JS SOAP connection Acrobat Reader
test33_6.pdf create_malpdf33_6() PDF101 research JS: SOAP.request() Acrobat JS SOAP request Acrobat Reader
test33_7.pdf create_malpdf33_7() PDF101 research JS: this.importDataObject() Acrobat JS data import Acrobat Reader
test33_8.pdf create_malpdf33_8() PDF101 research JS: app.openDoc() Acrobat JS open document Acrobat Reader
test33_9.pdf create_malpdf33_9() PDF101 research JS: fetch() Web API callback (PDF.js/browser) Firefox/PDF.js
test33_10.pdf create_malpdf33_10() PDF101 research JS: XMLHttpRequest Web API callback (PDF.js/browser) Firefox/PDF.js
test33_11.pdf create_malpdf33_11() PDF101 research JS: new Image() Web API image callback (PDF.js/browser) Firefox/PDF.js
test33_12.pdf create_malpdf33_12() PDF101 research JS: WebSocket Web API WebSocket callback (PDF.js/browser) Firefox/PDF.js
test33_13.pdf create_malpdf33_13() Adobe 0-day blog (Apr 2026) JS: RSS.addFeed() Acrobat JS RSS feed callback Acrobat Reader
test33_14.pdf create_malpdf33_14() Adobe 0-day blog (Apr 2026) JS: util.readFileIntoStream() + SOAP.request() Local file read + exfil chain (try/catch error path also callbacks) Acrobat Reader
test33_15.pdf create_malpdf33_15() Adobe 0-day blog (Apr 2026) Form-field-staged JS loader Base64 payload in /Tx widget /V, decoded via getField() + util.stringFromStream Acrobat Reader
test34_1.pdf create_malpdf34_1() PDF101 research UNC: XObject stream Image XObject with UNC path NTLM theft via page rendering
test34_2.pdf create_malpdf34_2() PDF101 research UNC: GoToR /GoToR action with UNC FileSpec NTLM theft via remote PDF
test34_3.pdf create_malpdf34_3() PDF101 research UNC: Thread /Thread action with UNC FileSpec NTLM theft via thread reference
test34_4.pdf create_malpdf34_4() PDF101 research UNC: URI /URI action with UNC path NTLM theft via URI action
test34_5.pdf create_malpdf34_5() PDF101 research UNC: JS submitForm this.submitForm() with UNC path NTLM theft via JS form submission
test34_6.pdf create_malpdf34_6() PDF101 research UNC: JS getURL this.getURL() with UNC path NTLM theft via JS URL fetch
test34_7.pdf create_malpdf34_7() PDF101 research UNC: JS launchURL app.launchURL() with UNC path NTLM theft via JS launch
test34_8.pdf create_malpdf34_8() PDF101 research UNC: JS SOAP SOAP.connect() with UNC path NTLM theft via JS SOAP
test34_9.pdf create_malpdf34_9() PDF101 research UNC: JS openDoc app.openDoc() with UNC path NTLM theft via JS open document
test35.pdf create_malpdf35() PDF101 research Names dictionary /Names /JavaScript catalog-level auto-execute trigger Alternative JS execution trigger
test36.pdf create_malpdf36() CVE-2016-2175 / CVE-2017-9096 XXE in XMP metadata XXE `` in /Metadata XMP stream Server-side callback (PDFBox, iText)
test37.pdf create_malpdf37() CVE-2016-2175 / CVE-2017-9096 XXE in XFA form data XXE `` in /AcroForm /XFA stream Server-side callback (PDFBox, iText)
test38.pdf create_malpdf38() CVE-2020-29075 Silent DNS tracking Catalog /AA with /WC, /WS, /DS triggers DNS callback without prompt (Acrobat)
test39.pdf create_malpdf39() CVE-2022-28244 CSP bypass RichMedia annotation with embedded HTML/JS Cross-origin request (Acrobat)
test40.pdf create_malpdf40() CVE-2018-5158 PostScript calculator injection /FunctionType 4 JS injection in image XObject JS execution in PDF.js worker (Firefox)
test41.pdf create_malpdf41() CVE-2018-20065 URI without user gesture /OpenAction with /S /URI auto-navigation Silent navigation (PDFium/Chrome)
test42.pdf create_malpdf42() CVE-2025-66516 XXE OOB parameter entity in XFA %xxe; param entity in /AcroForm /XFA forces DTD fetch Server-side blind XXE (Tika, Confluence, Jira)
test43.pdf create_malpdf43() CVE-2025-70401 Annotation /T field XSS `` tag in Text annotation /T (author) field XSS callback (Apryse WebViewer, web viewers)
test44.pdf create_malpdf44() CVE-2024-12426 LibreOffice URL expansion /URI with vnd.sun.star.expand: expands ${HOME} Env var exfiltration (LibreOffice withinitialize` event

Todo: New test cases

  • Acrobat JS fingerprinting APIs β€” Add test cases for reconnaissance/fingerprinting APIs used in the April 2026 Adobe 0-day exploit chain (ref): Collab.isDocReadOnly (filesystem probing), app.plugIns (enumerate installed plugins), app.viewerVersion (version fingerprinting)

Todo: Obfuscation methods not yet implemented

  • Empty-password PDF encryption β€” Encrypt all strings/streams with empty user password. Document opens without prompting but static analysis tools cannot read content. Biggest gap in current obfuscation. Ref: Didier Stevens, How secure is PDF encryption?
  • Object streams (ObjStm) β€” Hide PDF objects inside compressed stream containers. Simple parsers (including PDFiD without -O flag) miss objects entirely. Ref: PDF spec ISO 32000 Β§7.5.7
  • getAnnots() code storage β€” Split JavaScript payload across annotation metadata fields (subject, author). Retrieve at runtime via app.doc.getAnnots()[n].subject and eval. Ref: Julia Wolf - PDF Obfuscation using getAnnots()
  • Info dict data extraction β€” Store encoded payload in /Info trailer fields (/Title, /Author). Retrieve at runtime via info.Title in JS. Ref: corkami PDF tricks
  • AcroForm field value extraction β€” Store payload fragments in form field /V values. Retrieve via getField("name").value in JS. Ref: corkami PDF tricks
  • Names tree split execution β€” Split JavaScript across multiple /Names entries executed sequentially. Ref: corkami PDF tricks
  • Incremental updates after %%EOF β€” Append new objects/actions after the original %%EOF marker via incremental update. Ref: PDF101 content masking, Didier Stevens
  • JS unescape() encoding β€” Wrap JS payload in eval(unescape("%61%6C%65%72%74...")). Ref: corkami PDF tricks
  • Fake file headers β€” Prepend JPEG/HTML/other magic bytes before %PDF- header (spec allows header within first 1024 bytes). Confuses file-type detection. Ref: corkami, Decalage
  • Anti-emulation checks β€” Detect real Adobe Reader via event.target.zoomType == "FitPage" or global variable type checks before executing payload. Ref: corkami PDF tricks

Won't implement

  • CVE-2023-26369 - Adobe Acrobat TTF font heap OOB write β€” Requires binary exploitation (heap spray, ROP chains, shellcode). No public PoC. Cannot produce a simple callback.
  • CVE-2021-28550 - Adobe Acrobat Use-After-Free β€” Requires binary exploitation chain + sandbox escape (CVE-2021-31199/31201). No public PoC. Cannot produce a simple callback.

Star History

Star History Chart

Frequently asked questions

Is malicious-pdf free to use?

malicious-pdf is open source under the BSD-2-Clause licence. There is no licence fee and no seat count β€” you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does malicious-pdf do?

πŸ’€ Generate malicious PDF test files for testing phone-home callbacks, SSRF, XSS, NTLM credential theft, and data exfiltration in PDF viewers, converters, and w

What is malicious-pdf written in?

malicious-pdf is primarily written in Python. Its source is publicly available at https://github.com/jonaslejon/malicious-pdf, and it has 4,449 GitHub stars.