148 lines
4.4 KiB
Python
Executable File
148 lines
4.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Generate test fixture PDFs for CorbelPurge.
|
|
|
|
Creates four PDFs:
|
|
- benign.pdf — a clean PDF with just text
|
|
- malicious_js.pdf — a PDF with a /JavaScript action
|
|
- malicious_launch.pdf — a PDF with a /Launch action
|
|
- cve_writeup.pdf — an educational PDF that mentions JS / shellcode
|
|
|
|
Uses pypdf for xref-safe object injection.
|
|
"""
|
|
|
|
from pathlib import Path
|
|
from reportlab.pdfgen import canvas
|
|
from reportlab.lib.pagesizes import letter
|
|
|
|
import pypdf
|
|
from pypdf.generic import (
|
|
ArrayObject,
|
|
DictionaryObject,
|
|
NameObject,
|
|
NumberObject,
|
|
TextStringObject,
|
|
IndirectObject,
|
|
)
|
|
|
|
FIXTURES_DIR = Path(__file__).parent.parent / "tests" / "fixtures"
|
|
FIXTURES_DIR.mkdir(parents=True, exist_ok=True)
|
|
|
|
|
|
def make_benign_pdf():
|
|
"""A clean PDF with just text — no JavaScript, no embedded files."""
|
|
path = FIXTURES_DIR / "benign.pdf"
|
|
c = canvas.Canvas(str(path), pagesize=letter)
|
|
c.drawString(100, 700, "Hello, this is a benign PDF.")
|
|
c.drawString(100, 680, "It contains only text and no active content.")
|
|
c.setTitle("Benign Test PDF")
|
|
c.setAuthor("CorbelPurge Tests")
|
|
c.save()
|
|
return path
|
|
|
|
|
|
def make_malicious_js_pdf():
|
|
"""A PDF with a /JavaScript action attached to the catalog's /OpenAction."""
|
|
# First, generate a base PDF with reportlab.
|
|
base_path = FIXTURES_DIR / "_base_js.pdf"
|
|
c = canvas.Canvas(str(base_path), pagesize=letter)
|
|
c.drawString(100, 700, "This PDF has a JavaScript action.")
|
|
c.setTitle("Malicious JS Test PDF")
|
|
c.save()
|
|
|
|
# Now use pypdf to inject a /JavaScript action safely.
|
|
path = FIXTURES_DIR / "malicious_js.pdf"
|
|
reader = pypdf.PdfReader(str(base_path))
|
|
writer = pypdf.PdfWriter()
|
|
|
|
# Copy all pages.
|
|
for page in reader.pages:
|
|
writer.add_page(page)
|
|
|
|
# Add a new /JavaScript action object.
|
|
js_action = DictionaryObject({
|
|
NameObject("/Type"): NameObject("/Action"),
|
|
NameObject("/S"): NameObject("/JavaScript"),
|
|
NameObject("/JS"): TextStringObject("app.alert('XSS from PDF');"),
|
|
})
|
|
js_action_ref = writer._add_object(js_action)
|
|
|
|
# Attach it to the catalog's /OpenAction.
|
|
writer._root_object[NameObject("/OpenAction")] = js_action_ref
|
|
|
|
with open(path, "wb") as f:
|
|
writer.write(f)
|
|
|
|
base_path.unlink()
|
|
return path
|
|
|
|
|
|
def make_malicious_launch_pdf():
|
|
"""A PDF with a /Launch action."""
|
|
base_path = FIXTURES_DIR / "_base_launch.pdf"
|
|
c = canvas.Canvas(str(base_path), pagesize=letter)
|
|
c.drawString(100, 700, "This PDF has a Launch action.")
|
|
c.setTitle("Malicious Launch Test PDF")
|
|
c.save()
|
|
|
|
path = FIXTURES_DIR / "malicious_launch.pdf"
|
|
reader = pypdf.PdfReader(str(base_path))
|
|
writer = pypdf.PdfWriter()
|
|
|
|
for page in reader.pages:
|
|
writer.add_page(page)
|
|
|
|
# Add a /Launch action.
|
|
launch_action = DictionaryObject({
|
|
NameObject("/Type"): NameObject("/Action"),
|
|
NameObject("/S"): NameObject("/Launch"),
|
|
NameObject("/F"): TextStringObject("/bin/sh"),
|
|
NameObject("/Win"): DictionaryObject({
|
|
NameObject("/F"): TextStringObject("cmd.exe"),
|
|
}),
|
|
})
|
|
launch_ref = writer._add_object(launch_action)
|
|
|
|
writer._root_object[NameObject("/OpenAction")] = launch_ref
|
|
|
|
with open(path, "wb") as f:
|
|
writer.write(f)
|
|
|
|
base_path.unlink()
|
|
return path
|
|
|
|
|
|
def make_cve_writeup_pdf():
|
|
"""A PDF that *mentions* JavaScript and shellcode in an educational context."""
|
|
path = FIXTURES_DIR / "cve_writeup.pdf"
|
|
c = canvas.Canvas(str(path), pagesize=letter)
|
|
c.setTitle("CVE-2024-1234 Writeup")
|
|
c.setAuthor("Security Researcher")
|
|
c.drawString(100, 750, "CVE-2024-1234: PDF JavaScript Injection Analysis")
|
|
c.drawString(100, 720, "Abstract")
|
|
text = c.beginText(100, 700)
|
|
text.setFont("Helvetica", 10)
|
|
text.textLines(
|
|
"In this paper we describe a vulnerability in which a malicious PDF\n"
|
|
"uses a /JavaScript action to execute arbitrary code. The eval()\n"
|
|
"function is called with attacker-controlled input. Remediation:\n"
|
|
"patch the reader to ignore /JavaScript actions in /OpenAction."
|
|
)
|
|
c.drawText(text)
|
|
c.save()
|
|
return path
|
|
|
|
|
|
def main():
|
|
paths = [
|
|
make_benign_pdf(),
|
|
make_malicious_js_pdf(),
|
|
make_malicious_launch_pdf(),
|
|
make_cve_writeup_pdf(),
|
|
]
|
|
for p in paths:
|
|
print(f" wrote {p} ({p.stat().st_size} bytes)")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|