Flare-On 8 Challenge 9 - evil

The binary provided has a ton of invalid instructions like:

1
2
xor eax,eax
div eax

or

1
2
xor eax,eax
mov eax,[eax]

This led me to believe that SEH was in place. After digging for some time, I could not find the SEH handler for any of the exceptions triggered.

Turns out, the exception handler is registered during runtime, via the WinAPI AddRtlVectoredExceptionHandler.

We can set a breakpoint for the API in x32dbg using the command bp AddRtlVectoredExceptionHandler.
Upon hitting the breakpoint, we can use the return to user code function of x32dbg to view the arguments and address of the function call.

We can then find the exception handler at address offset 6AD0

vectored exception

Analysing and cleaning up the function reveals the inner workings of this binary.
The function resolves and loads WinAPIs by hashes passed in via the edx and ecx registers. The function then patches the 3 bytes after the exception-causing instructions to call eax, where eax is set to be the WinAPI function.

exception handler

Looking at the function, it seems that arg0 is the hash of the target DLL, while arg1 is the hash of the target WinAPI.

api resolver

The function does some operation on the DLL hash, and eventually calls LoadLibrary.

After which, it proceeds to read the export table of the DLL.

dll loading

It then iterates over each of the WinAPI names, generating a hash from the name. The loop stops when the generated hash matches the target hash.

hashing

The name hashing algorithm is as follows:

  1. Init hash value to 64
  2. Iterate over each char of the API name
  3. hash value = (current char - 0x45523f21 * hash value)
  4. Repeat until all chars accounted for

We can create a script to generate a WinAPI lookup table.

Within IDA, it is not possible to create a function at main due to the many problematic instructions. In order to generate a function and view the CFG (Control Flow Graph), patching of the 3 bytes to call eax after each exception-causing instruction is needed.
I did my patching using Binary Ninja.

After patching, we can now view the decompilation of main

main decompilation

In main, there are numerous calls to CreateThread, called via the evil way of exceptions. Right before the call to create the 3rd thread, there is a function call to create a UDP listener at address offset 3A70.

UDP listener

After that function, the 3rd thread is created. The thread 3 function receives data from the socket via recvfrom.

data recv

After parsing through the data and performing some checks, the Semaphore and Mutex are released.

release

While looking through the code in the 4th thread’s function, I stumbled upon a function that calls Windows CryptoAPIs at address offset 67A0.

crypto

There seems to be a intentional error made in the code, where the CALG_SEAL algorithm is chosen, but it is not supported. It is later found out that it should be CALG_RC4. A key is imported via CryptImportKey and CryptDecrypt is called to decrypt data.

decrypt

If we take a look at how the decrypt function is called in assembly:

decrypt call

We can see that a pointer to a DWORD is copied into ecx before the function is called.
If we take a closer look at loc_B148A0, it seems to look like a memcpy(?)

memcpy

If we trace esi and edx, we can see that a pointer to a ciphertext is copied into esi and edx is a counter, likely holding the length of the ciphertext to copy.

memcpy 2

Looking further down into the function, theres seems to be loop that looks an awful lot like decryption.

str decrypt

It is a little un-intuitive at first glance, but the ciphertext bytes are located just slightly above the decryption loop. dword_DE17A8 is just 1 byte after byte_DE17A7 and since the decryption begins from the back, it will use said values and perform the decryption on it.

cipher value

This loop can be seen repeating 4 times, with 4 different set of ciphertext bytes. After re-implementing the algorithm and decrypting the values, we get L0ve,s3cret,5Ex & g0d.

Recall that we see a pointer to dword_DE1680 being moved into ecx which is eventually used in CryptImportKey.
Looking at the xrefs to the location, we can find it being referenced into a local variable, eventually set after a CRC32 calculation.

dword set
crc set

Upon painful hours of tracing, it turns out that dword_DE1680 and the 3 dwords after are the CRC32 hashes of the decrypted values: L0ve,s3cret,5Ex & g0d

That would form our key for the RC4 decryption to get our flag.

The script to decrypt the flag can be found on my Github.

[email protected]