PICOCTF 2019 • FORENSICS • FILE HEADER SURGERY

c0rrupt: PNG Binary Specification & Hex Reconstruction

By AbdoAug 31, 2026
c0rrupt Challenge Analysis
Official Challenge Prompt

“We found this file. Recover the flag. You can also find the file in /problems/c0rrupt_0_1fcad1353b2255f250d60c14afed2100 on the shell server.”

Category: Forensics / File RepairPoints: 250 PTSFlag Format: picoCTF{...}
Provided File (Download & Practice)⬇️ Download mysterySize: 202,887 bytesType: Corrupted PNG Binary
Dissector: HexEd.it / HxD

💡 THE INTUITIVE ANALOGY (Why did the image break?)

Imagine receiving a sealed shipping container. The barcode on the outside is smudged, the label that says what is inside is torn off, and the weight sticker doesn't match the contents. The customs inspector immediately refuses to process it. That is exactly what happens when your OS tries to open mystery. The challenge creator took a legitimate PNG image and intentionally sabotaged 4 specific barcode and header bytes so every image viewer rejects it as corrupted.

1. The W3C PNG Binary Standard

To repair any damaged image file, you must master the binary architecture of the PNG (Portable Network Graphics) standard. Every valid PNG has two components:

1. The 8-Byte Magic Header (Official Signature):
   Hex:   89  50  4E  47  0D  0A  1A  0A
   ASCII: \x89  P   N   G  \r  \n \x1a \n

2. Sequential Data Chunks (Each chunk follows this exact 4-field rule):
   ┌──────────────────┬──────────────────┬──────────────────────────┬──────────────────┐
   │ Length (4 Bytes) │ Type (4 Bytes)   │ Data (Length Bytes)      │ CRC32 (4 Bytes)  │
   ├──────────────────┼──────────────────┼──────────────────────────┼──────────────────┤
   │ 00 00 00 0D      │ 49 48 44 52      │ Width, Height, Bit Depth │ 7C 8B AB 78      │
   │ (13 bytes data)  │ ("IHDR" ASCII)   │ Color Type, Compression  │ (Checksum test)  │
   └──────────────────┴──────────────────┴──────────────────────────┴──────────────────┘

If the chunk name is misspelled or the Length doesn't match the number of data bytes, the CRC32 check fails, and the image viewer aborts rendering.

2. Diagnostic Reconnaissance with `pngcheck`

Before touching any bytes, we run the automated diagnostic utility pngcheck to pinpoint the exact failing offsets:

$ pngcheck -v mystery
mystery: not a PNG file (starts with 89 65 4e 34 0d 0a b0 aa)
ERROR: mystery is corrupted at offset 0x00000000
METHOD A: HANDS-ON MANUAL REPAIR

3. Manual Hex Surgery (Click-by-Click via Hex Editor)

Learn how to edit raw bytes manually using HexEd.it (in your browser) or HxD on Windows.

🛠️ Manual Setup Instructions:

  1. Open https://hexed.it/ in Chrome/Edge or open HxD.
  2. Click Open file and select mystery.
  3. CRITICAL RULE: Make sure the mode at the bottom says OVR (Overwrite), not INS (Insert). (Insert pushes old bytes and ruins the file size).
  4. Never press Spacebar! The editor automatically advances to the next box after every 2 characters.
Fix #1: The 8-Byte Magic Header (Offset 0x00000000)Row 1, Bytes 0-7

Why it broke: The author replaced .PNG with .eN4.

Current Corrupted Bytes:
89 65 4E 34 0D 0A B0 AA
ASCII: .eN4....
Click byte 65 & Type:
89 50 4E 47 0D 0A 1A 0A
ASCII: .PNG\r\n\x1a\n
Fix #2: First Chunk Type → IHDR (Offset 0x0000000C)Row 1, Bytes 12-15

Why it broke: Right after the 4-byte length 00 00 00 0D, the chunk type spells C"DR instead of IHDR.

Current Corrupted Bytes:
43 22 44 52
ASCII: C"DR
Click 43 & Type:
49 48 44 52
ASCII: IHDR
Fix #3: pHYs Pixel Density Data (Offset 0x00000046)Row 00000040

Why it broke: The pHYs chunk defines physical pixels per meter ($5669 \times 5669$). An extra corrupted byte AA threw off the chunk length and CRC checksum.

Current Corrupted Bytes:
AA 00 16 25 00 00 16 25 01
Click AA & Type:
00 00 16 25 00 00 16 25 01
Fix #4: IDAT Compressed Image Data (Offset 0x00000053)Row 00000050

Why it broke: The actual image pixel data chunk header was scrambled from IDAT into \xabDET with an invalid length prefix.

💡 How we calculate IDAT length: Total file size (202,887 bytes) minus all header and other chunk bytes = 202,833 bytes = 0x00031851 in hex.
Current Corrupted Bytes:
AA AA FF A5 AB 44 45 54
ASCII: ....DET
Click AA & Type 8 Bytes:
00 03 18 51 49 44 41 54
ASCII: ...QIDAT (202,833 bytes)

💾 In HexEd.it, click Export → Save as fixed.png → Open with Windows Photo Viewer!

METHOD B: AUTOMATED FAST SOLVE & ONELINER

4. The 0.1-Second Python Fixer (`solve.py`)

In competition environments, manually typing hex bytes is too slow. Once you know what chunks are broken, you write an automated Python script using bytearray to slice and replace the corrupted segments in memory in 1 millisecond:

# Open the corrupted mystery file as raw bytes
with open("mystery", "rb") as f:
    data = bytearray(f.read())

# Fix 1: PNG Magic Header (Bytes 0 to 7)
data[0:8] = b"\x89PNG\r\n\x1a\n"

# Fix 2: First chunk name -> IHDR (Bytes 12 to 15)
data[12:16] = b"IHDR"

# Fix 3: pHYs chunk data (Offset 0x46)
data[0x46:0x4A] = b"\x00\x00\x16\x25"

# Fix 4: IDAT chunk length and name (Offset 0x53)
data[0x53:0x5B] = b"\x00\x03\x18\x51IDAT"

# Save the fully repaired image
with open("fixed.png", "wb") as f:
    f.write(data)

print("🎉 Successfully repaired! fixed.png generated.")
⚡ Terminal One-Liner (PowerShell / Bash):
python -c "d=bytearray(open('mystery','rb').read()); d[0:8]=b'\x89PNG\r\n\x1a\n'; d[12:16]=b'IHDR'; d[0x46:0x4A]=b'\x00\x00\x16\x25'; d[0x53:0x5B]=b'\x00\x03\x18\x51IDAT'; open('fixed.png','wb').write(d); print('fixed.png generated!')"

5. Restored Evidence & Extracted Flag

Visual Verification of Repaired Image (fixed.png):

c0rrupt Restored Image
picoCTF{c0rrupt10n_15_n3v3r_50_b4d_534e7cb8}

6. DFIR Diagnostic Cheat Sheet

Tool / UtilitySyntaxPurpose
pngcheckpngcheck -v file.pngVerifies magic bytes, chunk sequence, CRC32 checksums, and reports offset of failure.
HexEd.it / HxDGUI Hex Editor (OVR mode)Allows manual byte-by-byte carving, patching, and binary inspection.
Python bytearraydata[start:end] = b"..."Instant programmatic byte patching in memory without installing external tools.

7. The Complete Investigation Path & Mental Roadmap

Here is the complete chronological path and thought process from the initial broken file to the final extracted flag:

STEP 1
Initial Triage & Failure Mode

Downloaded mystery. Opening it failed with “Invalid image format”. Running pngcheck -v mystery flagged invalid signature bytes at offset 0x00.

STEP 2
Magic Header Surgery

Opened file in HexEd.it. Discovered .eN4 instead of .PNG. Replaced bytes 0..7 with official PNG signature 89 50 4E 47 0D 0A 1A 0A.

STEP 3
IHDR & pHYs Metadata Correction

Fixed corrupted chunk name C"DR back to IHDR. Removed extra stray byte AA from pHYs chunk to fix pixel resolution and restore CRC32 checksum.

STEP 4
IDAT Payload Calculation & Restoration

Calculated exact remaining payload size (202,887 - 54 = 202,833 bytes = 0x00031851). Replaced corrupted \xabDET with 00 03 18 51 49 44 41 54.

STEP 5
Export, Verify & Capture Flag

Exported fixed.png. Double-clicked image to render the visual flag: picoCTF{c0rrupt10n_15_n3v3r_50_b4d_534e7cb8}.

Cyber Amber
#f59e0b
PresetsClick to lock