Level 3 - Needle in a Greystack

An attack was detected on an internal network that blocked off all types of executable files. How did this happen? Upon further investigations, we recovered these 2 grey-scale images. What could they be?

Disclaimer: I did not manage to fully solve this during the competition itself. I did shortly after (an hour after the CTF), though, with the help of my friend Rainbowpigeon 😄

Understanding the BMP Files

I noticed early on that 1.bmp was essentially a Windows executable, but split into chunks with the order reversed. This is pretty obvious given the MZ and PE file signatures at the end. This allowed me to successfully recover the executable, but as we will see later, was insufficient for me to solve the challenge.

I did not notice that these 'chunks' were essentially the different bitmap lines, which 010 Editor would generously show me!

These lines consist of data followed by some padding (null bytes).

Extracting the data (in reverse order) and removing the padding bytes thus gives us the executable.

chunk_size = 148

with open('decoded' ,'rb') as infile, open('out.txt', 'wb') as outfile:
	data = infile.read()
	i = len(data) - chunk_size
	
	while i > 0:
		outfile.write(data[i:i + chunk_size][:-3])
		i -= chunk_size
		
	outfile.write(data[:chunk_size])

Understanding the Executable

First of all, we need a .txt file as an argument.

Failing which, the file will not be read!

Upon reading the file, its contents will then go thorugh a decoding algorithm (we don't actually need to know how it works for the purpose of this challenge).

Once decoded, a second function is called. The decoded buffer must then start with 0x5A4D (i.e. MZ), otherwise the function exits.

Subsequently, a DLL is loaded. Given that the decoded buffer must start with MZ, it is clear that we have to make our input file successfully decode into a DLL in memory, which is then executed.

I figured this much out during the competition, but the missing piece was finding an appropriate input file that will decode successfully. Supplying the contents of 2.bmp yielded the MZ file signature somewhere in the decoded contents, but not at the start as was expected.

It turns out that 2.bmp followed the same structure as 1.bmp, and with the newfound knowledge about the bitmap file structure, I only had to tweak the previous script to accomodate the line and padding sizes of this second bitmap file.

chunk_size = 100

with open('2.bmp' ,'rb') as infile, open('out.txt', 'wb') as outfile:
	data = infile.read()
	i = len(data) - chunk_size
	
	while i > 0:
		outfile.write(data[i:i + chunk_size][:-1])
		i -= chunk_size
		
	outfile.write(data[:chunk_size])

Great, we have some progress!

We want to know what the DLL is doing, so let's set a breakpoint and dump the decoded DLL from memory.

A key.txt file is required.

Now, the un-decoded version of 2.bmp looks conspicuously like a wordlist... and it turns out that while I was staring at the "wrong" stuff and trying to figure out its purpose, I had already stumbled upon what looks like a key!

With the correct key file, we get the flag.

Last updated