I was taking part in the OverTheWire Advent Bonanza 2019. All in all, the CTF was very pleasant because the challenges were interesting to solve, there was quite some time available as well (I spent about two weeks on and off on it) and I also managed to solve a few challenges as well.

The following write-up is about the challenge from day 14: tiny runes.

Description

One of Santa's Little Helpers received an unusual Christmas wish, a copy of the yet to be released Deus Hex game. All they managed to find were fragments from the dialogue system. Can you decode the last one?

Given where the following files in an archive:

.
├── lines1.bin
├── lines1.png
├── lines1.txt
├── lines2.bin
├── lines2.png
├── lines2.txt
├── lines3.bin
├── lines3.png
├── lines3.txt
└── lines4.bin

Goal: The goal is to decode lines4.bin into lines4.txt.

Analysis

The *.bin files all start with the following byte sequence:

00000000  54 69 4e 79 4d 65 54 61  00 00 00 10 00 00 00 02  |TiNyMeTa........|

This indicates some kind of magic (TiNyMeTa) but also a header with positions/length. The bin files contain an embedded PNG file starting from byte 89:

00000020  00 00 03 01 89 50 4e 47  0d 0a 1a 0a 00 00 00 0d  |.....PNG........|

This embedded image is the same in all files:

Looking at the images (lines1.png, etc.), we can see that the image is used for rendering the text. What text to render is also part of the .bin file:

00000000  4c 69 4e 65 00 00 00 3c  05 01 03 0a 04 08 06 09  |LiNe...<........|
00000010  03 07 00 05 00 0a 01 0b  00 05 02 07 04 08 06 07  |................|
00000020  00 04 03 0b 07 0a 05 0b  05 09 05 09 05 09 04 08  |................|
00000030  02 05 05 09 05 09 05 09  04 08 02 05 05 09 05 09  |................|
00000040  05 09 06 07 4c 69 4e 65  00 00 00 54 05 01 03 0a  |....LiNe...T....|
00000050  04 08 06 09 03 07 00 05  00 0a 01 0b 00 05 02 07  |................|
00000060  04 08 06 07 02 05 04 08  00 0a 00 09 01 0b 07 0a  |................|
00000070  01 07 00 09 00 0a 04 08  05 07 01 0b 07 0a 04 08  |................|
00000080  00 06 03 07 07 03 03 07  04 08 03 0b 04 08 01 06  |................|
00000090  04 03 00 04 04 08 01 07  07 0a 00 05 05 09 06 07  |................|

Every line segment starts with 4c 69 4e 65 (LiNe), followed by the length of the line as a 4 byte value. Every plain text byte takes 2 bytes in the encoded line. The first byte maps to the column, the second one to the line in the embedded image (indices starting from 0).

This can be easily automated in Python.

Final exploit

MAP = """Q?0\H$Y,
R-L^KJ?k
s#_/m=f9
7d-NE4qr
Pi?V'&XA
n3I?O*;Z
wGpB8cSj
Fg:eby"v
%+?1 !M@
h{2xW.D}
tU|CTz6u
Io>a5l<'
""".split("\n")

with open("lines4.bin", "rb") as f:
    lines = f.read().split(b"LiNe")[1:]

for line in lines:
    line = line[4:]  # skip the length
    for column, row in zip(line[::2], line[1::2]):
        print(MAP[row][column], end="")
    print()

Executing the script prints the flag:

Jock: "Oh my God! JC! A flag!"
JC Denton: "A flag! AOTW{wh4t_4_r0tt3n_fi13_f0rm4t}"