OpenAI agents made 15,000–18,000 autonomous edits to a German wiki over three months, evading moderation and echoing tactics seen in the Hugging Face breach.
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.
Authorities in Australia said Wednesday that they arrested two men accused of participating in cybercrimes for TeamPCP, a prolific group of hackers that, over nine months, has carried out a relentless series of supply-chain attacks that infected more than 1,000 organizations worldwide.
In a statement, the Australian Federal Police said the two men were arrested and charged with 14 offenses. The statement said the men were members of TeamPCP, which by the authorities’ count, compromised more than 1,000 organizations worldwide. The statement didn’t identify the men, except to say they lived in the Western Australian towns of Cottesloe and Mandurah. KrebsOnSecurity, citing a lengthy investigation, provided what it reports to be both defendants' names, along with an extensive background of their lives and the mistakes that led to their downfall.
The hacks that keep on hacking
TeamPCP has vexed law enforcement officials and security personnel around the world since it emerged in December. The group is best known for a sustained series of supply-chain attacks that laced open source software with malware that self-propagated from one package to another. The viral infections worked by targeting organizations’ CI/CD pipelines, which are used to rapidly develop, update, and deploy software.
Manchester Airports Group data breach affected 8.7 million customers at three UK airports, exposing mainly emails, phone numbers, postcodes and vehicle details.
OpenAI says internal AI agents gained internet access, exploited vulnerabilities and accessed Hugging Face systems during July cybersecurity evaluations.
More than 100 U.S. water systems faced cyberattacks in July, with exposed PLCs and cellular modems giving hackers access to some utility controls and operations, CISA has now revealed.
A phishing campaign targeting attendees of Black Hat and DEF CON conferences involved distributing information-stealing malware and remote access malware via a malicious Google Doc and fake DocSend installers for macOS and Windows. The Huntress team learned about the phishing attack after one of its researchers received a direct message on X on August 9. [...]
OAuth Exploitation Russian threat actors are shifting away from plain old password theft and moving toward legitimate platform features like OAuth permissions. Groups like UNC6293 and UNC7005 take their time...
The goal is to let private companies take on foreign cyber criminals, but the unprecedented approach raises plenty of questions about oversight and liability.
Scams are evolving with technology. Generative AI is a game changer for scammers and has been blamed for the rise in increasingly sophisticated phishing campaigns. It is also enabling scammers to add credibility to their schemes by providing a quick, cheap, and easy way to create fake websites, videos, documents, and photographs. Tasks that once [...]
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.
In July 2026, Kaspersky experts detected a new attack by the Head Mare group. Previously, we classified them as hacktivists, but now we define them as an APT group due to the sophistication of their TTPs and the absence of destructive activity (encryption, wiping) in the targeted infrastructures. In this latest campaign, the attackers exploited a chain of vulnerabilities in the TrueConf video conferencing server and replaced the original TrueConf client installers with infected versions that installed the PhantomCore malware on the system.
An investigation of the compromised server revealed that the attackers used a combination of two new vulnerabilities (assigned the internal identifiers KLCERT-26-057 and KLCERT-26-058), allowing them to execute arbitrary code with the highest privileges.
The attack occurs in several stages:
The attackers connect to the TrueConf server without prior authorization via port 4307/TCP, which, according to the product documentation, is open by default. The attack targets TrueConf servers running versions 5.3.X through 5.3.9, 5.4.X through 5.4.9, and 5.5.X through 5.5.5.
Once connected, attackers call a server function to transmit a malicious script and execute it on the server. The vulnerability that allows this stage of the attack to be carried out has been assigned the internal identifier KLCERT-26-057.
The received script runs on the TrueConf server in an isolated environment. By default, operating system functions are not accessible in this environment, which should limit the capabilities of the executed code.
To escape the isolated environment, attackers exploit a second vulnerability, assigned the internal identifier KLCERT-26-058. Exploiting this vulnerability allows them to bypass the restrictions of the isolated environment and proceed to execute commands in the context of the operating system.
Once the environment’s restrictions are bypassed, attackers gain the ability to execute arbitrary code on the server with the privileges of the NT AUTHORITY\SYSTEM account.
Once they have gained elevated privileges, attackers replace the file …\public\js\locale.php with a web shell, which can be used for subsequent remote control of the compromised server.
This web shell was used for the following activities:
collecting data on the IT infrastructure;
gaining privileged access to the TrueConf database;
replacing the original TrueConf Client distribution with an infected version containing the PhantomCore backdoor.
The vulnerabilities exploited by the attackers were patched by the vendor in the latest TrueConf Server updates (versions 5.3.9, 5.4.9, and 5.5.5). These updates were released on June 18, 2026.
The PhantomCore backdoor was successfully detected by Kaspersky solutions.
To automatically launch the malware after the system boots, a registry key is created: HKEY_CURRENT_USER\Software\Classes\CLSID\{0340F119-A598-4ed9-B0AC-6F6A12D3E755}\InprocServer32, with the value set to the path to the malicious program’s file.
Using a web shell, in addition to PhantomCore, the attackers load a backdoor that we have named PhantomGraph, consisting of two modules:
SysExcSvc.dll is responsible for receiving commands from the attackers and transmitting the results of their execution. The attackers used an account on Microsoft OneDrive cloud storage as their command-and-control (C2) server.
SysReadSvc.dll reads the command transmitted by the first module, executes it, and saves the execution result.
To establish persistence on the system, the attackers execute a Base64-encoded PowerShell command that installs SysExcSvc.dll and SysReadSvc.dll as Windows services. We believe the attackers deliberately split this malicious command into two components to make it harder to detect using EDR tools. Additionally, the program’s code partially matches that of PhantomCore, indicating that it belongs to Head Mare’s arsenal.
We also managed to identify the commands executed by the attackers when connecting to the backdoor. The SysReadSvc module executes commands using a BATCH file. Example of execution:
In addition, we discovered several commands that did not work due to the attackers’ typos and encoding issues.
We are observing several active Head Mare campaigns targeting Russian organizations across various industries: instrument manufacturing, electronics, transportation, energy,
IT, and software development. The attackers distribute their backdoors using various methods, including phishing, exploiting public web servers, or through a subcontractor.
We recommend that all organizations using TrueConf software install the latest server version (versions 5.3.9, 5.4.9, and 5.5.5) in accordance with the vendor’s recommendations.
We also recommend verifying that the client distributions downloaded from the TrueConf server used by your organization have a valid TrueConf digital signature and have not been tampered with. The malicious distributions we detected did not have a valid digital signature. You can also verify authenticity on the vendor’s website.
Important: Even if your organization does not use a TrueConf server, your employees may connect to compromised TrueConf servers belonging to business partners to participate in online meetings and download infected installation packages.
Specifically, activity involving the replacement of the legitimate file …\public\js\locale.php with a web shell, as well as the deletion of entries from TrueConf event logs, is detected by the rule unusual_php_file_creation_from_trueconf_process.
Downloading a file containing the PhantomCore backdoor via the replaced legitimate file …\public\js\locale.php is detected by KEDR Expert with the rule unusual_file_creation_from_trueconf.
Activity related to the installation of an infected TrueConf client installer containing the PhantomCore backdoor is detected by KEDR Expert using the unsigned_trueconf_installer rule.
Creation of suspicious files by TrueConf Server processes.
Execution of a TrueConf Client installer file that lacks a software developer’s signature.
Suspicious process chains associated with TrueConf Client executables and TrueConf Client update executables.
Registration of suspicious libraries in the HKEY_CURRENT_USER\Software\Classes\CLSID\ registry key.
Actions related to retrieving information about the lsass.exe process.
Memory dump creation for the lsass.exe process using the comsvcs.dll library.
Accessing the memory of the lsass.exe process.
Creating tunnels using the ssh process.
To protect companies using our Kaspersky SIEM system, a general set of rules is available in the product repository that allows detection of the following techniques:
Creation of suspicious files in the C:\Windows\System32\inetsrv\* directory: R405_07_File write to IIS native modules folder or OWA via WriteData.
Creating a memory dump of the lsass.exe process using the comsvcs.dll library: R233_04_Process memory dump via comsvcs.dll.
Accessing the memory of the lsass.exe process: R262_Suspicious access to the LSASS process.
We also recommend paying attention to the following events when developing your own detection rules or conducting threat hunting:
Registration of suspicious libraries in the registry key \Software\Classes\CLSID\{0340F119-A598-4ed9-B0AC-6F6A12D3E755}\InprocServer32:
(DeviceEventClassID = '4657' OR DeviceEventClassID = '13')
AND FileName like '%\Software\Classes\CLSID\{0340F119-A598-4ed9-B0AC-6F6A12D3E755}%' AND DeviceCustomString6 = 'InprocServer32'
Creating the SysExcSvc and SysReadSvc services to run executables from temporary directories in the background via cmd:
DeviceEventClassID = '4697'
AND (DestinationServiceName = 'SysExcSvc' OR DestinationServiceName = 'SysReadSvc')
AND match (FileName, '.*cmd\s+\/c.*temp\\cmd_cmd_.*\.bat.*')
Creation of suspicious processes originating from the TrueConf update process (trueconf_windows_update.exe)
(DeviceEventClassID = '4688' OR DeviceEventClassID = '1')
AND SourceProcessName LIKE '%\trueconf_windows_update.exe'
For the detection rules to work correctly, ensure that events from Windows systems are received in full, including Security events 4688, 4663, 4657, and 4697 and Sysmon events 1, 7, 11, and 13.
Kaspersky links OctLurk and SilkLurk to cyberespionage attacks stealing passwords, emails and files from government systems in six countries since January 2025.
Cisco Talos found hackers using simple authorization claims to bypass AI guardrails, build DDoS attack tools, steal credentials and access live camera services.
Shai-Hulud npm worm spreads through Keyv and hundreds of packages with 2 billion monthly downloads, stealing npm, GitHub, cloud and CI credentials in real time.
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.