QakBOT v5 Deep Malware Analysis

zw01f.github.io · Mohamed Ezat · 1 year ago · tool
quality 7/10 · good
0 net
QakBOT v5 Deep Malware Analysis - ZW01f You are using an outdated browser. Please upgrade your browser to improve your experience. Mohamed Ezzat Malware Analysis and RE Follow Email Twitter LinkedIn GitHub Custom Social Profile Link --> Meet Qakbot QakBot, also recognized as QBot, QuackBot, and Pinkslipbot, has been operational for years, initially as a financial malware targeting governments and businesses for financial fraud by pilfering user credentials and keystrokes. Over time, it has evolved into a malware dropper, spreading sensitive information to other network systems. The threat group has updated its code-base to support 64-bit versions of Windows, enhanced encryption algorithms, and added further obfuscation techniques. With the release of Qakbot version 5.0, the string encryption algorithm underwent a significant change. While strings are still encrypted using a simple XOR key, the key is no longer hard-coded in the data section. Instead, it is encrypted with AES. Technical in Points Qakbot uses API hashing to hide its imports. It uses CRC32 hashing, along with another layer of XORing with a hard-coded key. It’s parsing the loaded DLLs in memory and getting its export tables. As a result, Qakbot can resolve imported APIs and build its IAT. Qakbot comes with encrypted strings inside the .data section , These strings are encrypted using a XOR key and that key is encrypted using AES algorithm . Environment Detection : Qakbot includes checks to detect if it is running in a virtual machine or sandbox environment, commonly used tools for malware analysis. If such conditions are detected, Qakbot may change its behavior or terminate itself to avoid detection. Configuration Extraction: Qakbot comes with AES encrypted configuration.This configuration contains details related to the malicious campaign and the C2 which the malware will communicate with for further commands. C2 Communication : After extracting its C2, Qakbot establishes a connection with its C2 servers to receive commands for downloading, executing additional modules, updating configuration values, and exfiltrating gathered information from the infected system. Qakbot gathers comprehensive information about the compromised host to send to its C2 server and create a unique victim fingerprint. This includes OS version, domain trusts, computer name, username, screen resolution, system time, system uptime, and bot uptime. It mainly relies on Windows Management Instrumentation(WMI) to collect details such as hardware ID, installed languages, and installed programs. Sample Basic Information SHA-256 af6a9b7e7aefeb903c76417ed2b8399b73657440ad5f8b48a25cfe5e97ff868f File type Win DLL Target Machine x64 Creation Time 2024-01-29 13:43:37 UTC First Seen In The Wild 2024-02-07 10:12:50 UTC Figure (1): sample on VirusTotal Anti Analysis API Resolution QakBot uses Windows API Hashing (Dynamic API Resolution) to evade signature-based anti-malware scanners and make static analysis harder. Figure (2): API hashes We can see based on algorithm constants that Qakbot uses the CRC32 hash algorithm, also there is another layer of XORing, and here are the steps in some detail : The DllName is decrypted by XORing with a hard-coded key 0xA235CB91. After decryption, a handle to the DLL is obtained. This handle is then passed to a function that iterates over the DLL’s exported functions. A function resolves the addresses of the exports by iterating over the export table of the module, hashing the name of each export using CRC32, and comparing the result with a hard-coded CRC32 hash to determine if it has found the correct address. Figure (3): API resolving Steps With knowledge of the algorithm name and XOR key, we can use the awesome hashdb plugin from OALabs that performs string hash lookup against a remote database. Figure (4): hashdb result Once the HashDB plugin decrypts all API names, we create structures to store the API lists from each DLL. This simplifies our workflow and make our life easier while analysis . Figure (5): populated IAT Defeating encrypted Strings Qakbot strings are obfuscated, making the analysis more difficult, so the next step is to decrypt them. Decryption routine This version decrypts the strings with an XOR key just like the earlier versions but this XOR key is encrypted using the AES algorithm. It first Calculates a SHA256 hash for aes_key_ref and uses the calculated hash as the AES Key then decrypts the enc_xor_key blob using AES in CBC mode to have the dec_xor_key. Figure (6): The XOR key decryption process The final step is to use the dec_xor_key to decrypt the string array. Figure (7): String decryption process Writing a decryption script We now can write an IDAPython script to decrypt the strings and add comments to the code, making analysis easier here are some notes before the script : The first 16 bytes of the enc_xor_key are used as the AES IV. There are two encrypted string tables used. There are two decryption functions with 4 wraps. The wrap function decrypts the string array and selects the string based on an index [the only argument]. Figure (8): Index pattern used in script #--------------- imports --------------------# import hashlib from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import idautils #------------- helper ------------------------# def hex_to_int ( x ): if type ( x ) == int : return x return ( int ( x [: - 1 ], 16 )) def search_by_index ( table , ind ): return ( table [ ind :]. split ( ' \x00 ' )[ 0 ]) #------------- IDA py ------------------------# def read_data_ida ( address , size ): data = idc . get_bytes ( address , size ) return data def set_comment ( address , text ): idc . set_cmt ( address , text , 0 ) #------------ Decryption ---------------------# def calculate_sha256 ( input_data ): sha256_hash = hashlib . sha256 () sha256_hash . update ( input_data ) hash_hex = sha256_hash . digest () return hash_hex def aes_decrypt ( ciphertext , key , iv ): cipher = AES . new ( key , AES . MODE_CBC , iv ) plaintext = cipher . decrypt ( ciphertext ) unpadded_plaintext = unpad ( plaintext , AES . block_size ) return unpadded_plaintext def xor_decrypt ( data , key ): dec_data = '' for i in range ( len ( data )): dec_data += chr ( data [ i ] ^ key [ i % len ( key )]) return dec_data def full_dec ( enc_str , enc_xor_key , aes_key_init ): aes_key = calculate_sha256 ( aes_key_init ) dec_xor_key = aes_decrypt ( enc_xor_key [ 16 :], aes_key , enc_xor_key [: 16 ]) dec_str = xor_decrypt ( enc_str , dec_xor_key ) return dec_str #----------- Decrypt enc str tbl 1 -------------# enc_str_1 , enc_xor_key_1 , aes_key_init_1 = read_data_ida ( 0x1800297A0 , 0x1836 ) , read_data_ida ( 0x18002AFE0 , 0xA0 ) , read_data_ida ( 0x180029700 , 0x9F ) #read our data . tbl_1 = full_dec ( enc_str_1 , enc_xor_key_1 , aes_key_init_1 ) #----------- Decrypt enc str tbl 2 -------------# enc_str_2 , enc_xor_key_2 , aes_key_init_2 = read_data_ida ( 0x1800282A0 , 0x5AD ) , read_data_ida ( 0x1800281C0 , 0xD0 ) , read_data_ida ( 0x180028150 , 0x63 ) #read our data . tbl_2 = full_dec ( enc_str_2 , enc_xor_key_2 , aes_key_init_2 ) #--> pattern used: mov ecx , immediate_val def do_magic ( table , references ): for ref in references : prev_instruction_address = idc . prev_head ( ref ) if ( idc . print_insn_mnem ( prev_instruction_address ) == 'mov' and idc . print_operand ( prev_instruction_address , 0 ) == 'ecx' and idc . get_operand_type ( prev_instruction_address , 1 ) == 5 ): ind = print_operand ( prev_instruction_address , 1 ) set_comment ( ref , search_by_index ( table , hex_to_int ( ind ))) else : prev_instruction_address = idc . prev_head ( prev_instruction_address ) if ( idc . print_insn_mnem ( prev_instruction_address ) == 'mov' and idc . print_operand ( prev_instruction_address , 0 ) == 'ecx' and idc . get_operand_type ( prev_instruction_address , 1 ) == 5 ): ind = print_operand ( prev_instruction_address , 1 ) set_comment ( ref , search_by_index ( table , hex_to_int ( ind ))) else : prev_instruction_address = idc . prev_head ( prev_instruction_address ) if ( idc . print_insn_mnem ( prev_instruction_address ) == 'mov' and idc . print_operand ( prev_instruction_address , 0 ) == 'ecx' and idc . get_operand_type ( prev_instruction_address , 1 ) == 5 ): ind = print_operand ( prev_instruction_address , 1 ) set_comment ( ref , search_by_index ( table , hex_to_int ( ind ))) else : print ( 'not working' , hex ( ref )) reference_1 = list ( idautils . CodeRefsTo ( idc . get_name_ea_simple ( "wrap_mw_decrpyion_fun_1" ), 0 )) #codeRefs-to need "ea" as arguemt . reference_1 = reference_1 + list ( idautils . CodeRefsTo ( idc . get_name_ea_simple ( 'wrap_2_mw_decrpyion_fun_1' ) , 0 )) reference_2 = list ( idautils . CodeRefsTo ( idc . get_name_ea_simple ( 'wrap_2_mw_decrpyion_fun_2' ), 0 )) reference_2 = reference_2 + list ( idautils . CodeRefsTo ( idc . get_name_ea_simple ( 'wrap_mw_decrpyion_fun_2' ), 0 )) def main (): do_magic ( tbl_1 , reference_1 ) do_magic ( tbl_2 , reference_2 ) if __name__ == '__main__' : main () Figure (9): IDA python script result you can get the full decrypted strings list from here Emulation Check Qakbot uses the GetFileAttributesW function to check for a folder "C:\INTERNAL__empty." If this directory exists, it suggests that the environment might be used for analysis, such as Microsoft Defender emulation or sandbox, and then the process will be terminated. Figure (10): emulation check Checking Processes Qakbot loops through running processes on the system and compares their executable names against well-known static and dynamic malware analysis tools. Figure (11): Qakbot search for tool's process full processes list Expand to see more wireshark.exe filemon.exe procmon.exe idaq64.exe tcpview.exe frida-winjector-helper-32.exe frida-winjector-helper-64.exe tcpdump.exe windump.exe ethereal.exe ettercap.exe rtsniff.exe packetcapture.exe capturenet.exe qak_proxy dumpcap.exe CFF Explorer.exe not_rundll32.exe ProcessHacker.exe loaddll32.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 BehaviorDumper.exe processdumperx64.exe anti-virus.EXE sysinfoX64.exe sctoolswrapper.exe sysinfoX64.exe FakeExplorer.exe apimonitor-x86.exe idaq.exe dumper64.exe user_imitator.exe Velociraptor.exe Anti VM Qakbot exploits Windows Management Instrumentation (WMI), a system management technology used to administer remote systems and provide comprehensive data about the operating system, hardware, and installed software and applications on a computer. It uses WMI queries to gather system information, including details about virtualization. It queries classes such as Win32_ComputerSystem , Win32_Bios , Win32_DiskDrive , or Win32_PhysicalMemory , then check for patterns indicative of virtualized environments. These patterns include known manufacturer or model strings associated with virtualization platforms. Below are the classes and their corresponding checked values : Class Checked Values Win32_ComputerSystem MS_VM_CERT, VMware, Virtual Machine Win32_Bios VRTUAL, VMware, VMW, Xen Win32_DiskDrive VMware, PROD_VIRTUAL_DISK, VIRTUAL-DISK, XENSRC, 20202020 Win32_PhysicalMemory VMware, VMW, QEMU Win32_PnPEntity QEMU, VMware Pointing, VMware Accelerated, VMware SCSI,.. Qakbot also searches for ‘vmnat’, a process initiated by VMware upon startup. ‘vmnat’ manages communication in the Network Address Translation (NAT) set up with the guest machine . Qakbot’s C2 Functionality Malware needs to connect to C2 servers to execute remote commands, update its code, and exfiltrate stolen data. Before doing so, it needs to extract its C2 from an encrypted configuration. Configuration Extraction Qakbot, in this version, contains an embedded AES encrypted configuration within its .data section. Figure(12) : Encrypted configuration The AES decryption method used is the same as the one we’ve seen for decrypting strings. The key will be SHA-256 hashed before attempting the decryption, the first 16 bytes of the encrypted string used as IV. Then use the final key to decrypt the rest encrypted data. Figure (13): Decrypt the campaign INFO With the same method and key, Qakbot will decrypt its C2 list . Figure (14): Decrypt the C2 list With this information, we can reuse our string decryption script with some edits to have the configuration . notice that : The first 32 bytes in the decrypted data represent the SHA-256 validation, a cryptographic process used for data integrity verification. These bytes serve as a hash value that allows systems to confirm the authenticity and integrity of the data being processed. We can see the output of the script (configuration). Figure (15): the Decrypted configuration the malware use C2 communication QakBot mainly uses HTTP for C2 communication. The malware communicates with its C2 servers through encrypted AES payloads and then encodes the result in Base64. Figure (16): C2 communication fun Figure (17): AES Encryption and the key used while C2 communication Gather system INFO Part of QakBOT communication with its command and control is sending information about the computer. QakBot gathers computer information using a combination of Windows API calls, shell commands, and Windows Management Instrumentation (WMI) commands. This approach allows it to collect various details about the system, including hardware, software, and configuration data. By using these methods together, QakBot obtains a comprehensive overview of the target computer’s setup and specifications. VMI Queries Used Qakbot builds a WMI query by concatenating strings to form It then executes these queries to retrieve critical data and obtain a comprehensive overview of the system’s configuration and installed security measures. Figure (18): Qakbot create VMI queries Here are the WMI classes targeted and the information they retrieve: Class Properties Result Win32_OperatingSystem Caption OS Info [name and version] AntiVirusProduct * Information about antivirus products installed on a system Win32_Processor * Information about the processor Win32_ComputerSystem * Information about the computer system, including its hardware configuration, such as the manufacturer, model, system type, number of processors, memory Win32_Bios * Details about a computer’s BIOS, like its version, manufacturer, and release date Win32_DiskDrive * Information about the disk drives installed on a computer, including their model, manufacturer, interface type, capacity Win32_PhysicalMemory * Details about the physical memory modules in use, including their capacity, speed, manufacturer Win32_Product Caption, Description, Vendor, Version, InstallDate, InstallSource, PackageName Information about installed software, including its name, description, vendor, version, installation date, installation source, and package name Win32_PnPEntity Caption, Description, DeviceID, Manufacturer, Name, PNPDeviceID, Service, Status Details about Plug and Play devices, such as their name, description, device ID, manufacturer, name, PnP device ID, service, and status Windows command line Qakbot creates anonymous pipes to execute various built-in command-line tools processes, enabling it to retrieve information about the compromised system’s environment effectively. Figure (19): execute command-line tools Here is the list of commands that can be used to gather information about the system: Windows Command Output ipconfig /all Displays detailed configuration information about all network interfaces. whoami /all Displays user, group, and privileges information for the current user. nltest /domain_trusts /all_trusts Lists all domain trusts established with the current domain. qwinsta Lists information about all Remote Desktop sessions on the local system. nslookup -querytype=ALL -timeout=12 _ldap._tcp.dc._msdcs.%s Performs a DNS lookup for LDAP service records for the specified domain controller. net share Lists information about shared resources on the local system. net localgroup Lists information about local groups on the local system. netstat -nao Lists active network connections and associated processes. net view Lists information about shared resources on remote systems. route print Displays the IP routing table for the local system. arp -a Displays the ARP cache, which contains mappings of IP addresses to MAC addresses. Additionally, it will use Windows API calls to get different system details like computer name, screen size, AD domain info, user name, processor details, whether it’s a 32-bit or 64-bit Windows, and the operating system version, along with its respective full paths. Collect AntiViruses Information Qakbot checks for specific antivirus programs like Kaspersky, Avast, Norton, etc to see if any antivirus software is active on the system. It does this by scanning running programs and looking for related processes from these vendors. This list shows which antivirus vendors are associated with each process : processes Related Vendor ccSvcHst.exe;NortonSecurity.exe;nsWscSvc.exe Norton Security avgcsrvx.exe;avgsvcx.exe;avgcsrva.exe AVG Antivirus MsMpEng.exe Microsoft Defender Antivirus avp.exe;kavtray.exe Kaspersky Antivirus coreServiceShell.exe;PccNTMon.exe;NTRTScan.exe Trend Micro Antivirus fshoster32.exe F-Secure Antivirus fmon.exe FortiClient Antivirus egui.exe;ekrn.exe ESET bdagent.exe;vsserv.exe;vsservppl.exe Bitdefender AvastSvc.exe;aswEngSrv.exe;aswToolsSvc.exe;afwServ.exe;aswidsagent.exe;AvastUI.exe Avast Sophos UI.exe;SophosUI.exe;SAVAdminService.exe;SavService.exe Sophos WRSA.exe Webroot SecureAnywhere vkise.exe;isesrv.exe;cmdagent.exe Kaspersky ByteFence.exe ByteFence MBAMService.exe;mbamgui.exe Malwarebytes mcshield.exe McAfee dwengine.exe;dwarkdaemon.exe;dwwatcher.exe Datawatch SentinelServiceHost.exe;SentinelStaticEngine.exe;SentinelAgent.exe;… SentinelOne SonicWallClientProtectionService.exe;SWDash.exe SonicWall CynetEPS.exe;CynetMS.exe;CynetConsole.exe Cynet CSFalconService.exe;CSFalconContainer.exe CrowdStrike Falcon Executing C2 Commands After establishing communication, the C2 server will send commands to be executed. These commands are represented as integer values or indexes. Figure (20): The list of the C2 commands used by Qakbot Process Hollowing QakBot selects a system process for process hollowing based on the machine’s architecture (32-bit or 64-bit) and the installed antivirus software. This list includes the following system processes: Expand to see more %SystemRoot%\SysWOW64\AtBroker.exe %SystemRoot%\System32\AtBroker.exe %SystemRoot%\SysWOW64\xwizard.exe %SystemRoot%\System32\xwizard.exe %SystemRoot%\SysWOW64\explorer.exe %SystemRoot%\explorer.exe %SystemRoot%\SysWOW64\wermgr.exe %SystemRoot%\System32\wermgr.exe %SystemRoot%\SysWOW64\OneDriveSetup.exe %SystemRoot%\System32\OneDriveSetup.exe %SystemRoot%\SysWOW64\msra.exe %SystemRoot%\System32\msra.exe %SystemRoot%\SysWOW64\mobsync.exe %SystemRoot%\System32\mobsync.exe It first calls the CreateProcessW() API with the CREATE_SUSPENDED flag to start a new process, making it suspended at the beginning. Figure (21): create a suspended process Then it allocates virtual memory in a target process, writes data into the allocated region, and then modifies the memory protection to allow execution. Next, it retrieves the context of the thread to modify it to set the instruction pointer (EIP/RIP register) to point to the entry point of the injected code. It finally calls the API ResumeThread() to resume the new processes. Persistence QakBot sets itself to run on system reboot through a registry entry or Scheduled Task. Figure (22): Persistence function Conclusion Qakbot is an advanced malware with regular updates and powerful anti-analysis actions, ensuring it remains a persistent threat with a wide range of capabilities and techniques. YARA Rule rule detect_Qakbot_v5 { meta : description = "just a rule for Qakbot v5" author = "Mohamed Ezzat (@ZW01f)" hash1 = "af6a9b7e7aefeb903c76417ed2b8399b73657440ad5f8b48a25cfe5e97ff868f" hash2 = "59559e97962e40a15adb2237c4d01cfead03623aff1725616caeaa5a8d273a35" strings : $ s1 = "\\u%04X\\u%04X" ascii wide $ s2 = "%u;%u;%u" ascii wide $ s3 = "CfGetPlatformInfo" ascii wide $ p1 = { 45 33 C0 E8 ?? ?? ?? ?? 35 91 CB 35 A2 41 3 B C7 } $ p2 = { 0F B6 01 48 FF C1 44 33 C0 41 8B C0 41 C1 E8 04 83 E0 0F 44 33 04 82 41 8B C0 41 C1 E8 04 83 E0 0F 44 33 04 82 49 83 E9 01 75 ?? 41 F7 D041 8B C0 C3 } condition : uint16 ( 0 ) == 0 x5A4D and all of ( $ p *) and ( 2 of ( $ s *)) and filesize < 500 KB } Python Automated Configuration Extraction This python script is used to extract the configuration of the Qakbot malware : Open the binary file. Get the .data section. Extract the the key and the encrypted configuration data . SHA-256 hash the extracted key to get the final key. Use the key to decrypt the configurations. Parse the decrypted configurations to extract useful information. #--------------- imports --------------------# import hashlib from Crypto.Cipher import AES from Crypto.Util.Padding import unpad import socket from datetime import datetime import pytz #------------- helper ------------------------# def extract_data ( filename ): #finds the content of the ".data" section. . import pefile pe = pefile . PE ( filename ) for section in pe . sections : if '.data' in section . Name . decode ( encoding = 'utf-8' ). rstrip ( 'x00' ): return ( section . get_data ( section . VirtualAddress , section . SizeOfRawData )) def tohex ( data ): import binascii if type ( data ) == str : return binascii . hexlify ( data . encode ( 'utf-8' )) else : return binascii . hexlify ( data ) def get_ip ( ip_binary ): # Convert the binary network format to a human-readable string format ip_str = socket . inet_ntoa ( ip_binary ) return ip_str #------------ Decryption ---------------------# def calculate_sha256 ( input_data ): sha256_hash = hashlib . sha256 () sha256_hash . update ( input_data ) hash_hex = sha256_hash . digest () return hash_hex def aes_decrypt ( ciphertext , key , iv ): cipher = AES . new ( key , AES . MODE_CBC , iv ) plaintext = cipher . decrypt ( ciphertext ) unpadded_plaintext = unpad ( plaintext , AES . block_size ) return unpadded_plaintext def full_dec ( enc_str , aes_key_init ): aes_key = calculate_sha256 ( aes_key_init ) dec_str = aes_decrypt ( enc_str [ 17 :], aes_key , enc_str [ 1 : 17 ]) return dec_str def parse_camp ( input_str ): lines = input_str . strip (). split ( b ' \r\n ' ) parsed_data = {} for line in lines : key , value = line . split ( b '=' ) parsed_data [ key ] = value timestamp = int ( parsed_data [ b '3' ]) dt_obj = pytz . utc . localize ( datetime . utcfromtimestamp ( timestamp )) print ( f "Botnet ID : { parsed_data [ b '10' ] } '" ) print ( f "b'40' : { parsed_data [ b '40' ] } '" ) print ( f "Campaign Timestamp : { dt_obj } " ) def parse_c2 ( dec_ips ): i = 0 splitted_data = [ dec_ips [ i : i + 7 ] for i in range ( 1 , len ( dec_ips ), 8 )] for data in splitted_data : ip = get_ip ( data [: 4 ]) port = int ( tohex ( data [ 4 : 6 ]), 16 ) print ( 'IP[{0}] = {1}:{2}' . format ( i , ip , port )) i = i + 1 def main (): file_name = input ( "enter the file path: " ) # The config data begins at these offsets inside the .data section enc_ips_rva = 0x852 ; size_rva = 0x850 ; enc_config_rva = 0x1022 data_section = extract_data ( file_name ) #read data section size = ord ( data_section [ size_rva : size_rva + 1 ]) enc_config_ips = data_section [ enc_ips_rva : enc_ips_rva + size ] enc_config = data_section [ enc_config_rva : enc_config_rva + size ] init_key = b 'T2X!wWMVH1UkMHD7SBdbgfgXrNBd(5dmRNbBI9' aes_key = calculate_sha256 ( init_key ) campaign_info = full_dec ( enc_config , init_key ) dec_c2 = full_dec ( enc_config_ips , init_key ) print ( '##------------------- Campaign Info -------------------##' ) print ( 'sha256 :' , tohex ( campaign_info [: 32 ])) print ( '#--------------------------------------#' ) parse_camp ( campaign_info [ 32 :]) print ( '##------------------- Qakbot c2 -------------------##' ) print ( 'sha256 :' , tohex ( dec_c2 [: 32 ])) print ( '#--------------------------------------#' ) parse_c2 ( dec_c2 [ 32 :]) if __name__ == '__main__' : main () References [QuickNote] Qakbot 5.0 – Decrypt strings and configuration https://labs.k7computing.com/index.php/qakbot-returns/ Enter your search term...