Flare-On 8 – Task 7
2021-10-23 09:05:00 Author: hshrzd.wordpress.com(查看原文) 阅读量:45 收藏

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).

Unpacking

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:

Data Directory view shows that the Relocation Table is cut out

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:

PE-bear: removing the padding at the end of the dumped DLL

And the DLL is ready to be run… I just renamed it to its original name ldr.dll.

Tracing the DLL and writing a loader

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.

I set it in Tiny Tracer:

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.

The content of the PNG: possibly an encrypted buffer

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).

Static analysis

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.

Used obfuscation

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.

Examining the checked conditions

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…

Emulating the C2

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:

Running the commands

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:

Before decryption

…and the string that we got as the result of it:

After decryption
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:

HKCU\Software\Microsoft\Spell
[email protected]

Best spell checker ever… This time the flag makes sense, moreover, it passes the verification!


文章来源: https://hshrzd.wordpress.com/2021/10/23/flare-on-8-task-7/
如有侵权请联系:admin#unsafe.sh