Flare-On is an annual “reverse engineering marathon” organized by Mandiant (formerly by FireEye). You can see more information here. It is a Capture-The-Flag type of a contest, where you are given a set of crackmes with growing difficulity. This year we were provided with 10 tasks. I finished as 125. In this series of writeups I will present my solutions to the selected challenges, and guide you through the task, all the way till the final flag.
The task 7 comes with the following intro:
Download: 07_spel.7z (password:
flare
)
The attached file is a Windows executable, 64-bit.
When we run the application, the following window pops up:
At the beginning I wasn’t sure if the task runs correctly on my system. But I decided to trace it with Tiny Tracer to see what happens.
Watching the tracelog in real-time with the help of Baretail, I noticed that when I closed the window, something got unpacked it the memory and executed. Relevant fragment of the log:
18c049;user32.IsWindow 19a9d4;ntdll.RtlEnterCriticalSection 19a9e5;kernel32.TlsGetValue 19aa05;ntdll.RtlLeaveCriticalSection 19a9d4;ntdll.RtlEnterCriticalSection 19a9e5;kernel32.TlsGetValue 19aa05;ntdll.RtlLeaveCriticalSection 19a9d4;ntdll.RtlEnterCriticalSection 19a9e5;kernel32.TlsGetValue 19aa05;ntdll.RtlLeaveCriticalSection 18fec6;user32.GetActiveWindow 19a9d4;ntdll.RtlEnterCriticalSection 19a9e5;kernel32.TlsGetValue 19aa05;ntdll.RtlLeaveCriticalSection 19a9d4;ntdll.RtlEnterCriticalSection 19a9e5;kernel32.TlsGetValue 19aa05;ntdll.RtlLeaveCriticalSection 18bac6;kernel32.FreeResource 2d30;kernel32.GetModuleHandleExA 2d61;kernel32.GetProcAddress Arg[0] = ptr 0x00007ffa8a8f0000 Arg[1] = ptr 0x00007ff7094dc0b0 -> "VirtualAllocExNuma" 2d6c;kernel32.GetCurrentProcess 17970b;kernel32.VirtualAllocExNuma 17972f;called: ?? [1747b490000+0] > 1747b490000+1cd;ntdll.LdrLoadDll > 1747b490000+1f2;ntdll.LdrGetProcedureAddress > 1747b490000+218;ntdll.LdrGetProcedureAddress > 1747b490000+23d;ntdll.LdrGetProcedureAddress > 1747b490000+263;ntdll.LdrGetProcedureAddress > 1747b490000+289;ntdll.LdrGetProcedureAddress > 1747b490000+2ae;ntdll.LdrGetProcedureAddress > 1747b490000+2d4;ntdll.LdrGetProcedureAddress > 1747b490000+377;kernel32.GetNativeSystemInfo > 1747b490000+3c0;kernel32.VirtualAlloc > 1747b490000+648;kernel32.LoadLibraryA Arg[0] = ptr 0x00000001800152b6 -> "KERNEL32.dll" > 1747b490000+6ad;ntdll.LdrGetProcedureAddress > 1747b490000+6ad;ntdll.LdrGetProcedureAddress > 1747b490000+6ad;ntdll.LdrGetProcedureAddress > 1747b490000+6ad;ntdll.LdrGetProcedureAddress > 1747b490000+6ad;ntdll.LdrGetProcedureAddress > 1747b490000+6ad;ntdll.LdrGetProcedureAddress
We can see that it uses a function VirtualAllocExNuma
to allocate memory:
17970b;kernel32.VirtualAllocExNuma
Then, something is loaded into this memory and executed (the entry point at offset 0 suggests that it is a shellcode, not a PE):
17972f;called: ?? [1747b490000+0]
Next, we can see the functions executed from inside of the shellcode (prepended with “>
“):
> 1747b490000+1cd;ntdll.LdrLoadDll > 1747b490000+1f2;ntdll.LdrGetProcedureAddress > 1747b490000+218;ntdll.LdrGetProcedureAddress
We can see that it loads multiple imports (using LdrGetProcedureAddress
). This suggests that this shellcode is yet another loader (possibly for a PE payload).
The previous experiment showed that the executable is packed. So, I decided to unpack it with the help of mal_unpack
(one of the tools from PE-sieve family). Since manual closing of the window is required in order to trigger payload unpacking, I run mal_unpack
with the following commandline (infinite timeout):
mal_unpack.exe /timeout 0 /exe spel.exe
And then I closed the window.
Some DLLs got dumped.
Shellcode, as well as one of the DLLs seems to be nothing but the next stage loaders.
However, I noticed among them an interesting DLL with one function exported:
Unfortunately, the relocation table of this DLL was removed:
Due to this fact, it could not be used as a standalone DLL.
Manual reconstruction of a relocation table is difficult, and sometimes even impossible. But I got an idea that maybe I can still find a raw copy of this DLL, with the relocation table intact. So I scanned it again, this time with an option /data 3
to dump also PEs from non-executable memory.
mal_unpack.exe /timeout 0 /exe spel.exe /data 3
This time more DLLs were dumped.
One of them was indeed a raw copy of the DLL I was looking for – this time with a valid relocation table.
Now, all I needed to do was to remove padding of the dumped file. I did it with PE-bear:
And the DLL is ready to be run… I just renamed it to its original name ldr.dll
.
I decided to trace the found DLL with a TinyTracer. The DLL exports a function Start
so I suspected this will be the function that should be called.
set DLL_EXPORTS="Start"
Then I executed tracing the DLL by Tiny Tracer.
Reading the trace log, I noticed the DLL tries to load some resource. The resource is supposed to be fetched from the main application. I added to the TinyTracer tracking of related parameters, and I saw what exactly is being loaded. It is a PNG (full trace log available here).
1b4d;kernel32.GetModuleFileNameA 1b63;kernel32.GetModuleHandleA 1ba8;kernel32.FindResourceA Arg[0] = ptr 0x00007ff72e9e0000 Arg[1] = 0x0000000000000080 = 128 Arg[2] = ptr 0x0000005628ebf5e4 -> "PNG"
The relevant PNG is in the resources of the main application:
Interestingly, PE-bear fails to display it. It turns out other tools have the same problem. The content of the PNG is just invalid. I suspected that it will contain some encrypted data, possibly the flag.
Now we know that this PNG needs to be passed to the DLL. In order to do so, saved the resources by PE-bear. The aforementioned PNG is in the file named: _1_429cc0.png
.
Then, I created my own loader, that includes this PNG as a resource with identical name as the DLL requires. The code of the loader is available here. Now we can trace the execution of the ldr.dll via the prepared wrapper. We just need to change the traced module in the TinyTracer’s run_me.bat
(as described here).
set TRACED_MODULE="ldr.dll"
The other thing that we can notice in the trace log is a SleepEx
function. I also watched its parameter in Tiny Tracer:
1a1e;kernel32.SleepEx Arg[0] = 0x0000000000057e40 = 360000 Arg[1] = 0
The sleep time turns out pretty long: 6 minutes. Fortunately we can overwrite it in TinyTracer (more info here).
I opened the DLL in IDA in order to analyze it statically. Overview of the Start
function:
The decompiled code – final result of my analysis – is available here.
Most of the API functions are resolved by hashes, so the TAG file generated by TinyTracer came handy. I just applied tags on the IDA view (using IFL plugin), and the code became much more understandable. Example:
NOTE: this way of resolving API calls have some limitations: since the tags are generated during tracing, only the calls that were actually executed will be resolved. So, still we are left with some hashes that are not mapped. Fortunately, a quick google lookup shows that the hashing algorithm is well known, and there are already lists of common API functions with their corresponding hashes. This helped to find some more functions.
Not only the API calls are obfuscated, but also strings. Each used string is deobfuscated just before use, with the help of an inline XOR loop. Example:
Since the application doesn’t use many strings, I decided not to write any automatic solutions, but to resolve them manually under the debugger as I progress with the analysis.
There are some condition that the DLL checks, for example, the executable must be named Spell.EXE
– so I renamed my loader to this name.
After renaming my loader (and enabling sleep hooking in Tiny Tracer, as it was shown before), I traced it again. The produced log is available here. This time we can see something interesting: the application is trying to connect to the socket:
1a1e;kernel32.SleepEx Arg[0] = 0x0000000000057e40 = 360000 Arg[1] = 0 NtDelayExecution hooked. Overwriting DelayInterval: ffffffff296c5c00 -> fffffffffffe7960 1c3a;kernel32.VirtualAlloc 1318;kernel32.GetModuleHandleA 1339;kernel32.LoadLibraryA Arg[0] = ptr 0x000000c094952400 -> "ws2_32.dll" 1fc4;ws2_32.WSAStartup 1318;kernel32.GetModuleHandleA 20b8;ws2_32.socket 1318;kernel32.GetModuleHandleA 1339;kernel32.LoadLibraryA Arg[0] = ptr 0x000000c0949522e0 -> "user32.dll" 2d6c;user32.wvsprintfA 1318;kernel32.GetModuleHandleA 2146;ws2_32.gethostbyname 1318;kernel32.GetModuleHandleA 21ba;ws2_32.ntohs 1318;kernel32.GetModuleHandleA 21df;ws2_32.connect 1318;kernel32.GetModuleHandleA 2162;ws2_32.closesocket
I added tracking of the gethostbyname
parameters, and I saw the address it is trying to connect to:
2146;ws2_32.gethostbyname Arg[0] = ptr 0x0000007a6ef5f9b0 -> "inactive.flare-on.com"
After checking more details under the debugger, I found out that it queries one of the two addresses: invalid.flare-on.com
and invalid2.flare-on.com
, trying to connect to the port 888
. None of those addresses is active, so we have to somehow emulate this communication.
Once it connects to the C2, it sends a beacon “@” and is waiting for a command.
There are 3 commands available: “exe”, “run”, “flare.com”.
First two commands are used for running some received shellcode, or a PE file. Third of them leads to a function that seems to decrypt something…
One of the possible ways of emulating the communication, is to start a server locally, for example using netcat.
netcat -l -p 888
Then we can redirect the domain to it by editing the following file:
%windir%\system32\drivers\etc\hosts
We need to create the entry that will cause the the domain to be resolved as our localhost:
127.0.0.1 inactive.flare-on.com
Running the binary again, we can see that indeed the crackme connects to our emulated C2, and sends the expected prompt:
As mentioned earlier, the third command (“flare.com”) looks interesting, because it leads to some decryption. We can run the prepared loader again, via TinyTracer, and watch the APIs called during the communication with the fake C2. I let it connect, then set the command “flare-on.com”, at the same time observing the trace log in real-time and checking what happens.
First, the BCrypt library is loaded, and it is used to decrypt some buffer. Relevant fragment:
1339;kernel32.LoadLibraryA Arg[0] = ptr 0x000000726ce82180 -> "bcrypt.dll" 3047;bcrypt.BCryptOpenAlgorithmProvider 1318;kernel32.GetModuleHandleA 30eb;bcrypt.BCryptGetProperty 310f;kernel32.GetProcessHeap 1318;kernel32.GetModuleHandleA 3131;ntdll.RtlAllocateHeap 1318;kernel32.GetModuleHandleA 3231;bcrypt.BCryptSetProperty 1318;kernel32.GetModuleHandleA 327e;bcrypt.BCryptGenerateSymmetricKey 1318;kernel32.GetModuleHandleA 32dc;bcrypt.BCryptDecrypt 1318;kernel32.GetModuleHandleA 3353;bcrypt.BCryptCloseAlgorithmProvider 1318;kernel32.GetModuleHandleA 3373;bcrypt.BCryptDestroyKey
After that, some registry keys are set, and finally the function exits (execution goes back to the loader):
2b77;advapi32.RegOpenKeyExA 1318;kernel32.GetModuleHandleA 2c89;advapi32.RegSetValueExA 1318;kernel32.GetModuleHandleA 2cb0;advapi32.RegCloseKey 101f;spell.[.text+2ff]*
The full trace-log from this session is available here.
After adding the BCrypt functions to watched, and tracing again, we get some additional information:
sd3231;bcrypt.BCryptSetProperty Arg[0] = ptr 0x0000001ec8de0b90 -> {...} Arg[1] = ptr 0x0000001ec8cbfc24 -> L"ChainingMode" Arg[2] = ptr 0x0000001ec8cbfc44 -> L"ChainingModeCBC" Arg[3] = 0x0000000000000020 = 32 Arg[4] = 0x0000001e00000000 = 128849018880 1318;kernel32.GetModuleHandleA 327e;bcrypt.BCryptGenerateSymmetricKey Arg[0] = ptr 0x0000001ec8de0b90 -> {...} Arg[1] = ptr 0x0000001ec8cbfc78 -> {...} Arg[2] = ptr 0x0000001ec8de5f00 -> {...} Arg[3] = 0x000000000000028e = 654 Arg[4] = ptr 0x0000001edb9c0000 -> "d41d8cd98f00b204e9800998ecf8427e" Arg[5] = 0x0000000000000020 = 32 Arg[6] = 0 1318;kernel32.GetModuleHandleA 32dc;bcrypt.BCryptDecrypt Arg[0] = ptr 0x0000001ec8de5f00 -> L" " Arg[1] = ptr 0x00007ff6ebae610f -> {\xd7\xfb~b\x8d\xab\x87e\xcdq\x85\xceS\x0fZ\x8c-\x8aE7\x12Ky\x1d@\xdav\x86&\xd3\xd3r} Arg[2] = 0x0000000000000020 = 32 Arg[3] = 0 Arg[4] = ptr 0x0000001ec8cbfca8 -> {...} Arg[5] = 0x0000000000000010 = 16 Arg[6] = ptr 0x0000001ec8cbfc88 -> {\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00} Arg[7] = 0x0000000000000020 = 32 Arg[8] = ptr 0x0000001ec8cbfc80 -> {...} Arg[9] = 0x0000001e00000000 = 128849018880
We can spot that the content of the PNG file gets decrypted. Buffer:
{\xd7\xfb~b\x8d\xab\x87e\xcdq\x85\xceS\x0fZ\x8c-\x8aE7\x12Ky\x1d@\xdav\x86&\xd3\xd3r}
Is the same as the content of the previously reviewed PNG:
The used algorithm is AES in CBC mode, with the key generated from the string: “d41d8cd98f00b204e9800998ecf8427e”.
If we follow those functions under the debugger, we can see the aforementioned decryption:
…and the string that we got as the result of it:
l3rlcps_7r_vb33eehskc3
Later, this buffer is rewritten, with the suffix “flare-on.com” (typical for the flag) appended:
The string didn’t make much sense, but at least it is ASCII, so I thought it may be a flag. I tried to submit it, however, it turned out invalid. So I had to dig deeper.
I noticed this string is being XORed, scrambled, and the result is written into Windows Registry:
The function responsible for scrambling:
I decided to clear the buffers that are used for the XOR operations. The buffers:
C3 C1 A8 06 C2 96 33 00 00 00 00 00 00 00 00 00 8A 1D 89 15 14 9F C1 1D 99 7E 8A 1B 00 00 00 00 E2 A4 B7 A7 D7 AC 87 8D 9B 9C 85 0D D8 8E E5 FA
…were set to all 0s under the debugger:
As the result the valid flag was saved in the registry:
[email protected]
Best spell checker ever… This time the flag makes sense, moreover, it passes the verification!