Part I: Remcos .NET Loader
Summary
Quick RE of Remcos .NET loader (e0516fbf7582151fe90b672925f75734).
Static Analysis
I will skip the static analysis write up since it was not very exciting. We are working with a 32 bit .NET Windows PE and you can find a couple of interesting things statically by examining the file's metadata stream tables using CFF Explorer. Table "Manifest Resources" tells us that there are 8 resources inside the binary, and table "ImplMap" shows a bunch of Desktop Windows Management (dwmapi.dll) functions potentially used by the app. "ImplMap" table lists unmanaged methods used by the PE and could be pretty useful in figuring out binary functionality in some cases.
Dynamic Analysis
Once we are done with the static analysis, we can perform dynamic analysis with dnSpy. It might be useful to set up Module Breakpoints before you start debugging the sample so that you do not miss any module loads, and examine all module constructors (.cctors) and break on those since these guys will run before the Main method.
Most resources stored inside the sample look like junk except two. "String1" and "BBJchBL" seem out of place and could potentially be encrypted payloads used by the sample during the execution.
Upon examining of all classes and methods inside the binary, you will notice the "FlyPushBooks.Frm_MainMenu" .cctor performing the replacement operation on the binary's "String1" resource.
Another interesting methods that stand out are "KOMNJIUHB000*". They further deobfuscate the "String1" resource, load it, and pass execution to it.
The deobfuscation process is quite simple. It starts with replacing the characher "." with "$$". Then "$$" is replaced with "A". After that, the string is Base64 decoded and, if you examine the byte array passed to Assembly.Load, you will notice that it is a decrypted PE.
You can use dnSpy to dump the variable containing the payload1 bytes (404efdeb9931733904da07f96fabeb72) to examine it before it is loaded. After loading payload1 into DIE we can see that its internal name is "Cassa.dll". It does not appear to be obfuscated, and it also does not contain any .cctors. The original binary passes the execution to "QuestKingdom.WorkerHelper" inside Cassa.dll which runs the "Control_Run" method.
"Control_run" first sleeps for 25 seconds and when accesses the "BBJchBL" bitmap resource from the main binary, decrypts and loads it. Just like the last time, we can dump the decrypted payload2 bytes and examine it before letting it load.
Payload2's (678ba32c2ee5bce32be156fdc88882df) internal name is EffectiveKeySize.dll, even though DIE v3.4 did not detect any packers/obfuscators it does show the high entropy value and once you open it inside dnSpy you will notice a bunch of obfuscated strings. You can use De4Dot to clean it up and make it more readable.
After that you can use CyberChef to convert the clean EffectiveKeySize.dll version into bytes and replace with it the obfuscated version of EffectiveKeySize.dll in dnSpy before it is loaded.
Cassa.dll passes execution to EffectiveKeySize.dll "Class15.Main()" method. Don't forget about module constructors (.cctors) running before the main inside the EffectiveKeySize.dll.
The payload2 execution starts with decrypting and loading payload3 (fd2efb1d90508735dee5a05153b65bae), which is stored inside payload2 in the form of an array. Payload3 does not contain any classes or methods, it only contains a bunch of resources. One of these resources turns out to be the final Remcos payload.
EffectiveKeySize.dll creates a copy of the main binary under "C:\Users\username\AppData\Roaming\hXFVhOKfmWvZk.exe".
It uses scheduled tasks to establish persistence on the host by saving the scheduled task XML file in "C:\Users\username\AppData\Local\Temp\tmpF99.tmp" and by launching the hidden schtasks.exe process with the command line "C:\Windows\System32\schtasks.exe" /Create /TN "Updates\hXFVhOKfmWvZk" /XML "C:\Users\username\AppData\Local\Temp\tmpF99.tmp". The copy of Remcos saved under AppData\Roaming will run the next time the user logs on to the host. Note that the "tmpF99.tmp" is deleted once the schtasks.exe is launched and XML configuration is no longer needed.
Once the persistence is established, the copy if the original executable is launched in a suspended state. The final payload is retrieved from the payload3's resource "6T5Gu2ntM6R", decrypted and written into a new process using standard Windows APIs through process hollowing. Once the final payload runs in a new process, the parent process exits.
- CreateProcess
- GetThreadContext
- ReadProcessMemory
- VirtualAllocEx
- WriteProcessMemory
- SetThreadContext
- ResumeThread
FinalPayload (c5124d216e7b0f383ba7e05b69d84379) is a Win32 PE and we will take a look at it in the next blog 🙂
IOCs
- MD5 main binary: e0516fbf7582151fe90b672925f75734
- MD5 Payload1 (Cassa.dll): 404efdeb9931733904da07f96fabeb72
- MD5 Payload2 (EffectiveKeySize.dll): 678ba32c2ee5bce32be156fdc88882df
- MD5 Payload3 (QOxgTveETgpyLpbjPnKudOgYOHwb.dll): fd2efb1d90508735dee5a05153b65bae
- MD5 Payload4 (Remcos): c5124d216e7b0f383ba7e05b69d84379
- Copy of the main binary is placed in
C:\Users\username\AppData\Roaming\*.exe
- XML file is created in
C:\Users\username\AppData\Local\Temp\*.tmp
- Child process schtasks.exe is launched with command line (Parent: main binary - Child: schtasks.exe)
"C:\Windows\System32\schtasks.exe" /Create /TN "Updates\hXFVhOKfmWvZk" /XML "C:\Users\username\AppData\Local\Temp\tmpF99.tmp"
- Scheduled task
Updates\hXFVhOKfmWvZk
is created on the host to run at users log on (watch Windows Event ID 4698) - Child process running the main binary is launched in suspended state (Parent: main binary - Child: main binary)
- Process hollowing is performed using the standard set of Windows APIs
References
https://www.codeproject.com/Articles/12585/The-NET-File-Format