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