Affected platforms: Microsoft Windows
Impacted parties: Windows Users
Impact: Fully remotely control the compromised computer
Severity level: High
This analysis is a follow-up to the investigation titled ‘Intrusion into Middle East Critical National Infrastructure’ (full report here), led by the FortiGuard Incident Response Team (FGIR), which investigated a long-term cyber intrusion targeting critical national infrastructure (CNI) in the Middle East.
That report revealed that the attacker added several pieces of malware to the system’s Task Scheduler to maintain persistence. In this report, we conduct a detailed analysis of one of the malicious Havoc variant samples.
Havoc is a well-known post-exploitation command and control (C2) backdoor framework, primarily written in C++ and Go. We describe how this Havoc variant is decrypted from a DLL file and then deployed in a newly created “cmd.exe” process, how the Havoc demon communicates with its C2 server, and what malicious actions it can perform on the compromised Windows system.
The remote injector (conhost.exe) is launched by the system Task Scheduler using the following command line.
C:\Windows\System32\drivers\conhost.exe -f conhost.dll -ER --ln --path cmd.exe
Since the release of Windows 7, Windows OS has included conhost.exe (Console Window Host) to handle the command-line interface. The attacker disguised the remote injector as conhost.exe to mislead the victim.
According to our analysis, the remote injector supports multiple features controlled by command-line parameters. The “conhost.dll” passed with the “-f” parameter contains the encrypted Havoc payload, and the “cmd.exe” specified by the “--path” parameter is the target process, into which Havoc will be injected and executed.
Figure 1: Help information for the Remote Injector
When the fake conhost.exe is executed without any parameters, it displays its help information (Figure 1), which explains how to use the remote injector and its available options.
Once the remote injector starts, it creates a “cmd.exe” process, specified by the “-- path” argument, by calling the API CreateProcessA(), as shown in Figure 2.
Figure 2: Remote injector about to create the process “cmd.exe”
The remote injector then decrypts a Havoc payload, referred to as the Havoc agent or demon within the Havoc framework, using a piece of shellcode embedded in the conhost.dll file. Figure 3 shows the decryption function in the remote injector, a portion of the decrypted shellcode, and the decrypted Havoc payload in the memory at the bottom of the debugger. The decryption key and IV are generated from the first 30H bytes of the conhost.dll file.
Figure 3: Memory view of the decrypted conhost.dll payload.
Next, the remote injector calls two APIs, ZwAllocateVirtualMemory() and ZwWriteVirtualMemory(), using the ProcessHandle of the newly created “cmd.exe” process to inject the decrypted shellcode and Havoc executable into the process.
Finally, the remote injector creates a remote thread by calling the ZwCreateThreadEx() API. The ProcessHandle parameter is again set to the newly created “cmd.exe” process, and its lpStartAddress parameter points to the address of the injected shellcode within the process.
The purpose of shellcode is to deploy the subsequent Havoc payload (a DLL file) into the “cmd.exe” process and execute it.
Havoc Framework is a typical RAT (Remote Access Trojan) and an open-source project available on GitHub. The Framework is written in multiple languages, including Golang, C, C++, Qt, Python, and Assembly (ASM). It was developed by C5pider and released in 2022.
In Havoc, the Command and Control (C2) server is referred to as teamserver, while its UI dashboard, used to interact with teamserver, is referred to as the client. The Havoc agent, also known as a demon, runs on the compromised device to receive commands from the C2 server, allowing it to control the system. Figure 4 shows the Havoc client on the server side, where a demon is actively being connected and controlled.
Figure 4: C2 server’s dashboard UI – the client
Havoc supports HTTP, HTTPS, and SMB protocols to transport commands and results between the C2 server and the compromised devices.
In this sample, the C2 server is hardcoded as “apps[.]gist[.]githubapp[.]net.” Unfortunately, the server was unavailable during our analysis. To proceed with our analysis, we set up a simulated Command and Control (C2) server. We modified the protocol from HTTPS to HTTP, allowing the traffic to be captured and analyzed in plaintext without TLS encryption.
As long as the Havoc demon is running on the compromised device, it collects metadata about both the compromised Windows system and the Havoc process itself. This metadata is encrypted using the AES algorithm and then sent to the C2 server to register the compromised system on the C2 server. As shown at the bottom of Figure 5, the collected metadata includes various system and process details.
Figure 5: Encrypt the metadata with AES.
The metadata (size:0xBA) contains, but is not limited to, the following information: the agent ID (0x67C54600), Demon ID (0x0F), Host name, User name, Doman, IP address, process name (“C:\Windows\SYSTEM32\cmd.exe”), process ID (0x1ED4), parent PID (0x2444), the process’s load base address, OS version information, OS architecture, and more.
Figure 6: Demon-init packet.
Figure 6 shows a demon-init packet containing the AES-encrypted metadata mentioned earlier. This packet must be sent as the first packet to the C2 server to register the victim’s system. We have divided the packet into color-coded sections and broken it down in the table below to explain the contents of each part.
Offset |
Comments |
+00h |
The data size, 0xFA. |
+04h |
Magic value, 0xDEADBEEF. |
+08h |
Agent ID, 0x67C54600. |
+0Ch |
Command ID, 0x63. DEMON_INIT. |
+10h |
Request ID, 0x0. |
+14h |
AES Key, 20h bytes. |
+34h |
AES IV, 10h bytes. |
+44h |
The AES encrypted metadata. |
The demon-init packet is sent as the body of an HTTP POST request. When the C2 server receives the packet, it verifies the magic value and decrypts the metadata using the AES key and AES IV included in the packet to complete the registration process. Meanwhile, the demon appears on the client dashboard with the compromised system metadata, just as illustrated in Figure 4.
Figure 7 shows a Wireshark screenshot capturing a demon-init packet sent via an HTTP Post request.
Figure 7: Display the demon-init packet.
Havoc defines a wide range of control commands to control the compromised system, listed below:
COMMAND_GET_JOB(0x1)
COMMAND_INLINEEXECUTE_EXCEPTION(0x1)
COMMAND_INLINEEXECUTE_SYMBOL_NOT_FOUND(0x2)
COMMAND_INLINEEXECUTE_RAN_OK(0x3)
COMMAND_INLINEEXECUTE_COULD_NO_RUN(0x4)
COMMAND_INLINEEXECUTE(0x14)
COMMAND_NOJOB(0xA)
COMMAND_SLEEP(0xB)
COMMAND_PROC_LIST(0xC)
COMMAND_FS(0xF)
COMMAND_JOB(0x15)
COMMAND_INJECT_DLL(0x16)
COMMAND_INJECT_SHELLCODE(0x18)
COMMAND_SPAWNDLL(0x1A)
COMMAND_PROC_PPIDSPOOF(0x1B)
CALLBACK_OUTPUT(0x0)
CALLBACK_FILE(0x2)
CALLBACK_FILE_WRITE(0x8)
CALLBACK_FILE_CLOSE(0x9)
CALLBACK_ERROR(0xD)
CALLBACK_OUTPUT_OEM(0x1E)
CALLBACK_OUTPUT_UTF8(0x20)
DEMON_INIT(0x63)
DEMON_INFO(0x59)
BEACON_OUTPUT(0x5E)
COMMAND_TOKEN(0x28)
COMMAND_OUTPUT(0x5A)
COMMAND_ERROR(0x5B)
COMMAND_EXIT(0x5C)
COMMAND_KILL_DATE(0x5D)
COMMAND_CHECKIN(0x64)
COMMAND_EXCEPTION(0x98)
COMMAND_SYMBOL_NOT_FOUND(0x99)
COMMAND_NET(0x834)
COMMAND_CONFIG(0x9C4)
COMMAND_SCREENSHOT(0x9CE)
COMMAND_PIVOT(0x9D8)
COMMAND_TRANSFER(0x9E2)
COMMAND_SOCKET(0x9EC)
COMMAND_KERBEROS(0x9F6)
COMMAND_MEM_FILE(0xA00)
COMMAND_PACKAGE_DROPPED(0xA0A)
COMMAND_PROC(0x1010)
COMMAND_PS_IMPORT(0x1011)
COMMAND_ASSEMBLY_INLINE_EXECUTE(0x2001)
COMMAND_ASSEMBLY_LIST_VERSIONS(0x2003)
Most of the commands come with sub-commands to extend their capabilities.
For example, the COMMAND_FS(0xF) has 10 sub-commands:
DEMON_COMMAND_FS_DIR(1)
DEMON_COMMAND_FS_DOWNLOAD(2)
DEMON_COMMAND_FS_UPLOAD(3)
DEMON_COMMAND_FS_CD(4)
DEMON_COMMAND_FS_REMOVE(5)
DEMON_COMMAND_FS_MKDIR(6)
DEMON_COMMAND_FS_COPY(7)
DEMON_COMMAND_FS_MOVE(8)
DEMON_COMMAND_FS_GET_PWD(9)
DEMON_COMMAND_FS_CAT(10)
Suppose the attacker needs to create a folder named “test” on the compromised system. The packet shown in Figure 8 can be observed:
Figure 8: Packet with DEMON_COMMAND_FS and DEMON_COMMAND_FS_MKDIR
0x0F is the command ID for DEMON_COMMAND_FS, and 0xA18D02EC is the request ID. The following value indicates the size of the encrypted sub-command and parameters. Within the decrypted sub-command data, 0x06 is the ID for DEMON_COMMAND_FS_MKDIR, and the subsequent data represents the folder name (size + data).
In addition to the command and subcommand approach, Havoc also supports the in-memory execution of object files, commonly known as BOFs (Beacon Object Files). The C2 server sends a compiled Object File containing a piece of binary shellcode, which is executed directly in the memory of the demon process on the compromised system.
Using BOFs allows Havoc to extend its functionality without replacing or updating the demon itself.
Figure 8 shows a recently decrypted object file carried in a packet for the COMMAND_MEM_FILE command (command ID: 0xA00). The packet was sent when we typed “enum_filter_driver” in the client UI.
Figure 9: View of a decrypted Object File
In addition to the control commands, sub-commands, and BOFs introduced earlier, Havoc also implements a wide range of features. These features are categorized into two types: command and module, with each module containing multiple commands.
Once we type a command name or a module name followed by its command name, the C2 server generates a command packet with the corresponding command ID and sub-command ID or BOF, which is then sent to the demon to control the compromised system.
All the features of Havoc are listed in the table below.
Command Name |
Type |
Description |
adcs_enum |
Command |
Enumerate CAs and templates in the AD. |
adcs_request |
Command |
Request an enrollment certificate. |
adduser |
Command |
Add a new user to a machine. |
addusertogroup |
Command |
Add a user to the specified group. |
arp |
Command |
Lists out ARP table. |
bofbelt |
Command |
A Seatbelt port using BOFs (Beacon Object Files). |
cacls |
Command |
List user permissions for the specified file. |
cat |
Command |
Display content of the specified file. |
cd |
Command |
Change to a specified directory. |
checkin |
Command |
Request a checkin request. |
config |
Module |
Configure the behavior of the demon session. |
cp |
Command |
Copy file. |
dcenum |
Command |
Enumerate domain information. |
dir |
Command |
List directory. |
dll |
Module |
DLL spawn and injection modules. |
domainenum |
Command |
Lists users accounts in the current domain. |
dotnet |
Module |
Execute and manage dotnet assemblies. |
download |
Command |
Downloads a specified file. |
driversigs |
Command |
Checks drivers for known EDR vendor names. |
enableuser |
Command |
Activates the specified user account. |
enum_filter_driver |
Command |
Enumerate filter drivers. |
enumlocalsessions |
Command |
Enumerate currently attached user sessions. |
env |
Command |
Print environment variables. |
exit |
Command |
Cleanup and exit. |
get-asrep |
Command |
Enumerate a given domain for user accounts with ASREP. |
get-delegation |
Command |
Enumerate a given domain for different types of abusable Kerberos Delegation settings. |
get-netsession |
Command |
Enumerate sessions on the remote device. |
get-spns |
Command |
Enumerate a given domain for user accounts with SPNs. |
get_password_policy |
Command |
Gets a server's configured password policy. |
help |
Command |
Shows help message of specified command. |
inline-execute |
Command |
Executes an object file. |
ipconfig |
Command |
Display network configuration settings . |
job |
Module |
Job manager. |
jump-exec |
Module |
Lateral movement module. |
kerberoast |
Command |
Perform Kerberoasting against specified SPN. |
klist |
Command |
List Kerberos tickets. |
ldapsearch |
Command |
Execute LDAP searches. |
listdns |
Command |
Obtains DNS cache entries. |
locale |
Command |
Prints the locale information of the server. |
luid |
Command |
Get current logon ID. |
mkdir |
Command |
Create new directory. |
mv |
Command |
Move a file or folder. |
nanodump |
Command |
Dump the LSASS process. |
nanodump_ppl_dump |
Command |
Bypass PPL and dump LSASS. |
nanodump_ppl_medic |
Command |
Bypass PPL and dump LSASS. |
nanodump_ssp |
Command |
Load a Security Support Provider (SSP) into LSASS. |
net |
Module |
Network and host enumeration module. |
netGroupList |
Command |
List groups. |
netGroupListMembers |
Command |
List group members. |
netLclGrpLstMmbrs |
Command |
List local group members. |
netLocalGroupList |
Command |
List local group. |
netshares |
Command |
List shared folders. |
netsharesAdmin |
Command |
List details of the shared folders. |
netstat |
Command |
List listening and connected network connections. |
netuptime |
Command |
Obtains the boot time information. |
netuser |
Command |
Get information about specific user. |
netview |
Command |
Lists the workstations and servers. |
noconsolation |
Command |
Execute a PE inline. |
nslookup |
Command |
Make a DNS query on the compromised device . |
pivot |
Module |
Pivoting module. |
powerpick |
Command |
Executes unmanaged powershell commands. |
powershell |
Command |
Executes powershell.exe commands. |
proc |
Module |
Process enumeration and management. |
ptt |
Command |
Import Kerberos ticket into a logon session. |
purge |
Command |
Purge a Kerberos ticket. |
pwd |
Command |
Get current directory. |
quser |
Command |
Simple implementation of quser.exe. |
reg_delete |
Command |
Deletes the registry key or value. |
reg_query |
Command |
Query a registry value or enumerate a single key. |
reg_query_recursive |
Command |
Recursively enumerate a key. |
reg_save |
Command |
Saves the registry path and all subkeys to a file. |
reg_set |
Command |
Creates or sets the specified key or value. |
remove |
Command |
Remove file or directory. |
resources |
Command |
List information of memory and disk drive. |
routeprint |
Command |
Prints route information. |
rportfwd |
Module |
Reverse port forwarding. |
samdump |
Command |
Dumps the SAM, SECURITY and SYSTEM registries to files. |
sc_create |
Command |
Creates a service on the target device. |
sc_delete |
Command |
Deletes the specified service. |
sc_description |
Command |
Sets the description of an existing service. |
sc_enum |
Command |
Enumerate services. |
sc_qc |
Command |
Queries a service with name in BOF (Beacon Object Files). |
sc_qdescription |
Command |
Queries a services description |
sc_qfailure |
Command |
Query a service for failure conditions. |
sc_qtriggerinfo |
Command |
Query a service for trigger conditions. |
sc_query |
Command |
Query services in BOF (Beacon Object Files). |
sc_start |
Command |
Starts a specified service. |
sc_stop |
Command |
Stops a specified service. |
schtasksenum |
Command |
Enumerate scheduled tasks. |
schtasksquery |
Command |
Query the given task in scheduled tasks. |
screenshot |
Command |
Takes a screenshot. |
sessions |
Command |
Get logon sessions. |
setuserpass |
Command |
Sets the password to a specified user account. |
shell |
Command |
Executes Windows commands in cmd.exe. |
shellcode |
Module |
Shellcode injection techniques. |
sleep |
Command |
Sets the delay to sleep. |
socks |
Module |
Manages socks5 proxy. |
task |
Module |
Task manager. |
tasklist |
Command |
List running processes on the remote device. |
tgtdeleg |
Command |
Retrieve a usable TGT for the current user. |
token |
Module |
Token manipulation and impersonation. |
transfer |
Command |
Download transfer module. |
upload |
Command |
Uploads a file. |
uptime |
Command |
Lists system boot time. |
userenum |
Command |
Lists user accounts. |
whoami |
Command |
Get the login user information in BOF (Beacon Object Files). |
windowlist |
Command |
List visible windows, like program windows’ title. |
wmi_query |
Command |
Run a wmi query and display results in CSV format. |
Here is how a control command is packaged into a packet. Acting as an attacker, we entered the “pwd” command inside the Havoc C2 server and sent it to the demon, which then displayed the command result on our screen.
Figure 10: Heartbeat and control command packet.
Typically, once the demon has connected to the C2 server, it sends a heartbeat packet to the C2 server approximately every 3 seconds (a random number) to notify the C2 server that the demon is still alive. The heartbeat packet structure is shown in Figure 10.
Figure 10 also shows the command packet for the “pwd” command generated by the C2 server at the bottom. The control command data is sent within the response to the heartbeat packet.
The command packet begins with a command ID (0x0F for COMMAND_FS), followed by a request ID (0xc5312b04), the size of the encrypted data, and then the encrypted data itself. The encrypted data, “51 7c 66 9c,” decrypts to “00 00 00 09,” which is the sub-command ID 0x9 (for DEMON_COMMAND_FS_GET_PWD) under the command ID 0x0F (COMMAND_FS).
This analysis provides a detailed examination of a Havoc variant involved in a long-term cyber intrusion targeting critical national infrastructure in the Middle East. It demonstrates how this remote injector leverages a disguised conhost.exe process to deploy the Havoc payload into a newly created cmd.exe process.
The Havoc framework’s modular design, supporting commands, sub-commands, and in-memory execution of Beacon Object Files (BOFs), offers attackers a flexible method to control the remote demon process.
Overall, understanding the packet structures, encryption mechanisms, and command execution workflows will help researchers detect and analyze this sophisticated RAT framework.
Fortinet customers are already protected from this malware with AntiVirus service, FortiGuard’s Anti-Botnet service, FortiGuard’s AntiSPAM service, and FortiGuard’s Web Filtering service as follows:
The FortiGuard’s Anti-Botnet service blocks the DNS requests for the C2 server domain.
The domain to the C2 server is rated as “Malicious Websites” by the FortiGuard Web Filtering service.
FortiGuard Antivirus service detects the remote injector and the encrypted Havoc DLL file with the following AV signatures.
W64/Havoc.d16b!tr
Data/Havoc.e5b0!tr
FortiGuard IPS service detects Havoc traffic with the signature “Backdoor.Havoc.Agent”.
FortiGate, FortiMail, FortiClient, and FortiEDR support the FortiGuard AntiVirus service. The FortiGuard AntiVirus engine is part of each solution. As a result, customers who have these products with up-to-date protections are already protected.
We also suggest that our readers go through the free NSE training: NSE 1 – Information Security Awareness, a module on Internet threats designed to help end-users learn how to identify and protect themselves from phishing attacks.
If you believe this or any other cybersecurity threat has impacted your organization, please contact our Global FortiGuard Incident Response Team.
apps[.]gist[.]githubapp[.]net
[conhost.exe / the remote injector]
22BD09FBAB54963D4B0234585D33571A47A2DF569DBAB8B40988415AB0A3C37B
[conhost.dll / encrypted Havoc sample with shellcode]
9208034AF160357C99B45564FF54570B1510BAF3BC033999AE4281482617FF5B