| File | orion-challenge-03.pdf |
| SHA-256 | d7d364d73985683aaf496eec338d5f4a13cb0d0d3ae9eec7a9c6d1e9fde05348 |
| Size | 11,447 bytes, 2 pages, PDF 1.4 |
| Context | Found on the NORTHSEA-DEV workstation during an internal investigation |
During the investigation into the Project ORION leak, a PDF was recovered from the suspect's workstation. It presents itself as an internal quarterly report, but the DFIR team flagged anomalies in its structure.
Your job is to analyse the file with PDF forensic tools, identify the techniques used to hide information, and extract the IOCs that corroborate the earlier challenges.
🔗 ORION thread: this challenge is part of the same investigation as #01 (VBA maldoc), #02 (disk forensics) and #03 (OSINT attribution). The IOCs extracted here cross-reference the earlier results.
pdfinfo misses it.# exiftool sudo apt-get install -y libimage-exiftool-perl # Didier Stevens tools (pdfid + pdf-parser) mkdir ~/pdf-tools && cd ~/pdf-tools wget -q https://didierstevens.com/files/software/pdfid_v0_2_9.zip wget -q https://didierstevens.com/files/software/pdf-parser_v0_7_9.zip unzip -q pdfid_v0_2_9.zip unzip -q pdf-parser_v0_7_9.zip
💡 CyberChef runs online at gchq.github.io/CyberChef. The recipe buttons below open the right recipe directly.
mkdir -p ~/lab04 && cd ~/lab04 cp /path/to/orion-challenge-03.pdf . sha256sum orion-challenge-03.pdf # d7d364d73985683aaf496eec338d5f4a13cb0d0d3ae9eec7a9c6d1e9fde05348
Always start by identifying what you have before going deep.
pdfinfo orion-challenge-03.pdf
Title: Project ORION -- Q3 Assessment Report Author: alex.m Creator: LibreOffice 24.2 -- AlexM workstation Producer: Microsoft Word for Microsoft 365 CreationDate: Tue Jun 16 07:11:00 2026 UTC Pages: 2 JavaScript: no PDF version: 1.4
🚩 JavaScript: no, but that is wrong. pdfinfo uses a heuristic that looks for a /Names /JavaScript namespace in the catalog. Our JS lives in an /Action object, so pdfinfo misses it. We prove that in step 3.
exiftool reads every field of the /Info dictionary and presents it clearly. Look for what does not add up.
exiftool orion-challenge-03.pdf
Title : Project ORION -- Q3 Assessment Report Author : alex.m Subject : North Sea Digital / Internal research Keywords : ORION NSD confidential Q3 2026 Creator : LibreOffice 24.2 -- AlexM workstation Producer : Microsoft Word for Microsoft 365 Create Date : 2026:06:16 09:11:00+02:00
🔴 IOC, Creator / Producer discordance: Creator claims LibreOffice, Producer claims Microsoft Word. These two tools do not coexist in a single legitimate PDF production chain. The Producer field was edited by hand after generation, a common way to muddy the file's origin.
💡 Creation date: 2026-06-16 09:11 CEST. If you did Challenge #02 (disk forensics), that time should ring a bell: it is the exact moment the orion-export.zip archive was created on alex's machine.
pdfid.py does no semantic analysis, it counts PDF keyword occurrences in the raw bytes. That is far harder to fool than pdfinfo's heuristics.
python3 ~/pdf-tools/pdfid.py orion-challenge-03.pdf
PDFiD 0.2.9 orion-challenge-03.pdf PDF Header: %PDF-1.4 obj 10 stream 3 /Page 2 /JS 1 /JavaScript 1 /OpenAction 1 /Launch 0 /EmbeddedFile 0
🚩 /JS 1, /JavaScript 1, /OpenAction 1: the three together are characteristic of a PDF that runs JavaScript on open. pdfinfo said JavaScript: no. Lesson: never trust a single tool.
python3 ~/pdf-tools/pdf-parser.py --search /JavaScript orion-challenge-03.pdf
obj 8 0
Type: /Action
<< /Type /Action
/S /JavaScript
/JS (app.alert({cMsg:'ORION verification required. Token: ORION-Q3-NSD',
cTitle:'Project ORION',nIcon:3}); )
>>💡 Here the JavaScript is benign (an app.alert()). In a real maldoc this action can trigger shellcode, a use-after-free exploit in the reader, or a download. The structure is identical, only the content of /JS changes.
Before decoding, map every stream and its encoding methods. This gives you the whole structure at a glance.
python3 ~/pdf-tools/pdf-parser.py --search /Filter orion-challenge-03.pdf
obj 5 0 Contains stream << /Length 618 /Filter /FlateDecode >> obj 7 0 Contains stream << /Length 879 /Filter [/ASCIIHexDecode /FlateDecode] >> obj 10 0 Contains stream << /Subtype /Image /Filter /FlateDecode >>
Object 5 holds the page-1 stream compressed with zlib (FlateDecode). Two methods: let pdf-parser.py do it, or extract the raw bytes and decode by hand in CyberChef.
python3 ~/pdf-tools/pdf-parser.py --object 5 --filter orion-challenge-03.pdf
Stream b'BT\n/F1 20 Tf 50 750 Td (PROJECT ORION) Tj\n/F1 11 Tf 0 -35 Td (Q3 Assessment Report -- North Sea Digital) Tj\n0 -18 Td (Classification: INTERNAL -- NOT FOR DISTRIBUTION) Tj ...'
Object 7 uses a chain of two filters: the content is ASCII hex first, itself zlib-compressed. To decode, apply the operations in the reverse order of the /Filter list, right to left.
python3 ~/pdf-tools/pdf-parser.py --object 7 --filter orion-challenge-03.pdf
ORION INTERNAL MEMO -- RESTRICTED
Stage 2 transfer confirmed.
Package delivered via USB device. Serial: SN-LAB-042.
Label: TRANSFER
Await signal from N0rthSea before proceeding to stage 3.
Verification token: ORION-{d3c0d3d}-Q3-N0rthS3a
Stream encoding: [/ASCIIHexDecode /FlateDecode]🔬 Open the CyberChef recipe, From Hex then Raw Inflate✅ The memo reveals the token ORION-{d3c0d3d}-Q3-N0rthS3a and confirms the transfer over USB device SN-LAB-042 labelled TRANSFER, the two artefacts identified in the disk-forensics Challenge #02.
| Type | Value | Source |
|---|---|---|
| Metadata | Author: alex.m | exiftool, /Info obj 2 |
| IOC discordance | Creator: LibreOffice 24.2 / Producer: Microsoft Word | exiftool, contradictory fields |
| JavaScript | /S /JavaScript, /OpenAction obj 8 | pdfid.py, missed by pdfinfo |
| Token | ORION-{d3c0d3d}-Q3-N0rthS3a | obj 7, ASCIIHex+FlateDecode decoded |
| USB device | Serial: SN-LAB-042, Label: TRANSFER | obj 7, internal memo |
| Persona | N0rthSea, stage 3 mentioned | obj 7, internal memo |
| Timestamp | 2026-06-16 09:11 CEST (UTC+2) | exiftool, CreationDate |
Did you find all three techniques and extract every IOC?
✅ The three techniques to find:
1. Creator/Producer discordance in the metadata (obj 2)
2. JavaScript embedded via /OpenAction (obj 8), missed by pdfinfo
3. Double-encoded stream [ASCIIHexDecode + FlateDecode] (obj 7)
pdfinfo looks for a /Names << /JavaScript ... >> structure in the catalog, the usual pattern for AcroForm forms with JS. Our action uses /OpenAction -> /S /JavaScript directly, without the /Names namespace. It is an implementation difference, not a limitation. pdfid.py counts raw string occurrences and catches both patterns.
[/ASCIIHexDecode /FlateDecode] chain decodes right to left: From Hex first, then Raw Inflate.ORION-{d3c0d3d}-Q3-N0rthS3a ties all four labs together, it is the pivot artefact of the investigation.The reference suite for PDF forensics: pdfid, pdf-parser, make-pdf.
Metadata extraction for 150+ formats including PDF, DOCX, images.
Raw Inflate, From Hex, From Base64, XOR, every decoding recipe in one tool.
Obfuscation techniques in malicious files: encoding, compression, encryption.
Code execution via JavaScript embedded in PDF or HTML documents.
The full PDF format specification: object structure, filters, actions.
Entirely fictional environment, for educational use only. The embedded JavaScript is a benign app.alert().
A Belgium-based provider of cybersecurity solutions, and the team behind SynapseRM / TPRM.