Salim Bitam

BUGHATCH Malware Analysis

Malware analysis of the BUGHATCH downloader.

34 min readMalware analysis
BUGHATCH Malware Analysis

Key takeaways

  • Elastic Security Labs is releasing a BUGHATCH malware analysis report from a recent campaign
  • This report covers detailed code analysis, network communication protocols, command handling, and observed TTPs
  • From this research we produced a YARA rule to detect the BUGHATCH downloader

Preamble

BUGHATCH is an implant of a custom C2 deployed during the CUBA ransomware campaigns we observed in February of 2022, this tool was most likely built by the threat actor themselves as it was not used previously.

BUGHATCH is capable of downloading and executing commands and arbitrary code, it gives the operator the freedom to execute payloads with different techniques like reflection, shellcode execution, system command execution, and so on. The samples we have seen were not obfuscated and were deployed using a custom obfuscated in-memory dropper written in PowerShell and referred to as TERMITE by Mandiant.

In this document, we will go through the execution flow of BUGHATCH highlighting its functionalities and code execution techniques, a YARA rule and the MITRE ATT&CK mapping can be found in the appendix.

In this analysis we will describe the following:

  • Token adjustment
  • Information collection
  • Threading and thread synchronization
  • Network communication protocol
  • Command handling

For information on the CUBA ransomware campaign and associated malware analysis, check out our blog posts detailing this:

Static analysis

| | | | ------------ | ---------------------------------------------------------------- | --- | | SHA256 | F1325F8A55164E904A4B183186F44F815693A008A9445D2606215A232658C3CF | | File Size | 35840 bytes | | File Type: | Win32 executable | | Signed? | No | | Packer? | No | | Compiler | Visual Studio 2017 - 15.5.0 preview 2 | | Compile Time | Sun Feb 06 21:05:18 2022 | UTC | | Entropy | 6.109 |

Sections

NameVirtualAddressVirtual SizeRaw SizeEntropyMD5
.text0x10000x60000x54005.933A6E30CCF838569781703C943F18DC3F5
.rdata0x70000x30000x2A006.2179D9AD1251943ECACE81644A7AC320B3C
.data0xA0000x10000x4001.163B983B8EB258220628BE2A88CA44286B4
.reloc0xB0000x4240x6005.23539324A58D79FC5B8910CBD9AFBF1A6CB

Code analysis

BUGHATCH is an in-memory implant loaded by an obfuscated PowerShell script that decodes and executes an embedded shellcode blob in its allocated memory space using common Windows APIs ( VirtualAlloc , CreateThread, WaitForSingleObject ).

The PowerShell loader uses inline C# to load APIs needed for shellcode injection as seen in the following pseudocode.

Pseudocode PowerShell inline C#

The PowerShell script is obfuscated with random functions and variable names and contains the shellcode in a reverse-Base64 format.

Pseudocode embedded shellcode in Base64 format

The script first decodes the reverse-Base64 encoded data, then allocates a memory region with VirtualAlloc before copying the shellcode into it. Finally, the script executes the shellcode by creating a new thread with the CreateThread API.

Pseudocode PowerShell creates a new thread to execute the shellcode

The shellcode downloads another shellcode blob and the encrypted PE implant from the C2 server, this second shellcode decrypts and reflectively loads the PE malware.

This section dives deeper into the BUGHATCH execution flow, threading and encryption implementation, communication protocol with C2, and finally supported commands and payload execution techniques implemented.

The following is a diagram summarizing the execution flow of the implant:

Execution flow diagram of BUGHATCH

Pseudocode of the main function

Token adjustment

The implant starts by elevating permissions using the SeDebugPrivilege method, enabling the malware to access and read the memory of other processes. It leverages common Windows APIs to achieve this as shown in the pseudocode below:

Information collection

The malware collects host-based information used to fingerprint the infected system, this information will be stored in a custom structure that will be 2-byte XOR encrypted and sent to the C2 server in an HTTP POST request.

The following lists the collected information:

  • Current value of the performance counter
  • Network information
  • System information
  • Token information
  • Domain and Username of the current process
  • Current process path

Current value of the performance counter

Using the QueryPerformanceCounter API, it collects the amount of time since the system was last booted. This value will be used to compute the 2-byte XOR encryption key to encrypt communications between the implant and the C2 server, a detailed analysis of the encryption implementation will follow.

Pseudocode QueryPerformanceCounter function

Network information

It collects the addresses of network interfaces connected to the infected machine by using the GetIpAddrTable Windows API.

Pseudocode collecting interface addresses

System information

BUGHATCH collects key system information which includes:

  • Windows major release, minor release, and build number
  • Processor architecture (either 32-bit or 64-bit)
  • Computer name

Pseudocode collecting system information

Token information

The agent proceeds to collect the current process token group membership, it invokes the AllocateAndInitializeSid API followed by the CheckTokenMembership API, concatenating the SDDL SID strings for every group the process token is part of. While not unique to BUGHATCH, this is detected by Elastic's Enumeration of Privileged Local Groups Membership detection rule.

Pseudocode collecting token group membership information

Domain and username of the current process

The malware opens a handle to the current process with OpenProcessToken and gets the structure that contains the user account of the token with GetTokenInformation. It then retrieves the username and domain of the user account with the LookupAccountSidW API and concatenates the 2 strings in the following format: DOMAIN\USERNAME.

Current process path

Finally, it collects the current process path with GetModuleFileNameW. The malware then encrypts the entire populated structure with a simple 2-byte XOR algorithm, this encryption implementation is detailed later in the report.

Threading and thread synchronization

The implant is multithreaded; it uses two different linked lists, one is filled with the commands received from the C2 server and the other is filled with the output of the commands executed.

It spawns 5 worker threads, each handling a command received from the C2 server by accessing the appropriate linked list using the CriticalSection object. The main process’ thread also retrieves the command's output from the second linked list using the CriticalSection object for synchronization purposes, to avoid any race conditions.

Pseudocode of the thread creation function

Network communication protocol

In this section we will detail:

  • Base communication protocol
  • Encryption implementation

The implant we analyzed uses HTTP(S) for communications. On top of the SSL encryption of the protocol, the malware and C2 encrypt the data with a 2-byte XOR key computed by the malware for each new session. The values to compute the 2-byte XOR key are prepended at the beginning of the base protocol packet which the server extracts to decrypt/encrypt commands.

When launched, the malware will first send an HTTP POST request to the C2 server containing all the collected information extracted from the victim’s machine, the C2 then responds with the operator’s command if available, or else the agent sleeps for 60 seconds. After executing the command and only if the output of the executed command is available, the malware will send a POST request containing both the collected information and the command’s output, otherwise, it sends the collected information and waits for new commands.

Example of an implant HTTP POST request to an emulated C2 server

Base communication protocol

The author(s) of BUGHATCH implemented a custom network protocol, the following is the syntax that the agent and server use for their communication:

BUGHATCH agent and server communications

  • XOR key values: The values to compute the 2-byte XOR encryption key used to encrypt the rest of the data
  • Separator: A static value ( 0x389D3AB7 ) that separates Msg chunks, example: the server can send different instructions in the same HTTP request separated by the Separator
  • Chunk length: Is the length of the Msg , Separator and Chunk length
  • Msg: Is the message to be sent, the message differs from the agent to the server.

We will dive deeper into the encapsulation of the Msg for both the agent and the server.

Pseudocode extracting commands according to the separator value

Encryption implementation

The malware uses 2-byte XOR encryption when communicating with the C&C server; a 2-byte XOR key is generated and computed by the implant for every session with the C2 server.

The agent uses two DWORD values returned by QueryPerformanceCounter API as stated earlier, it then computes a 2-byte XOR key by XOR-encoding the DWORD values and then multiplying and adding hardcoded values. The following is a Python pseudocode of how the KEY is computed:

tmp = (PerformanceCount[0] ^ PerformanceCount[1]) & 0xFFFFFFFF
XorKey = (0x343FD * tmp + 0x269EC3)& 0xFFFFFFFF
XorKey = p16(XorKey >> 16).ljust(2, b'\x00')

Pseudocode of the encryption implementation

Command handling

In this section, we will dive deeper into the functionalities implemented in the agent and their respective Msg structure that will be encapsulated in the base communication protocol structure as mentioned previously.

Once the working threads are started, the main thread will continue beaconing to the C2 server to retrieve commands. The main loop is made up of the following:

  • Send POST request
  • Decrypt the received command and add it to the linked list
  • Sleep for 60 seconds

A working thread will first execute the RemoveEntryRecvLinkedList function that accesses and retrieves the data sent by the C2 server from the linked list.

Pseudocode retrieves data sent by the C2

The thread will then de-encapsulate the data received from the C2 and extract the Msg(Command). The malware implements different functionalities according to a command flag, the table below illustrates the functionalities of each command:

Command FLAGDescription
1Group functions related to code and command execution
2Group functions related to utilities like impersonation and migration
3Process injection of a PE file in a suspended child process

Command 1

This command gives access to functionalities related to payload execution, from DLL to PE executable to PowerShell and cmd scripts.

Some of the sub-commands use pipes to redirect the standard input/output of the child process, which enables the attacker to execute payloads and retrieve its output, for example, PowerShell or Mimikatz, etc…

The following is the list of sub commands:

Sub Command FlagFunction NameFunctionality description
2ReflectivelyExecutePERemoteReflectively loads PE files in a child process and redirects its standard input output, the output will be sent to the operator C2 server
3DropPEDiskExecuteDrops a PE file to disk and executes it, the execution output is then sent to the operator’s C2 server
4SelfShellcodeExecuteExecutes a shellcode in the same process
5RemoteShellcodeExecuteExecutes a shellcode in a suspended spawned child process
6ExecuteCmdExecutes a CMD script/command
7ExecutePowershellExecutes a Powershell script/command
9ReflectivelyLoadDllRemoteExecutes a DLL reflectively in a remote process using CreateRemoteThread API

The following is the structure that is used by the above commands:

struct ExecutePayloadCommandStruct
{
  DWORD commandFlag;
  DWORD field_0;
  DWORD subCommandFlag_1;
  DWORD readPipeTimeOut_2;
  DWORD payloadSize_3;
  DWORD commandLineArgumentSize_4;
  DWORD STDINDataSize_5;
  CHAR payload_cmdline_stdin[n];
};
  • commandFlag: Indicates the command
  • subCommandFlag: Indicates the subcommand
  • readPipeTimeOut: Indicates the timeout for reading the output of child processes from a pipe
  • payloadSize: Indicates the payload size
  • commandLineArgumentSize: Indicates length of the command line arguments when executing the payload, example a PE binary
  • STDINDataSize: Indicates the length of the standard input data that will be sent to the child process
  • Payload_cmdline_stdin: Can contain the payload PE file for example, its command line arguments and the standard input data that will be forwarded to the child process, the malware knows the beginning and end of each of these using their respective length.

ReflectivelyExecutePERemote

The agent reflectively loads PE binaries in the memory space of a created process in a suspended state (either cmd.exe or svchost.exe ). The agent leverages anonymous (unnamed) pipes within Windows to redirect the created child process's standard input and output handles. It first creates an anonymous pipe that will be used to retrieve the output of the created process, then the pipe handles are specified in the STARTUPINFO structure of the child process.

Pseudocode for anonymous pipe creation

After creating the suspended process, the malware allocates a large memory block to write shellcode and a XOR encrypted PE file.

The shellcode will 2-byte XOR decrypt and load the embedded PE similar to ( Command 3 ). This command can load 64bit and 32bit binaries, each architecture has its own shellcode PE loader, after injecting the shellcode it will point the instruction pointer of the child process’s thread to the shellcode and resume the thread.

Pseudocode of Reflective Loading PE into child processes

The following is an example of a packet captured from our custom emulated C2 server, we can see the structure discussed earlier on the left side and the packet bytes on the right side, for each command implemented in the malware, a packet example will be given.

Example of a ReflectivelyExecutePERemote command received from an emulated C2

DropPEDiskExecute

With this subcommand, the operator can drop a PE file on disk and execute it. The agent has 3 different implementations depending on the PE file type, GUI Application, CUI (Console Application), or a DLL.

For CUI binaries, the malware first generates a random path in the temporary folder and writes the PE file to it using CreateFileA and WriteFile API.

Pseudocode writing payload to disk

It then creates a process of the dropped binary file as a child process by redirecting its standard input and output handles; after execution of the payload the output is sent to the operator’s C2 server.

For GUI PE binaries, the agent simply writes it to disk and executes it directly with CreateProcessA API.

And lastly, for DLL PE files, the malware first writes the DLL to a randomly generated path in the temporary folder, then uses c:\windows\system32\rundll32.exe or c:\windows\syswow64\rundll32.exe (depending on the architecture of the DLL) to run either an exported function specified by the operator or the function start if no export functions were specified.

Pseudocode running the payload dropped by DropPEDiskExecute function

Example of a SelfShellcodeExecute command received from an emulated C2

SelfShellcodeExecute

This subcommand tasks the agent to execute shellcode in its own memory space by allocating a memory region using VirtualAlloc API and then copying the shellcode to it, the shellcode is executed by creating a thread using CreateThread API.

Pseudocode of SelfShellcodeExecute command

Example of a SelfShellcodeExecute command received from an emulated C2

RemoteShellcodeExecute

This sub-command can be used to execute a 32-bit or a 64-bit position independent shellcode in another process memory space.

Similarly to the SpawnAgent subcommand, the malware creates a suspended svchost.exe process with CreateProcessA API, allocates a memory region for the shellcode sent by the C2 server with VirtualAllocEx , and writes to it with WriteProcessMemory , it then sets the suspended thread instruction pointer to point to the injected shellcode with SetThreadContext and finally it will resume the thread with ResumeThread to execute the payload.

Pseudocode writes shellcode to remote process

Pseudocode set EIP of child process using SetThreadContext

Example of a RemoteShellcodeExecute command received from an emulated C2

ExecuteCmd and ExecutePowershell

An operator can execute PowerShell scripts or CMD scripts in the infected machine, the malware can either write the script to a file in the temporary folder with a randomly generated name as follow: TEMP<digits>.PS1 for PowerShell or TEMP<digits>.CMD for a Command shell. The malware then passes parameters to it if specified by the malicious actor and executes it, the malware uses named pipes to retrieve the output of the PowerShell process.

Pseudocode of ExecuteCmd command

Example of an ExecutePowershell command received from an emulated C2

ReflectivelyLoadDllRemote

Execute reflectively a 32-bit or 64-bit DLL in a process created in a suspended state, the following summarizes the execution flow:

  • Check if the PE file is a 32 or 64-bit DLL
  • Create a suspended svchost.exe process
  • Allocate memory for the DLL and the parameter for the DLL if specified by the C2 command with the VirtualAllocEx API
  • Write to the remotely allocated memory withthe WriteProcessMemory API the DLL and the parameter if specified
  • Create a remote thread to execute the injected DLL with the CreateRemoteThread API

Pseudocode of a ReflectivelyLoadDllRemote command

Example of a ReflectivelyLoadDllRemote command received from an emulated C2

Command 2

The command 2 has multiple sub functionalities as shown in the command table above, according to a subCommandFlag the malware can do 6 different operations as follows:

Sub Command FlagFunction NameFunctionality description
1ExitProcessExit process
2SelfDeleteExitProcessSelf delete and exit process
3SpawnAgent64Spawn 64-bit agent
4SpawnAgent32Spawn 32-bit agent
0x1001ImpersonateTokenImpersonate explorer
0x1002MigrateC2Change C2 config

The following is the structure that is used by the above commands:

struct ImpersonateReplicateStruct
{
  int subCommandFlag;
  int impersonateExplorerToken;
  char padding[16];
  __int16 isParameterSet;
  WCHAR w_parameters[n];
};

ExitProcess

Calls the ExitProcess(0) API to terminate.

Example of an ExitProcess command received from an emulated C2

SelfDeleteExitProcess

The agent gets the PATH of the current process with GetModuleFileNameA and then executes the following command to self-delete: cmd.exe /c del FILEPATH \>\> NUL using CreateProcessA then simply exit the process with ExitProcess(0).

Example of a SelfDeleteExitProcess command received from an emulated C2

SpawnAgent64 and SpawnAgent32

When subcommands 3 or 4 are specified, the malware will spawn another agent on the same machine depending on the subcommand sent by the C2, as shown in the table above.

The malware first retrieves the C2 IP address embedded in it, it will then do an HTTP GET request to download a packed agent in shellcode format, in the sample we analyzed /Agent32.bin URI is for the 32-bit agent, and /Agent64.bin is for 64-bit the agent.

Pseudocode spawning another agent

The malware then creates a suspended svchost.exe process with CreateProcessA API, writes the agent shellcode to the process, sets its instruction pointer to point to the injected shellcode with SetThreadContext , and finally it will resume the thread with ResumeThread to execute the injected payload.

Example of a SpawnAgent32 command received from an emulated C2

ImpersonateToken

This subcommand is specific to process tokens; an attacker can either impersonate the explorer.exe token or create a token from credentials (Domain\Username, Password) sent by the C2 to spawn another instance of the current process.

Pseudocode ImpersonateToken command

It will first check if the current process is a local system account or local service account or network service account by testing whether the given process token is a member of the group with the specified RID ( SECURITY_LOCAL_SYSTEM_RID , SECURITY_LOCAL_SERVICE_RID , SECURITY_NETWORK_SERVICE_RID ) respectively.

Pseudocode check token group membership

Then depending if the operator specified credentials or not, the malware will first call LogonUserW with the Domain\User and password to create a token then it will spawn another instance of the current process with this token.

Pseudocode LogonUserW to create a token

If not, the implant will impersonate the explore.exe process by duplicating its token with DuplicateTokenEx and then spawn the current process with the duplicated token if no credentials are specified.

Example of an ImpersonateToken command received from an emulated C2

MigrateC2

The operator can migrate the implant to another C2 server by specifying the subcommand 0x1001 with the IP address of the new C2.

Pseudocode migrating the implant

Example of a MigrateC2 command received from an emulated C2

Command 3

When command 3 is received the malware will reflectively load a PE file embedded as payload in the C&C request in another process's memory space, the following is an overview of the execution:

  • Determine the type and architecture of the PE file
  • Create a suspended process
  • Allocate a large memory in the suspended process
  • Write a shellcode in the allocated memory that will locate, decrypt and reflectively load the PE file
  • 2-byte XOR encrypt the PE file and append it after the shellcode
  • Set the EIP context of the suspended process to execute the shellcode

The shellcode will then reflectively load the PE file

Pseudocode for Command 3's main logic

The agent first parses the PE file received from the C2 server to determine the type and architecture of the PE file.

Pseudocode determines the PE file architecture

And according to this information, a Windows signed executable will be chosen to inject into.

If the PE file is CUI (Console User Interface), the malware will choose cmd.exe , however, if it is GUI (Graphical User Interface) or a DLL PE file it will choose svchost.exe.

Options for malware to inject into

The malware will then create a suspended process with CreateProcessA API (either cmd.exe or svchost.exe ) and allocate a large amount of memory with VirtualAllocEx in the created process, it will then copy a position independent shellcode stored in the .rdata section to the newly allocated memory that is responsible for locating according to a specific tag the appended PE file, decrypt it and reflectively load it in memory.

Then it appends after the shellcode a 12 bytes structure composed of a tag, the size of the PE file, and a 2-byte XOR key.

It will then 2-byte XOR encrypt the PE file and append it after the structure, the following is an overview of the written data to the allocated memory:

SHELLCODETAGPE SIZE2-byte XOR KEY2-byte XOR encrypted PE file

Pseudocode write shellcode and PE to child process

The agent will then set the thread context with SetThreadContext and point the instruction pointer of the suspended process to the shellcode then it will simply resume the execution with ResumeThread.

The shellcode will first locate the 2-byte XOR encrypted PE file according to the tag value ( 0x80706050 ), it will then 2-byte XOR decrypt it and load it reflectively on the same process memory.

Observed adversary tactics and techniques

Elastic uses the MITRE ATT&CK framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.

Tactics

Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.

Techniques / sub techniques

Techniques and Sub techniques represent how an adversary achieves a tactical goal by performing an action.

Detections

Detection rules

The following detection rule was observed during the analysis of the BUGHATCH sample. This rule is not exclusive to BUGHATCH activity.

YARA rule

Elastic Security has created a YARA rule to identify this activity.

rule Windows_Trojan_BUGHATCH {
    meta:
        author = “Elastic Security”
        creation_date = "2022-05-09"
        last_modified = "2022-06-09"
        license = “Elastic License v2”
        os = "Windows"
        arch = "x86"
        category_type = "Trojan"
        family = "BUGHATCH"
        threat_name = "Windows.Trojan.BUGHATCH"
        reference_sample = "b495456a2239f3ba48e43ef295d6c00066473d6a7991051e1705a48746e8051f"

    strings:
    $a1 = { 8B 45 ?? 33 D2 B9 A7 00 00 00 F7 F1 85 D2 75 ?? B8 01 00 00 00 EB 33 C0 }
    $a2 = { 8B 45 ?? 0F B7 48 04 81 F9 64 86 00 00 75 3B 8B 55 ?? 0F B7 42 16 25 00 20 00 00 ?? ?? B8 06 00 00 00 EB ?? }
    $a3 = { 69 4D 10 FD 43 03 00 81 C1 C3 9E 26 00 89 4D 10 8B 55 FC 8B 45 F8 0F B7 0C 50 8B 55 10 C1 EA 10 81 E2 FF FF 00 00 33 CA 8B 45 FC 8B 55 F8 66 89 0C 42 }
     $c1 = "-windowstyle hidden -executionpolicy bypass -file"
     $c2 = "C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe"
     $c3 = "ReflectiveLoader"
     $c4 = "\\Sysnative\\"
     $c5 = "TEMP%u.CMD"
     $c6 = "TEMP%u.PS1"
     $c7 = "\\TEMP%d.%s"
     $c8 = "NtSetContextThread"
     $c9 = "NtResumeThread"

    condition:
        any of ($a*) or 6 of ($c*)
}