1/16 Security Lab December 15, 2020 QakBot reducing its on disk artifacts hornetsecurity.com/en/threat-research/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 campaigns , 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 ransomware 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. 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. 1 2 3 https://www.hornetsecurity.com/en/threat-research/qakbot-reducing-its-on-disk-artifacts/ 2/16 Technical Analysis In the following analysis we briefly analyze the infection chain of QakBot after being downloaded and launched by the malicious Excel document. https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_chain_en.png 3/16 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. https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_process_injection_tree.png https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_pe_header_manipulation.png 4/16 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. 5/16 $ 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. 6/16 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 https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_list_processes_api_log.png 7/16 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. 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. 4 8/16 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 . 9/16 https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_process_injection_api_log.png CreateProcessinternalW ApplicationName: CommandLine: C:\Windows\expLorer.exe CreationFlags: CREATE_SUSPENDED Processid: 1516 Threadid: 2508 ProcessHandle: 0x0000019c ThreadHandle: 6xeooee198 StackPivoted: no NtCreateSection SectionHandle: 6x000001a4 DesiredAccess: SECTION MAP_READ|SECTION_MAP_WRITE|SECTION MAP_EXECUTE Obj FileHandle: @x@o0ee000 tAttributes: NtMapViewOfSection SectionHandle: 6x000001a4 ProcessHandle: Oxf ftttttt BaseAdoress: 0x004a0000 SectionOffset: 6x99e00000 ViewSize: 0x9e020000 Win32Protect: PAGE_EXECUTE_READWRITE StackPivoted: no NtMapViewOfSection SectionHandle: @x000001a4 ProcessHandle: 0x0000019c BaseAdoress: 0x80060000 SectionOffset: 6x99e00000 ize: 8x00020080 Win32Protect: PAGE _EXECUTE_READWRITE StackPivoted: no NtAllocateVirtualMemory ProcessHandle: 0x0000019c BaseAdoress: Oxeans80000 Regionsize: 0x00002000 Protection: PAGE_READWRITE StackPivoted: no WriteProcessMemory ProcessHandle: 0x@000019c BaseAdoress: 0x00080000 Buffer: \x9c\xO0\ x60) x00\ x06) x00)x00\ x00 x01) x00) x00\xO0\,xb1\x1d\ x04 x00\x02\x00\x08\x00Service Pack 1\x60',x60', x05, x004x00) x00, xO0', x005 x80) x00) x005 x00) x00 x00) x00) x00' x00’, x60', x00) x00) x00) x005 x00\x00 \xO0)\x00', x00) x00), x00) x00) x00), x00), x00), x00 x00) x00) x00) x00\x00' x00) xO0', x005 x00) x00) x00\x005 x00) x00 \xO0'x005,x005 x005, x00 x00) x00' x00, x00', x005 x00) x00) x00 x00 x00' x00) x005, x005 x00', x00) x00\x00' x00 x00 \x60' x05, x06 x05, x00), x00) x00), xO0\,x00', x05 x00) x005,x005,x005x80', x60) x05, xO65 x00', x00), x00) x00', x80), x08 \x80) x08), x00), x00), x00' x00) x00) x80) x00 x005 x00) x00'xO0' x005 x80 xO0\ x05 x00) x01) x00) x00) x00 x00 x01 \xO1\xle', x00) x00) x00' x00) x01) x00, x00), x90) x00) x00), x00\, x00 xO0'xG0\xO0\ x00) x8e\x19\x89\xa7bdvmxiiz \x00)\x00), x00) x00, x004x00\x00',xO04 x00 x00 x00) xO0\ x00) x00 x80) xO0\ x00}, x00 x00, x00 xO0\ x00) x00) xOOb \xOOd\xOOv\xOOm) xOOx' x01), x001\x00Z\, x00), x00) xO0\xO0\ xO0\ xO0', x06), x00)x00) x00), x00 xO0\ x00, x06), x00, x00, x00 \xO0'/x00', x00 x00), x00) x00) x00' x00) x00), x00), x00) x00) x005 x00\x00' x00 BuFFerLength: 0x@000lac4 StackPivoted: no memcpy Z\) xOO'x03' ? xOO'x FEN XE F x00) x00) xb8\ x005 xO05 x 804x00\x08 .x80\ x00, 4 ,x00\,x00',xO0\ x00) x00\x00 O\x00\x y y 1f\xba\x0 run in DOS mode. Q ch CCT \XTT YK Rich\xdcjn 0\x00 @x100 NtUnmapViewOfSection ProcessHandle: Oxf fftt fff BaseAddress: 0x004a0000 Regionsize: 0x00020000 NtResumeThread ThreadHandle: 8x80000198 9/16 10/16 The injected DLL can be extracted via PE-sieve 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. 5 https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_process_injection_api_log.png https://github.com/hasherezade/pe-sieve https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_pe_sieve.png https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_c2_procmon.png 11/16 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-sieve . 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 5 https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_c2_storage.png https://github.com/hasherezade/pe-sieve https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_qakfixtractor.png 12/16 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. https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_config_reg.png https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_ent_before.png 13/16 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. https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_ent_after.png https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_reboot_tree.png 14/16 This way QakBot’s persistence can not be detected at runtime. Egregor While we have previously reported on QakBot deliverying the ProLock ransomware, latests reports indicated that QakBot is now used to deliver the Egregor ransomware. We previously reported on the Egregor ransomware as part of an article on ransomware leaksites in which we explain the practice of ransomware operators stealing their victims data before encrypting it to extort them not only with decryption but also public release of the stolen data. 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: 2 6 https://www.hornetsecurity.com/wp-content/uploads/2020/12/20201215T1337_reboot_cleanup.png https://www.hornetsecurity.com/en/security-information/qakbot-malspam-leading-to-prolock/ https://www.hornetsecurity.com/en/security-informationen-en/leakware-ransomware-hybrid-attacks/ https://www.hornetsecurity.com/en/services/spam-filter/ https://www.hornetsecurity.com/en/services/advanced-threat-protection/ 15/16 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 16/16 Tactic Technique 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")