Flare-On 8 Challenge 9 - evil
Binary Analysis
The binary provided has a ton of invalid instructions like:
|
|
or
|
|
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
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.
WinAPI Resolver Function Analysis
Looking at the function, it seems that arg0
is the hash of the target DLL, while arg1
is the hash of the target WinAPI.
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.
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.
The name hashing algorithm is as follows:
- Init hash value to 64
- Iterate over each char of the API name
- hash value = (current char - 0x45523f21 * hash value)
- Repeat until all chars accounted for
We can create a script to generate a WinAPI lookup table.
Main Function Of Binary
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
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
.
After that function, the 3rd thread is created. The thread 3 function receives data from the socket via recvfrom
.
After parsing through the data and performing some checks, the Semaphore and Mutex are released.
Thread 4 Functionality
While looking through the code in the 4th thread’s function, I stumbled upon a function that calls Windows CryptoAPIs at address offset 67A0
.
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.
If we take a look at how the decrypt function is called in assembly:
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
(?)
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.
Getting The Key
Looking further down into the function, theres seems to be loop that looks an awful lot like decryption.
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.
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.
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.