Study of an APT attack on a telecommunications company in Kazakhstan Study of an APT attack on a telecommunications company in Kazakhstan Doctor Web Head Office 2-12A, 3rd str. Yamskogo polya Moscow, Russia 125040 Website: www.drweb.com Phone: +7 (495) 789-45-87 Refer to the official website for regional and international office information. Study of an APT attack on a telecommunications company in Kazakhstan 3/23/2022 © Doctor Web, Ltd., 2022. All rights reserved. This document is the property of Doctor Web, Ltd. (hereinafter - Doctor Web). No part of this document may be reproduced, published or transmitted in any form or by any means for any purpose without proper attribution. Doctor Web develops and distributes Dr.Web information security solutions which provide efficient protection from malicious software and spam. Doctor Web customers can be found among home users from all over the world and in government enterprises, small companies and nationwide corporations. Dr.Web antivirus solutions are well known since 1992 for continuing excellence in malware detection and compliance with international information security standards. State certificates and awards received by the Dr.Web solutions, as well as the globally widespread use of our products are the best evidence of exceptional trust to the company products. 3 3 Table of Contents 4Introduction 5Remote Rover 7Conclusion 8Operating Routine of Discovered Malware Samples 8BackDoor.PlugX.93 18BackDoor.Siggen2.3622 21BackDoor.Whitebird.30 27Trojan.DownLoader43.44599 37Trojan.Loader.891 45Trojan.Loader.896 54Trojan.Uacbypass.21 59Appendix. Indicators of Compromise 4 4 Introduction In October 2021, one of Kazakhstan’s telecommunication companies contacted Doctor Web, with suspicion of malware in the corporate network. During the first look, we found backdoors that were previously only used in targeted attacks. During the investigation, we also found out that the company’s internal servers had been compromised since 2019. For several years, Backdoor.PlugX.93 and BackDoor.Whitebird.30, the Fast Reverse Proxy (FRP) utilities, and RemCom have been the main attackers' tools. Because of the hackers' mistake, we got a unique opportunity to study the lists of victims and find out what backdoor management tools were used. Based on the acquired information, we concluded that the hacker group specialized in compromising the Asian companies’ mail servers with Microsoft Exchange software installed. That said, we also found victims from other countries, including: · Egyptian government agency · Italian airport · USA marketing company · Canadian transport and woodworking companies The logs collected along with the command and control server included victims infected from August 2021 to early November of the same year. Yet, in some cases, BackDoor.Whitebird.30 was installed not only on the server running Microsoft Exchange, but on domain controllers, too. Based on the tools, methods, and infrastructure used, we conclude that the Calypso APT hacker group is behind the attack. 5 5 Remote Rover Command and control server for BackDoor.Whitebird.30 calls Remote Rover. It allows hackers to remotely launch applications, update the backdoor configuration, download and upload files. Besides that, you can use a command shell via Remote Rover. This is what the control server interface looks like: Remote Rover came with a configuration file CFG\default.ini with the following content: E:\ \ \2021\RR\ \telecom.cfg OneClock.exe If you translate the content from Chinese into English, you can get this path: E:\personal use\Independent research and development remote\2021\RR\Configuration backup\telecom.cfg For a detailed description of the malware used and how it works, see the Dr.Web Virus Library. · BackDoor.Siggen2.3622 · BackDoor.PlugX.93 · BackDoor.Whitebird.30 · Trojan.Loader.891 6 6 · Trojan.Loader.896 · Trojan.Uacbypass.21 · Trojan.DownLoader43.44599 7 7 Conclusion During the investigation of the targeted attack, Doctor Web virus analysts found and described several backdoors and trojans. It’s worth noting that the attackers managed to remain undetected for as long as other targeted attack incidents. A hacker group compromised a telecommunications company's network more than two years ago. Doctor Web specialists recommend regularly checking network resources’ efficiency and timely fixing failures that may indicate the presence of malware on the network. Data compromise is one of targeted attacks’ main dangers, but the long-term presence of intruders is also a cause for concern. Such development allows them to control the organization’s work for many years and gain access to especially sensitive information at the proper time. If you suspect malicious activity in the corporate network, the best option is to contact the Doctor Web virus laboratory for qualified help. Dr.Web FixIt! helps you detect malware on servers and workstations. Taking adequate measures timely will minimize the damage and prevent the serious consequences of targeted attacks. 8 8 Operating Routine of Discovered Malware Samples BackDoor.PlugX.93 Added to the Dr.Web virus database: 2021-10-22 Virus description added: 2021-10-30 Packer: absent Compilation date: 2020-08-13 SHA1 hash: a8bff99e1ea76d3de660ffdbd78ad04f81a8c659 Description The PlugX backdoor module is written in C. It’s designed to decrypt the shellcode from the registry that loads the main backdoor into memory. Operating principle First, the backdoor receives the address of the VirtualProtect() function by hash. It then uses this address to change access rights to PAGE_EXECUTE_READWRITE, starting from the function at 0x10001000 and ending with the entire .text section: Getting the function’s address by the hash passed as a parameter: 9 9 Script to get a function by hash: import pefile   ror = lambda val, r_bits, max_bits: \ 10 10   ((val & (2**max_bits-1)) >> r_bits%max_bits) | \   (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))   max_bits = 32   library_path_list = [...] # absolute path dlls   def get_func_addr(hash):   for library_path in library_path_list:   library = library_path.split('\\')   name_dll = library[len(library) - 1].upper() + b'\x00'     hash_name_dll = 0   for i in name_dll:   hash_name_dll = ord(i) + ror(hash_name_dll, 0x0D, max_bits)   hash_name_dll = 0 + ror(hash_name_dll, 0x0D, max_bits)     pe = pefile.PE(library_path)   for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:   func_name = exp.name + b'\x00'     hash_name_func = 0   for i in func_name:   hash_name_func = ord(i) + ror(hash_name_func, 0x0D, max_bits)     if (hash_name_dll + hash_name_func == hash):   print '{}-> 0x{:08x} -> {}'.format(name_dll, hash, exp.name)   return Changing the permissions to PAGE_EXECUTE_READWRITE was necessary to decrypt the code using the XOR operation: 11 11 One version of the backdoor has dynamic XOR encryption. It has decryption at the beginning of the function: And with encryption at the end of the function: 12 12 Facilitating the script’s work for IDAPython: import idaapi   def xor_dec(address, count, key):   for i in xrange(count):   idaapi.patch_dword(address, idaapi.get_dword(address) ^ key)    key += idaapi.get_dword(address)   address += 4 Before performing malicious actions, the backdoor, as in the case of VirtualProtect(), receives functions’ addresses that it needs to work 13 13 Received features: Function name Hash CloseHandle 0x528796C6 CreateFileA 0x4FDAF6DA DeleteFileA  0x13DD2ED7 ExitProcess 0x56A2B5F0 GetAdaptersInfo 0x62C9E1BD GetModuleFileNameA 0xFE61445D GetSystemDirectoryA 0x60BCDE05 LoadLibraryA 0x726774C ReadFile 0xBB5F9EAD 14 14 Function name Hash RegCloseKey 0x81C2AC44 RegDeleteValueA 0x3846A3A8 RegEnumValueA 0x2EC95AA4 RegOpenKeyExA 0x3E9E3F88 RegQueryValueExA 0x8FF0E305 VirtualAlloc 0xE553A458 VirtualFree 0x300F2F0B VirtualProtect 0xC38AE110 WinExec 0x876F8B31 WriteFile 0x5BAE572D In addition, the backdoor checks if it is executed in a sandbox: 15 15 After receiving the function addresses and checking for execution in the sandbox, BackDoor.PlugX.93 removes the updatecfgSetup task from the task scheduler: The key for shellcode encryption is MD5 from the following registry key values: 16 16 HKLM\Software\Microsoft\Windows NT\CurrentVersion\InstallDate HKLM\System\ControlSet001\Control\ComputerName\ComputerName The shellcode is stored in the following registry keys: HKLM\Software\BINARY HKCU\Software\BINARY 17 17 Before running the shellcode, it’ll be decrypted in 2 steps: first, using the RC4 algorithm: then, with XOR: 18 18 BackDoor.Siggen2.3622 Added to the Dr.Web virus database: 2021-11-03 Virus description added: 2021-xx-xx Packer: UPX SHA1 hash: be4d8344669f73e9620b9060fd87bc519a05617a Description A backdoor written in Go. It’s packed by UPX. Investigated backdoor version V2.5.5 z 2021.7.19. Operating principle In the beginning, the malicious code checks if another backdoor copy is running. The trojan checks for the c:\windows\inf\mdmslbv.inf file. If it exists, the trojan starts reading. You can use the following script to decrypt: import sys   with open(sys.argv[1], 'rb') as f:   d = f.read()   s = bytearray()   for i in range(len(d)):   s.append(d[i])   for i in range(len(s)-2, 0, -1):   s[i] = (((s[i + 1] * s[i + 1]) ^ s[i]) & 0xff)   with open(sys.argv[1] + '.dec', 'wb') as f:   f.write(s) Encrypted file’s length The packet’s structure: 19 19 · random string from 10 to 19 characters long · between the ... tags contains the backdoor process’s PID · between the ... tags is the process’s name · random string from 10 to 19 characters long The trojan checks for the existence of a process with the specified parameters. If it finds it, the trojan terminates its work. If it doesn’t find a process with the specified parameters or the mdmslbv.inf file itself, the trojan generates data as shown above. Then, it encrypts and writes to the c: \windows\inf\mdmslbv.inf. Communication with the command and control server The trojan has command and control server: blog[.]globnewsline[.]com. The trojan sends a GET request to the following URL: hxxps://blog.globnewsline.com:443/db/db.asp using User-Agent "Mozilla/5.0 (X11; Windows x86_64; rv:70.0) Gecko/20100101 Firefox/70.0". If the server response contains the substring Website under construction, then the trojan considers that the control server is available. If the server is unavailable, the malicious code checks for the presence of a proxy configuration file c:\windows\inf\bksotw.inf. If that’s present, the trojan reads the parameters written in the file. The backdoor uses MAC addresses as the network interface bot ID. For heartbeat requests, the following POST requests are used: https://blog.globnewsline.com:443/db/db.asp?m=w&n=~A.t where is the MAC address string, converted to uppercase with colons removed. Next, a GET request is sent to get a list of commands: https://blog.globnewsline.com:443/db/A.c The server response is encrypted in the same way as the file with the backdoor process’s PID. The following commands can be executed: · up · down · bg · bgd · getinfo https://blog.globnewsline.com:443/db/A.c 20 20 The command’s result is encrypted the same way as the command itself was encrypted. Then, it’s sent in the POST request’s body to the following URL: https://blog.globnewsline.com:443/db/A.c 21 21 BackDoor.Whitebird.30 Added to the Dr.Web virus database: 2021-10-21 Virus description added: 2021-xx-xx Packer: absent Compilation date: 2021-29-03 SHA1 hash: abfd737b14413a7c6a21c8757aeb6e151701626a Description A multi-functional backdoor trojan for 64-bit and 32-bit Microsoft Windows operating system family. It’s designed to establish an encrypted connection with the command and control server and unauthorized control of an infected computer. It has a file manager and Remote Shell’s functions. Preparing procedures At the beginning of the work, the backdoor decrypts the overlay provided by the shellcode. The first encryption layer is removed by the following algorithm: k = 0x37 s = bytearray() for i in range(len(d)):   c = d[i] ^ k   s.append(c)   k = (k + c) & 0xff The second layer is the XOR operation with the key 0xCC. This overlay contains: · configuration of trojan · module for bypassing UAC Configuration looks as follows: struct st_proxy {   char proxy_addr[32];   char proxy_login[64];   char proxy_password[64]; 22 22  _BYTE pad[2]; };     struct st_config   {   char cnc_addr[4][34];   st_proxy proxies[4];   char home_dir[260];   char exe_name[50];   char loader_name[50];   char shellcode_name[50];   char software_name[260];   char startup_argument[50];   _DWORD reg_hkey;   char reg_run_key[200];   char reg_value_name[52];   char taskname[52];   _DWORD mstask_mo;   char svcname[50];   char svcdisplayname[50];   char svcdescription[256];   char reg_uninstall_key[50];   char inject_target_usr[260];   char inject_target[260];   _BYTE byte0[2];   _BYTE flags;   _BYTE pad[3];   _DWORD keepalivetime;   unsigned __int8 key[16]; }; The flags field displays which autoload methods the trojan should use, and what launch features are: 23 23 enum em_flags {   GOT_ENOUGH_RIGHTS= 0x1,   UNK_FLAG_2 = 0x2,   UNK_FLAG_4 = 0x4,   INSTALL_AS_MSTASK = 0x8,   INSTALL_AS_SERVICE = 0x10,   RUN_WITH_ARGUMENT = 0x20,   INJECT_TO_PROCESS = 0x40,   RUN_AS_USER = 0x80, }; If the launch is specified via the task scheduler (INSTALL_AS_MSTASK), then the configuration flags creates a mutex after decrypting. That prevents restart: Next, it checks if the trojan has enough rights to launch in the way that was previously specified in the configuration. If not, it restarts itself to bypass UAC. Trojan checks for the presence of a file in the path C:Users\Public\Downloads\clockinstall.tmp, and if it exists, it deletes clockinstall.tmp. If the clockinstall.tmp file is missing, it checks if the install file exists in the folder from which the trojan was launched. If it exists, it removes it. Then, it installs itself into the system in accordance with the type specified in the configuration. The backdoor will also try to hide its activity from the user.  If the trojan runs on a 32-bit OS, then the same mechanism for hiding a service from running ones is valid, as in BackDoor.PlugX.28, deleting that structure from the list of ServiceDatabase structures. That corresponds to the trojan service. If the configuration specifies that the trojan should be injected into a process, then it’ll be injected into the target process. If the RUN_AS_USER flag is specified in the configuration, then the trojan will wait until at least one authorized user appears. After that, it’ll create its own process, but on behalf of the user. https://vms.drweb.ru/virus/?i=21507745 24 24 Regardless of the trojan's autorun type, only one process can communicate with the command and control server. This creates a mutex: Before attempting to establish a connection with the command and control server, trojan determines the proxy server settings. For this purpose: · The presence of the .ini file in the folder from which the trojan process was launched is checked. Example of the configuration: [AntiVir] Cloud=0A0804D2242000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000299CC1003C9CC10098F11900DCF1190062F21900000000 00E02AC300CC004501D8F11900000000000000000000000000000000000000000000000000 00000000000000000000000000000000000000000000000000000000000001 · Reads a file named .tmp in the trojan folder, where is the value from the configuration · Reads proxy settings from registry [HKCU\Software\Microsoft\Windows\CurrentVersion\Internet Settings, keys ProxyEnable and ProxyServer · Reads proxy settings from Mozilla Firefox settings - %APPDATA% \Mozilla\Firefox\\prefs.js · Checks for stored login:password from the proxy server in Mozilla Firefox and Internet Explorer Control server protocol Establishing a connection to the server mimics the creation of a TLS1.0 connection between the client and the server. Trojan body contains two buffers: 1. Contains the TLS1.0 Client Hello package: 2. Contains TLS 1.0 Client Key Exchange packets with key length 0x100 bytes, Change Cipher Spec, Client Handshake Finished: 25 25 When sending a Client Hello packet, the trojan encrypts all bytes of the Client Random field, starting from the 4th one, using the XOR method with random bytes. It also records the current time in the first 4. The server's response to this message is accepted, but the data is ignored. When sending the second packet, the backdoor also encrypts the Client Key Exchange packet’s public key field using the XOR method with random bytes, and writes its 28-byte key into the data of the Client Handshake Finished packet. That’ll be used to encrypt and decrypt packets sent or received from the server. The backdoor encrypts the last 4 bytes of the Client Handshake Finished packet with random bytes. Then, it sends it to the command and control server. In response, the server sends its own key. That key is used to initialize the key shared with the client. After that, the backdoor enters the command processing cycle from the control server. The traffic between the client and the server is encrypted using the RC4 algorithm. The list of commands: opcode Command 0x01 Gathering information regarding the infected device 0x02 Remote shell 0x03 File manager (see below for commands ending in 3) 0x100 Keep-Alive 0x103 Open file for writing 26 26 0x203 Download a file 0x303 Data to be written 0x400 Reconnect to server 0x403 Obtain information about disk or directory listing; 0x500 To finish work 0x503 Move a file 0x600 Delete proxy configuration ini file 0x603 Delete a file 0x703 Run a process 0x700 Execute a command during ShellExecute 0x800 Renew configuration 27 27 Trojan.DownLoader43.44599 Added to the Dr.Web virus database: 2021-10-15 Virus description added: 2021-10-20 Packer: absent Compilation date: 2020-07-13 SHA1 hash: 1a4b8232237651881750911853cf22d570eada9e Description The trojan is written in C++. It’s used for unauthorized control of an infected computer. Operating principle In the beginning, the trojan decrypts the C&C server’s IP addresses and ports using the XOR operation: import idaapi   address = 0x416200   for i in xrange(0x7c):   idaapi.patch_byte(address + i, idaapi.get_byte(address + i) ^ 0xEF) Decryption result: 28 28 C&C server—159.65.157.100:443 Communication with it occurs using sockets: 29 29 Depending on the time, the connection to the required C&C server will be selected: 30 30 The trojan creates file tmp.0 in folder %tmp%, that it use as log. 31 31 Collect information about the system: 32 32 35; Dr.WEB ljint *get_infof{} it 3] UINT code_page id; // edi 4) UINT oem_code page id; // ebx 5} HMODULE ntdll_addr; // eax 6) FARPROC rtl_get_nt_version_numbers; // eax 7| HMODULE w4; // eax 8| HANDLE w5; // eax 9) int computer_bitness; // esi 18) struct hostent *hostent; // eax MAPDST li) aint (__stdeall *lstrlenA_Tunc)(LPCSTR); // ebx 12| char *h_addr_list; // ecx 13) int idx_addr; // esi 14| char *address; // eax 15| unsigned int v13; // eax 16) signed int computer_info_len; // edi 17| HANDLE w15; // eax 18} int *vi6; // esi 19) struct in_addr in; // [esp+Ch] [ebp-3cth] 28) struct WSAData WSAData; // [espt+le@h] [ebp-3cah] 21) int major_version; // [esp+1Aeh] [ebp-238h] 22) int minor_version; // [esp+1A4h] [ebp-234h] 23) DWORD nSize; // [esp+lAsh] [ebp-23¢h] 24) int build number; /f [esptlACh] [ebp-22Ch] 25) char name[256]; // [esp+1B@h] [ebp-228h] 26| CHAR computer_name[64]; // [esp+28@h] [ebp-128h] 27) CHAR user_name[64]; // [espt+2F8h] [ebp-E8h] 28) char computer_info[128]; // [espt33@h] [ebp-Ash] 29} char RtlGetNtVersionNumbers[24]3 // [esp+3Beh] [ebp-28h] 38) CHAR ntdll lib[12]; // [esp+3c8h] [ebp-18h] 31 32| code page id = GetACP(); 33) ocem_code_page_id = GetOEMCP{); 34) major_version = @; 35| minor_version = @; 36| build_number = @; 37| strcpy{ntdll_ lib, “ntdll.d1l"); 38| ntdll_addr = LoadLibraryA(ntd1ll_lib); 39) *RtlGetNtVersionNumbers = _mm_load_sil28(aRtlgetntversio); 48) strcepy(SatlGetntVersionNumbers[16], “umbers"); 41) rtl_get_nt_version_numbers = GetProcAddress(ntdll addr, RtlGetNtVersionNumbers); 42) if ( rtl_get_nt_version_numbers } 43 (rtl_get_nt_version_numbers)(&major_version, &minor_version, &build_number); 44) build number = build number; 45| in = @; 33 33 35; Dr.WEB 46| kernel32_addr = GetModuleHandlew(L"kernel32"); 47| IsWow64Process = GetProcAddress(kernel32_addr, “IsWow64Process"); 48) if ( IsWow64Process } 49) 58 v5 = GetCurrentProcess(); 51 IsWow64Process(v5, Sin); 52| } 53/ computer_bitness = 32; 54/ nSize = 64; 55| if { in } 56 computer_bitness = 64; 57| GetComputerNameA(computer_name, &nSize); 58| nSize = 64; 59) GetUserNameA(user_name, &nSize); 68) wsprintTag 61 computer_info, 62 "$5.35: Bd. Sd. Hs ds ss Bd Hd", 63 computer_name, 6-4 user_name, 65 major_version, 66 minor_version, 67 build number, 68 computer_bitness, 69 aMarch@l, 78 code page id, 71 oem_code page id); 72| WSAStartup(@x202u, &WSAData); 73| gethostname(name, 256); 74| hostent = gethostbyname(name); 75| LlstrlenA_fune = lstrlendA; 76| if ( hostent } iri tent-+h_addr_list; 78 h_addr_list = *hos 79 if ( h_addr_list } a6 al idx_addr = @; a2 do 83 { a4 memmove(in, h addr list, hostent->h_length); 85 address = inet_ntoa(in); 86 lstreatA(computer_info, address); a7 IstrcatA{computer_info, "#"); 34 34 Trojan.DownLoader43.44599 pushes each value onto a stack before encrypting and sending the collected data. The transferred data looks as follows: struct computer_info {   string computer_name;   string user_name;   uint32_t major_version;   uint32_t minor_version;   uint32_t build_number;   uint32_t computer_bitness;   string March01;   uint32_t code_page_id;   uint32_t oem_code_page_id; }; To encrypt the information collected about the system, the AES128 algorithm is used in CBC mode. The key and initialization vector are embedded inside: 35 35 The decryption method looks as follows: from Crypto.Cipher import AES   key = '\x95\x2B\x2D\xBF\x09\xC5\x2F\x80\xB4\xBC\x47\x27\x29\xB3\x28\x09' iv = '\x63\x5F\x72\x2A\xBB\xE3\xE8\x95\xF8\xF9\x32\x87\x53\x6A\x77\xFB' enc = ...   decipher = AES.new(key, AES.MODE_CBC, iv) open('dec', 'wb').write(decipher.decrypt(enc)) The command execution cycle received from the C&C server: 36 36 Table of commands compiled from the results of this cycle: Command ID Command 0x51 Creating cmd.exe process 0x52 Execution command exit in cmd.exe 0x54 Execute commands in the cmd.exe console; 0x60 Creating the flow that reads, writes, and encrypts files. 37 37 Trojan.Loader.891 Added to the Dr.Web virus database: 2021-10-15 Virus description added: 2021-xx-xx Packer: absent Compilation date: 2021-09-03 12:04:44 SHA1 hash: 595b5a7f25834df7a4af757a6f1c2838eea09f7b Description This trojan is written in C. The program contains several files, and the trojan uses each file sequentially. The trojan’s main task is to decrypt the shellcode and execute it. The decrypted shellcode contains BackDoor.Whitebird.30, a module for bypassing UAC and backdoor configuration. Operating principle The trojan folder contains the following files: · mcupdui.exe — the executable file into which the malicious library is loaded using Hijacking DLL has a valid McAfee signature: 4F638B91E12390598F037E533C0AEA529AD1A371: CN=McAfee, Inc., OU=IIS, OU=Digital ID Class 3 - Microsoft Software Validation v2, O=McAfee, Inc., L=Santa Clara, S=California, C=US · McUiCfg.dll — downloader · mscuicfg.dat — encrypted shellcode · mcupdui.ini — configuration of trojan To move to the main malicious functionality, the trojan modifies the process memory: The instruction following the malicious library’s download library is modified: 38 38 Trojan.Loader.891 finds all the functions it needs by hashes using the PEB (Process Environment Block) structure. At the same time, the names of libraries and functions are hashed differently: library names are hashed as Unicode strings converted to upper case. Function names are hashed as ASCII strings without changing the case. The resulting two hashes are added together and then compared with the desired one. ror = lambda val, r_bits, max_bits: \   ((val & (2 ** max_bits - 1)) >> r_bits % max_bits) | \   (val << (max_bits - (r_bits % max_bits)) & (2 ** max_bits - 1))   def hash_lib_whitebird(name: bytes) -> int:   a = name.upper() + b'\x00'   c = 0     for i in range(0, len(a)):   c = (a[i] + ror(c, 13, 32)) & 0xffffffff   # library name is a unicode string   c = (0 + ror(c, 13, 32))     return c     39 39 def hash_func_whitebird(name: bytes) -> int:   a = name + b'\x00'   c = 0     for i in range(0, len(a)):   c = (a[i] + ror(c, 13, 32)) & 0xffffffff     return c Trojan’s main functions are encrypted. When the function is called, it decrypts its code, and when it exits, it encrypts it back. Main function: Trojan.Loader.891 obtains the MAC addresses of all network interfaces on the computer. The trojan then reads data from the mscuicfg.dat file. If the last 6 bytes are zero, then it writes the first MAC address from the list into them and encrypts this file with the RC4 algorithm. In this 40 40 case, the key is equal to the MAC address written to the file, so the encrypted data is saved to the file mscuicfg.dat. After that, in any way, the trojan reads the file again, sorting through each of the received MAC addresses until it finds the right one. The decryption’s correctness is checked by matching the last 6 decrypted bytes with the encryption key. Upon successful decryption, the trojan cuts them off and decrypts the file again using the RC4 algorithm, but takes the string mscuicfg.dat as the key. The received data is a shellcode with a configuration and a payload. Shellcode The shellcode is obfuscated with a lot of JMP instructions and each value is computed with a lot of SUB, ADD, and XOR operations: The shellcode’s principle is to decrypt the payload and load it into memory for execution. The last DWORD of the shellcode contains the OFFSET before the start of the payload. Encrypted data at this stage: 41 41 wee er ANNI ie TANNIN TANNIN TANNIN TANNIN TANNIN TANNIN nnn TANNIN nnn TANNIN TANNNANANNNNNANnn© TANNA AINA TimliMag="4 -¥ Ex eeeeceecEENg,- bt Leff fad |] | [pve ALIULLWEF FFP PY VE Ft fdhh1111111 1hppp ppppp ++ EEE EHAHE Resi Vueesseettits O00 putt e1HIIIIII 3999999999999) 379] Ja/iwannnnnn nnannnnannnnnnannn nnannnnannnnnnannn NANNANANNANNNNN ? 2? 42 42 For decryption, XOR with a dynamic key is used: k = 0x37 s = bytearray() for i in range(len(d)):   c = d[i] ^ k   s.append(c)   k = (k + c) & 0xff The decrypted data contains an MZPE file with signatures replaced: 43 43 The decoded module is BackDoor.Whitebird.30. In addition, the module overlay contains an encrypted configuration and a module for bypassing UAC: 44 44 <9; Dr. WEB 45 45 Trojan.Loader.896 Added to the Dr.Web virus database: 2021-11-03 Virus description added: 2021-11-17 Packer: absent Compilation date: 2020-14-10 SHA1 hash: ff82dcadb969307f93d73bbed1b1f46233da762f Description The backdoors downloader, PlugX, is written in C. Operating principle After loading from the main module (msrers.exe) using the LoadLibraryW function, the trojan loads the kernel32.dll library using the LoadLibraryA. Then, it gets the address of the exported function GetModuleFileNameA: It then obtains the name of the main module using the previously obtained function GetModuleFileNameA. It checks if the name contains the substring "ers." (msrers.exe): 46 46 From the hash, 0xEF64A41E gets the function VirtualProtect to change the memory access rights to PAGE_EXECUTE_READWRITE at 0x416362 (msrers.exe): The following fragment will modify the code at 0x416362 (msrers.exe): 47 47 push 0xFFFFFFFF push 0x100010B0 ; func_addr ret Place in the main module to be modified: Next, a function is called that receives the base kernel32.dll, and the addresses of the functions by hashes. Script to get a function by hash: 48 48 import pefile   ror = lambda val, r_bits, max_bits: \   ((val & (2**max_bits-1)) >> r_bits%max_bits) | \   (val << (max_bits-(r_bits%max_bits)) & (2**max_bits-1))   max_bits = 32   library_path_list = [...] # absolute path dlls   def get_func_addr(hash):   for i in xrange(len(library_path_list)):   library = library_path_list[i].split('\\')   name_dll = library[len(library) - 1]     pe = pefile.PE(library_path_list[i])   for exp in pe.DIRECTORY_ENTRY_EXPORT.symbols:   func_name = exp.name     hash_name_func = 0   for j in func_name:   hash_name_func = ord(j) + ror(hash_name_func, 0x07, max_bits)     if (hash_name_func == hash):   print '0x{:08x} -> {} -> {}'.format(hash, name_dll, exp.name)   return Received features: Function name Hash VirtualProtect 0xEF64A41E GetLastError 0x12F461BB CloseHandle 0xFF0D6657 49 49 Function name Hash ReadFile 0x130F36B2 VirtualAlloc 0x1EDE5967 GetFileSize 0xAC0A138E CreateFileA 0x94E43293 lstrcat 0x3E8F97C3 GetModuleFileNameA 0xB4FFAFED In the following, the below structure is used to call these functions: struct api_addr {   DWORD  (__stdcall *GetModuleFileNameA)(HMODULE, LPSTR, DWORD);   LPSTR  (__stdcall *lstrcat)(LPSTR, LPCSTR);   HANDLE (__stdcall *CreateFileA)(LPCSTR, DWORD, DWORD, LPSECURITY_ATTRIBUTES, DWORD, DWORD, HANDLE);   DWORD  (__stdcall *GetFileSize)(HANDLE, LPDWORD);   LPVOID (__stdcall *VirtualAlloc)(LPVOID, SIZE_T, DWORD, DWORD);   BOOL  (__stdcall *ReadFile)(HANDLE, LPVOID, DWORD, LPDWORD, LPOVERLAPPED);   BOOL  (__stdcall *CloseHandle)(HANDLE);   DWORD  (__stdcall *GetLastError)(); }; Trojan takes the name dll (TmDbgLog.dll) and adds the ".TSC" extension to it. Next, it opens the file TmDbgLog.dll.TSC for reading and decrypts its contents, which turns out to be a shellcode. After decrypting the shellcode (TmDbgLog.dll), the trojan starts executing it: 50 50 The below is how the script for decrypting the shellcode looks like: enc = bytearray(open('TmDbgLog.dll.TSC', 'rb').read())   dec = bytearray() for i in xrange(len(enc)):   dec.append(((enc[i] ^ 0xbb) - 1) & 0xff)   open('TmDbgLog.dll.TSC.dec', 'wb').write(dec) Before decrypting and running the payload, the shellcode assembles the following structure: struct st_mw {   DWORD magic;   DWORD *shell_base;   DWORD shell_size;   DWORD *enc_payload;   DWORD enc_payload_size;   DWORD *enc_config;   DWORD enc_config_size;   DWORD *payload_entry; }; This is what the encrypted config looks like: 51 51 The config’s decryption will be done directly in the payload: import struct   enc = open('enc_cfg', 'rb').read() key, = struct.unpack('I', enc[0:4])   key1 = key key2 = key key3 = key   dec = bytearray()   for i in xrange(len(enc)):   key = (key + (key >> 3) - 0x11111111) & 0xFFFFFFFF   key1 = (key1 + (key1 >> 5) - 0x22222222) & 0xFFFFFFFF   key2 = (key2 + 0x33333333 - (key2 << 7)) & 0xFFFFFFFF   key3 = (key3 + 0x44444444 - (key3 << 9)) & 0xFFFFFFFF   dec.append(ord(enc[i]) ^ (key + key1 + key2 + key3) & 0xFF)   open('dec_cfg', 'wb').write(dec) And it’ll look like this: 52 52 Encrypted payload: Script to decrypt the payload: import struct import ctypes   enc = open('enc_payload', 'rb').read()   key, = struct.unpack('I', enc[0:4])   key1 = key key2 = key 53 53 key3 = key   dec = bytearray()   for i in xrange(len(enc)):   key = (key + (key >> 3) + 0x55555556) & 0xFFFFFFFF   key1 = (key1 + (key1 >> 5) + 0x44444445) & 0xFFFFFFFF   key2 = (key2 + 0xCCCCCCCC - (key2 << 7)) & 0xFFFFFFFF   key3 = (key3 + 0xDDDDDDDD - (key3 << 9)) & 0xFFFFFFFF   dec.append(ord(enc[i]) ^ (key + key1 + key2 + key3) & 0xFF)   d = bytes(dec)   uncompress_size, = struct.unpack('I', d[8:12])   buf_decompressed = ctypes.create_string_buffer(uncompress_size) final_size = ctypes.c_ulong(0) ctypes.windll.ntdll.RtlDecompressBuffer(2, buf_decompressed, ctypes.sizeof(buf_decompressed), ctypes.c_char_p(d[0x10:]), len(d), ctypes.byref(final_size))   open('dec_payload', 'wb').write(buf_decompressed) After decrypting the payload, the shellcode transfers control to the trojan, with the previously assembled structure st_mw acting as one of the parameters: Further, the trojan works in the same way as the backdoor BackDoor.PlugX.28. https://vms.drweb.com/virus/?lng=en&i=21507745 54 54 Trojan.Uacbypass.21 Added to the Dr.Web virus database: 2021-10-22 Virus description added: 2021-10-22 Packer: absent Compilation date: 2019-09-29 SHA1 hash: 7412b13e27433db64b610f40232eb4f0bf2c8487 Description This trojan is written in C. It elevates backdoor privileges. It also disguises itself as a legitimate process and uses a COM object to bypass User Account Control (UAC). In this way, it elevates the executable process’s privileges. Operating principle The trojan disguises as a legitimate process C:\Windows\explorer.exe via PEB (Process Environment Block). That’s how it fools the IFileOperation COM object into thinking it’s being called from a Windows Explorer shell. 55 55 35; Dr.WEB 35 36 37 38 34 468 41 42 43 45 46 47 46 49 58 51 52 53 54 55 56 5? HRESULT — cdecl bypass uac_with_cmdf{int file, int param) t DWORD proc_id; // esi FARPROC nt_query_information_process; // ebx HANDLE h_procs // esi _UNICODE_STRING *full_dll name; // eax MAPDST FARPROC rtl_init_unicode string; // esi WCHAR win_directory_tmp[260]; // [esp+Ch] [ebp-47@h] WCHAR path to explorer[260]; // [esp+214h] [ebp-268h] char process information[4]; // [espt+4lCh] [ebp-68h] PEB *peb; // [esp+428h] [ebp-5Ch] MAPDST HMODULE h_module ntdll; // [esp+434h] [ebp-48h] _DWORD explorer_exe[7]; // [espt+438h] [ebp-44h] _UNICODE_STRING *base dll name; // [esp+458h] [ebp-24h] CHAR str[28]; // [esp+46eh] [ebp-1ch] full dll name = 6; memset(path_to explorer, @, sizeof(path_to_explorer}); base dll name = @; memset(win_directory_tmp, @, sizeof(win_directory_tmp)); GetWindowsDirectoryWiwin_directory_tmp, @x1@4u); IstrcpyW(path_to_explorer, win_directory_tmp}; lstrcatW(path_to_explorer, &g slash); LOWORD(explorer_exe[1]) = 'p'; explorer_exe[5] = "e\@x'; LOWORD(explorer_exe[6]} = "\@"; *(Sexplorer_exe[1] + 2) = ‘o\e@l'; explorer_exe[@] = 'x\@e'; *(Sexplorer_exe[2] + 2) = '.\er\@e\er's HIWORD(explorer_exe[4]) = ‘e'; IlstrcatW(path_to_explorer, explorer_exe); proc_id = GetCurrentProcessId({}; peb = @; strcpy(str, “ntdll.d11"); strcpy(str, “ntdll.dll"); h_module ntdll = LoadLibraryA({str}; strcpy(str, "NtQueryInformationProcess”); nt_query information process = GetProcAddress(h_ module ntdll, str}; h_prec = OpenProcess(@xLFFFFFu, @, proc_id); if € h_proc && (nt_query_information_process)({h_proec, 6, process_information, 24, @) < @ ) CloseHandle(h_proc); peb = @; CloseHandle(h_proc); if ( peb ) peb = NtCurrentPeb(); full dil name = &NtCurrentPeb()->Ldr->InLoadOrderModuleList. Flink->FullD1l1Name; base _d1ll name = &full_dll_name[1]; strcpy(str, “RtlInitUnicodeString"); rtl_init_unicede string = GetProcAddress(h_module ntdll, str); (rtl_init_unicede_ string) (&peb->ProcessParameters->ImagePathName, path_to_ explorer); (rtl_init_unicede_string)(&peb->ProcessParameters->CommandLine, path_to_explorer); €rtl_init_unicode string){full dll name, path to explorer); €rtl_init_unicode_string){base_dll_name, explorer_exe); return elevate(file, param); 56 56 The trojan obtains a COM object to implement UAC bypass via privilege elevation (https://github.com/cnsimo/BypassUAC/blob/master/BypassUAC_Dll/dllmain .cpp): It allows Trojan.Uacbypass.21 to run the file that was passed to it as an argument as a legitimate Windows process: 57 57 35; Dr.WEB 1JHRESULT — cdecl elevate(int file, int param) alt 3| HRESULT v2; // esi 4) HRESULT result; // eax 5| IID iid; // [esp+ch] [ebp-11eh] 6| BIND_OPTS pBindOptions; // [esp+lCh] [ebp-19eh] 7| __intl28 we; // [esp+2ch] [ebp-Feh] 8) int v7; // [espt3ch] [ebp-E@h] S| _DWORD pszName[34]; // [esp+4eh] [ebp-Dch] 18 DWORD iid str[2@]; // [espt+Céh] [ebp-54h] 11) I¢MLuaUtil *ppv; // [esp+118h] [ebp-4h] 12 13] w2 = CoInitializeEx(®, 6u}; 14/ pov = @; 15] iid str[9] = "-\@5'; 16] LOWORD(iid str[2]) = "D'; 17| iid _str[@] = "6\e{'; 18} *(Siid str[12] + 2) = '4\@7\e@5\eE'; 19} iid str[11] = "A\e6': 28) LOWORD(iid str[19]) = ‘\@'; 21) iid str[16] = 'E\@5'; 22) *(&iid str[6] + 2) = "E\e4\e-\e7'; 23) iid str[18] = '}\ec'; 24] *(Siid str[2] + 2) = ‘4\e7\eD\e6'; 25] iid _str[1@] = '7\@B'; 26) *(&iid_ str[14] + 2) = "9\ee'; 27) iid str[17] = '4\@2'; 28) *(&iid str[4] + 2) = "@\ee\ec\e-'; 29) iid str[1] = "D\@E'; 308] HIWORD(iid str[15]) = ‘9°; 31| HIWORD(iid str[8]) = '7'; 32) LOWORD(iid str[12]) = "-'; 33) IIDFromString(iid_str, &iid)s; 34| v7 = @; 35| pBindOptions = 6164; 36| v6 = 6164; 37) if ( v2 += @ ) 38] { 39 LOWORD(pszMame[21]}) = '-"; 46 LOWORD(pszName[24]) = 41 LOWORD(pszName[16]) = "F's; 42 pBindOptions.cbStruct = 36; 43 DWORDI(vG) = 4; Ad *(&oszName[29] + 2) "E\OB\OF\O4' 5 45 HIWORD(pszMame[27]) "2° 46 LOWORD(pszName[11])} "hr" 47 HIWORD (pszMame[22]} "6 48 HIWORD(pszMame[31]) "C' 49 HIWORD(pszName[14]} = "3" 58 LOWORD(pszName[@]}) = "E'; 51 LOWORD (pszName[33])} 8; | Oo ‘ ee ee fe 58 58 35; Dr.WEB 52 53 5-4 55 56 a7 55 59 68 61 62 63 6-4 65 66 67 68 69 76 Fl 2 #3 f4 #5 76 iri 75 79 66 él a2 a3 5 pszName[6] ‘i\em'; pszName[5] "d\@a'; pszName[32] = "}\e7'; *(&pszName[21] + 2) = "3\e4'; pszName[15] = "S\@E'; pszName[9] = ‘a\er'; *(SposzName[16] + 2) = 'S\eF\e7\ec'; *(pszMame + 2) = “a\Ov\@e\el'; *(SpszName[11] + 2) = ‘'w\@e\en\e!'; ] + * (€oszName[26 2) = "1\8A'; pszName[28] = '2\@0'; pszName[2@] = '1\05'; pszMame[19] = ‘A\ea'; HIWORD(pszName[4]) = 's"5 *(Spsciame[24] + 2) = '-\@3\e6\ee'; pszName[8] = "t\s'; *(&pszName[13] + 2) = "{\er'; LOWORD(pszName[29]}) = "4"; pszName[23] = '-\er'; pszName[7] = "i\@n'; pszName[1@] = ‘o\et'; *(SoszNamel2] + 2) = "n\@o\ei\et'; HIWORD(pszName[18]} = "-"; result = CoGetObject(pszName, &pBindOptions, &iid, if (€ result ) return result; w2 = (ppv->lpVtbl->ShellExec)(ppv, file, param, @, if ( ppv ) (ppw->lpvtbl-sRelease) (ppv); return v2; &ppv) @, B); 59 59 Appendix. Indicators of Compromise SHA1 hashes Trojan.Loader.889 f783fc5d3fc3f923c2b99ef3a15a38a015e2735a: McUiCfg.dll Trojan.Loader.890 65f64cc7aaff29d4e62520afa83b621465a79823: SRVCON.OCX 8b9e60735344f91146627213bd13c967c975a783: CLNTCON.OCX 84d5f015d8b095d24738e45d2e541989e6221786: sti.dll 3d8a3fcfa2584c8b598836efb08e0c749d4c4aab: iviewers.dll Trojan.Loader.891 595b5a7f25834df7a4af757a6f1c2838eea09f7b: McUiCfg.dll Trojan.Loader.893 46e999d88b76cae484455e568c2d39ad7c99e79f: McUiCfg.dll Trojan.Loader.894 b1041acbe71d46891381f3834c387049cbbb0806: iviewers.dll Trojan.Loader.895 635e3cf8fc165a3595bb9e25030875f94affe40f: McUiCfg.dll Trojan.Loader.896 ff82dcadb969307f93d73bbed1b1f46233da762f: TmDbgLog.dll Trojan.Loader.898 429357f91dfa514380f06ca014d3801e3175894d: CLNTCON.OCX 60 60 Trojan.Loader.899 cc5bce8c91331f198bb080d364aed1d3301bfb0c: LDVPTASK.OCX BackDoor.PlugX.93 a8bff99e1ea76d3de660ffdbd78ad04f81a8c659: CLNTCON.OCX BackDoor.PlugX.94 5a171b55b644188d81218d3f469cf0500f966bac BackDoor.PlugX.95 b3ecb0ac5bebc87a3e31adc82fb6b8cc4fb66d63: netcfg.dll BackDoor.PlugX.96 a3347d3dc5e7c3502d3832ce3a7dd0fc72e6ea49 BackDoor.PlugX.97 36624dc9cd88540c67826d10b34bf09f46809da7 BackDoor.PlugX.100 16728655e5e91a46b16c3fe126d4d18054a570a1 BackDoor.Whitebird.30 abfd737b14413a7c6a21c8757aeb6e151701626a a5829ed81f59bebf35ffde10928c4bc54cadc93b Trojan.Siggen12.35113 4f0ea31a363cfe0d2bbb4a0b4c5d558a87d8683e: rapi.dll Trojan.Uacbypass.21 20ad53e4bc4826dadb0da7d6fb86dd38f1d13255 61 61 Program.RemoteAdmin.877 23873bf2670cf64c2440058130548d4e4da412dd: AkavMiqo.exe Tool.Frp a6e9f5d8295d67ff0a5608bb45b8ba45a671d84c: firefox.exe 39c5459c920e7c0a325e053116713bfd8bc5ddaf: firefox.exe Network indicators Domains webmail.surfanny.com www.sultris.com mail.sultris.com pop3.wordmoss.com zmail.wordmoss.com youtubemail.club clark.l8t.net blog.globnewsline.com mail.globnewsline.com IPs 45.144.242.216 45.147.228.131 46.105.227.110 5.183.178.181 5.188.228.53 103.30.17.44 103.93.252.150 103.230.15.41 103.251.94.93 104.233.163.136 159.65.157.100 180.149.241.88 185.105.1.226 62 62 192.236.177.250 209.250.241.35 Table of Contents Introduction Remote Rover Conclusion Operating Routine of Discovered Malware Samples BackDoor.PlugX.93 BackDoor.Siggen2.3622 BackDoor.Whitebird.30 Trojan.DownLoader43.44599 Trojan.Loader.891 Trojan.Loader.896 Trojan.Uacbypass.21 Appendix. Indicators of Compromise