The coolest part of this blog post may be the CTF art! DEADFACE CTF was great, with many of the challenges being a mixture of things to do. The CTF had this phased thing going on, so challenges were gradually released in 5 phases. I wasn’t too fond of that, especially as a non-US player where our prime time often had no challenges left.

Here are some of the challenges I solved playing for Hack South, where we managed to get 11th place out of 1195 teams that scored.


The challenges weren’t available when I got to this writeup, so lot’s of detail on that front is missing.


Flag: flag{themz_the_ru1es}

I noticed this flag before the CTF even started in this medium article. The flag was at the bottom.


Flag: flag{0h-look-a-FlaG}

I forgot the name of this challenge, but it was the first programming one. We get some code:

#!/usr/bin/env python3
from binascii import unhexlify as u

def get_flag():
    flag = '666c61677b30682d6c6f6f6b2d612d466c61477d'
    return u(flag).decode('utf-8')

print(f'The flag is: ')

Solve by calling get_flag().

print(f'The flag is: ' + get_flag())

cereal killer

Flag: flag{c0unt-ch0cula-cereal-FTW}

We’re given Windows and Linux bins. Decompilation reveals a bunch of work that looks like a string getting XOR’d before asking the user for input.

Solve by breaking anywhere after that really and checking out stack contents. Either:

  1. break on main with b *main
  2. r
  3. disas main
  4. b *0x0000555555555119 where 0x0000555555555119 is the address to call 0x555555555080 <puts@plt>
  5. c and read flag off stack


  1. break on the call to puts with b puts
  2. r
  3. read flag off stack

poor megan

Flag: flag{Six-Parts-Honey-One-Part-Garlic}

I don’t really know how this works, but thanks to CyberChef’s “magic” module it somehow figured out the custom character set needed to base64 decode it.

Given the input j2rXjx9dkhW9eLKsnMR9cLDVjh/9dwz1QfGXm+b9=wKslL1Zpb45, this recipe reveals the flag.

file 101

Flag: flag{Easy_Right}

You got a zip file to download that you had to crack the password for. Use zip2john to get a hash, and then your favourite password cracker to finally reveal pumpkinpie as the password (Reelix solved that part, thanks!).

Next, the image you get from the zipfile is corrupt. Use a hex editor to fix up the header (I used another valid PNG as reference) and open the file to reveal the flag.

the count

Flag: flag{d1c037808d23acd0dc0e3b897f344571ddce4b294e742b434888b3d9f69d9944}

This challenge was hosted. Connecting to the target gave you the instructions:

DEADFACE gatekeeper: Let us see how good your programming skills are.
If a = 0, b = 1, c = 2, etc.. Tell me what the sum of this word is:

 You have 5 seconds to give me an answer.

Your word is: calendar

A alphabetic character position counter it is! One cool thing I learnt here is that I can call index() on the character sets provided by string to get the alphabetic position!

import string
from pwn import *

pos = lambda t : string.ascii_lowercase.index(t)

r = remote('', 50000)
r.recvuntil('Your word is:')

w = r.recvline().strip().decode()
v = sum([pos(x) for x in w])

print(f'{w} = {v}')


TheZeal0t’s Cryptoware IOC 1


Run the binary you get and view the network traffic in Wireshark to reveal the flag as the User-Agent header in an HTTP request.

Cereal Killer 3

Flag: flag{B00-B00-B00-Bury-IZ-DA-BOMB}

I remember laughing at this challenge. It was worth something like 500 points, and I think it was one I solved the fastest xD

Run the bin in gdb, then:

  1. break on puts with b puts
  2. r
  3. c
  4. read the flag as a string in $ecx.
  5. lol

El Paso

Flag: flag{$877,401.00}

Some challenges built on top a “leaked” MySQL database that you had to run some queries on. The query to solve this challenge was:

select sum(loans.balance) from employees
 right join loans on loans.employee_id = employees.employee_id
where = 'El Paso';

Trick or Treat

Flag: flag{CaNT_ch34t_d34th}

This challenge was built on PyGame, where you get the source code.

I originally solved it by commenting out the code that checked for collisions. Leaving that running, after a while the flag simply printed to the terminal. Later I realised, I could have just run a function called gs() manually that would print the flag as well.

Syncopated Beat


This was an audio steganography challenge. We’re given a video where the audio track sounds obviously weird at ~2 minutes in. I couldn’t make out what exactly it was, so I played around with it in Sonic Visualiser at first. That revealed nothing interesting.

Next, after extracting the audio from the video, opening the track in audacity reveals the section of interest pretty clearly.

I cut that section out and applied the “reverse” effect by browsing the menu system: “Effect -> Reverse”. Next, play the audio to hear the flag.

Decrypting Lytton Labs Cryptoware 2

Flag: flag{PEANUT-BUTTER-Crunch-Mixed-With-Cocoa-Puffs-Beats-All-Those-Cereals!}

More Golang malware (puke), but this one took me a while. We also got a decryptor this time. The code flow for the encryptor was not too different when compared to the first cryptoware challenge, which helped. The function to focus on was fetchKey. As usual, a “key” was fetched remotely that could be revealed in Wireshark (or by blocking network access to the bin causing a panic that reveals the URL as well).

While normally I use (and really like) the Ghidra decompiler in cutter/r2, this time jsdec helped a lot in realising that once the key was fetched, an MD5 sum is calculated of the content. This was also visible in the disassembly, but I missed that initially

To decrypt the file, I ran ./zealotcrypt-02-decrypt.bin d8f5c876b36f019254a7307c1eb0fe09.

The Victims of Lytton Labs

Flag: flag{D0nt-ME$$-with-The-ZEAL0t!!!}

(I didn’t solve this challenge myself completely, but it was interesting enough to writeup.)

We’re given a (fairly large) pcap to work with. There’s a lot going on, but after spending some time you’d see what looks like an FTP credential brute force that’s eventually successful. Then, some files are downloaded.

To extract the files from the pcap, just follow the TCP stream, view as Raw and hit “Save As”. This way you’d end up with a “secret” (encryption-password-cgeschicker.txt), an encryptor (lytton-crypt.bin) and some encrypted files (*.lcr files). There was also what looked like a small reverse shell in a binary called secret_decoder.bin.

Running the cryptor, we’d see a few flags could be passed:

$ ./lytton-crypt.bin
Usage is: ./lytton-crypt.bin -[orc][-sN] file1 file2..
  -o Write output to standard out
  -r Do NOT remove input files after processing
  -c Do NOT compress files before encryption
  -sN How many times to overwrite input files with random data

A lot of the cryptor logic was wrapped up in the main function, and depending on what files you passed it it, the cryptor figured out if it needed to encrypt or decrypt the input file.

As for the secret file you got, the value 75AC98147C07752767E09EF781CF998E401D19B01E30CBAA5109D6AD7EC9A174 from the encryption-password-cgeschicker.txt was not a valid encryption key.

$ echo "75AC98147C07752767E09EF781CF998E401D19B01E30CBAA5109D6AD7EC9A174" | ./lytton-crypt.bin david-k.txt.lcr
Encryption key:
Invalid encryption key for file: david-k.txt.lcr

Stuck here for quite a while, @carlmonning realised the key value is actually a SHA256 hash, that when cracked is demagorgon.

So, do decrypt the files and reveal the flag run:

echo "demagorgon" | ../lytton-crypt.bin *.lcr