09/23/2025 | News release | Distributed by Public on 09/23/2025 09:06
Zscaler Blog
Get the latest Zscaler blog updates in your inbox
SubscribeZscaler ThreatLabz has identified a new malware family that we named YiBackdoor, which was first observed in June 2025. The malware is particularly interesting because it contains significant code overlaps with IcedID and Latrodectus. Similar to Zloaderand Qakbot, IcedID was originally designed for facilitating banking and wire fraud. However, IcedID has since been repurposed to provide initial access for ransomware attacks. The exact connection to YiBackdoor is not yet clear, but it may be used in conjunction with Latrodectus and IcedID during attacks. YiBackdoor enables threat actors to collect system information, capture screenshots, execute arbitrary commands, and deploy plugins.
In this section, the features and capabilities of YiBackdoor are described along with the code similarities with IcedID and Latrodectus.
ANALYST NOTE: YiBackdoor generates and uses pseudo-random values at different stages (e.g. for generating the registry persistence value name). The malware implements custom algorithms for deriving random values, which are primarily based on the bot ID (used as a seed) combined with an implementation of Microsoft's Linear Congruential Generator (LCG). Since not all pseudo-random values are generated using a single method, ThreatLabz reversed each function and ported them to Python individually. To ensure consistency and clarity throughout this blog, the random values that are referenced can be derived using the Python script available in the ThreatLabz GitHub repository.
YiBackdoor includes a limited set of anti-analysis techniques with most of them targeting virtualized environments, and by extension, malware sandboxes. The malware employs the following anti-analysis methods:
[[nodiscard]] bool isHyperVisor() { uint64_t timer1 = 0; uint64_t timer2 = 0; int loop_counter = 16; int cpuInfo[4] = { 0 }; while (loop_counter) { SwitchToThread(); uint64_t first_rdtsc_timer_value = __rdtsc(); __cpuid(cpuInfo, 1); timer1 += __rdtsc() - first_rdtsc_timer_value; SwitchToThread(); uint64_t second_rdtsc = __rdtsc(); uint64_t third_rdtsc = __rdtsc(); timer2 += ((third_rdtsc
It is worth noting that YiBackdoor stores the aforementioned information internally, but does not use the information or transmit it to the C2 server. As a result, the detection methods outlined above currently have no impact on the code's behavior.
There are several actions that YiBackdoor performs during the initialization phase including injecting code into a remote process and establishing persistence.
YiBackdoor first checks for existing instances of itself by attempting to create a mutex with a host-based name. If the mutex already exists, indicating another instance is active, YiBackdoor will terminate execution.
Before proceeding to the core functionality, YiBackdoor ensures that it is running within an injected process. YiBackdoor determines this by checking whether its current memory address falls within the memory range of any loaded DLLs. If it does, YiBackdoor creates a new svchost.exe process and injects its code into it.
The injection begins with YiBackdoor allocating memory in the remote svchost.exe target process and copying its code into that new region. YiBackdoor patches the Windows API function RtlExitUserProcesswith assembly code that pushes YiBackdoor's entry point on the stack, which is then followed by a return instruction. Thus, when the RtlExitUserProcessfunction is called, the process execution flow will be redirected to the YiBackdoor's entry point. Interestingly, the svchost.exe target process is created without any special flags (e.g., in a suspended state). However, YiBackdoor does have enough time to inject its code between the process creation and termination. Since the RtlExitUserProcessfunction is hooked, the malware's code executes just as the target process is about to terminate. This injection technique may allow YiBackdoor to evade detection by some security products.
After completing the code injection phase, YiBackdoor proceeds to establish persistence on the compromised host using the Windows Run registry key. YiBackdoor first copies itself (the malware DLL) into a newly created directory under a random name. Next, YiBackdoor adds regsvr32.exe malicious_pathin the registry value name (derived using a pseudo-random algorithm) and self-deletes to hinder forensic analysis.
YiBackdoor contains an embedded configuration stored in an encrypted state. The configuration blob is decrypted and initialized at runtime. The decryption algorithm uses a 64-byte string as the key, as shown in the decryption routine below.
def decrypt(data: bytes, key: bytes) -> bytearray: decrypted_config = bytearray() for i in range(len(data)): x = i % len(key) y = (i + 1) % len(key) cipher = key[x] + key[y] cipher = (cipher ^ data[i]) & 0xFF decrypted_config.append(cipher) rotation_x = ror(n=key[x] >> (key[y] & 7), bits=key[x] > ( rotation_x & 7), bits=key[y]
The decrypted configuration data includes the following information:
The configuration's structure is provided below.
#pragma pack(push, 1) struct configuration { char C2s[300]; char response_triple_des_key_table[192]; char request_triple_des_key_table[192]; char triple_des_iv[128]; uint32_t uri1; uint32_t pad; uint32_t uri2; char botnet_id[64]; }; #pragma pack(pop)
ANALYST NOTE: Before decrypting the configuration data, YiBackdoor ensures that the encrypted configuration does not start with the hardcoded string "YYYYYYYYYY". If a match is found, the embedded configuration data is considered corrupted and the execution stops. ThreatLabz has not been able to confirm the reason for this check yet. Moreover, two of the three configuration C2s are local IP addresses, which further supports the argument that YiBackdoor is still in a development or testing phase.
Before initializing a network session with the C2, YiBackdoor derives the C2 URL by reading the following values from the decrypted configuration blob.
Thus, the C2 URL is structured as http(s)://C2/bot_id/uri1/uri2.
Next, YiBackdoor creates a JSON packet that contains the host's system time (UTC format) and username. The JSON packet is then encrypted using the TripleDES encryption algorithm. The creation of encryption/decryption keys along with the IV is quite unique. The configuration blob includes three strings with each one of them used for deriving the encryption key, decryption key, and IV. However, YiBackdoor does not use their entire values. Instead, it uses the current day of the week as an offset to calculate the starting address of the target value. Using this approach, YiBackdoor manages to have dynamic (and different) encryption keys per day and as a result makes the network traffic more resilient against static-based signatures. This algorithm is shown in the figure below:
Figure 1: Network dynamic key derivation function for YiBackdoor.
The encrypted output is then Base64-encoded and appended to the HTTP header X-tag, and sent in an HTTP GET request.
The C2 response decryption process is similar. YiBackdoor verifies the presence of the HTTP header X-tag and decrypts it. The decrypted header contains the same information that was included in the HTTP request. YiBackdoor then decrypts and parses the HTTP body data, which contains incoming commands, which are in a JSON format.
YiBackdoor supports the commands described in the table below.
Command Name |
Command Parameters |
Description |
Systeminfo |
None |
Collects the following system information:
|
screen |
None |
Takes a screenshot of the compromised host's desktop. |
CMD |
|
Executes a system shell command using cmd.exe. |
PWS |
|
Executes a system shell command using PowerShell. |
plugin |
|
Passes a command to an existing plugin to execute based on its name and reports the result to the C2 server. |
task |
|
Initializes and executes a new plugin. If the plugin already exists, then reload the plugin using the data that was received. |
Table 1: YiBackdoor network commands.
Note that the command names above use inconsistent casing (e.g., camel case, lowercase, and uppercase).
The structures (in C format) that YiBackdoor uses to parse both tasks received and network commands are shown below.
enum Command { system_info = 0x3, screenshot = 0x4, execute_new_plugin = 0x5, execute_loaded_plugin = 0x8, execute_cmd = 0x9, execute_powershell = 0xA, }; struct custom_string { char *string; size_t size; size_t capacity; }; #pragma pack(push, 1) struct task_info { uint32_t task_id; Command cmd_id; uint32_t unknown_ID; custom_string command_parameter; custom_string plugin_name; uint32_t timeout_time; }; #pragma pack(pop)
YiBackdoor reports the output of each command to the C2 by sending an HTTP POST request. Each command status packet is in a JSON format and includes the following information:
The reported output is summarized in the table below.
Network Command |
Reported Information |
Systeminfo |
|
screen |
|
task |
|
plugin |
|
CMD/PWS |
|
Table 2: YiBackdoor command status messages.
ANALYST NOTE: The task status for the network command 'task' is always set to true (success) regardless of the plugin's loading status.
YiBackdoor stores each plugin that is received locally in the Windows temporary folder using a random filename with the file extension .bin. The malware identifies a target plugin by validating the filename against its own filename generation algorithm. The plugins are reloaded each time YiBackdoor is executed.
Each plugin is stored in an encrypted format. The following Python code snippet represents the encryption/decryption algorithm.
def fix_key(key: bytearray, x: int, y: int) -> bytearray: temp_val = key[y:y + 4] temp_val = int.from_bytes(temp_val, byteorder="little") rot_val = (temp_val & 7) & 0xFF temp_val = key[x:x + 4] temp_val = int.from_bytes(temp_val, byteorder="little") temp_val = ror(temp_val, rot_val) & 0xFFFFFFFF temp_val += 1 temp_val &= 0xFFFFFFFF temp_val_x = temp_val.to_bytes(4, byteorder="little") rot_val = (temp_val & 7) & 0xFF temp_val = key[y:y + 4] temp_val = int.from_bytes(temp_val, byteorder="little") temp_val = ror(temp_val, rot_val) & 0xFFFFFFFF temp_val += 1 temp_val &= 0xFFFFFFFF temp_val_y = temp_val.to_bytes(4, byteorder="little") temp_key = key[:x] + temp_val_x + key[x + 4:] temp_key = temp_key[:y] + temp_val_y + temp_key[y + 4:] return temp_key def crypt_plugin(data: bytes, key: int) -> bytes: decrypted_plugin = [] for i in range(len(data)): x = (i & 3) y = ((i + 1) & 3) c = key[y * 4] + key[x * 4] c = (c ^ data[i]) & 0xFF decrypted_plugin.append(c.to_bytes(1, byteorder="little")) key = fix_key(key, x * 4, y * 4) return b''.join(decrypted_plugin)
YiBackdoor manages and parses any plugins by using the structures provided below.
#pragma pack(push, 1) struct struct_plugin_execution_info { uint32_t unknown_field; uint32_t plugin_id; uint8_t do_start_plugin; char plugin_disk_name[16]; IMAGE_DOS_HEADER* plugin_memory_data; }; #pragma pack(pop) struct plugin { custom_string plugin_name; void *plugin_entry_address; void *plugin_data; void *sizeof_plugin_data; struct_plugin_execution_info *plugin_execution_info; void *mapped_plugin_memory_address; }; struct plugin_manager { plugin *plugins[1]; uint64_t number_of_plugins; uint64_t max_allowed_plugins; };
ThreatLabz observed notable code overlaps between YiBackdoor, IcedID, and Latrodectus. IcedID is a malware family that consists of several different components such as a downloader (which has gone through various updates in the past), a main module backdoor, and a main module loader. These similarities are present in both critical and non-critical parts of YiBackdoor's code.
The code similarities between YiBackdoor, IcedID, and Latrodectus are the following:
Figure 2: Comparison of YiBackdoor and IcedID GZIP decryption routines.
In summary, YiBackdoor is a newly discovered backdoor that has been active since at least June 2025. Based on code similarities observed by ThreatLabz during analysis, ThreatLabz assesses with medium to high confidence that there is a connection between the developers of YiBackdoor, IcedID, and Latrodectus. YiBackdoor by default has somewhat limited functionality, however, threat actors can deploy additional plugins that expand the malware's capabilities. Given the limited deployment to date, it is likely that threat actors are still developing or testing YiBackdoor.
The Zscaler Cloud Sandbox has been successful in detecting this campaign. The figure below depicts the Zscaler Cloud Sandbox, showing detection details for YiBackdoor.
Figure 3: Zscaler Cloud Sandbox report for YiBackdoor.
Indicator |
Description |
af912f6f4bea757de772d22f01dc853fc4d7ab228dc5f7b7eab2a93f64855fbe |
YiBackdoor SHA256 |
http:/136.243.146[.]46:8898 |
YiBackdoor C2 |
Disclaimer: This blog post has been created by Zscaler for informational purposes only and is provided "as is" without any guarantees of accuracy, completeness or reliability. Zscaler assumes no responsibility for any errors or omissions or for any actions taken based on the information provided. Any third-party websites or resources linked in this blog post are provided for convenience only, and Zscaler is not responsible for their content or practices. All content is subject to change without notice. By accessing this blog, you agree to these terms and acknowledge your sole responsibility to verify and use the information as appropriate for your needs.
Technical Analysis of Zloader Updates
Tracking 15 Years of Qakbot Development
Operation Endgame: Up In Smoke
By submitting the form, you are agreeing to our privacy policy.