N
Back to writeup archive
Hack@10 International Capture The Flag Pre-Eliminary Round 2026

Dear Hiring Manger

Malicious PDF object analysis and ASCII85 payload reconstruction.

ForensicsMarkdown + PDFMediumsolved2 minread·497words
route
/TryN3rr0r/writeups/hack10-pre-eliminary-2026/dear-hiring-manger
archive index
5 / 15
read mode
parsed markdown
Writeup loaded.

Dear Hiring Manger

challenge brief
target profile, scoring, and execution stack
verified solve
target
resume.pdf
category
Forensics
points
500
difficulty
Easy-Medium
flag format
hack10{flag_here}
tools used
fileexiftoolpdfidpdf-parserxxdPython 3
final flag
hack10{M4l1ci0s_PDF}
reveal and copy from section 5

1. Challenge Overview

The scenario indicates that opening a candidate's resume (resume.pdf) resulted in arbitrary code execution that froze company systems. The objective is to perform digital forensics on the malicious PDF file, identify the trigger mechanism, and extract the hidden payload to recover the flag.

PDF evidence frame 01

2. Initial Reconnaissance

Initial triage involved verifying the file type and inspecting system metadata, looking for clues indicative of malicious PDF artifacts.

bash15 lines
1$ ls -la \&\& file resume.pdf \&\& exiftool resume.pdf2-rw-rw-r-- 1 nopalinto nopalinto 158277 Mar 27 10:09 resume.pdf3resume.pdf: PDF document, version 1.3, 1 page(s)4ExifTool Version Number         : 13.505File Name                       : resume.pdf6Directory                       : .7File Size                       : 158 kB8File Type                       : PDF9MIME Type                       : application/pdf10PDF Version                     : 1.311[...]12Producer                        : Acrobat Distiller 5.0.5 (Windows)13Author                          : Luann Barnes14Creator                         : Luann Barnes15Title                           : Microsoft Word - Document6

To determine if the PDF contained active content, we executed pdfid, which quickly flags suspicious embedded actions.

bash10 lines
1$ pdfid resume.pdf || echo "pdfid not found, attempting grep" \&\& grep -a -oE "/JS|/JavaScript|/OpenAction|/Launch" resume.pdf2PDFiD 0.2.10 resume.pdf3 PDF Header: %PDF-1.34 obj                   385 endobj                386 [...]7 /JS                    18 /JavaScript            19 /AA                    010 /OpenAction            1
PDF evidence frame 02

3. Analysis / Forensics Path

The pdfid scan confirmed the presence of exactly one /OpenAction trigger and one embedded /JavaScript payload. We utilized pdf-parser to locate the specific objects holding this code.

bash6 lines
1$ pdf-parser -a resume.pdf2[...]3Search keywords:4 /JS 1: 55 /JavaScript 1: 56 /OpenAction 1: 1

Finding that Object 5 held the JavaScript, we dumped its contents.

bash1 lines
1$ pdf-parser -o 5 -w resume.pdf | xxd
bash11 lines
1$ pdf-parser -o 5 resume.pdf2[...]3obj 5 04 Type: /Action5 Referencing: 6  <<7    /JS '(\\\\n    var a=["BOPCd","0edrK"," 1i+m"];\\\\n    var b=["VBeX","U8:","ddd8$"];\\\\n    eval(atob(a.join("")+b.join("")));\\\\n  )'9    /S /JavaScript10    /Type /Action11  >>
PDF evidence frame 03

The core logic attempts to assemble two arrays (a and b) into a single string: BOPCd0edrK 1i+mVBeXU8:ddd$.

While the script attempts to decode this using atob (a standard JavaScript function for Base64 decoding), there is an anomaly: the string contains characters like : and $ which are strictly invalid in standard Base64 alphabets. Standard browsers will throw an InvalidCharacterError if evaluated.

In the context of PDF encoding schemes, however, the target string maps perfectly to the ASCII85 (Base85) character set (/ASCII85Decode), which utilizes characters from ! to u. The author deliberately named the function atob or obfuscated the routine to misdirect researchers into assuming Base64 formulation.

4. Exploitation / Recovery

Knowing that the payload is actually encoded in Ascii85 (ignoring whitespaces), we can build an offline decoder in Python. By bypassing the malicious execution framework and extracting the exact arrays directly, we decode the string without triggering the payload bomb.

Solver Script (solve.py):

python9 lines
1import base642# Arrays extracted from PDF Object 5:3js_a = ["BOPCd", "0edrK", " 1i+m"]4js_b = ["VBeX", "U8:", "ddd$"]5# Reconstruct the obfuscated payload6payload = "".join(js_a) + "".join(js_b)7# Decode via ASCII85 (Standard PDF obfuscation encoding format)8flag = base64.a85decode(payload.encode('ascii')).decode('utf-8')9print(f"Recovered Flag: {flag}")

Running the solver successfully strips the execution trap and recovers the flag in plaintext:

bash3 lines
1$ python3 solve.py23Recovered Flag: hack10{M4l1ci0s_PDF}
PDF evidence frame 04

5. Flag

captured flag
hack10{M4l1ci0s_PDF}

6. Summary of Approach & Key Takeaways

Initial Recon: Mapped PDF structure using pdfid to confirm the presence of /OpenAction and /JS objects which are notoriously used for malware delivery or system freezing routines.

Payload Extraction: Dissected the document using Didier Stevens' pdf-parser to extract the literal string block held within Object 5.

Deobfuscation/Analysis: Identified that the payload was structured as a split string passed to an eval(atob(...)) wrapper.

Encoding Recognition: Noticed the presence of invalid Base64 delimiters ($, :) within the joined string, correctly pivoting the investigation to Adobe's native ASCII85 (/ASCII85Decode) structure.

Recovery: Reassembled the arrays and ran them through Python's base64.a85decode() library to cleanly extract the flag.

Takeaway: Never blindly trust variables or standard function naming conventions (like atob) in CTF obfuscation scenarios. Threat actors (and challenge developers) often leave red herring wrappers mapping to completely different underlying encoding standards (like ASCII85) explicitly present in the document type specs.

PDF evidence frame 05
PDF evidence frame 06