Huntress CTF 2024: Ocean Locust

Challenge Name: ocean_locust

Category: Malware

Author: @JohnHammond

Challenge Description:

Wow-ee zow-ee!! Some advanced persistent threats have been doing some tricks with hiding payloads in image files!

Artifact Files:

Approach

After unzipping the archive, we are presented with two executables and an image file, "inconspicuous.png".

Inconspicuous Image

I started by running both executables and noted that the binaries are written in rust, and that they both required an image and flag as input. Because I have no clue how to reverse rust, I decided to take a look at the image file.

Image for Question 1

I took a look at the png image in 010 editor, and noticed a handful of ancillary data chunks, with types "biTa" through "biTh". These chunks all contained 5 bytes of data, which made me think that they were XORed, with the key being the name of the chunk.

PNG Chunks

I decided to test my theory by decoding the first chunk in alphabetical order, XORing its data against the chunk type "biT[letter]", and was pleased to see that the first few characters of the flag were revealed.

I did this using binary refinery, emitting the bytes, carving out the hex structure for the biTa chunk, and then XORing it against "biTa".

Chunk Decoding

I wrote a script to automate the process of decoding the chunks, and after a few iterations, the flag was revealed.


import struct

def parse(data):
    decoded_data = b""
    for letter in range(ord("a"), ord("h") + 1):  # biTa to biTh
        chunk_name = f"biT{chr(letter)}".encode()
        if chunk_name in data:
            start_index = data.index(chunk_name) + len(chunk_name)
            additional_data = data[start_index : start_index + 6]
            unpacked_data = struct.unpack("6B", additional_data)
            decoded_data += bytes(
                a ^ b for a, b in zip(unpacked_data[:5], [*chunk_name, chunk_name[0]])
            )
    print(decoded_data.strip(b"\xaa").decode())  # some padding bytes at the end


parse(open("inconspicuous.png", "rb").read())   
        

Flag:flag{fec87c690b8ec8d65b8bb10ee7bb65d0}

Reflections

Initially I was afraid that I would have to reverse rust, which sent me into cold sweats, so I was happily surprised when I was able to grab the flag directly from the image :)