$ cat writeup.md…
$ cat writeup.md…
fortid2026
Task: stripped x86-64 ELF replays custom .pmaf bucket-fill operation files onto a start PNG to paint each flag part. Solution: reverse the PMAF format and splitmix64 hash-chain keystream in Ghidra, reimplement the decoder plus flood fill in C for speed, render parts at a partial record count, and concatenate the painted text of parts 01..13.
$ cat /etc/rate-limit
Rate limit reached (20 reads/hour per IP). Showing preview only — full content returns at the next hour roll-over.
It was an honor to paint this flag, I hope you'll enjoy it.
The task description links to the Microsoft Paint Wikipedia article — a strong
hint at the paint-bucket / flood-fill tool. The handout is a handout.zip
(≈121 MB) containing:
flag_part_01..13.pmaf — 13 custom binary "Paint Me A Flag" operation filesflag_part_01..13.png — 13 start imagespaint_me_a_flag — a stripped x86-64 ELFThe shipped tool is invoked as:
paint_me_a_flag START.png OPERATIONS.pmaf OUTPUT.png
It replays the bucket-fill operations encoded in the .pmaf file onto the start
PNG and writes an output PNG. The goal is to recover the flag, which is painted
as text across the 13 parts and concatenates in order.
"It was an honor to paint this flag" + the Microsoft Paint link ⇒ the .pmaf
stream is a sequence of flood-fill / bucket operations, executed exactly the
way the Paint bucket tool spreads a colour over a connected region.
Static analysis in Ghidra (paint_me_a_flag is stripped) plus struct
cross-references produced the .pmaf layout:
| Offset | Size | Field |
|---|---|---|
| 0x00 | 8 | magic "PMAF\x03\r\n\x1a" |
| 0x08 | 4 | u32 width |
| 0x0c | 4 | u32 height |
| 0x10 | 8 | u64 record_count (== width * height) |
| 0x18 | 8 | u64 seed |
| 0x20 | 4 | u32 fill_count (0 for part 01; 64 for parts 02..13) |
| 0x24 | 4 | u32 reserved (must be 0) |
| 0x28 | 8×N | record_count × 8-byte records |
Total header = 40 bytes, then record_count * 8 bytes of records.
All mixing is splitmix64-style with these constants:
...
$ grep --similar