FlareOn-9 Writeups 06 à la mode
Introduction
Description:
FLARE FACT #824: Disregard flare fact #823 if you are a .NET Reverser too. We will now reward your fantastic effort with a small binary challenge. You’ve earned it kid!
This time, we’ll be looking at a Windows library named HowdoesThisWork.dll
.
A text file called IR chat log.txt
is also provided:
[FLARE Team] Hey IR Team, it looks like this sample has some other binary that might
interact with it, do you have any other files that might be of help.
[IR Team] Nope, sorry this is all we got from the client, let us know what you got.
The .NET rabbit hole
We can immediately understand what this text file is referring to by opening the library in dnSpy:
From what we can observe, this library only have a single namespace FlareOn
, with a single method called GetFlag()
.
This function is basically taking a password as input, send it to the local named pipe FlareOn
and returns the answer from what’s on the other end of the pipe.
This is great, but we are obviously missing a key component of the application here.
Taking a step back on the challenge can help us understand that something is wrong. The binary is 77 KB in size, which is way larger than what’s needed to store this small method.
And if we observe the program in a hex editor, we stumble upon some interesting stuff:
Here is a bunch of string that are typical of a C++ compiled binary.
We also have some sections names that are not detected in dnSpy:
Looks like this binary is more than it pretend to be, and that a C++ program is somehow hidden in it.
Loading it in IDA as a PE file, not as a dot net module, reveal a set of new functions!
Once again, we got tricked by the challenge description that forces us to assume that this was a dot net binary :)
Hidden entrypoint
The technique used to hide the entrypoint and act as a valid dot net module while being able to execute raw C/C++ code is described in this article.
Basically, a new entry was added into the dll_dispatch
function, which is part of the .Net CRT (DllMainCRTStartup
), that is not going to raise any suspicions:
We can observe this entry in the dispatcher at 0x1000174F
:
By using this “hidden” additional entrypoint, the code is able to act as a valide .Net binary, without having any references to the protected code.
That’s also a smart way to backdoor a dot net application, even if the fact that something is off is pretty obvious in this particular case.
API hashing
Opening up the first function from our new entrypoint display something that some trained eyes can instantly recognize:
API hashing is a popular technique used by a lot of malware authors to hide external API references by resolving them at runtime. The classic way of dynamically resolving an API in Windows is by using LoadLibrary
to map the desired library in our process, then to utilize GetProcAddress
to obtain a handle to an arbitrary function from the library. That way, instead of calling a suspicious API, the malware is calling a bunch of variables that does not make any sens when taking a first quick look.
Unfortunately, this way of operating is not stealthy as the library and the function name must be referenced by string:
typedef int(WINAPI *InternetOpenUrl_proc)(HINTERNET, LPCTSTR, LPCTSTR, DWORD, DOWRD, DWORD);
HANDLE lib = LoadLibraryA("Wininet.dll");
InternetOpenUrl_proc hInternetOpenUrl = (InternetOpenUrl_proc)GetProcAddress(hModule, "InternetOpenUrl");
hInternetOpenUrl([...]);
This small example show that a simple dynamic resolve of the desired API is not stealthy at all, and is in fact even more suspicious and easily noticeable.
That where the API hashing technique comes into place.
Its goal is to perform the same task, without leaking the names of the target functions.
Hiding the name of the target DLL is a matter of encoding / encryption / obfuscation and is not technically part of the API hashing routine.
To retrieve a pointer to our external function, the goal is to get rid of the GetProcAddress
API. The main way of doing so is to manually walk down the loaded library list (available from the PEB
under InMemoryOrderModuleList
), loop on each loaded library export table until we found the desired one. This can be done with a simple comparison, or by storing a “hash” (most of the time a CRC + XOR, or some variations of that as the operation should stay lightweight) of the desired function, and using the same algorithm used to produce this hash against the library exports.
By doing this, an analyst has to reverse the hashing mechanism before starting the actual reverse-engineering of the malware (In reality, this is easy to deal with once used to it).
Here we have multiple hints that this function is resolving a bunch of function for a later use. First, a list of continuous DWORD
are initialized, and there cross-references show that they are called later:
Then, we can aslo recognize a pattern in the use of the sub_100014AE
function, which takes two encoded strings as parameters:
And by tracing the result of this function, it is used in sub_1000125C
as a parameter along with a second constant parameter v0
.
We can emit the hypothesis that the first function is decoding / decrypting the name of the external function to resolve, when the second one is locating it in the target library. v0
being a handle to that said library.
We can confirm this by taking a look at sub_100012DB
, which indeed returns a pointer to kernelbase.dll
by locating it from the PEB
(FS
register + 0x30, no need to resolve any external library this time):
Finally, the function that we suspected to resolve the external function names is performing a xor between the two given argument, returning a function name.
After a bit of renaming and cleaning, we obtain the following:
We can immediately spot the CreateNamedPipeA
function, indicating that we are closer to the missing “server” component from the .Net file.
From now, with the resolved imports, the analysis will be simplified.
Analysis
The next function is launched as an independent thread through CreateThread
. The thread entrypoint is listening for an input on the \\.\pipe\FlareOn
named pipe:
When something is received, the function sub_10001000
is called. As a reminder, the received input is the one sent by the .Net component. So we can assume that this function is the one in charge of checking the validity of the password.
This function is straight forward. It is a simple RC4 decryption of a blob of data followed by a comparison on the input password.
Exactly as in the 4th challenge, we can identify the RC4 algorithm through its KSA
and PRGA
phases (which are optimized by the compiler this time):
The comparison function, which is straighforward do deal with, is the following:
As the RC4 key is simply stored as a byte array, we can dump it along with the encrypted password, and decrypt it to reveal the flag to validate the challenge:
“M1x3d_M0dE_4_l1f3@flare-on.com”