QakBot reducing its on disk artifacts
Summary
QakBot has been updated with more evasion techniques. QakBot’s configuration is now stored in a registry key instead of a file. The run key for persistence is not permanently present in the registry but only written right before shutdown or reboot, and deleted immediately after QakBot is executed again. QakBot’s executable is also not stored permanently on the file system anymore, but similarly to the run key registry entry, dropped onto the file system before reboots and deleted afterwards. This way security software can only detect QakBot artifacts on disk, right before system shutdown, and shortly after system boot. However, at that time security software itself is shutting down and booting up, hence may not detect QakBot’s new persistence method.
Other changes include dynamic just-in-time decoding and destruction of strings at runtime. So any string used in the malware is only decoded at runtime into memory only and destroyed right afterwards.
The delivery method for the observed QakBot campaigns identified via the regular expression pattern of abc[0-9]+
is still XLM macro documents as reported previously.
Background
QakBot (also known as QBot, QuakBot, Pinkslipbot) has been around since 2008. It is distributed via Emotet, i.e., Emotet will download QakBot onto victims that are already infected with Emotet but it is also distributed directly via email. To this end, it uses email conversation thread hijacking in its campaigns1, i.e., it will reply to emails that it finds in its victim’s mailboxes. QakBot is known to escalate intrusions by downloading the ProLock ransomware2 or lately the Egregor ransomware.
The observed QakBot campaigns identified by campaign ID abc
use XLM macro documents for infection. We previously reported on their low detection.3
An overview of the current chain of infection used by the QakBot campaign with identifiers following the regular expression pattern of abc[0-9]+
can be seen in the following flow graph.
Technical Analysis
In the following analysis we briefly analyze the infection chain of QakBot after being downloaded and launched by the malicious Excel document.
Evasion
QakBot uses various evasion techniques to avoid detection by anti-virus software.
PE header manipulation
We observed some QakBot DLLs with a manipulated PE header. The message text This program cannot be run in DOS mode.
has been altered.
This seems like an attempt to circumvent some static detection rules matching for this message in the legacy MS-DOS stub of PE binaries.
Code signing
First, the initial downloaded and executed DLL is signed with a (at the time the analyzed sample was distributed) valid code signing certificate.
$ chktrust 904400.jpg
Mono CheckTrust - version 6.8.0.123
Verify if an PE executable has a valid Authenticode(tm) signature
Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed.
WARNING! 904400.jpg is not timestamped!
SUCCESS: 904400.jpg signature is valid
and can be traced back to a trusted root!
The signing CA is Sectigo and the organization is given as Aqua Direct s.r.o., which is an existing company.
$ osslsigncode verify 904400.jpg
Current PE checksum : 00091021
Calculated PE checksum: 00091021
Message digest algorithm : SHA1
Current message digest : 632DCB214EE9FB08441C640D240F672A7ABA6EB1
Calculated message digest : 632DCB214EE9FB08441C640D240F672A7ABA6EB1
Signature verification: ok
Number of signers: 1
Signer #0:
Subject: /C=CZ/postalCode=619 00/L=Brno/street=\xC5\xBDelezn\xC3\xA1 646/8/O=Aqua Direct s.r.o./CN=Aqua Direct s.r.o.
Issuer : /C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Code Signing CA
Number of certificates: 4
Cert #0:
Subject: /C=CZ/postalCode=619 00/L=Brno/street=\xC5\xBDelezn\xC3\xA1 646/8/O=Aqua Direct s.r.o./CN=Aqua Direct s.r.o.
Issuer : /C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Code Signing CA
Cert #1:
Subject: /C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=AAA Certificate Services
Issuer : /C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=AAA Certificate Services
Cert #2:
Subject: /C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
Issuer : /C=GB/ST=Greater Manchester/L=Salford/O=Comodo CA Limited/CN=AAA Certificate Services
Cert #3:
Subject: /C=GB/ST=Greater Manchester/L=Salford/O=Sectigo Limited/CN=Sectigo RSA Code Signing CA
Issuer : /C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
Succeeded
It is unknown whether the certificate was obtained from Sectigo by giving false information, the certificate was stolen from Aqua Direct s.r.o., or whether the certificate was obtained from Sectigo by giving stolen information from Aqua Direct s.r.o..
QakBot is known to steal victim emails and use them in future malspam campaigns. So it is likely that they also use stolen victim data to obtain code signing certificates. However, the actors behind QakBot can also buy the code signing certificate from a (malicious) third party.
Strings only decoded at runtime
QakBot will decode its strings only at runtime into memory. After usage the decoded strings are removed from memory again.
Processes
QakBot uses CreateToolhelp32Snapshot
and Process32{First,Next}W
to enumerate the running processes.
It checks for the following processes:
CcSvcHst.exe
avgcsrvx.exe
avgsvcx.exe
avgcsrva.exe
MsMpEng.exe
mcshield.exe
avp.exe
kavtray.exe
egui.exe
ekrn.exe
bdagent.exe
vsserv.exe
vsservppl.exe
AvastSvc.exe
coreServiceShell.exe
PccNTMon.exe
NTRTScan.exe
SAVAdminService.exe
SavService.exe
fshoster32.exe
WRSA.exe
vkise.exe
iserv.exe
cmdagent.exe
ByteFence.exe
MBAMService.exe
mbamgui.exe
fmon.exe
QakBot will set specific bits in a bit mask for each running process it finds. Depending on the resulting bit mask the further infection path is altered, e.g., if avp.exe
has been encountered. QakBot will later inject its code into mobsync.exe
instead of explorer.exe
. Because the searched process names are related to security solutions, we believe that this way QakBot tailors its execution path to evade detection by specific vendors.
Then in another loop, again using CreateToolhelp32Snapshot
and Process32{First,Next}W
, it checks for:
srvpost.exe
frida-winjector-helper-32.exe
frida-winjector-helper-64.exe
If it detects any of those processes the execution flow will run into a loop continuously calling WaitForSingleObject(handle, 0x1fa)
on a handle
previously generated via CreateEvent(NULL, FALSE, FALSE, ...)
, i.e., it runs in an infinite loop.
Device drivers
Next, QakBot uses SetupDiGetDeviceRegistryPropertyA
(querying properties SPDRP_DEVICEDESC
and SPDRP_SERVICE
) to check for device drivers containing the following strings:
VBoxVideo
Red Hat VirtIO
QEMU
A3E64E55_pr
We believe the search for A3E64E55_pr
is used to detect an artifact of the ANY.RUN sandbox.3 Alternatively, but unlikely, it could be used to detect an artifact of the long ago defunct xCore Complex Protection AV solution using a similar driver with the name A3E64E55_pr.sys
.
If it detects any of those device drivers the execution flow will run into the same infinite loop continuously calling WaitForSingleObject(handle, 0x1fa)
on a handle
previously generated via CreateEvent(NULL, FALSE, FALSE, ...)
, as previously mentioned.
Process injection
QakBot starts C:\Windows\SysWOW64\explorer.exe
in suspended state and injects a DLL into it using CreateProcessInternalW
, NtMapViewOfSection
, NtAllocateVirtualMemory
, WriteProcessMemory
, memcpy
, NtProtectVirtualMemory
and NtResumeThread
.
The injected DLL can be extracted via PE-sieve4 or other tools for simplyfied further analysis.
Depending on whether the previous process enumeration yielded results on the list, QakBot will inject into mobsync.exe
(e.g., in case a avp.exe
process is found running) instead of explorer.exe
. But for simplicity we will only follow the explorer.exe
process injection path we observed in our analysis environment.
C2 communication
After avoiding detection, the injected QakBot code within explorer.exe
will start communicating with the C2 servers.
Like in previous versions of QakBot the C2 IP list is stored RC4 encrypted in resource section 311
. The first 20 bytes of the section contains the RC4 key with which the rest of the section is decrypted. The first 20 bytes of the decrypted data will contain the SHA1 sum calculated over the rest of the decrypted data. It is used as a verification for correct decryption. Unlike in previous version, the C2 list is now stored in binary form and not as ASCII text anymore.
For details on how to extract the C2 list and QakBot’s configuration see the Python3 script in the appendix. The input to the script is the path to the DLL that QakBot injected into explorer.exe
, which we previously extracted via PE-sieve5.
The configuration is stored using the same RC4 encryption scheme in resource section 308
. In it we can see the bot and/or campaign ID abc103
that is associated with the analyzed sample. It is still stored in plain ACSII text. For each campaign the number is increased by one. This allows the operators behind QakBot to keep track to which campaign each victim connecting to their C2 server belongs to. Another currently observed identifier is tr02
. This identifier, however, stayed the same over multiple malspam campaigns.
Via the C2 connection the operators behind QakBot can remote control the malware and deploy additional malicious modules.
QakBot will not store its configuration and C2 list on disk anymore. It will use the registry for storage.
Wiping
The previous QakBot version used to overwrite its initial executable with a copy of cmd.exe
. This version will overwrite the portion of the initially downloaded DLL after the PE header with zeros.
Here is the entropy of the QakBot DLL as downloaded.
The zeroing of data after the header can be clearly seen when comparing the previous plot against a plot of the DLL file after wiping.
Persistence
The persistence mechanism of QakBot has also changed. While it still uses a run key registry entry under HKCU\Software\Microsoft\Windows\CurrentVersion\Run
, this key is only set right before the system is shutdown, rebooted or put to sleep. The corresponding DLL is also only dropped to disk right before shutdown, rebooted or sleep.
After the system boots up again, QakBot is started via the run key. The execution tree also starts via regsvr32.exe -s ...
like the initial execution from Excel. QakBot follows the same steps as previously outlined resulting in process injection into explorer.exe
.
QakBot will then delete the run key registry entry and delete the DLL it dropped to disk prior to the reboot.
This way QakBot’s persistence can not be detected at runtime.
Conclusion and Countermeasures
From our analysis we can conclude that QakBot is trying to avoid persistent file artifacts. In previous version the configuration and QakBot executable were permanently stored on disk. This made it easy for security tools to detect them. The new version tries to avoid permanently leaving its artifacts on disk. While QakBot is not going fully fileless, it new tactics will sure lower its detection.
But even though QakBot has changed, the delivery mechanism behind the QakBot “abc[A-Z]+
” campaign did not. Hence, an infection by this threat actor can be successfully prevented by blocking the initial emails.
Hornetsecurity’s Spam Filter and Malware Protection, with the highest detection rates on the market, already detects and blocks the outlined threat. Hornetsecurity’s Advanced Threat Protection extends this protection by also detecting yet unknown threats.
References
- 1 https://www.hornetsecurity.com/en/security-information/email-conversation-thread-hijacking/
- 2 https://www.hornetsecurity.com/en/threat-research/qakbot-distributed-by-xlsb-files/
- 3 https://app.any.run/tasks/2d4eb3c8-6670-4c25-a96b-0cd46c3b6d7c/
- 4 https://github.com/hasherezade/pe-sieve
Indicators of Compromise (IOCs)
Hashes
The hashes of the analyzed QakBot samples are:
MD5 | Filename | Description |
---|---|---|
6bc0584f6cbb74714add1718b0322655 | 904400.jpg | QakBot DLL as downloaded by XLM macro |
e23bc27212f61520cfb130185d74cfb1 | 26e0000.dll | Extracted QakBot DLL |
MITRE ATT&CK
The tactics and techniques used by QakBot as defined by the MITRE ATT&CK framework are as follows:
Tactic | Technique |
---|---|
TA0001 – Initial Access | T1566.001 – Phishing: Spearphishing Attachment |
TA0001 – Initial Access | T1566.002 – Phishing: Spearphishing Link |
TA0002 – Execution | T1027 – Obfuscated Files or Information |
TA0002 – Execution | T1204.002 – User Execution: Malicious File |
TA0003 – Persistence | T1547.001 – Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder |
TA0004 – Privilege Escalation | T1053.005 – Scheduled Task/Job: Scheduled Task |
TA0005 – Defense Evasion | T1027.002 – Obfuscated Files or Information: Software Packing |
TA0005 – Defense Evasion | T1055 – Process Injection |
TA0005 – Defense Evasion | T1055.012 – Process Injection: Process Hollowing |
TA0005 – Defense Evasion | T1070 – Indicator Removal on Host |
TA0005 – Defense Evasion | T1497.001 – Virtualization/Sandbox Evasion: System Checks |
TA0006 – Credential Access | T1003 – OS Credential Dumping |
TA0006 – Credential Access | T1110.001 – Brute Force: Password Guessing |
TA0006 – Credential Access | T1555.003 – Credentials from Password Stores: Credentials from Web Browsers |
TA0011 – Command and Control | T1071.001 – Application Layer Protocol: Web Protocols |
TA0011 – Command and Control | T1090 – Proxy |
TA0011 – Command and Control | T1090.002 – Proxy: External Proxy |
Appendix
Qakbot configuration extraction Python3 script
import sys
import pefile
from arc4 import ARC4
pe = pefile.PE(sys.argv[1])
c2list = []
for entry in pe.DIRECTORY_ENTRY_RESOURCE.entries:
for e in entry.directory.entries:
n = e.name.string.decode()
data = pe.get_data(e.directory.entries[0].data.struct.OffsetToData, e.directory.entries[0].data.struct.Size)
data = ARC4(data[:20]).decrypt(data[20:])[20:]
if n == '311':
for i in range(1,len(data),7):
c2 = list(data[i:i+6])
c2list.append("%d.%d.%d.%d:%d" % (c2[0],c2[1],c2[2],c2[3],(c2[4]<<8)+c2[5]))
elif n == '308':
config = data.decode().split()
print("# QakBot Config\n\n```\n" + "\n".join(config) + "\n```\n")
print("# QakBot C2\n\n```\n" + "\n".join(c2list) + "\n```\n")