Headless Horseman - Buckeye CTF 2021

I only had a go at the rev challenge headless_horseman.

We’re originally given an ELF called headless_horseman and three files that have something to do with bodies. They look like ELF files also, with the header missing…

headless_horseman runs and will decrypt extra files for us if we’re able to provide a correct number, one that satisfies the following requirements:

  • (x & 0xFFFF0000) >> 16 == 0xDEAD
  • x << 16 == 0xFACE0000

Thus, x == 0xDEADFACE, or 3735943886.

After receiving the correct number it decrypts 6 “heads”, and implies that we should figure out which bodies to stitch them to. I stitched every head to every body and ran ls | xargs -n1 file

dessicated_head_bloated_body.corpse: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, too large section header offset 546785024
dessicated_head_decomposing_body.corpse: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, BuildID[sha1]=c96a5a55d131a48d6e034236330d1925e890f360, for GNU/Linux 3.2.0, not stripped
dessicated_head_rotting_body.corpse: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, missing section headers

...

From the output you can see that file knows when an ELF is malformed. Just search for any lines without errors…

We end up with

dessicated_head_decomposing_body.corpse: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, BuildID[sha1]=c96a5a55d131a48d6e034236330d1925e890f360, for GNU/Linux 3.2.0, not stripped
moldy_head_bloated_body.corpse: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, BuildID[sha1]=fb3ef826027d1a22e0926cd609bc9453dab03662, for GNU/Linux 3.2.0, not stripped
shrunken_head_rotting_body.corpse: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, BuildID[sha1]=46ee2457ce9871242b7ad74249a3a19091cd0c52, for GNU/Linux 3.2.0, not stripped

Note the different architectures…

Each binary asks for some input as verification I’ve reversed it, and then prints a third of the flag. Other than in the ARM binary the input check doesn’t have any effect on the decryption scheme, just whether it’s called. Each decryption is reasonably straight forward so it can all be done statically (sorry QEMU).

File Parts Architecture (32 bit) Input Check Decryption Scheme Flag Part
moldy_head_bloated_body.corpse MIPS Enter string “GUNPOWDER” Base64 decode flag{the_horseman_just_
dessicated_head_decomposing_body.corpse ARM - XOR against “Sleepy Hollow” really_loves_
shrunken_head_rotting_body.corpse Intel Intentionally overflow integer check Add bytes against key pumpkin_pie}

Flag: flag{the_horseman_just_really_loves_pumpkin_pie}