← Back to Writeups
HTBEasyForensics

Invisible Ink

D3v0o0Nu119/2/20264 min read
#forensics#htb#easy#pdf_polyglot_extraction#pdf_hidden_text_extraction#zip_password_extraction#imagemagick_swirl_reversal#trailing_data_analysis

Invisible Ink

Platform: Tjctf | Category: Forensics | Type: Challenge | Difficulty: Easy | OS: NA | Author: D3v0o0Nu11 | Date: 2026-05-16 | Status: Solved Techniques: pdf_polyglot_extraction, pdf_hidden_text_extraction, zip_password_extraction, imagemagick_swirl_reversal, trailing_data_analysis

Summary

Task: 638KB PDF polyglot file with hidden text, appended ZIP archive, and swirl-distorted PNG image. Solution: extract hidden password via pdftotext, carve ZIP from PDF trailing data, decrypt archive, reverse ImageMagick swirl distortion to read flag.

Recon

Port scan

bash
nmap -p- -sV -sC <TARGET> --min-rate 1000 -Pn
PortServiceVersionNotes
<PORT><SVC><VER><notes>

Enumeration highlights

  • Event: tjctf | ID: 20260516_tjctf_invisible_ink
  • Tags: imagemagick, zip, hidden_text, pdf, polyglot, pdftotext, password_protected_zip, swirl_distortion
  • Indicators: PDF file size much larger than expected for text-only content, data after %%EOF marker in PDF, PK magic bytes in trailing data, swirl-distorted text in PNG image, hidden text layer in PDF not visible in viewer
  • Source: 20260516_tjctf_invisible_ink.md

Foothold

Vulnerability / Misconfiguration

  1. pdf_polyglot_extraction
  2. pdf_hidden_text_extraction
  3. zip_password_extraction
  4. imagemagick_swirl_reversal
  5. trailing_data_analysis
bash
<command>

Exploitation

  • See original writeup content for detailed exploitation.

Privilege Escalation

Enumeration

bash
sudo -l
find / -perm -4000 2>/dev/null
getcap -r / 2>/dev/null
cat /etc/crontab
ps aux

Exploitation

  1. N/A for challenge-type writeup; see exploitation above.
  2. Flag obtained via challenge solve.
bash
<command>

Flags

FlagLocationValue
flagREDACTED

Key Takeaways / Lessons

  • pdf_polyglot_extraction
  • pdf_hidden_text_extraction
  • zip_password_extraction
  • imagemagick_swirl_reversal
  • trailing_data_analysis
  • Tags: imagemagick, zip, hidden_text, pdf, polyglot, pdftotext, password_protected_zip, swirl_distortion

Original Writeup

<details><summary>Click to expand original content</summary>

Description

Check out this cool PDF I found... I wonder if there's anything hidden inside!

A 638KB PDF file (chall.pdf) is provided. The goal is to find a hidden flag. The file is suspiciously large for a 2-page text-only document, hinting at embedded data.

Analysis

Stage 1: PDF Reconnaissance

The PDF has 2 pages, created with "Skia/PDF m149 Google Docs Renderer". Key observations:

  • File size: 638KB — far too large for a text-only PDF (actual PDF structure is only ~31KB)
  • No embedded images: pdfimages -list returns nothing
  • No attachments: pdfdetach -list shows 0 embedded files
  • Hidden text: pdftotext extracts text not visible in the PDF viewer, including a poem about CTFs and crucially a password
bash
pdftotext chall.pdf -

The extracted text includes:

  • A decoy message: "There's nothing here lol why are you looking"
  • A poem about CTF challenges
  • A password string: DBf8nEBgwRhZ

Stage 2: Polyglot Detection — ZIP After PDF EOF

The PDF structure ends at ~31KB with the %%EOF marker, but the file continues for another ~607KB. Examining the trailing bytes reveals a PK (ZIP) magic header immediately after EOF:

python
with open('chall.pdf', 'rb') as f:
    data = f.read()

# Find the last %%EOF marker
eof_pos = data.rfind(b'%%EOF')
trailing = data[eof_pos + 6:]  # skip %%EOF\n

# Write the trailing ZIP data
with open('hidden.zip', 'wb') as f:
    f.write(trailing)

The carved ZIP archive is 606KB and contains a single password-protected file: original_distorted.png.

Stage 3: ZIP Extraction

Using the password discovered in the PDF hidden text:

bash
unzip -P "DBf8nEBgwRhZ" hidden.zip

This extracts original_distorted.png — a 1920×1080 RGB PNG image.

Stage 4: Image Analysis

The PNG shows red handwritten text on a white background that has been distorted with a swirl effect (rotation that varies with distance from center). Pixel analysis reveals:

  • Red channel: only 36 unique values (220–255) — the text is painted in shades of red
  • Green and Blue channels: all 256 values present — standard white background
  • Some text is partially readable at the edges: "tjctf{..." and fragments

Solution

The swirl distortion was applied using ImageMagick's -swirl option. To reverse it, apply the same operation with a negative angle. Testing various angles:

bash
convert original_distorted.png -swirl -240 unswirled.png

At -240 degrees, the text becomes clearly readable across three lines:

  • Line 1: tjctf{p0lygl0t_
  • Line 2: REDACTED
  • Line 3: c00l}

Full Solve Script

python
#!/usr/bin/env python3
"""Solve script for Invisible Ink - TJCTF 2026"""
import subprocess

# Step 1: Extract hidden text from PDF to find password
result = subprocess.run(['pdftotext', 'chall.pdf', '-'], capture_output=True, text=True)
# Password found in output: DBf8nEBgwRhZ

# Step 2: Carve ZIP from PDF trailing data
with open('chall.pdf', 'rb') as f:
    data = f.read()

eof_pos = data.rfind(b'%%EOF')
trailing = data[eof_pos + 6:]

with open('hidden.zip', 'wb') as f:
    f.write(trailing)

# Step 3: Extract password-protected ZIP
subprocess.run(['unzip', '-o', '-P', 'DBf8nEBgwRhZ', 'hidden.zip'])

# Step 4: Reverse swirl distortion
subprocess.run(['convert', 'original_distorted.png', '-swirl', '-240', 'unswirled.png'])

print("Flag visible in unswirled.png: tjctf{REDACTED}")
</details>

signed by XESXOR