dust
Challenge
Imported from local notes.md.
Solution
Original Notes
dust
Challenge Summary
- Given: A zip archive containing
encoder.cand an encoded output file,output.txt. - Goal: Reconstruct the original input to the custom binary image compression algorithm.
- Constraints: Only the encoder was provided, so the decompressor had to be derived from the implementation.
Initial Recon / Triage
- Observations:
encoder.cnever reads image formats directly; it reads aninput.txtfile made of0and1characters.- Input validation enforces even row count and widths that are multiples of 3.
- Compression level 1 converts each 2x3 block of bits into a printable character via
0b00100000 + value. writeArrayserializes each compressed row followed by}and terminates the stream with~.
- File identification:
artifacts/unpacked/encoder.c- compressor implementation.artifacts/unpacked/output.txt- compressed text stream.artifacts/decoded_binary.txt- recovered binary image.artifacts/decoded.png- rendered bitmap showing the flag.
- Entry points:
compressArraydefines the 2x3-to-character packing.writeArraydefines the row and end-of-stream delimiters.
Hypotheses & Approach
- Hypothesis 1: The original input was a monochrome bitmap stored as ASCII
0/1rows. - Hypothesis 2: Decompression is the inverse of
compressArray: subtract 32 from each character, unpack 6 bits, and expand them back into two rows of three pixels.
Execution Steps (Reproducible)
Stage 1
Commands:
cd /root/dawg2026CTF/dust
unzip -o starting_files/chal.zip -d artifacts/unpacked
sed -n '1,220p' artifacts/unpacked/encoder.c
sed -n '1,5p' artifacts/unpacked/output.txt
Results:
- The archive contains only
encoder.candoutput.txt. - Each compressed character encodes a 6-bit value taken from a 2x3 block of source pixels.
- Rows in
output.txtare separated by}and the full stream ends with~.
Stage 2
Commands:
cd /root/dawg2026CTF/dust
python3 - <<'PY'
from pathlib import Path
text = Path('artifacts/unpacked/output.txt').read_text()
assert text.endswith('~')
rows = text[:-1].split('}')
if rows and rows[-1] == '':
rows = rows[:-1]
decoded = []
for row in rows:
top = []
bottom = []
for ch in row:
bits = f'{ord(ch) - 32:06b}'
top.append(bits[:3])
bottom.append(bits[3:])
decoded.append(''.join(top))
decoded.append(''.join(bottom))
Path('artifacts/decoded_binary.txt').write_text('\n'.join(decoded) + '\n')
PY
python3 - <<'PY'
from pathlib import Path
lines = Path('artifacts/decoded_binary.txt').read_text().splitlines()
with open('artifacts/decoded.pbm', 'w') as f:
f.write(f'P1\n{len(lines[0])} {len(lines)}\n')
for line in lines:
f.write(' '.join(line) + '\n')
PY
convert artifacts/decoded.pbm artifacts/decoded.png
Results:
- The decompressed bitmap is
198x100pixels. - Rendering the PBM reveals the flag text in the recovered image.
- The flag reads
DawgCTF{Th1s_w45_1nspIr3d_By_UND3RT4L3!}.
Artifacts Produced
artifacts/unpacked/encoder.c- extracted source for the compressor.artifacts/unpacked/output.txt- extracted compressed stream.artifacts/decoded_binary.txt- recovered binary pixel matrix.artifacts/decoded.pbm- reconstructed bitmap.artifacts/decoded.png- rendered image containing the flag.
Flag
DawgCTF{Th1s_w45_1nspIr3d_By_UND3RT4L3!}