Tag List
Sign In

Hidden

Hidden

For this challenge we are given two files:

Taking a look at the executable first, we see that it's a fairly simple program that appears to be XORing a pre-existing flag and then writing it to a file. Since it appears the flag is hard coded in the binary, with a dummy flag being present in the binary given to us, the first step is to extract the XORed output since we can use the following property of XOR to calculate the flag: (key XOR flag) XOR (key XOR dummyflag) XOR dummyflag = flag

However it doesn't just write to any type of file, in the program the tcgetattr and tcsetattr functions are used to set some terminal attributes that only exist for files that are backed by serial interfaces. Therefore the program will only ever write to such a file.

Luckily we can just emulate a serial interface using the command socat -d -d pty,raw,echo=0 - > output, which will create a virtual serial interface (probably at /dev/pts/X) and save anything written to output.

Additionally to run the program on an x86 machine, we'll need to use an emulator such as qemu. I Used this guide to set up my machine so I could run an ARM docker container, allowing me to run the firmware program.

By using docker run --rm -it -v (pwd):/root/ -v /dev/pts/5:/f arm32v7/ubuntu to start up an arm container with the virtual terminal made by socat mounted to /f inside the container, we get the following output and the encoded flag written to output.

root@0ea2fbc64380:~# ./firmware /f
[!] sending data to /f
λ xxd output
00000000: 0b00 0000 0500 0000 0100 0000 0700 0000  ................
00000010: 0d00 0000 0100 0000 0b00 0000 0400 0000  ................
00000020: 0d00 0000 0400 0000 1000 0000 0f00 0000  ................
00000030: 0500 0000 0a00 0000 0400 0000 0f00 0000  ................
00000040: 0900 0000 0600 0000 0700 0000 1000 0000  ................
00000050: 0700 0000 0c00 0000 0700 0000 0900 0000  ................
00000060: 0400 0000 0a00 0000 0b00 0000 0300 0000  ................
00000070: 0900 0000 0700 0000 0e00 0000 0a00 0000  ................
00000080: 0800 0000 0300 0000 0700 0000 0b00 0000  ................
00000090: 0e00 0000 0d00 0000 0600 0000 0500 0000  ................
000000a0: 0e00 0000 0b00 0000 1000 0000 0b00 0000  ................
000000b0: 0500 0000 0c00 0000 0400 0000 0100 0000  ................
000000c0: 0500 0000 0200 0000 0f00 0000 1000 0000  ................
000000d0: 0a00 0000 0600 0000 1000 0000 0700 0000  ................
000000e0: 0100 0000 0f00 0000 0400 0000 0300 0000  ................
000000f0: 1000 0000 0900 0000 0800 0000 1000 0000  ................
00000100: 0f00 0000 0d00 0000 0f00 0000 0d00 0000  ................
00000110: 0500 0000 1000 0000 0100 0000 0300 0000  ................
00000120: 0800 0000 0400 0000 0200 0000 0400 0000  ................
00000130: 0800 0000 0600 0000 1000 0000 0600 0000  ................
00000140: 0a00 0000 0400 0000 0500 0000 0c00 0000  ................
00000150: 0100 0000 0200 0000 0200 0000 0800 0000  ................
00000160: 0500 0000 0600 0000 0700 0000 0300 0000  ................
00000170: 0400 0000 0b00 0000 0b00 0000 0100 0000  ................
00000180: 0600 0000 0e00 0000 0700 0000 0e00 0000  ................

Looking at the reconstruction of the decompiled binary, we can see that the program does some funny stuff with the XORed flag before writing it to the terminal. Instead of just writing the flag straight to the serial port, instead the higher and lower four bits of each character is placed next to each other in an array of int32_t, which is then written.

This matches up with the output, as it should have the format [upper, 0, 0, 0, lower, 0, 0, 0, ...]

Now that we know what to expect in the serial trace, we can open it up in Saleae logic.

Opening up the trace we see something odd, normally each character sent down a serial port has eight bits, first the signal goes low for a cycle, then the bits are sent, then finally the signal is set high for two cycles to signal the end of the character. But for this signal it appears that only five bits are being sent per transmission. Makes sense why each character is split into the upper and lower nibbles before hand.

Looking at the decoded data, we can see it looks very similar to the output of the firmware program.

To export this data the csv export feature of Saleae logic was used, xsv select data out.csv | grep "[^\"]" > out, Cyberchef was then used to get this as a string of hex numbers because I'm lazy.

With both transmissions now formatted nicely, I wrote a small python script to decrypt the flag:

output_a = bytes.fromhex("2b 00 00 00 05 00 00 00 22 00 00 00 2b 00 00 00 2e 00 00 00 2d 00 00 00 2b 00 00 00 24 00 00 00 2d 00 00 00 24 00 00 00 09 00 00 00 0f 00 00 00 24 00 00 00 09 00 00 00 21 00 00 00 09 00 00 00 0a 00 00 00 0c 00 00 00 22 00 00 00 30 00 00 00 22 00 00 00 0a 00 00 00 06 00 00 00 30 00 00 00 05 00 00 00 0c 00 00 00 2e 00 00 00 03 00 00 00 0a 00 00 00 09 00 00 00 30 00 00 00 0f 00 00 00 21 00 00 00 03 00 00 00 22 00 00 00 0a 00 00 00 30 00 00 00 21 00 00 00 27 00 00 00 03 00 00 00 2b 00 00 00 30 00 00 00 09 00 00 00 0a 00 00 00 24 00 00 00 0a 00 00 00 05 00 00 00 05 00 00 00 06 00 00 00 30 00 00 00 0a 00 00 00 0a 00 00 00 0f 00 00 00 21 00 00 00 09 00 00 00 27 00 00 00 03 00 00 00 05 00 00 00 21 00 00 00 21 00 00 00 0f 00 00 00 27 00 00 00 21 00 00 00 0a 00 00 00 0a 00 00 00 2e 00 00 00 30 00 00 00 03 00 00 00 27 00 00 00 27 00 00 00 28 00 00 00 28 00 00 00 21 00 00 00 21 00 00 00 24 00 00 00 27 00 00 00 05 00 00 00 24 00 00 00 09 00 00 00 21 00 00 00 0f 00 00 00 21 00 00 00 24 00 00 00 0a 00 00 00 22 00 00 00 30 00 00 00 28 00 00 00 28 00 00 00 05 00 00 00 05 00 00 00 22 00 00 00 21 00 00 00 05 00 00 00 30 00 00 00 2e 00 00 00 21 00 00 00 03 00 00 00 05 00 00 00 27 00 00 00 2e 00 00 00")
output_b = bytes.fromhex("0b 00 00 00 05 00 00 00 01 00 00 00 07 00 00 00 0d 00 00 00 01 00 00 00 0b 00 00 00 04 00 00 00 0d 00 00 00 04 00 00 00 10 00 00 00 0f 00 00 00 05 00 00 00 0a 00 00 00 04 00 00 00 0f 00 00 00 09 00 00 00 06 00 00 00 07 00 00 00 10 00 00 00 07 00 00 00 0c 00 00 00 07 00 00 00 09 00 00 00 04 00 00 00 0a 00 00 00 0b 00 00 00 03 00 00 00 09 00 00 00 07 00 00 00 0e 00 00 00 0a 00 00 00 08 00 00 00 03 00 00 00 07 00 00 00 0b 00 00 00 0e 00 00 00 0d 00 00 00 06 00 00 00 05 00 00 00 0e 00 00 00 0b 00 00 00 10 00 00 00 0b 00 00 00 05 00 00 00 0c 00 00 00 04 00 00 00 01 00 00 00 05 00 00 00 02 00 00 00 0f 00 00 00 10 00 00 00 0a 00 00 00 06 00 00 00 10 00 00 00 07 00 00 00 01 00 00 00 0f 00 00 00 04 00 00 00 03 00 00 00 10 00 00 00 09 00 00 00 08 00 00 00 10 00 00 00 0f 00 00 00 0d 00 00 00 0f 00 00 00 0d 00 00 00 05 00 00 00 10 00 00 00 01 00 00 00 03 00 00 00 08 00 00 00 04 00 00 00 02 00 00 00 04 00 00 00 08 00 00 00 06 00 00 00 10 00 00 00 06 00 00 00 0a 00 00 00 04 00 00 00 05 00 00 00 0c 00 00 00 01 00 00 00 02 00 00 00 02 00 00 00 08 00 00 00 05 00 00 00 06 00 00 00 07 00 00 00 03 00 00 00 04 00 00 00 0b 00 00 00 0b 00 00 00 01 00 00 00 06 00 00 00 0e 00 00 00 07 00 00 00 0e 00 00 00")

key = "CTHB{AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA}"

def chunks(lst, n):
    """Yield successive n-sized chunks from lst."""
    for i in range(0, len(lst), n):
        yield lst[i:i + n]

import itertools

def recombine(b):
    for c in chunks(list(b), 8):
        upper = int.from_bytes(c[0:4], byteorder='little', signed=False)
        lower = int.from_bytes(c[4:8], byteorder='little', signed=False)

        upper = (upper - 1) << 4

        lower -= 1
        lower &= 0xf

        yield (upper | lower) & 0xff

r = [a ^ b ^ ord(c) for a, b, c in zip(recombine(output_a),
                                       recombine(output_b),
                                       list(key))]
print(r)
print("".join(map(chr, r)))

Running this gives us the flag: CHTB{10w_13v31_f12mw4235_741ks_70_h42dw423_!@3418}