Flag: hiCTF{r3c0nstruct3d_th3_sn4pf1l3_by73_by_by73}
Summary
snaptool (stripped Crystal x86-64 ELF) validates a 104-byte (0x68) .snap
header, verifies a custom 64-bit checksum, and - on success - prints the
snapshot's embedded label, which is the flag. No sample .snap ships with the
challenge, so the format had to be reconstructed from the binary alone.
The whole validator lives inside the (inlined) user main at 0x400d50
(entry 0x42e8e0 → 0x43a749 → user main). The interesting block is
0x402c26 - 0x4032b8. Any failing check jumps to 0x4035e4, which prints
invalid snapfile and exits 1.
.snap file layout (104 bytes)
| Offset | Size | Meaning | Required value |
|---|---|---|---|
| 0x00 | 4 | magic | SNAP (53 4E 41 50) |
| 0x04 | 2 | version, big-endian u16 (read as rol ax,8) |
0x0002 |
| 0x06 | 1 | format flags | 0x03 |
| 0x07 | 1 | reserved | 0x00 |
| 0x08 | 8 | format tag, ASCII | SNAP-LNG |
| 0x10 | 4 | BE u32 section A offset | 0x28 |
| 0x14 | 4 | BE u32 section A length | 0x10 (16) |
| 0x18 | 4 | BE u32 section B offset | 0x38 |
| 0x1c | 4 | BE u32 section B length | 0x20 (32) |
| 0x20 | 4 | BE u32 section C offset | 0x58 |
| 0x24 | 4 | BE u32 section C length | 0x08 (8) |
| 0x28 | 16 | section A payload | (free) |
| 0x38 | 32 | section B payload | (free) |
| 0x58 | 8 | section C payload | (free) |
| 0x60 | 8 | checksum, big-endian u64 | computed |
Structural consistency checks enforced by the binary:
- total length
== 0x68 A.off + A.len == B.off(0x28+0x10 = 0x38)B.off + B.len == C.off(0x38+0x20 = 0x58)C.off + C.len == 0x60(0x58+0x08 = 0x60)- the six u32 fields at 0x10/0x14/0x18/0x1c/0x20/0x24 are read through a
big-endian accessor
FUN_00438800(buf,len,offset)(with bounds/overflow guards).
Checksum algorithm (0x402dbb - 0x402e4b)
A 64-bit rolling hash over header bytes 0x08..0x5F (88 bytes), FNV-ish with a xor-shift finalizer:
1MASK = (1 << 64) - 1
2h = 0x1357bdf02468ace1
3for i in range(0x08, 0x60): # 88 bytes
4 h = ((h + header[i]) * 0xd1b54a32d192ed03) & MASK
5 h ^= h >> 29
6# stored field = little-endian-friendly: written BIG-ENDIAN at 0x60
7checksum = (h ^ 0x58) & MASK
The final check is (BE_u64(header[0x60:0x68]) ^ hash) == 0x58, i.e. the
8-byte field is hash ^ 0x58 stored big-endian.
Label / flag decoding
On success the tool runs a second stage (0x402e66 - 0x4032b8):
FUN_00438890(0x28, …, buf)computesSHA256(header[0x00:0x28]).- A 46-byte label is reconstructed in a fresh buffer:
label[i] = key[i] ^ ciphertext[i](xorps/xorpdbulk-copy loop at0x403200), where the two operand tables are Crystal globals materialised at startup (pointers in.bssat0x48eb90/ ciphertext buffer). - The decoded label is re-hashed with SHA256 and compared (
bcmpat0x403290) against an embedded constant; then a singlewrite(1, label, 47)(0x4032b3→0x433d40) prints it.
The label is a fixed constant (it does not depend on the section payloads), verified by running with all-zero sections and with random sections - both produce the same flag. The header SHA-256 is used only inside this stage.
Reconstructing a valid file
mk.py in this folder builds a byte-exact valid .snap (solution.snap):
1import struct
2MASK=(1<<64)-1; MULT=0xd1b54a32d192ed03
3def checksum(hdr):
4 h=0x1357bdf02468ace1
5 for i in range(8,0x60):
6 h=((hdr[i]+h)*MULT)&MASK; h^=h>>0x1d
7 return (h^0x58)&MASK
8h=bytearray(0x68)
9h[0:4]=b'SNAP'; h[4:6]=struct.pack('>H',2); h[6]=3; h[7]=0
10h[8:16]=b'SNAP-LNG'
11for off,val in [(0x10,0x28),(0x14,0x10),(0x18,0x38),(0x1c,0x20),(0x20,0x58),(0x24,0x08)]:
12 struct.pack_into('>I',h,off,val)
13struct.pack_into('>Q',h,0x60,checksum(h))
14open('solution.snap','wb').write(h)
Proof of success
$ ./snaptool solution.snap
hiCTF{r3c0nstruct3d_th3_sn4pf1l3_by73_by_by73}
exit=0
Hex dump of solution.snap (104 bytes):
00000000: 534e 4150 0002 0300 534e 4150 2d4c 4e47 SNAP....SNAP-LNG
00000010: 0000 0028 0000 0010 0000 0038 0000 0020 ...(.......8...
00000020: 0000 0058 0000 0008 0000 0000 0000 0000 ...X............
00000030: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000060: fd47 ab98 9124 f3f2 .G...$..
Negative control: a truncated/bad file yields invalid snapfile with exit 1.