- sha256: 0ebdcedc0afeec0cfeb024ff14c30df57d5d05eb
- sha256: 50e5ad854a8504f3202af9c05223766872859521
- sha256: 574cae4767378b8e90df49bdd5182d63e893a35c
- sha256: 77046282917539e33d61e7ea7ec01d6ae4df40f4
- sha256: e99b52cfd83601de8ec144097c338e9aab654d02
- sha256: ed4e00891f6688be21f872ae12e7dbd6872ff69c
Description
A malicious program for OS Windows and Linux written in Go and packed using the Garble packer. Classified as a Remote Access Trojan, it has several characteristic features: all exchanges with the C2-server are serialized using Protocol Buffers, the Linux version is bundled with two rootkits, and its configurations are encrypted and hosted on several public platforms (GitHub, GitLab and a Chinese blog).
Operating routine
Depending on the OS installed on the compromised machine, the attack chain has a different number of stages.
Windows version
Stage 1. Decrypting the program name that the trojan is masquerading as and the URL of the remote repository containing the configuration.
Stage 2. Collecting system information and checking whether the trojan is running in a virtual machine. System information is stored in the gMWASkoS_WinSystemInfo struct. The struct is populated by running the systeminfo.exe utility:
struct gMWASkoS_WinSystemInfo
{
string HostName;
string OSName;
string OSVersion;
string OSManufacturer;
string OSConfiguration;
string OSBuildType;
string RegisteredOwner;
string RegisteredOrganization;
string ProductID;
string OriginalInstallDate;
string SystemBootTime;
string SystemManufacturer;
string SystemModel;
string SystemType;
_slice_string Processors;
string BIOSVersion;
string WindowsDirectory;
string SystemDirectory;
string BootDevice;
string SystemLocale;
string InputLocale;
string TimeZone;
string TotalPhysicalMemory;
string AvailablePhysicalMemory;
string VirtualMemoryMaxSize;
string VirtualMemoryAvailable;
string VirtualMemoryInUse;
_slice_string PageFileLocations;
string Domain;
string LogonServer;
_slice_string Hotfixs;
_slice_string NetworkCards;
string HyperVRequirements;
};
Stage 3. Downloading the configuration from a remote repository.
The configuration arrives as a hexadecimal string, encrypted using XOR. The key is embedded in the trojan code.
Stage 4. Receiving commands.
After the handshake, the trojan sends system information to the C2 server. It then waits for a packet containing the PONG command (all available commands are described in the table below). All commands are encrypted using the algorithm described above. After decryption, the packets are parsed using Protocol Buffers and then piped to the switch-case statement. The trojan waits for the arrival of commands until the Kill_Self command is delivered. After receiving the Kill_Self command, the trojan removes itself using the command-line interpreter.
Linux version
The trojan code has a number of functions; the main functionality is implemented in the main_setup() and main_start_networking() functions (names are arbitrary).
The main_setup() function
Stage 1. Checking the availability of the following encrypted strings
- a list of URLs where the trojan configuration is hosted. Threat actors are using the CDNs of GitLab and GitHub
- the name of the application the trojan is masquerading as (8_Bitrue_linux_amd64__console, linux-t, yundun, etc.)
Stage 2. Checking the boolean global static variable; if it is set to true, the trojan log will be written to the /tmp/3391 directory.
Stage 3. Checking the APACHESC variable. If it is not empty, its contents are executed as native shell code.
Stage 4. Collecting information about the system and filling the session_info struct:
struct session_info
{
string os; //generic OS name, embedded in code ("linux")
string arch; //CPU architecture name, embedded in executable
string OSName; //OS name obtained by parsing NAME field in /etc/os-release
string OSVersion; //OS version obtained by parsing VERSION field in /etc/os-release
string OSKernel; //kernel version obtained by executing uname
string OSHost; //host name obtained by executing uname
string MachineId; //MD5 hash of the concatenation of the OSName+OSVersion+OSKernel+OSHost values
string Networks;// list of network interfaces present in the system
string Env; // shell context
__int64 pid__h;
string uid; //UUID
string User; //username obtained by executing the uname command
string ExeFile; //path to executable
bool in_docker; //check whether trojan is launched in Docker by checking for presence of /.dockerenv file
bool rootkits_loaded; //set in special cases
string WinSySInfo;
};
Stage 5. Measuring function execution time as an anti-debugging technique.
Stage 6. Decrypting strings. At this stage, the trojan decrypts the URL containing the settings and the name of the application it is masquerading as. The cipher is an XOR. The key to the cipher is a string embedded in the trojan body.
Stage 7. Passing the URL to main_start_networking().
The main_start_networking() function
Stage 1. Obtaining and decrypting settings. The trojan accesses the server where its configuration is being stored and downloads the settings from there. The settings are encrypted hexadecimal values contained in the template_begin and template_end substrings. The decryption procedure is similar to the one described above.
Configuration example
{"url":"https://103.230.15.187:80/statics/css/",
"tls":true,"tcp_addr":"103.230.15.187:443",
"init_path":"88353","rpc_path":"51362",
"assets_path":"75139","header_user_agent":"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36",
"template_begin":"8f11d77c1190d8",
"template_end":"2596aa623fce26cb1b072034ee07c3a050fbf64b9fcd76fc",
"sleep":15,"jitter":20}
Stage 2. Downloading and installing rootkits if the global boolean static variable is set.
- Downloading and installing eBPF loader and eBPF rootkit. For this purpose the following request is executed %URL_FROM_CONFIG%/%ASSETS_PATH_FROM_CONFIG/%HARCODED_FILENAME% with the header_user_agent parameter from the configuration.
- Setting variables _LANG_ID, X_LANG_BTF (optional btf file) and X_LANG_LIB (contains the path to the main eBPF executable file)
- Starting the eBPF loader, which is an ELF file written with cgo. Its task is to read the path to the main executable from the X_LANG_LIB variable, which is implemented as a shared library (.so). It is run using the standard calls dlopen() and dlsym().
- Loading the eBPF rootkit, which is a modified pidhide rootkit from the bad-bpf project. Its source code was modified to contain an array, which is a whitelist of process IDs that do not need to be hidden.
Stage 3. Installing the LKM rootkit. If the installation of the eBPF rootkit was successful, the Diamorphine LKM rootkit is downloaded. For this to happen, a Sec header is added to the HTTP request, the value of which is the encrypted version of the OS kernel. Attackers store on their C2 servers a set of rootkits compiled for different kernel versions. The rootkit is installed by executing the insmod command. The rootkit hides all files and directories prefixed with aclsmos_.
Rootkits for the following kernel versions have been observed:
- 4.14.105 SMP mod_unload modversions
- 4.15.0-143-generic SMP mod_unload modversions
- 4.19.0-22-amd64 SMP mod_unload modversions
- 4.4.0-21-generic SMP mod_unload modversions
- 5.10.60-9.al8.x86_64 SMP mod_unload modversions
- 5.13.5-1.el7.elrepo.x86_64 SMP mod_unload modversions
- 5.15.0-50-generic SMP mod_unload modversions
- 5.4.0-107-generic SMP mod_unload modversions
- 6.1.23-36.46.amzn2023.x86_64 SMP preempt mod_unload modversions
- 6.2.0-20-generic SMP preempt mod_unload modversions
- 5.4.0-107-generic SMP mod_unload modversions
- 5.4.0-107-generic SMP mod_unload modversions
- 5.4.0-107-generic SMP mod_unload modversions
The final element of the infection chain is a remote access trojan that supports the following commands:
Command | Code | Windows | Linux | Parameters | Description |
---|---|---|---|---|---|
PONG | 9 | + | + | Stub, used to confirm that the connection is operational | |
set_id | 10 | + | + | string ServerId | Sets the server ID (used only when collecting system information) |
Set_sleep | 11 | + | + | uint32 Sleep, uint32 Jitter |
Sets the interval between requests to the C2 server |
switch_TCP | 12 | + | + | Enables the receipt of commands over TCP using the tcp_addr field from the configuration | |
TCP_Tunnel | 13 | + | + | string Target | Implements a TCP tunnel |
switch_HTTP | 14 | + | + | Disables the transfer of commands via TCP; communication with the C2 server continues using the default method, i.e., the URL from the configuration | |
ssh_tunnel | 15 | + | + | string Shell, string Password, string Env, string PreCmd |
Implements an SSH tunnel |
Socks5_tunnel | 16 | + | + | string Target | Implements an SOCK5 tunnel |
RunCode | 17 | + | + | uint32 Checksum, bytes Code |
Checks the integrity of the payload, using CRC32 |
KillSelf | 18 | + | + | Terminates the trojan process | |
Reload_LKM | 19 | + | + | Reboots the rootkit; the Windows version sends system information to the C2 server | |
RunCmd | 20 | + | + | int64 CmdId, int64 Timeout, string Cmd, string Args, string Envs |
Contains a shell script for execution and a CRC32 call to check the integrity of the script |
Mitre matrix
Stage | Technique |
---|---|
Defense evasion (TA0005) | Virtualization/Sandbox evasion (T1497) |
Discovery (TA0007) |
Query registry (T1012) System information discovery (T1082) Virtualization/Sandbox evasion (T1497) |
Command and control (TA0011) |
Application layer protocol (T1071) Non-application layer protocols (T1095) Encrypted channel (T1573) Protocol tunneling (T1572) |