In the first article in this multipart series, malware researchers on the Elastic Security Labs team give a short introduction about the REMCOS threat and dive into the first half of its execution flow, from loading its configuration to cleaning the infected machine web browsers.
Introduction
Elastic Security Labs continues its examination of high-impact threats, focusing on the internal complexities of REMCOS version 4.9.3 Pro (November 26, 2023).
Developed by Breaking-Security, REMCOS is a piece of software that began life as a red teaming tool but has since been adopted by threats of all kinds targeting practically every sector.
When we performed our analysis in mid-January, it was the most prevalent malware family reported by ANY.RUN. Furthermore, it remains under active development, as evidenced by the recent announcement of version 4.9.4's release by the company on March 9, 2024.
All the samples we analyzed were derived from the same REMCOS 4.9.3 Pro x86 build. The software is coded in C++ with intensive use of the std::string
class for its string and byte-related operations.
REMCOS is packed with a wide range of functionality, including evasion techniques, privilege escalation, process injection, recording capabilities, etc.
This article series provides an extensive analysis of the following:
- Execution and capabilities
- Detection and hunting strategies using Elastic’s ES|QL queries
- Recovery of approximately 80% of its configuration fields
- Recovery of about 90% of its C2 commands
- Sample virtual addresses under each IDA Pro screenshot
- And more!
For any questions or feedback, feel free to reach out to us on social media @elasticseclabs or in the Elastic Community Slack.
Loading the configuration
The REMCOS configuration is stored in an encrypted blob within a resource named SETTINGS
. This name appears consistent across different versions of REMCOS.
The malware begins by loading the encrypted configuration blob from its resource section.
To load the encrypted configuration, we use the following Python script and the Lief module.
import lief
def read_encrypted_configuration(path: pathlib.Path) -> bytes | None:
if not (pe := lief.parse(path)):
return None
for first_level_child in pe.resources.childs:
if first_level_child.id != 10:
continue
for second_level_child in first_level_child.childs:
if second_level_child.name == "SETTINGS":
return bytes(second_level_child.childs[0].content)
We can confirm that version 4.9.3 maintains the same structure and decryption scheme as previously described by Fortinet researchers:
We refer to the “encrypted configuration” as the structure that contains the decryption key and the encrypted data blob, which appears as follows:
struct ctf::EncryptedConfiguration
{
uint8_t key_size;
uint8_t key[key_size];
uint8_t data
};
The configuration is still decrypted using the RC4 algorithm, as seen in the following screenshot.
To decrypt the configuration, we employ the following algorithm.
def decrypt_encrypted_configuration(
encrypted_configuration: bytes,
) -> tuple[bytes, bytes]:
key_size = int.from_bytes(encrypted_configuration[:1], "little")
key = encrypted_configuration[1 : 1 + key_size]
return key, ARC4.ARC4Cipher(key).decrypt(encrypted_configuration[key_size + 1 :])
The configuration is used to initialize a global vector that we call g_configuration_vector
by splitting it with the string \x7c\x1f\x1e\x1e\x7c
as a delimiter.
We provide a detailed explanation of the configuration later in this series.
UAC Bypass
When the enable_uac_bypass_flag
(index 0x2e
) is enabled in the configuration, REMCOS attempts a UAC bypass using a known COM-based technique.
Beforehand, the REMCOS masquerades its process in an effort to avoid detection.
REMCOS modifies the PEB structure of the current process by replacing the image path and command line with the explorer.exe
string while saving the original information in global variables for later use.
The well-known technique exploits the CoGetObject
API to pass the Elevation:Administrator!new:
moniker, along with the CMSTPLUA
CLSID and ICMLuaUtil
IID, to instantiate an elevated COM interface. REMCOS then uses the ShellExec()
method of the interface to launch a new process with administrator privileges, and exit.
This technique was previously documented in an Elastic Security Labs article from 2023: Exploring Windows UAC Bypasses: Techniques and Detection Strategies.
Below is a recent screenshot of the detection of this exploit using the Elastic Defend agent.
Disabling UAC
When the disable_uac_flag
is enabled in the configuration (index 0x27
), REMCOS disables UAC in the registry by setting the HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\SystemEnableLUA
value to 0
using the reg.exe
Windows binary."
Install and persistence
When enable_install_flag
(index 0x3
) is activated in the configuration, REMCOS will install itself on the host machine.
The installation path is constructed using the following configuration values:
install_parent_directory
(index0x9
)install_directory
(0x30
)install_filename
(0xA
)
The malware binary is copied to {install_parent_directory}/{install_directory}/{install_filename}
. In this example, it is %ProgramData%\Remcos\remcos.exe
.
If the enable_persistence_directory_and_binary_hiding_flag
(index 0xC
) is enabled in the configuration, the install folder and the malware binary are set to super hidden (even if the user enables showing hidden files or folders the file is kept hidden by Windows to protect files with system attributes) and read-only by applying read-only, hidden, and system attributes to them.
After installation, REMCOS establishes persistence in the registry depending on which of the following flags are enabled in the configuration:
enable_hkcu_run_persistence_flag
(index0x4
)HKCU\Software\Microsoft\Windows\CurrentVersion\Run\
enable_hklm_run_persistence_flag
(index0x5
)HKLM\Software\Microsoft\Windows\CurrentVersion\Run\
enable_hklm_policies_explorer_run_flag
(index0x8
)HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\Explorer\Run\
The malware is then relaunched from the installation folder using ShellExecuteW
, followed by termination of the initial process.
Injection de processus
When the enable_process_injection_flag
(index 0xD
) is enabled in the configuration, REMCOS injects itself into either a specified or a Windows process chosen from an hardcoded list to evade detection.
The enable_process_injection_flag
can be either a boolean or the name of a target process. When set to true (1), the injected process is chosen in a “best effort” manner from the following options:
iexplorer.exe
ieinstal.exe
ielowutil.exe
Note: there is only one injection method available in REMCOS, when we talk about process injection we are specifically referring to the method outlined here
REMCOS uses a classic ZwMapViewOfSection
+ SetThreadContext
+ ResumeThread
technique for process injection. This involves copying itself into the injected binary via shared memory, mapped using ZwMapViewOfSection
and then hijacking its execution flow to the REMCOS entry point using SetThreadContext
and ResumeThread
methods.
It starts by creating the target process in suspended mode using the CreateProcessW
API and retrieving its thread context using the GetThreadContext
API.
Then, it creates a shared memory using the ZwCreateSection
API and maps it into the target process using the ZwMapViewOfSection
API, along with the handle to the remote process.
The binary is next loaded into the remote process by copying its header and sections into shared memory.
Relocations are applied if necessary. Then, the PEB ImageBaseAddress
is fixed using the WriteProcessMemory
API. Subsequently, the thread context is set with a new entry point pointing to the REMCOS entry point, and process execution resumes.
Below is the detection of this process injection technique by our agent:
Setting up logging mode
REMCOS has three logging mode values that can be selected with the logging_mode
(index 0x28
) field of the configuration:
- 0: No logging
- 1: Start minimized in tray icon
- 2: Console logging
Setting this field to 2 enables the console, even when process injection is enabled, and exposes additional information.
Cleaning browsers
When the enable_browser_cleaning_on_startup_flag
(index 0x2B
) is enabled, REMCOS will delete cookies and login information from the installed web browsers on the host.
According to the official documentation the goal of this capability is to increase the system security against password theft:
Currently, the supported browsers are Internet Explorer, Firefox, and Chrome.
The cleaning process involves deleting cookies and login files from browsers' known directory paths using the FindFirstFileA
, FindNextFileA
, and DeleteFileA
APIs:
When the job is completed, REMCOS prints a message to the console.
It's worth mentioning two related fields in the configuration:
enable_browser_cleaning_only_for_the_first_run_flag
(index0x2C
)browser_cleaning_sleep_time_in_minutes
(index0x2D
)
The browser_cleaning_sleep_time_in_minutes
configuration value determines how much time REMCOS will sleep before performing the job.
When enable_browser_cleaning_only_for_the_first_run_flag
is enabled, the cleaning will occur only at the first run of REMCOS. Afterward, the HKCU/SOFTWARE/{mutex}/FR
registry value is set.
On subsequent runs, the function directly returns if the value exists and is set in the registry.
That’s the end of the first article. The second part will cover the second half of REMCOS' execution flow, starting from its watchdog to the first communication with its C2.