Deobfuscating/REversing Remcos - AutoIt, Shellcode, and RunPE

Remcos is a robust RAT actively being used in the wild. 
This multi-staged/evasive RAT provides powerful functionality to an attacker. Each stage is written in a different language: AutoIt -> Shellcode -> C++. I wanted to explore both the evasiveness, and core functionality of the malware.

This variant is a compiled AutoIt script.
AutoIt executables store their payload in the resource section, and load it at runtime via LoadResource.
AutoIt is de-compilable, this can be performed by dropping the executable into Exe2Aut.
The source code is obfuscated, but can be easily deobfuscated with Python.  Here is a block of code obfuscated with a recurring algorithm used throughout the malware.
Function and variable names are randomized, and the first line of the function begins with defining an array of 169 elements.  Each element is an integer, the result of either an addition or subtraction.  The malware then iterates through this array, passing each element to the ChrW() function, which will convert each integer to an ASCII character, building a string one character at a time.

We can use some greedy regex to grab all of these arrays and decode them with Python (snippet below).
The Python eval() function can be used to perform the addition/subtraction for each element.
As you can see, many of these integers are within the ASCII range, thus can be resolved with the chr() function.  The .join() function is used to join the list into one string, and the [list comprehension] is used to iterate through the list, performing the chr() function on each element.
I then added a function to decode all hex strings from the output, which first prints the hex itself, then the decoded ASCII on the following line.  One of the last lines of hex failed to decode (highlighted below).  There are several interesting hex values that stand out in the string that lead me to believe that this was shellcode.  E.g. E9 is a jump near, 55 8B EC is the start of a function prologue (push ebp, esp), 4D 5A ('MZ'), 50 45 ('PE'), etc...
I copied this hex string into HxD and saved it to disk to create the binary.
I then loaded it into IDA for further analysis. 
The first jmp transfers control flow to an address outside of this blob of shellcode, which suggests that there is likely more shellcode somewhere else within the malware. A function prologue/new stack frame is then committed (push ebp, esp....sub esp, 0x14).  The first cmp is dereferencing the address in EBX, checking for the value 'MZ', which is the magic bytes for a Windows Executable (.exe, .dll, .sys).  If this value is found at that location, then the the jump is taken (JZ = Jump if Zero/Equal...5A4D - 5A4D = 0 sets the Zero Flag).  0x3C from the 0x0 byte / 'MZ' header of an executable is the offset to the start of PE header. This offset is obtained and then checked for the value 'PE', which is the magic bytes of the PE header.

For example, here is a hex dump of a windows executable with the offset of the PE header (0x3c) highlighted.
Here is the start of the PE header - magic bytes = 'PE'
Back to the shellcode, the malware then locates the Export table, which is 0x78 from the start of the PE header.  It then obtains pointers to several important tables.  These tables can be walked by shellcode to locate the addresses of WinAPI functions it may want to use.  A manual/evasive way of giving you what GetProcAddress would return. 

Back to the AutoIt code, I added a few lines to the Python to actually replace the encoded values with the decoded ones, so that I could get a better look at how they are being used.
I then started picking through the code, renaming variables to make more sense, and decoding any other obfuscated code I came across.  The decdata function pulls a payload from the resource section through calling the user-defined function 'globaldata()'.  There is a lot of garbage code (variables that are defined but never used and pointless arithmetic) that could be removed to clean it up.
This variable is defined, but never used, thus can be deleted!
Deobfuscated and cleaned up, you can see how much garbage was added to the function.  The deobfuscation algorithm appears to be: hex -> ASCII (BinaryToString), reversed (StringReverse), and replace '%$=' with '/'.
The malware is using the WinAPI function CryptBinaryToString to convert the byte array '$sdata' to a string.  The output string will be stored within a struct, which will be accessed by DllStructGetData to return the decoded data.

As mentioned earlier, the encoded data is pulled from the resource section of the executable via the 'globaldata()'function.
The resource that gets loaded is of type RCDATA, which is raw data.  
Dumping this resource and popping it into a hex editor shows a reversed base64 string.
Here is the end of the resource ('TVqQ' is the base64 encoding of 'MZ'):
Using python to decode the payload, I stored the reversed base64 string in the variable 's', then performed the reverse and replace.
Decoded and copied to disk, the fully decoded payload is a windows executable.
The malware also attempts to privesc through the event viewer registry hijack to bypass UAC.  Here is the deobfuscated/beautified code.
Another piece of code that stands out is the runpe() function.  This routine is invoked via the wddtuykqzw() function.
RunPE is passed the path to the malware, a variable that invokes the decdata() function, and boolean values for the $protect and $persist parameters.
The first local variable contains the shellcode we loaded into IDA.  More shellcode is appended to this variable throughout this function.
The shellcode is being appended to this variable 169 bytes at a time.
To quickly pull these values out, I grep'd for ["169"], and then awk'd for the 4th column, which contains the hex string.
I then formatted and copied this string, dropped it into the hex editor, and then loaded the binary into IDA.  As you can see, there are now 6 recognized functions.
sub_22D prepares for resolving WinAPIs that the malware needs by building stack strings.  al is set to 0, and used repeatedly to insert a null-terminator to the end of each string/API.
Converting the hex to ASCII:
Two structs are created: one to store the shellcode, and the other to store the in-memory executable from the resource section.  The struct 'silkrefud' stores the executable, and the shellcode is stored in the 'uderboss' struct.  Control flow is then transferred to the shellcode via the 'DllCallAddress' function, which is passed a pointer to the 'silkrefud' struct as an argument.
To debug the shellcode, I loaded the executable into the debugger (x32dbg) and set a breakpoint on VirtualAlloc (trying to hit the block of code above).  Before hitting the bp, I received an error message, and the program exited.
To bypass this mechanism, I set the BeingDebugged flag in the Process Environment Block (PEB) to 0.  This is to trick the malware into thinking we aren't debugging it. To view the PEB in the memory dump window, the command is: dump fs:[30]

BeingDebugged (offset 0x2 of PEB) is set to 1 (PEB -> fs:[30])
Setting flag to 0
After clearing the flag, I ran the program again and hit the VirtualAlloc called at the end of the runpe() function.
Here are the args passed to VirtualAlloc (matches the runpe() instance of this call)
I then set a memory-write breakpoint on the return value of this function, which is the base address of the newly allocated region of memory.
The breakpoint is hit on the 'repe movsb' instruction.
This instruction copies the shellcode to the newly allocated memory.
Control flow is then transferred to the shellcode that we analyzed earlier.
As we saw at the end of the runpe() function, the address of the 'silkrefud' struct (executable) is passed as an argument to the shellcode.
The shellcode uses stack strings to store the names of its WinAPI functions.  These strings are placed into local variables 4 bytes at a time.  As mentioned earlier, the al register is set to 0, and is used to place a null-byte at the end of each string.
 API names resolved in memory:
CreateProcessW is dynamically resolved, which is used to create another instance of the malware in a suspended state.  The original executable is then overwritten in memory with the executable from the malware's executable section (RunPE / process hollowing).
Below is the injected executable at address 0x00400000. This is the default address of the image base, this is where the original executable was overwritten in memory.
GetModuleHandleA is called just before WinMain is called in this new executable.
I set a breakpoint on this API call to catch the suspended thread just before main is called, then allowed the original process to call NtResumeThread, allowing the executive thread of the injected process to run.  
The malware then checks to see if it has infected the system through trying to obtain a handle to its hardcoded mutex.
If not present, it then queries the registry for a value also related to the malware.  If found, it deletes it.
A new mutex is then created:
The next function resolves the addresses of several WinAPIs, storing them in global variables.
Following this function, the malware enters an anti-analysis routine.  The first check performed is to attempt to obtain a handle to 'SbieDll.dll', this will detect if the malware is running in Sandboxie.
The malware also checks the NtGlobalFlag in the PEB to detect being debugged.
The malware then checks for procmon by attempting to obtain a handle to the tool's window.  It attempts to detect Process Explorer using the same technique.
The malware detects VMware through the 'in' instruction. If running in VMware, EBX will return the string 'VMXh'.
VirtualBox is detected through attempting to open a registry key associated with VirtualBox.
Admin rights are checked via the WinAPI call IsUserAnAdmin.  This return value is then moved into a global variable.
If not run as admin, the malware attempts to privsec (bypass UAC) through the Event Viewer registry hijack (same technique used in the AutoIt code).
The malware then attempts to set the 'EnableLUA' registry value to 0. This is to stop the system from prompting the user when attempting to install something or make a system change (UAC message). 
Command: cmd.exe /k %windir%\System32\reg.exe ADD HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA /t REG_DWORD /d 0 /f
The mutex checked for earlier is then created, just before the malware performs the RunPE technique.
RunPE is performed through the standard sequence of risky WinAPI calls.

The malware contains several interesting commands/modules.  Here is a snippet of several of the hardcoded commands available.
'downloadfromurltofile' will ultimately call URLDownloadToFile.
The downloaded file is then passed to ShellExecuteA to be ran.
'getproclist' will retrieve a list of running process by iterating through a snapshot of running processes via CreateToolhelp32Snapshot.
'execom' allows the malware to spawn additional processes and provide them commandline args.  This is performed via WinExec:

Alternatively, the malware also has the capability to run native commands that will be passed to cmd.exe. Cmd.exe stdout is redirected to a named pipe, which the malware reads in.

The 'scrcap' function will take a screenshot and send it back to C2.
The malware has both online and offline options for logging keystrokes.
This code block is translating virtual key codes to their respective key names.
'clearlogins' clears IE cookies, the Mozilla sqlite database, and Chrome stored logins.  A related function has the ability to steal this information prior to deletion.
The malware also has the ability to hijack the user's webcam. The malware calls out to C2 for an additional library for this functionality.
This library is mapped, then loaded in memory via LoadLibrary.  The addresses of this DLL's functions are manually resolved, and placed in global variables.
'miccapture' will eavesdrop on the victim machine's mic.
Clipboard data is also stolen and exfiltrated.
Thanks for reading, and happy REversing!


  1. Thanks for the article & i appreciate for your hard work.
    Your article helped me alot.
    Again really thanks for the article.


Post a Comment