FlareOn-9 Writeups 04 Darn Mice
Introduction
Description:
“If it crashes its user error.” -Flare Team
The provided binary, “darn_mice.exe” is a small C/C++ Windows executable.
It seems to act as a crack-me that wait for an input through argv[1]
:
# darn_mice.exe password
On your plate, you see four olives.
You leave the room, and a mouse EATS one!
When testing some random passwords, we can observe that the binary takes a very long time between the displayed string and its actual termination. At a first glance, we can assume that it is either doing some heavy cryptographic operations or that it’s simply crashing, causing the OS to properly terminate the binary (which is time-consuming).
From the description of the challenge, we can safely assume that the binary is going to crash every time we provide an incorrect password.
We can confirm this by taking a look at the process tree:
And more precisely, the exception being raised is an EXCEPTION_ACCESS_VIOLATION
:
EXCEPTION_DEBUG_INFO:
dwFirstChance: 0
ExceptionCode: C0000005 (EXCEPTION_ACCESS_VIOLATION)
ExceptionFlags: 00000000
ExceptionAddress: 00780001
NumberParameters: 2
ExceptionInformation[00]: 00000001 Write
ExceptionInformation[01]: 00000091 Inaccessible Address
Last chance exception on 00780001 (C0000005, EXCEPTION_ACCESS_VIOLATION)!
Somehow, the input password seems to interact with the execution flow of the program.
Analysis
Let’s start our analysis from the main
routine.
We can confirm that the binary is indeed waiting for a single parameter being passed through command-line:
We can already spot the fact that the provided password should be 35 characters long in order to be process by the program:
Then, something interesting happens. A huge array is constructed on the stack, with some hardcoded values:
This array, which is also exactly 35 characters long, can be synthesized to the following:
array = [0x50,0x5E,0x5E,0xA3,0x4F,0x5B,0x51,0x5E,0x5E,0x97,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA2,0xA3,0x6B,0x7F,0x00]
Then, for each character of the provided password, a new buffer is allocated (VirtualAlloc()) with a size of 0x1000 and the read-write-execute permissions:
The value of the current password character is added with the corresponding value in the stack array, before being written into the newly allocated buffer:
And finally, what’s inside the buffer is executed.
The operations performed by the program can be resumed by the following pseudocode:
for ( cnt = 0; cnt < 36 && array[cnt] && arg_input[cnt]; ++cnt ) {
buffer = (DWORD*)VirtualAlloc(0, 0x1000, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
*buffer = arg_input[cnt] + array[cnt];
buffer();
printf("Nibble...\n");
}
This explains why our binary is crashing non-stop, as the provided input is directly used to calculate the code to execute. That way, an incorrect password will destroy the execution-flow, crashing the program.
Our goal is to find a string that will be successfully processed by the program, preventing it to crash.
Solving
As the input processing routine is done through a simple addition with a set of constant values (the stack array), and since each character have its own buffer, be can easily calculate the correct password.
The instruction we want to place in the buffer for each character is a ret
(opcode 0xC3
in x86).
To calculate the input string that produce a bunch of ret
instructions, we have to subtract each value of the stack array with 0xC3
:
ret_opcode = 0xC3
array = [0x50,0x5E,0x5E,0xA3,0x4F,0x5B,0x51,0x5E,0x5E,0x97,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA3,0x80,0x90,0xA2,0xA3,0x6B,0x7F,0x0]
passwd = ""
for value in array:
passwd += chr(ret_opcode - value)
print('"' + passwd + '"')
“see three, C3 C3 C3 C3 C3 C3 C3! XD”
Launching the binary with the correct password gives us the flag:
# darn_mice.exe "see three, C3 C3 C3 C3 C3 C3 C3! XD"
On your plate, you see four olives.
You leave the room, and a mouse EATS one!
Nibble...
Nibble...
[...]
Nibble...
Nibble...
When you return, you only: see three, C3 C3 C3 C3 C3 C3 C3! XD
i_w0uld_l1k3_to_RETurn_this_joke@flare-on.com
“i_w0uld_l1k3_to_RETurn_this_joke@flare-on.com”
Flag decryption
Since the flag is somehow different from the correct password, we can go a bit further and take a quick peak at how the flag is stored and retrieved.
Following the execution flow after the password validation leads us to a bunch of cryptographic Windows API:
The password, which should be correct at this point, is hashed using the SHA512
algorithm using the standard Microsoft cryptographic provider though BCryptDeriveKeyPBKDF2(). The hardcoded salt "salty"
is used.
Then, this hash is used as a RC4 key do decrypt a blob stored in memory.
We can observe a standard RC4 KSA (Key Scheduling Algorithm) that produces the RC4 internal state (called S
in the literature):
The RC4 algorithm (which we will meet again latter in the other challenges) can quickly be recognized though it’s KSA phase that iterate over an array of 256 elements in order to do the permutation and the key diffusion across the initial RC4 state.
And as expected, the RC4 initial state is then used as a seed for the PRGA (Pseudo-Random Generation Algorithm) to generate the key stream. During the same phase, the generated keys are xored against the encrypted blob to achieve its decryption:
And since the RC4 decryption key is based on the correct input, this implementation prevents us from attacking the flag decryption routine without having the correct password :)