corbel/scripts/gen_fixtures.py

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()