Header Monthly Email Threat Review

QakBot reducing its on disk artifacts

Written by Security Lab / 15.12.2020 /
Home » Blog » 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 CreateProcessInternalWNtMapViewOfSectionNtAllocateVirtualMemoryWriteProcessMemorymemcpyNtProtectVirtualMemory 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

Indicators of Compromise (IOCs)

Hashes

The hashes of the analyzed QakBot samples are:

MD5FilenameDescription
6bc0584f6cbb74714add1718b0322655904400.jpgQakBot DLL as downloaded by XLM macro
e23bc27212f61520cfb130185d74cfb126e0000.dllExtracted QakBot DLL

MITRE ATT&CK

The tactics and techniques used by QakBot as defined by the MITRE ATT&CK framework are as follows:

TacticTechnique
TA0001 – Initial AccessT1566.001 – Phishing: Spearphishing Attachment
TA0001 – Initial AccessT1566.002 – Phishing: Spearphishing Link
TA0002 – ExecutionT1027 – Obfuscated Files or Information
TA0002 – ExecutionT1204.002 – User Execution: Malicious File
TA0003 – PersistenceT1547.001 – Boot or Logon Autostart Execution: Registry Run Keys / Startup Folder
TA0004 – Privilege EscalationT1053.005 – Scheduled Task/Job: Scheduled Task
TA0005 – Defense EvasionT1027.002 – Obfuscated Files or Information: Software Packing
TA0005 – Defense EvasionT1055 – Process Injection
TA0005 – Defense EvasionT1055.012 – Process Injection: Process Hollowing
TA0005 – Defense EvasionT1070 – Indicator Removal on Host
TA0005 – Defense EvasionT1497.001 – Virtualization/Sandbox Evasion: System Checks
TA0006 – Credential AccessT1003 – OS Credential Dumping
TA0006 – Credential AccessT1110.001 – Brute Force: Password Guessing
TA0006 – Credential AccessT1555.003 – Credentials from Password Stores: Credentials from Web Browsers
TA0011 – Command and ControlT1071.001 – Application Layer Protocol: Web Protocols
TA0011 – Command and ControlT1090 – Proxy
TA0011 – Command and ControlT1090.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")

You might also be interested in: