Oct 21, 2018
This blog post walks through how to solve the SpyFi problem from the picoCTF 2018 competition. The problem involves an encrypted message that uses an AES block cipher and the objective is to exploit the weakness of the block cipher and find the secret message inside. The code was written in Julia.
James Brahm, James Bond’s less-franchised cousin, has left his secure communication with HQ running, but we couldn’t find a way to steal his agent identification code. Can you?
It comes with a simple Python program that runs on the server:
When I connect to the server, it asks me to enter the situation report. Then, it spits out the encrypted text. The agent code is embedded in the encrypted message, and the goal is to figure out what that code is.
To better visualize how the message is chunked in n-byte blocks, I created the following functions. The str2hex function converts a string into a byte-array, and the grouped function takes a byte-array and split it into n-byte chunks (without padding).
I didn’t quite like the verbosity of the 0x prefixes though. Plus, I will have more context if the text is displayed on the right hand side. So I created a custom print function:
The Agent Message How does the agent message look when it’s chunked up into 16-byte blocks?
Being able to cut up the agent code into separate block allows us to decipher the agent code, one character at a time. How?
Cut the agent code such that the first character is at the last position of the block; Replicate a similar block in a different part of the message; In step 2, we can manipulate the last character using a brute-force attack. If the cipher text for both blocks match then we know what the first character of the agent code!
Let’s take it through an example. Below, if the cipher text for block 5 and 7 are the same, then we know the first character is x.
Once we figure out the first character, we just need to shift the agent code block left. But wait, it’s a bit trickier than I thought. I can remove the B from block 4, and add a new guessing character but then the agent block isn’t shifted at all.
The solution is to put additional blocks of dummy text at the end of the situation report, so that we have something to take off when shifting left. Now, given this additional padding, we will have to compare the cipher text of block #5 and #9. If they match, we have a hit!
My julia function now comes handy. Let’s analyze the byte array. We can clearly see block 6 and 7 are identical. They came from the two dummy blocks of 0123456789ABCDEF.
Submitting Tests Automatically No one would perform any brute force attack manually. Here’s the automated checking code:
It basically hits the server, submit the situation report and grab the result. Knowing that the first block is always the same, it finds the line that begins with ce046.
Here we go for the first character!
And, the second character!
Running it deciphers the agent code one character at a time.
That’s all, folks! Happy hacking!