Automating The Analysis Of An AutoIT Script That Wraps A Remcos RAT

Amged Wageh
11 min readJan 2, 2022

Threat actors usually depend on some sort of a first stager to drop their malware, one good candidate for this mission is AutoIT scripts because of their capabilities of interacting with COM objects, calling DLLs’ functions, and simulating user interactions.

In this story, we’ll discuss some important tips for analyzing AutoIT malware and we’ll apply those tips to a real-world sample from the wild. We’ll also write a couple of python scripts to automate the analysis and to extract and decrypt the config of the Remcos agent.

Delivery

AutoIT scripts can be delivered in two ways:

  1. An au3 script, which requires AutoIT to be either installed on the system or it could be shipped with the script.
  2. The script could be compiled with the interpreter itself as a standalone executable. In this case, we’d need to extract the script from the exe file using either Exe2Aut or MyAut2Exe.

Important Functions

Like any other scripting language, there are some specific functions and macros that malware authors could use to perform their nefarious tasks, the following is a list of the functions that are prone to be abused by malware authors:

DllCall()
DllStructCreate()
DllStructGetPtr()
DllCallAddress()
IsAdmin()
ObjCreate()
ObjectGet()
BinaryToString()
StringToBinary()
Binary()
BinaryLen()
AutoItSetOption()
EnvGet()
Eval()
Execute()
InetGet()
ProcessList()
RegRead()
RegWrite()
FileRead()
FileWrite()
ShellExecute()
ShellExecuteWait()
Run()
RunAs()
Sleep()
StringReverse()
TCPSend()
TCPRecv()
UDPSend()
UDPRecv()

And here is a list of macros that could be abused by malware authors:

@AppDataCommonDir
@AppDataDir
@AutoItExe
@AutoItPID
@AutoItX64
@CommonFilesDir
@ComputerName
@ComSpec
@CPUArch
@DesktopCommonDir
@DesktopDir
@DocumentsCommonDir
@FavoritesCommonDir
@FavoritesDir
@HomeDrive
@HomePath
@HomeShare
@LocalAppDataDir
@OSArch
@ProgramFilesDir
@ProgramsCommonDir
@ProgramsDir
@ScriptDir
@ScriptFullPath
@ScriptName
@StartMenuCommonDir
@StartMenuDir
@StartupCommonDir
@StartupDir
@SystemDir
@TempDir
@UserName
@UserProfileDir
@WindowsDir

Refer to the AutoIT documentation to understand what each function and macro does, links in the references section.

Example From The Wild

During one of the incidents that I have engaged with, I found a Remcos agent that has been delivered as a UPX packed binary wrapped inside an AutoIT script. The script has been delivered separately from the interpreter alongside a batch script that glues everything together and executes the script.

Sogni.tmp

This script starts by adding MZ to a file named Mia.exe.com then, it copies the contents of another file called Dai.tmp to the same file, theDai.tmp file contains the AutoIT interpreter without MZ at the beginning. This trick usually being done to bypass the security controls that inspect the files with the MZ header.

Sogni.tmp Contents

Then a file named Arteria.tmp will be passed to the interpreter to be run. The Arteria.tmp is the file that contains the malicious AutoIT script.

Arteria.tmp

By examining that script, we can see that it is being obfuscated with four techniques:

  1. A custom-written obfuscator has been applied.
  2. Declaring switch cases inside a loop that will only be executed once.
  3. Wired variable and function names.
  4. Declaring functions and variables that are never being used.

Identifying the De-obfuscation Function

De-obfuscating the malware is very crucial to have a successful analysis, mostly, the de-obfuscation function is the function that is being called the most, in this case, a function called faBwnHc is the function that is being called the most and it’s not an AutoIT function (custom written) so this is the most suspected function.

To make it harder for analysts, the function itself applies the same obfuscation techniques 2, 3, and 4 that have been mentioned previously so, we need to defeat them in order to understand how to deobfuscate the rest of the script.

Defeating unnecessary loops and switch cases:
The loops and the switch cases always have the same pattern as follows:

<variable1> = number1    ; a number that determines the switch case
<variable2> = number2 ; a not used variable
Do ; the start of the loop
switch variable1 ; start of the switch cases
case numberX
statements
case numberY
statements
...
case number1 ; the case that contains the statements
statements ; that will get executed.
ExitLoop ; to exit the loop so, it runs only once
case numberZ
statements
EndSwitch
Until numberW
Next

So, for defeating this technique, we have to remove the unnecessary code. The following python function has been written for that purpose:

After running this function and saving the returned semi de-obfuscated script, we’ll get a clearer view of the script’s deobfuscation function.

Deobfuscation function

Defeating a custom written obfuscator:
By manually going through the function to further understand what it does, we can see the following:

The deobfuscation function deobfuscated

It accepts two arguments, the first one is a string of numbers separated by dots however, the second argument is just a number. The function splits the first string then it loops over the numbers list and subtracts the second argument from each number, then it converts the result to a character and concatenates those characters into a string, that string will be the de-obfuscated string.

A python function has been written to follow the same de-obfuscation technique.

And another function that automates the deobfuscation by looping over all the function calls and replacing them with the deobfuscated string

Once the script gets de-obfuscated, we can have a clearer idea of what is going on.

Semi de-obfuscated script

Defeating the unnecessary declared variables and functions:
The malware author has stuffed the script with so many unused functions and variables, to defeat this obfuscation technique, a python script has been written to tokenize the script and remove the unused declared functions and variables as follows:

Defeating the weird named variables and functions:
Now, we need to manually go through the script to understand what it does so we can rename the variables and the functions with meaningful names.

Buffer Decompression
The function that is originally called ZsMuinqqKkZyNasdzfRZygWqdvdbo is responsible for decompressing a buffer that was originally being compressed with an inflating compression,

Buffer decompression

Killing Switches
For evading AV sandboxes, the malware checks the computer name and halts if the computer name is one of the known names that AV products use, for example: tz which is being used by Bitdefender, NfZtFbPfH which is being used by Kaspersky, and ELICZ which is being used by AVG. It also halts if it found a file named aaa_TouchMeNot_.txt under the C:\ directory, mostly is trick is being used to avoid self-infection and to evade Defender AV Emulators.

Kill Switches

Anti Analysis
The function that is originally named YlNhpDElbjgxIsNhKUV, applies an anti-analysis technique where it gets the number of milliseconds that have elapsed since the system was started then it sleeps for a number of milliseconds and it recalculates the number of milliseconds that have elapsed again, then it calculates that delta and exits if the difference is either bigger than the delta + 500 or smaller than the delta — 500.

Anti Analysis

Process Hollowing
The function that is originally called wJKkYYrSKDWGKUxnJpLSngpYQJY takes a binary buffer and a command line that points to the path of the same process to be spawned by passing the command line to CreateProcessW.

Spawning itself

It calls the function that was originally named iIGEjbvuSPKbstetiETZTXUzxISkJG for calling NtUnmapViewOfSection to hollow out the contents of the spawned process then it calls the functions that were originally named oRnvqSVbsowWGStGmLJnlz and YRZhNmLxfZ to call VirtualAllocExNuma to allocate a new memory region inside the hollowed process.

Hollowing

Then, it calls WriteProcessMemory to write the binary buffer.

Writing the buffer

It calls VirtualProtectEx to add the execution permission to the newly created region.

Adding execution permissions

Then, it calls SetThreadContext to point the thread’s entry point to the newly created section, finally, it calls NtAlertResumeThread to resume the suspended thread.

Resume thread

Dynamically dumping the injected buffer

After we understood how it injects the buffer, let’s dynamically dump it out to continue our analysis.

Firstly, let’s hollow out the contents of the anti-analysis function so it wouldn’t bother us.

Defeating the anti-analysis function

Now, let's open the AutoIT interpreter in x32dbg and adjust the command line to pass the script as an argument and reload.

x32dbg command line

Set a breakpoint on IsDebuggerPresent, WriteProcessMemory, and NtAlertResumeThread.

Since it is an AutoIT script, it will call IsDebuggerPresent to check whether the script is being debugged or not. Once the breakpoint hits, follow the instructions until you get to the memory location where it holds the BeingDebugged flag in the PEB then, change the flag to 0 so if by any chance there is any other check it fails.

BeingDebugged flag

Once the CreateProcessW’s breakpoint hits, change the arguments of the function to change the create flag to 4 to create the process in a suspended mode.

Create in suspended mode

Then, attach another debugger to the newly created process. make sure that the same breakpoints are being set and resume the process.

Once resumed, it will hit on the CreatePricessW with a creation flag of 4.

Suspended process creation

After running the debugger again, it’ll hit on the VirtualAllocExNuma, by checking the first parameter which is a handle to the process in which it allocates a region of memory, we can see it’s a handle to the lastly created process.

Process handle

The second parameter reveals that it allocates the memory in the 0x400000 base address.

Base Address

By inspecting the allocated memory via Process Hacker and we can see it’s empty as of now.

Allocated memory

Once we let the WriteProcessMemory function runs, it will write the buffer into that region of memory, which appears to be an executable.

Written buffer

Now, we can hit the save button to dump the buffer on the disk.

By checking dumped file via PE-Bear, it appears to be a UPX packed binary and the Section headers need to be fixed.

PE-Bear inspection

For fixing the headers, we need to make the raw addresses point to the same location as the virtual addresses, and we need to recalculate the raw size respectively.

Fixing the section headers

We also need to make sure that the base image points to the same base where we dumped the binary, which is correct in this case.

Checking the Image Base

Once we fix the headers, the import table will be fixed and populated automatically.

The import table

As an anti-packing technique, the author has set the optional header’s checksum to 0, so we can’t unpack it using the UPX binary, however, we still can unpack it manually.

Checksum set to 0

The UPX packed binaries have two sections, UPX0, AND UPX1. The UPX1 section has the unpacking routine and once it runs, it writes the unpacked binary into the UPX0 section so, we can add an execution hardware breakpoint on the UPX0 section, once this breakpoint hits, that means that the binary has been fully unpacked and the execution flow has been transferred to the newly unpacked binary.

UPX0 is empty

Open the UPX packed binary in x32dbg, get to the UPX0 section in the memory map, and follow that section in dump.

Following UPX0 in the dump

Then, set an execution hardware breakpoint on that section.

Setting an execution hardware breakpoint

Once the breakpoint hits, we can check the strings, and we can see that it’s a Remcos RAT agent version 3.3.2 Light.

Remcos strings

Let’s use Scylla plugin to dump the unpacked binary.

Dumping with Scylla

Then, use the Scylla plugin again to fix the dumped binary.

Fixing the dump

It’ll show you a box to select the dumped file, and it will create another file with the same name prefixed with SCY.

Remcos agents store its config file in the resources in a file called SETTINGS, that file is encrypted with RC4, so let’s open the remcos agent in PEStudio and dump out the config file.

Dumping the config file

Remcos configs have the following pattern, the first byte is the key length followed by the key itself, then the encrypted data which is the agent’s configuration.

So, by opening the configuration file in a hex editor, we can see that the key length here is 14 bytes.

RC4 Key length

Finally, we can use CyberChef for decrypting the configuration file and get the C2 address.

The config file

A python script has been written to automate the config extraction and decryption.

Thanks for reading, I’d appreciate your comments and feedback. 😊

--

--

Amged Wageh

A Sr. DFIR Consultant who has a proven track record of successfully leading investigations into cyber security incidents.