❌

Reading view

Operation FlashHole: Lazarus breaks security, defies control

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

Operation FlashHole attack flow during initial compromise

Operation FlashHole attack flow during initial compromise

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

Background

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

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

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

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

Initial infection vector

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

Appearance of the malicious websites

Appearance of the malicious websites

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

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

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

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

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

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

Full infection chain

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

Initial infection chain

Initial infection chain

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

Expanded infection chain after gaining persistence

Expanded infection chain after gaining persistence

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

Malware set

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

TechHub – the start of the chain

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

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

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

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

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

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

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

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

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

Cubridge – the framework orchestrator

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

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

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

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

The predefined word list for page2 is shown below.

article, list, story

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

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

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

Cubridge's communication sequence

Cubridge’s communication sequence

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

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

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

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

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

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

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

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

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

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

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

TouchHub installs a service for TouchShift

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

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

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

TouchShift, a prevalent loader employed across multiple infection chains

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

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

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

ClientHub

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

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

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

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

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

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

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

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

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

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

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

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

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

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

LPEClient

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

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

Timeline of LPEClient

Timeline of LPEClient

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

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

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

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

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

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

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

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

Data relay flow between LPEClient and C2 via ClientHub

Data relay flow between LPEClient and C2 via ClientHub

Comparison of plugins and modules

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

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

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

Infrastructure

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

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

Victims

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

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

Attribution and links to other campaigns

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

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

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

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

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

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

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

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

Overlaps between the recent attacks by Lazarus Group

Overlaps between the recent attacks by Lazarus Group

Conclusion

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

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

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

Indicators of compromise

File hashes

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

ClientHub Loader
ff07641dc56914dd90ee0bd7404d7ad7               liblzma.dll

File paths

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

C2 servers

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

  •  

Operation FlashHole: Lazarus breaks security, defies control

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

Operation FlashHole attack flow during initial compromise

Operation FlashHole attack flow during initial compromise

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

Background

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

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

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

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

Initial infection vector

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

Appearance of the malicious websites

Appearance of the malicious websites

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

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

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

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

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

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

Full infection chain

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

Initial infection chain

Initial infection chain

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

Expanded infection chain after gaining persistence

Expanded infection chain after gaining persistence

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

Malware set

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

TechHub – the start of the chain

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

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

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

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

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

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

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

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

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

Cubridge – the framework orchestrator

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

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

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

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

The predefined word list for page2 is shown below.

article, list, story

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

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

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

Cubridge's communication sequence

Cubridge’s communication sequence

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

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

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

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

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

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

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

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

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

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

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

TouchHub installs a service for TouchShift

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

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

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

TouchShift, a prevalent loader employed across multiple infection chains

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

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

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

ClientHub

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

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

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

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

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

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

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

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

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

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

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

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

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

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

LPEClient

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

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

Timeline of LPEClient

Timeline of LPEClient

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

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

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

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

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

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

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

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

Data relay flow between LPEClient and C2 via ClientHub

Data relay flow between LPEClient and C2 via ClientHub

Comparison of plugins and modules

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

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

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

Infrastructure

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

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

Victims

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

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

Attribution and links to other campaigns

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

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

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

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

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

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

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

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

Overlaps between the recent attacks by Lazarus Group

Overlaps between the recent attacks by Lazarus Group

Conclusion

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

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

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

Indicators of compromise

File hashes

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

ClientHub Loader
ff07641dc56914dd90ee0bd7404d7ad7               liblzma.dll

File paths

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

C2 servers

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

  •  

RemotePE: The Lazarus RAT that lives in memory

Authors: Yun Zheng Hu and Mick Koomen

Summary

Last year, we published research1 about a North Korean Lazarus subgroup targeting financial and cryptocurrency organizations, encountered during multiple incident response engagements. This Lazarus subgroup overlaps with activity linked to AppleJeus2, Citrine Sleet3, UNC47364, and Gleaming Pisces5. In one investigation, we observed that the actor had replaced ThemeForestRAT and PondRAT with a more sophisticated memory-only toolset. This follow-up post covers all three malware families from that toolset: DPAPILoader, RemotePELoader and RemotePE.

The three form a chain. DPAPILoader decrypts and loads RemotePELoader from disk using the Windows Data Protection API (DPAPI). RemotePELoader beacons to a C2 server and waits until it receives the next stage: RemotePE, a RAT executed entirely in memory and never written to disk, leaving no filesystem artifacts. At the time of writing, we have not found samples of RemotePELoader or RemotePE on VirusTotal.

The toolset’s environmental keying, memory-only execution, EDR evasion, and low forensic footprint suggest it is purpose-built for long-term observation campaigns. This allows the actor to quietly maintain access over an extended period before moving to a high-impact final objective such as data theft or a large-scale financial heist, consistent with this actor’s known history.
We are sharing samples with detection rules and indicators of compromise (IOCs) to help defenders identify and respond to this toolset in their environments.

Figure 1: The three-stage chain: DPAPILoader decrypts and loads RemotePELoader from disk, which retrieves and executes RemotePE in memory

DPAPILoader: First-stage, environmentally keyed loader

DPAPILoader is implemented as a DLL whose purpose is to decrypt and load an encrypted payload from disk using DPAPI. In the incident response case, it was found as C:\Windows\System32\Iassvc.dll, installed under the service name “Internet Authentication Service.” This service runs Iassvc.dll automatically on system startup, providing persistence for the toolset. The filename and service name are chosen to mimic the legitimate Windows Server Internet Authentication Service (IAS) and its accompanying DLL C:\Windows\System32\iassvcs.dll (note the extra ‘s’ in the filename).

In Listing 1, we list a Windows service record, extracted from the forensic image using Dissect6, that shows the masquerading in detail.

          name (string) = Ias
   displayname (string) = Internet Authentication Service
   description (string) = Internet Authentication Service (IAS) is a component of Windows Server operating systems that provides centralized user authentication, authorization and accounting.
      servicedll (path) = %SystemRoot%\system32\Iassvc.dll
       imagepath (path) = %systemroot%\system32\svchost.exe
imagepath_args (string) = -k netsvcs -p
    objectname (string) = LocalSystem
         start (string) = Auto Start (2)
          type (string) = Service - Own Process (0x10)
  errorcontrol (string) = Normal (1)

Listing 1: Service record from Dissect showing Windows service that runs DPAPILoader

The sample from our investigation first checks whether it is running under C:\Windows\System32\Svchost.exe. It then loops over all files matching the wildcard path C:\ProgramData\Microsoft\Windows\DeviceMetadataStore\en-US*.*. This directory normally contains Microsoft Cabinet files used for device metadata packages. DPAPILoader skips any file beginning with the Cabinet magic bytes (MSCF / 4D 53 43 46), filtering out legitimate metadata packages. Any file that passes this check and is larger than 51200 bytes (50 KiB) is decrypted using DPAPI and loaded into memory using libpeconv7 , an open-source reflective PE loading library.

Across the DPAPILoader samples we observed, the loading mechanism and host process differ, as documented in the Observed Samples section, but the core behaviour is consistent.

DPAPI Encryption

DPAPILoader uses the Windows Data Protection API (DPAPI) to decrypt its payload. DPAPI ties cryptographic keys to a specific user account, with key management handled entirely by the OS. The caller only invokes encrypt and decrypt functions.

This offers the actor two advantages. First, the encrypted payload on disk is never in plaintext: if a sample is uploaded to VirusTotal, it is useless without the victim’s DPAPI keys. Static analysis is effectively impossible without them. Second, each deployment produces a unique encrypted blob, meaning the payload hash differs across victims and evades hash-based detection. The only prerequisite is prior access to the target machine to encrypt and drop the payload, something the actor has at this stage of the intrusion.

After DPAPI decryption, the payload is additionally XORed with 0x8D before loading. This is consistent across all observed DPAPILoader samples. This approach is an instance of environmental keying8, where malware is bound to a specific victim environment and cannot be analysed or executed elsewhere.

Observed Samples

We identified three DPAPILoader samples spanning roughly nine months, with differences in loading mechanism, host process, and payload storage.

The first sample (Iassvc.dll) is loaded as a Windows service via Svchost.exe, the second (sspicli.dll) is sideloaded by ESET’s edp.exe, and the third (wmiclnt.dll) uses the WmiOpenBlock export with no identified host process.

PE timestampDLL nameExportString obfuscation
2023-11-14Iassvc.dllServiceMainXOR 0x8D
2024-02-21sspicli.dllInitSecurityInterfaceWXOR 0x8D
2024-08-21wmiclnt.dllWmiOpenBlockDPAPI + XOR 0x8D
Table 1: Observed DPAPILoader samples by PE timestamp

The first two samples load the DPAPI-encrypted payload from the DeviceMetadataStore path. The third embeds the encrypted payload directly in the DLL, removing the dependency on a separate file on disk.

The second and third samples were found on VirusTotal. Without the victims’ DPAPI keys, we are unable to decrypt them. Both are a practical demonstration of the environmental keying discussed earlier.

The first sample comes from our incident response case, where a full forensic image of the compromised machine gave us access to the victim’s DPAPI keys, allowing us to trivially decrypt the payload using a Dissect9 shell:

Figure 2: Decrypting the DPAPI-encrypted PE payload using Dissect

It turns out the decrypted payload is another loader, which we named RemotePELoader.

RemotePELoader: Second-stage, operator-controlled loader

RemotePELoader is decrypted from the DPAPI payload on disk and is responsible for retrieving the core module from a C2 server and loading it into memory. Both the loader and the core module share a configuration file stored on disk, and are designed to work as a pair, deployed together as part of the same installation. Upon execution, RemotePELoader spawns a thread that first applies evasion techniques, reads the configuration, and then enters a C2 polling loop. It has no RAT functionality of its own; its sole purpose is to load the next stage.

HellsGate & EDR Evasion

RemotePELoader applies two evasion techniques before performing any further actions. The first is HellsGate10 (specifically the TartarusGate11 variant), a technique that dynamically resolves Windows syscall numbers at runtime. It scans the loaded ntdll.dll for syscall stubs to obtain the numbers for NtOpenSection, NtMapViewOfSection, NtUnmapViewOfSection, NtProtectVirtualMemory, and NtClose. Using these direct syscalls, RemotePELoader iterates the Process Environment Block’s module list and remaps each DLL from its \KnownDlls section object, a kernel-maintained mapping of trusted system DLLs, replacing any hooked in-memory copies with clean ones and effectively unhooking all userland security product hooks.

The second is patching Event Tracing for Windows (ETW), a Windows mechanism used by security products to monitor process behaviour at runtime. RemotePELoader patches function EtwEventWrite() in the current process using a well-known technique, overwriting it with the following bytes.

48 33 c0          ; XOR    RAX, RAX
c3                ; RET

Listing 2: Bytes written to EtwEventWrite to disable ETW event generation

This causes EtwEventWrite to immediately return 0, suppressing all ETW event generation and preventing security tooling that relies on ETW telemetry from receiving events.

Together, these two techniques hinder detection by endpoint security products that rely on userland API hooking or ETW telemetry.

Configuration

After applying evasion techniques, RemotePELoader reads a configuration file using the same wildcard search as DPAPILoader:

\??\C:\ProgramData\Microsoft\Windows\DeviceMetadataStore\en-US*.*

The configuration file is smaller than the encrypted RemotePELoader payload, so it identifies it by looking for a file that does not begin with Cabinet magic bytes and is smaller than 20480 bytes (20 KiB). When found, it decrypts the contents using DPAPI and XORs all bytes with 0x8D.

Figure 3: Decrypting the DPAPI-encrypted config using Dissect

The configuration file structure is depicted in Listing 3.

struct RemotePEC2Config 	// sizeof=0xb38
{
  int dwReconnectMinutes;	// minutes to wait after C2 session ends
  int dwSleepUntilEpoch;    // UNIX epoch wake-up timestamp
  int dwSleepMin;		    // minimum sleep time between C2 polls
  int dwSleepMax;           // maximum sleep time between C2 polls
  wchar_t wsC2Url_1[260];   // C2 URL (up to three)
  wchar_t wsC2Url_2[260];
  wchar_t wsC2Url_3[260];
  wchar_t wsProxy[260];     // optional proxy address
  char sProxyUserName[128]; // optional proxy username
  char sProxyPassword[128]; // optional proxy password
  wchar_t wsUserAgent[260]; // configurable HTTP user-agent string
};

Listing 3: RemotePE C2 configuration structure on disk

Since both RemotePELoader and the configuration file reside in the same directory, a size check is used to distinguish between them, without it, the configuration file could be mistakenly loaded as a PE, or the PE read as a configuration file. This shared logic, combined with the identical cryptographic scheme, further ties the two loaders together as a coordinated toolset.

C2 Communication

After reading the configuration, RemotePELoader enters a loop until it receives a PE payload from the server. On the first run it sleeps until the configured wake-up timestamp and on subsequent iterations it sleeps for a random interval within the configured bounds. It then finds an active C2 server via a check-in request and keeps polling for a PE payload. If no payload is returned, it restarts the loop. Once a payload is received, it sends a confirmation request to the active C2, loads the retrieved PE payload using libpeconv, and exits the thread.

RemotePELoader communicates with the C2 server over HTTP, using POST requests. Host information is passed via the HTTP Cookie header, with a check-in request identified by the presence of at_check=true. The server responds with a JSON object where the odata.metadata key contains the C2 session ID. Once a session ID is obtained, subsequent requests replace the at_check cookie with ai_session, set to the session ID received from the server. The table below documents each cookie field used in the check-in request.

Cookie nameCookie value description
MSCCRandom buffer with regex [0-9a-z]{24} prepended to the string “-c1=2-c2=2-c3=2”
MicrosoftApplicationsTelemetryDeviceIdBot ID
MSFPCRandom numbers with format string “%08lx%08lx%08lx%08lx”
HASHRandom number with format string “%04x”
LVCurrent year and month in YYYYMM format
VConstant number
LUEpoch of current time
MS0Random numbers with format string “%08lx%08lx%08lx%08lx”, likely to indicate RemotePELoader request
at_checkIndicates a check-in request (no session yet)
ai_sessionSession ID from C2 after initial check-in
Table 2: RemotePELoader check-in request Cookie fields

Once a C2 session is established, RemotePELoader polls the server at random intervals between the configured minimum and maximum sleep times. In our tests, the server did not immediately return a payload, suggesting an actor-in-the-loop model where the operator manually decides when to deliver it. When the operator delivers the payload, the server returns a JSON object where the odata.metadata key contains the PE payload, AES-GCM encrypted and Base64-encoded.

Figure 4: RemotePELoader C2 session showing the server returning the encrypted PE payload

All messages exchanged with the C2 server are AES-encrypted, except for the initial check-in response containing the session ID. The AES key and nonce for each message are derived using SplitMix64, seeded with a random value generated by a Mersenne Twister PRNG. Each message is structured as follows, with the seed prepended to the AES-GCM tag and ciphertext:

struct C2Message {
    uint64_t aes_seed;          // SplitMix64 seed for AES key and nonce
    unsigned char aes_tag[16];  // AES authentication tag
    unsigned char ciphertext[]; // AES-GCM encrypted data
};

Listing 4: C2 message structure used by RemotePELoader and RemotePE

The decrypted payload is RemotePE, a fully-fledged RAT that runs entirely in memory, covered in the next section.

RemotePE: Final-stage, in-memory RAT

RemotePE is a fully-fledged RAT that we retrieved directly from a RemotePELoader C2 server by emulating its C2 protocol.

Written in C++ using object-oriented programming, RemotePE is a multithreaded program that appears to share a codebase with RemotePELoader. Both components share the same on-disk configuration file, this is by design: if an operator updates the configuration and the host reboots, both components need to read the same updated values to maintain access. Furthermore, C2 logic, including session handling, AES-GCM encryption, and the C2Message structure are equal. Also, in the samples from our investigation, RemotePELoader and RemotePE each verify they were loaded by the previous stage by checking that lpReserved == 0x1000 in DllMain, enforcing the integrity of the chain.

Control flow

RemotePE starts two threads at startup. The first, IChannelController, handles C2 communication. The second, IMiddleController, processes commands received from the C2 server. When the C2 server ends the current session, both threads stop and RemotePE either exits or sleeps until the configured wake-up time.

The IChannelController thread first locates an active C2 server and then polls it for commands. Between each polling iteration, the thread sleeps for a configured random interval, or wakes immediately if command output is available. In that case, the output is sent back to the C2 server without waiting for the next polling interval, allowing the operator to issue the next command promptly. Received commands are pushed to a queue consumed by IMiddleController.
The IMiddleController thread processes commands from the queue and pushes output back to a queue read by IChannelController. Each C2 message from the server consists of a list of entries delimited by $, where each entry is a bundle of commands (see the C2 Protocol section). Commands can optionally be executed in a separate thread, and all output is merged into a single reply sent back to the server.

While sleeping, RemotePE also checks for the existence of a Windows event named 554D5C1F-AABE-49E4-AB57-994D22ECED28. If present, it wakes immediately and restarts both controller threads. Neither RemotePE nor the loaders create this event, implying it is created externally as an out-of-band mechanism to wake RemotePE on demand.

Commands

RemotePE supports six categories of commands, identified by their C++ runtime type information (RTTI) class names. The table below lists each class along with the functionality it exposes. An operator invokes a function by specifying its class ID and function ID, along with any required parameters.

Table 3: RemotePE commands with their RTTI class names
Internal class name Class ID Function ID Description
IConfigProfile 0 0 Get the current C2 configuration
1 Set the C2 configuration
IConsole 1 0 Get the current working directory
1 Change the current working directory
2 Execute a command and return its output
3 Get loaded modules (DLLs)
4 Register a new module (DLL)
5 Invoke a registered module’s function pointer with arguments
6 Unload a module (DLL)
IFileExplorer 2 0 Get information on the drives of the system
1 List the files in a directory
2 Delete a file
3 Rename a file
4 Read from a file
5 Write to a file
6 ZIP a file or directory and return it as data
IProcess 3 0 Get process listing
1 Kill process by ID
2 Search for a file in the directories of a given environment variable
3 Create a process
4 Create a process as a user
ITimer 4 0 Sleep for X minutes, non-persistent
1 Sleep for X minutes, and persist this also in the C2 configuration on disk
2 Exit RemotePE
IPing 5 N/a A no-op command

Most commands provide standard RAT functionality. One notable exception is the file deletion command, which overwrites each file with constant bytes seven times before renaming and deleting it, a secure deletion pattern consistent with PondRAT and POOLRAT, two malware families previously associated with this actor. Unlike some implementations that overwrite with random bytes, RemotePE uses constant bytes, though the multi-pass overwrite and rename pattern is shared.

RemotePE also implements a plugin system that allows the operator to dynamically register DLL payloads at runtime. These payloads must be valid both as a Windows DLL and as reflective shellcode, with the DLL entry point re-executed to unload them: a dual-format requirement and unload behaviour that matches pe_to_shellcode12 , which refers to such payloads as “shellcodified DLLs”. RemotePE can hold multiple plugins simultaneously, which the operator can invoke via the IConsole commands described above.

C2 Protocol

Similar to RemotePELoader, the IChannelController thread begins by locating an active C2 server via a check-in request, then polls it in a loop. The request format is largely identical to that of RemotePELoader, with one exception: RemotePE uses the MUID cookie instead of MS0, which the C2 server likely uses to differentiate between the two families. Session handling is identical to RemotePELoader. For a full description of cookie fields, see the RemotePELoader C2 Communication section.

Though RemotePE communicates with the same C2 server as RemotePELoader, the protocol diverges after the initial check-in. The outer message structure is identical to RemotePELoader’s C2Message (seed, AES-GCM tag, and ciphertext). The decrypted ciphertext, however, contains a RemotePE-specific structure, see Listing 5.

struct C2Command {
    uint32_t payload_size;
    uint16_t class_id;    	 // class ID from the commands table
    uint16_t function_id; 	 // function ID from the commands table
    uint32_t request_id;  	 // used to match responses
    unsigned char payload[]; // variable length, payload_size bytes
};

struct C2CommandBatch {
    uint16_t command_count;
    C2Command commands[];	 // variable length, command_count entries
};

Listing 5: RemotePE C2 command structures

Command responses sent back to the server use the structures defined in Listing 6.

struct C2CommandResponse {
    uint32_t response_size;
    uint32_t error;	   	      // error code, if any
    uint32_t request_id;  	  // used to respond to a C2Command request
    unsigned char payload[];  // variable length, compressed, response_size bytes
};

struct C2CommandResponseBatch {
    uint16_t command_count;
    C2CommandResponse commands[];	 // variable length, command_count entries
};

Listing 6: RemotePE command output structures

When IChannelController receives a C2CommandBatch, it decrypts it and pushes the commands to the queue consumed by IMiddleController, as described in the Control Flow section. Command output is compressed using MSZIP via the Windows Cabinet compression API (cabinet.dll).

Figure 5: RemotePE command parsing

Figure 5 shows the C2 server command parsing of the IMiddleController thread. At first, command batches can be delimited by the “$”, where each command of a batch is traversed. After running the commands, all command outputs that were not run as a separate thread are merged into a C2 reply that is sent back to the server.

Command output is compressed, and the whole C2CommandResponseBatch structure is AES-GCM encrypted and Base64-encoded, before being sent back to the C2 server in the armAuthorization JSON key. An example of this is shown in Figure 6. The JSON keys and HTTP cookie names used within the C2 protocol, e.g., armAuthorization, odata.metadata, and MSFPC are also used within the Microsoft ecosystem.

Figure 6: RemotePE returning command output to the C2 server via the armAuthorization JSON key

A example Python script to decrypt C2 command responses can be found here:

Figure 7: Example of a decrypted C2 command response

Retrieved Samples

We obtained four RemotePE samples: three retrieved from active C2 servers and one recovered through forensic analysis. The C2 servers were identified during the incident response engagement or through fingerprinting. Ordering the samples by PE compile timestamp reveals incremental changes across versions, primarily in the config loading mechanism and bot identification method, suggesting active development between mid-2023 and mid-2024.

PE timestampConfig loadingBot ID
2023-07-04Find DPAPI encrypted config on diskSOFTWARE\Microsoft\SQMClient\MachineId
2023-10-17C2 URLs passed via lpThreadParameter, fixed User-AgentSOFTWARE\Microsoft\SQMClient\MachineId
2024-04-18Find DPAPI encrypted config on diskSOFTWARE\Microsoft\SQMClient\MachineId
2024-05-11DPAPI config path passed via lpThreadParameterSoftware\Microsoft\Cryptography\MachineGuid
Table 4: Observed RemotePE samples by PE timestamp

The 2023-10-17 sample does not use DPAPI and instead receives its C2 urls directly via lpThreadParameter, parsed using CommandLineToArgvW. Unlike the other samples, it also performs HellsGate syscall resolution and ETW patching itself, rather than relying on RemotePELoader to do so. This suggests that early versions of RemotePE were more standalone and not exclusively tied to the DPAPILoader/RemotePELoader chain, capable of being deployed by any loader passing the configuration as a thread parameter.

The table below shows the time between our initial check-in and RemotePE payload delivery across six successful retrieval sessions, along with the payload delivery time converted to Korea Standard Time (KST, UTC+9).

C2 session started (UTC)Payload returned (UTC)DeltaPayload returned (KST,UTC+9)
2024-02-07 00:212024-02-07 01:0948 min2024-02-07 10:09
2024-12-09 08:482024-12-09 09:0820 min2024-12-09 18:08
2024-12-10 23:572024-12-11 00:4649 min2024-12-11 09:46
2025-01-10 08:212025-01-10 08:210 min2025-01-10 17:21
2025-02-10 21:562025-02-10 23:0367 min2025-02-11 08:03
2025-07-09 11:572025-07-10 07:5020 hrs2025-07-10 16:50
Table 5: RemotePELoader C2 session and RemotePE payload delivery timestamps

Many other sessions yielded no payload. All six successful payload deliveries fall within daytime hours in the UTC+9 timezone (08:00–19:00 KST), as shown in Table 5.

Infrastructure

The RemotePE C2 infrastructure is hosted on Namecheap shared hosting, consistent with what we observed in earlier campaigns involving ThemeForestRAT and PondRAT. As with those campaigns, the use of shared hosting makes IP-based blocking ineffective, since the same server hosts legitimate domains.

Through fingerprinting of C2 server characteristics, we identified additional domains and servers beyond those found during the incident response engagement. These are listed in the IOCs section.

At the time of writing, several C2 servers we identified never returned a payload during our emulated sessions, though some remain live. Others that had previously delivered RemotePE appear to no longer do so. Whether this reflects the infrastructure going dormant, being abandoned, a change in C2 protocol, or the actor detecting unexpected connections is unclear.

Conclusion

The DPAPILoader, RemotePELoader, and RemotePE toolset represents a deliberate effort to minimise forensic footprint. A RemotePELoader sample from disk uploaded to VirusTotal is useless without the victim’s DPAPI keys. Furthermore, by combining environmental keying via DPAPI with fully in-memory execution of the final payload, the actor ensures that forensic imaging of the disk will not yield recoverable artifacts of RemotePE.

The actor-in-the-loop delivery model and the toolset’s low detection rate (neither RemotePELoader nor RemotePE appeared on VirusTotal prior to this publication) suggest this toolset may be reserved for high-value targets where long-term, stealthy access is the objective, consistent with this Lazarus subgroup’s known focus on financial and cryptocurrency organisations.

Defenders should focus on host-based detection. The most reliable indicators are DPAPI-encrypted blobs in unexpected directories, in our case this was the DeviceMetadataStore directory, though this can vary. Another indicator is to look for suspicious DLLs masquerading as legitimate Windows services or sideloaded DLLs.

For network-based detection, SNI fields and DNS queries for known C2 domains are the most actionable opportunities. Pivoting on Namecheap shared hosting infrastructure also proved effective in identifying additional malicious C2 servers during our investigation. Organisations with TLS inspection can detect the characteristic cookie fields and JSON keys, though care should be taken to avoid false positives given the traffic’s close resemblance to legitimate Microsoft traffic.

We are sharing the samples, including decrypted versions that would otherwise remain inaccessible due to environmental keying, both for preservation and to help defenders detect and respond to this toolset. YARA rules and IOCs are provided below.

Indicators of Compromise

If you have any questions or need assistance based on these findings, please contact Fox-IT CERT at cert@fox-it.com. For urgent matters, call 0800-FOXCERT (0800-3692378) within the Netherlands, or +31152847999 internationally to reach one of our incident responders.

Domains

DomainFirst seenLast seen
livedrivefiles[.].com2023-07-172025-07-27
aes-secure[.]net2023-09-18*
azureglobalaccelerator[.]com2023-09-18*
msdeliverycontent[.]com2024-02-192026-05-09
akamaicloud[.]com2024-02-192025-02-14
intelcloudinsights[.]com2024-04-132026-04-23
devicelinkintel[.]com2024-08-16*
Table 6: RemotePE(Loader) C2 domains. Entries marked with * in the “Last seen” column were still active at the time of writing.

Host based indicators

TypeIndicatorComment
file.nameIassvc.dllFilename used for DPAPILoader
event.name554D5C1F-AABE-49E4-AB57-994D22ECED28RemotePE specific event name
Table 7: RemotePE host-based indicators

Samples

digest.sha256Comment
4f6ae0110cf652264293df571d66955f7109e3424a070423b5e50edc3eb43874DPAPILoader (Iassvc.dll)
aa4a2d1215f864481994234f13ab485b95150161b4566c180419d93dda7ac039DPAPILoader (wmiclnt.dll)
159471e1abc9adf6733af9d24781fbf27a776b81d182901c2e04e28f3fe2e6f3DPAPILoader (sspicli.dll)
7a05188ab0129b0b4f38e2e7599c5c52149ce0131140db33feb251d926428d68RemotePELoader (decrypted from disk)
37f5afb9ed3761e73feb95daceb7a1fdbb13c8b5fc1a2ba22e0ef7994c7920efRemotePE (2023-07-04)
6b33d20196267b0d64bca815ca863558d26b17cee77caf62a6cce8eae555ac8dRemotePE (2023-10-17)
62e040a32aac2d2faa8d2bffa2cf7ab662228cebf9bb78eaa0a633c0b729d119RemotePE (2024-04-18)
710f15302859c7af1c1e25219d704841b3fdbc48f16a5a574d5ab6cf4f4842e8RemotePE (2024-05-11)
Table 8: Samples observed related to this activity

YARA Rules

rule Lazarus_DPAPILoader_Hunting {
  meta:
    description = "Hunting rule to detect DPAPILoader, a loader used to load RemotePE."
    author      = "Fox-IT / NCC Group"
 
  strings:
    $msg_1 = "[!] Could not allocate memory at the desired base!\n"
    $msg_2 = "[!] Virtual section size is out ouf bounds: "
    $msg_3 = "[!] Invalid relocDir pointer\n"
    $msg_4 = "[-] Not supported relocations format at %d: %d\n"
    $msg_5 = "[!] Cannot fill imports into 32 bit PE via 64 bit loader!\n"
 
  condition:
    any of them and pe.imports("Crypt32.dll", "CryptUnprotectData")
}
 
rule Lazarus_RemotePE_C2_strings {
  meta:
    description = "RemotePE strings used for C2."
    author      = "Fox-IT / NCC Group"
 
  strings:
    $a = "MicrosoftApplicationsTelemetryDeviceId" wide ascii xor
    $b = "armAuthorization" wide ascii xor
    $c = "ai_session" wide ascii xor
 
  condition:
    uint16(0) == 0x5A4D and all of them
}
 
rule Lazarus_RemotePE_class_strings {
  meta:
    description = "RemotePE class strings."
    author      = "Fox-IT / NCC Group"
 
  strings:
    $a = "IMiddleController" ascii wide xor
    $b = "IChannelController" ascii wide xor
    $c = "IConfigProfile" ascii wide xor
    $d = "IKernelModule" ascii wide xor
 
  condition:
    all of them
}

rule Lazarus_RemotePE_DPAPI_Encrypted_config {
  meta:
    description = "Detects RemotePE DPAPI-encrypted config on disk"
    author      = "Fox-IT Security Research Team"
  condition:
    filesize == 3094
    and uint32(0) == 0x00000001      // DPAPI blob version = 1
    and uint32(0x8E) == 0x00000B40   // dwDataLen = 0xB40 (padded config)
}

Listing 7: YARA rules for DPAPILoader, RemotePELoader and RemotePE

References

  1. https://blog.fox-it.com/2025/09/01/three-lazarus-rats-coming-for-your-cheese ↩︎
  2. https://securelist.com/operation-applejeus/87553/ ↩︎
  3. https://www.microsoft.com/en-us/security/blog/2024/08/30/north-korean-threat-actor-citrine-sleet-exploiting-chromium-zero-day/ ↩︎
  4. https://cloud.google.com/blog/topics/threat-intelligence/3cx-software-supply-chain-compromise ↩︎
  5. https://unit42.paloaltonetworks.com/threat-assessment-north-korean-threat-groups-2024/ ↩︎
  6. https://docs.dissect.tools/en/stable/ ↩︎
  7. https://github.com/hasherezade/libpeconv ↩︎
  8. https://attack.mitre.org/techniques/T1480/001/ ↩︎
  9. https://docs.dissect.tools/en/stable ↩︎
  10. https://github.com/am0nsec/HellsGate ↩︎
  11. https://github.com/trickster0/TartarusGate ↩︎
  12. https://github.com/hasherezade/pe_to_shellcode/releases/tag/v1.2 ↩︎

  •  

Three Lazarus RATs coming for your cheese

Authors: Yun Zheng Hu and Mick Koomen

A Telegram from Pyongyang

Introduction

In the past few years, Fox-IT and NCC Group have conducted multiple incident response cases involving a Lazarus subgroup that specifically targets organizations in the financial and cryptocurrency sector. This Lazarus subgroup overlaps with activity linked to AppleJeus1, Citrine Sleet2, UNC47363, and Gleaming Pisces4. This actor uses different remote access trojans (RATs) in their operations, known as PondRAT5, ThemeForestRAT and RemotePE. In this article, we analyse and discuss these three.

First, we describe an incident response case from 2024, where we observed the three RATs. This gives insights into the tactics, techniques, and procedures (TTPs) of this actor. Then, we discuss PondRAT, ThemeForestRAT and RemotePE, respectively.

PondRAT received quite some attention last year, we give a brief overview of the malware and document other similarities between PondRAT and POOLRAT (also known as SimpleTea) that have not yet been publicly documented. Secondly, we discuss ThemeForestRAT, a RAT that has been in use for at least six years now, but has not yet been discussed publicly. These two malware families were used in conjunction, where PondRAT was on disk and ThemeForestRAT seemed to only run in memory.

Lastly, we briefly describe RemotePE, a more advanced RAT of this group. We found evidence that the actor cleaned up PondRAT and ThemeForestRAT artifacts and subsequently installed RemotePE, potentially signifying a next stage in the attack. We cannot directly link RemotePE to any public malware family at the time of this writing.

In all cases, the actor used social engineering as an initial access vector. In one case, we suspect a zero-day might have been used to achieve code execution on one of the victim’s machines. We think this highlights their advanced capabilities, and with their history of activity, also shows their determination.

A Telegram from Pyongyang

In 2024, Fox-IT investigated an incident at an organisation in decentralized finance (DeFi). There, an employee’s machine was compromised through social engineering. From there, the actor performed discovery from inside the network using different RATs in combination with other tools, for example, to harvest credentials or proxy connections. Afterwards, the actor moved to a stealthier RAT, likely signifying a next stage in the attack.

In Figure 1, we provide an overview of the attack chain, where we highlight four phases of the attack:

  1. Social engineering: the actor impersonates an existing employee of a trading company on Telegram and sets up a meeting with the victim, using fake meeting websites.
  2. Exploitation: the victim machine gets compromised and shortly afterwards PondRAT is deployed. We are uncertain how the compromise was achieved, though we suspect a Chrome zero-day vulnerability was used.
  3. Discovery: the actor uses various tooling to explore the victim network and observe daily activities.
  4. Next phase: after three months, the actor removes PerfhLoader, PondRAT and ThemeForestRAT and deploys a more advanced RAT, which we named RemotePE.
Figure 1: Overview of the attack chain from a 2024 incident response case involving a Lazarus subgroup

Social Engineering

We found traces matching a social engineering technique previously described by SlowMist6. This social engineering campaign targets employees of companies active in the cryptocurrency sector by posing as employees of investment institutions on Telegram.

This Lazarus subgroup uses fake Calendly and Picktime websites, including fake websites of the organisations they impersonate. We found traces of two impersonated employees of two different companies. We did not observe any domains linked to the “Access Restricted” trick as described by SlowMist. In Figure 2, you can see a Telegram message from the actor, impersonating an existing employee of a trading company. Looking up the impersonated person, showed that the person indeed worked at the trading company.

Figure 2: Lazarus subgroup impersonating an employee at a trading company interested in the cryptocurrency sector

From the forensic data, we could not establish a clear initial access vector. We suspect a Chrome zero-day exploit was used. Although, we have no actual forensic data to back up this claim, we did notice changes in endpoint logging behaviour. Around the time of compromise, we noted a sudden decrease in the logging of the endpoint detection agent that was running on the machine. Later, Microsoft published a blogpost7, describing Citrine Sleet using a zero-day Chrome exploit to launch an evasive rootkit called FudModule8, which could explain this behaviour.

Persistence with PerfhLoader

The actor leveraged the SessionEnv service for persistence. This existing Windows service is vulnerable to phantom DLL loading9. A custom TSVIPSrv.dll can be placed inside the %SystemRoot%\System32\ directory, which SessionEnv will load upon startup. The actor placed its own loader in this directory, which we refer to as PerfhLoader. Persistence was ensured by making the service start automatically at reboot using the following command:

sc config sessionenv start=auto

The actor also modified the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SessionEnv\RequiredPrivileges registry key by adding SeDebugPrivilege and SeLoadDriverPrivilege privileges. These elevated privileges enable loading kernel drivers, which can bypass or disable Endpoint Detection and Response (EDR) tools on the compromised system.

Figure 3: PerfhLoader loaded through SessionEnv service via Phantom DLL Loading which in turn loads PondRAT or POOLRAT

In a case from 202010, this actor used the IKEEXT service for phantom DLL loading, writing PerfhLoader to the path %SystemRoot%\System32\wlbsctrl.dll. The vulnerable VIAGLT64.SYS kernel driver (CVE-2017-16237) was also used to gain SYSTEM privileges.

PerfhLoader is a simple loader that reads a file with a hardcoded filename (perfh011.dat) from its current directory, decrypts its contents, loads it into memory and executes it. In all observed cases, both PerfhLoader and the encrypted DLL were in the %SystemRoot%\System32\ folder. Normally, perfhXXX.dat files located in this folder contain Windows Performance Monitor data, which makes it blend in with normal Windows file names.

The cipher used to encrypt and decrypt the payload uses a rolling XOR key, we denote the implementation in Python code in Listing 1.

def crypt_buf(data: bytes) -> bytes:
    xor_key = bytearray(range(0x10))
    buf = bytearray(data)
    for idx in range(len(buf)):
        a = xor_key[(idx + 5) & 0xF]
        b = xor_key[(idx - 3) & 0xF]
        c = xor_key[(idx - 7) & 0xF]
        xor_byte = a ^ b ^ c
        buf[idx] ^= xor_byte
        xor_key[idx & 0xF] = xor_byte
 
    return bytes(buf)

Listing 1: Python implementation of the XOR cipher used by PerfhLoader

The decrypted content contains a DLL that PerfhLoader loads into memory using the Manual-DLL-Loader project11. Interestingly, PondRAT uses this same project for DLL loading.

Discovery

After establishing a foothold, the actor deployed various tools in combination with the RATs described earlier. These included both custom tooling and publicly available tools. Table 1 lists some of the tools we recovered that the actor used.

ToolTool OriginDescription
ScreenshotterActorA tool that takes periodic screenshots and stores them locally
KeyloggerActorA Windows keylogger that writes user keystrokes to a file
Chromium browser dumperActorA browser dump tool that dumps Chromium-based browser cookies and credentials
MidProxyActorProxy tool
Mimikatz12PublicWindows secrets dumper
Proxy Mini13PublicProxy tool
frpc14PublicFast reverse proxy client
Table 1: Tools observed during incident response case (public and actor-developed)

Interestingly, the Fast Reverse Proxy client we found was the same client found in the 3CX compromise by Mandiant15. This client is version 0.32.116 and is from 2020, which is remarkable. We also found traces of a Themida-packed version of Quasar17, a malware family we did not see this Lazarus subgroup use before.

The actor used PondRAT in combination with ThemeForestRAT for roughly three months, to afterwards clean up and install the more sophisticated RAT called RemotePE. We will now discuss these three RATs.

PondRAT

PondRAT is a simple RAT, which its authors seem to refer to as “firstloader”, based on the compilation metadata string objc_firstloader that is present in the macOS samples.

In our case, PondRAT was the initial access payload used to deploy other types of malware, including ThemeForestRAT. Judging from network data, apart from ThemeForestRAT activity, we observed significant activity to the PondRAT C2 server, indicating it was not just used for its loader functionality. In the incident response case from 2020 we encountered POOLRAT in combination with ThemeForestRAT. This could indicate that PondRAT is a successor of POOLRAT.

Overview

PondRAT is a straightforward RAT that allows an operator to read and write files, start processes and run shellcode. It has already been described by some vendors. As far as we know, the earliest sample is from 2021, referenced in a CISA article18. Based on PondRAT’s user-agent, we also noticed that PondRAT was used in an AppleJeus campaign Volexity wrote about19 (MSI file with hash 435c7b4fd5e1eaafcb5826a7e7c16a83). 360 Threat Intelligence Center wrote about PondRAT as well20, linking it to Lazarus and later writing about it being distributed through Python Package Index (PyPI) packages21. Vipyr Security wrote22 about malware that was dropped through malicious Python packages distributed through PyPI, which turned out to be PondRAT. Unit42 published an analysis23 of the RAT, referring to it as PondRAT and showing similarities between PondRAT and another RAT used by Lazarus: POOLRAT.

As described by Unit42, there are similarities between POOLRAT and PondRAT. There is overlap in function and class naming and both families check for successful responses in a similar way.

POOLRAT has more functionality than PondRAT. For example, POOLRAT has a configuration file for C2 servers, can timestomp24 files, can move files around, functionalities that PondRAT lacks. We think this is because there is no need for more functionality if its main function is to load other malware, allowing for a smaller code base and less maintenance.

Command and Control

PondRAT communicates over HTTP(S) with a hardcoded C2 server. Messages sent between the malware and the server are XOR-ed first and then Base64-encoded. For XORing it uses the hex-encoded key 774C71664D5D25775478607E74555462773E525E18237947355228337F433A3B.

Figure 4: PondRAT check-in request

Figure 4 contains an example check-in request to the C2 server. The tuid parameter contains the bot ID, control indicates the request type, and the payload parameter contains the encrypted check-in information. In this case, control is set to fconn, indicating it is a bot check-in, matching with the corresponding function name FConnectProxy(). When receiving a server reply starting with OK, PondRAT fetches a command from the server. For at least one Linux and macOS variant, the parameter names and string values consisted of scrambled letters, e.g. lkjyhnmiop instead of tuid and odlsjdfhw instead of fconn.

Commands

PondRAT has basic commands, such as reading and writing files and executing programs. Table 2 lists all commands and their names from the symbol data. When a bot command is executed, the response includes both the original command ID and a status code indicating either success (0x89A) or failure (0x89B).

Command ID / Status codeSymbol nameDescription
0x892csleepSleep
0x893MsgDownRead file
0x894MsgUpWrite file
0x895Ping
0x896Load PE from C2 in memory
0x897MsgRunLaunch process
0x898MsgCmdExecute command through the shell
0x899Exit
0x89aStatus code indicating command succeeded
0x89bStatus code indicating command failed
0x89cRun shellcode in process
Table 2: PondRAT command IDs and their descriptions

Windows

Only the Windows samples we analysed had support for commands 0x896 and 0x89C. The DLL loading functionality seems to be based on the open-source project “Manual-DLL-Loader”25. As a sidenote, we analysed another POOLRAT Windows sample that used the “SimplePELoader” project26.

POOLRAT’s Little Brother

As mentioned by Palo Alto’s Unit42, PondRAT has similarities with POOLRAT. There is overlap in XOR keys, function naming and class naming. However, there are more similarities. Firstly, the Windows versions of PondRAT and POOLRAT use the format string %sd.e%sc "%s > %s 2>&1" for launching a shell command. Format strings have been discussed in the past27 and this specific format string was linked to Operation Blockbuster Sequel. Furthermore, PondRAT has a peculiar way of generating its bot ID, see the decompiled code below.

Figure 5: Bot ID generation for PondRAT (left) and POOLRAT (right)

Figure 5 shows how PondRAT and POOLRAT compute their bot ID. For PondRAT, tuid is the bot ID. It computes two parts of a 32-bit integer, that are split in two based on the bit_shift variable. Some of the POOLRAT samples compute the bot ID in a similar manner. The sample 6f2f61783a4a59449db4ba37211fa331 has symbol information available and contains a function named GenerateSessionId() that has this same logic.

More similarities can be found as part of the C2 protocol. PondRAT provides feedback to commands issued by the C2 server by returning the command ID concatenated with the status code. POOLRAT uses the same concept, see Figure 6.

Figure 6: Command status concatenation for PondRAT (left) and POOLRAT (right)

Another similarity can be found when comparing the Windows versions of POOLRAT and PondRAT. When running a Shell command (command ID 0x898) with PondRAT, the Windows version creates a temporary file with the prefix TLT in which it saves the command output. Then, it reads the file and sends the contents back to the C2 server and subsequently removes it. However, the way it removes the temporary file is remarkable.

It generates a buffer with random bytes and overwrites the file contents with it. Then, it renames the file 27 times, replacing all letters with only A’s, then B’s, etc. and with the last iteration renames all letters with random uppercase letters. For instance, when the file C:\Windows\Temp\tlt1bd8.tmp is deleted, it would first be renamed to C:\Windows\Temp\AAAAAAA.AAA, then to C:\Windows\Temp\BBBBBBB.BBB, and lastly to something like VYLDVAP.XQA. POOLRAT’s Windows version has the same functionality, see Figure 7.

Figure 7: Windows file name generation for PondRAT (left) and POOLRAT (right)

These similarities show that apart from variable data and symbol names, PondRAT is similar to POOLRAT in coding concepts as well. This further strengthens the connection between the two.

Summary

PondRAT is a simple RAT. Judging from the symbol data of macOS samples, its authors seem to refer to the malware as firstloader, a RAT that targets all three major operating systems. In our case, we observed it in combination with social engineering campaigns, whereas others have seen PondRAT being dropped through malicious software packages. Despite being simple in nature, it seems to do the job, given the frequency in which it is used. Judging from past incidents we investigated, PondRAT is a successor of POOLRAT.

Run, ThemeForest, Run!

In two incident response cases we found traces of a different RAT being used in conjunction with POOLRAT or PondRAT. We named it ThemeForestRAT, based on the substring ThemeForest which it uses in its C2 protocol. It is written in C++ and contains class names such as CServer, CJobManager, CSocketEx, CZipper and CUsbMan. ThemeForestRAT has more functionalities compared to PondRAT and POOLRAT.

In an earlier incident response case in 2020, we observed ThemeForestRAT in combination with POOLRAT. In the case from 2024, we observed it together with PondRAT. Its continued activity over at least five years demonstrates that ThemeForestRAT remains a relevant and capable tool for this actor. Besides Windows, we have observed Linux and macOS versions of the malware.

We believe that on Windows, this RAT is injected and executed in memory only, for example via PondRAT, or a dedicated loader, and is used as stealthier second-stage RAT with more functionality. The fact there are no direct samples of ThemeForestRAT on VirusTotal indicates it is quite successful in staying under the radar.

Overview

On startup, ThemeForestRAT attempts to read the configuration file from disk. When absent, it generates a unique bot ID and uses the hardcoded C2 configuration settings in the binary to create the configuration file.

Interestingly, the Windows variant creates two Windows events and accompanying threads that are used for signalling purposes (see Figure 8). However, the first thread related to the class CUsbMan only creates the temporary directory Z802056 and returns, this turned out to be legacy code as we will describe later.

The second thread monitors for new Remote Desktop (RDP) sessions and notifies the main thread when one is detected. Additionally, the thread checks for new physical console sessions and can optionally spawn extra commands under this session if this is enabled in the configuration.

Figure 8: ThemeForestRAT startup code creating two Windows events and threads for signalling

After creating these two threads it hibernates before connecting to the C2 server. The default hibernation period is three minutes but when it runs for the first time it checks in immediately. There are two cases where ThemeForestRAT wakes up from hibernation, either the hibernation period has passed, or one of the two events is signalled.

When it wakes up from hibernation it randomly selects a C2 server from its list and attempts to establish a connection. Upon receiving a response:OK acknowledgment, it downloads a 4-byte file that must decrypt to the 32-bit constant 0x20191127 to establish a valid C2 session. If this fails it will retry a different C2 and start over again, when the list of servers is exhausted it will go back into hibernation and try again later.

If it succeeds in establishing a C2 session, ThemeForestRAT sends basic system information including its wake-up reason to the C2 server, and the operator can now interact with the RAT as it keeps polling for new commands. When the operator sends an OnTerminate or OnSleep command (see Table 4), the C2 session ends, and the RAT goes back to hibernation.

struct SystemInfoWindows   // sizeof=0x478
{
    uint32  job_id;        // 0x10005 = Windows
    wchar   bot_id[20];
    wchar   hostname[64];
    wchar   whoami[50];
    uint32  dwMajorVersion;
    uint32  dwMinorVersion;
    uint32  dwPlatformId;
    uint16  padding1;
    wchar   ip_address[20];
    wchar   timezone[50];
    wchar   gpu[50];
    wchar   memory[50];
    uint16  padding2;
    uint32  wakeup_reason; // 0 = hibernation, 1 = USB, 2 = RDP
    wchar   os_version[256];
};

struct SystemInfoPOSIX     // sizeof=0x478
{
    uint32  job_id;        // 0x20005 = POSIX
    char    bot_id[16];
    char    unused1[24];
    char    hostname[128];
    char    username[114];
    char    ip_address[40];
    char    timezone[100];
    char    arch[100];
    char    memory[100];
    char    unused2[6];
    char    os_version[512];
};

Listing 2: ThemeForestRAT system information structure that is sent after establishing a C2 session

Listing 2 shows the structure definitions that ThemeForestRAT uses for sending system information when establishing a C2 session. The job_id field indicates the OS type, 0x10005 for Windows, and 0x20005 for both Linux and macOS as they share the same structure.

Configuration

The configuration file of ThemeForestRAT is encrypted with RC4 using the hex-encoded key 201A192D838F4853E300 and contains the following settings:

  • 64-bit unique bot ID
  • List of ten C2 server URLs
  • Command interpreter, for example cmd.exe (not used)
  • List of optional commands to execute under the user of the active console session (Windows only, empty by default)
  • Matching array to enable the optional console command
  • Last check-in timestamp
  • Hibernation time between C2 sessions in minutes, default value is 3
  • C2 callback settings, for example to immediately check in on a new active RDP connection

The configuration can be parsed using the C structure definition from Listing 3.

struct ThemeForestC2Config
{
    uint64  bot_id;
    wchar   urls[10][1024];
    wchar   shell[1024];
    wchar   wts_console_cmdline[10][1024];
    char    wts_console_cmdline_enabled[10];
    uint32  last_checkin_epoch;
    uint32  configured_hibernate_minutes;
    uint32  active_hibernate_minutes;
    uint16  callback_settings;
};

Listing 3: ThemeForestRAT configuration structure definition for Windows

The configuration path that the RAT reads from disk is hardcoded. On macOS and Linux, this is an absolute path, while on Windows it looks in the current working directory where the RAT is launched. In Table 3 we list the observed configuration paths and hardcoded configuration file sizes for ThemeForestRAT.

Operating systemThemeForestRAT configuration file on diskFile size
Windowsnetraid.inf43048 bytes
Linux/var/crash/cups43044 bytes
macOS/private/etc/imap43044 bytes
Table 3: Observed ThemeForestRAT configuration paths and their file sizes on Windows, Linux and macOS

Command and Control

ThemeForestRAT communicates over HTTP(S). The filenames it uses for retrieving commands from the C2 server are prefixed with ThemeForest_. The response data is sent back to the operator as a file prefixed with Thumb_, see Figure 6. On Windows it uses the Ryeol Http Client28 library for HTTP communications, and on macOS and Linux it uses libcurl. ThemeForestRAT has a single hardcoded C2 in the binary, but its configuration can be updated by sending the SetInfo command.

Figure 9: ThemeForestRAT sending encrypted system information to C2 server on initial check-in

Commands

In terms of command functionality, ThemeForestRAT supports over twenty commands, at least twice as much as PondRAT. The Linux and macOS versions contain debug symbols, which allows us to map the command IDs to function names where available.

Symbol nameCommand IDDescription
ListDrives0x10001000Get list of drives
CServer::OnFileBrowse0x10001001Get directory listing
CServer::OnFileCopy0x10001002Copy file from source to destination on victim machine
CServer::OnFileDelete0x10001003Delete a file
FileDeleteSecure0x10001004Delete a file securely
CServer::OnFileUpload0x10001005Open a file for writing on victim machine
CServer::FileDownload0x10001006Download file from victim machine
Run0x10001007Execute a command and return the exit code
CServer::OnChfTime0x10001008Timestomp file based on another file on disk
–0x10001009–
CServer::OnTestConn0x1000100aTest TCP connection to host and port
CServer::OnCmdRun0x1000100bRun command in background and return output
CServer::OnSleep0x1000100cHibernate for X seconds, this will also be saved in the configuration file
CServer::OnViewProcess0x1000100dGet process listing
CServer::OnKillProcess0x1000100eKill process by process ID
–0x1000100f–
CServer::OnFileProperty0x10001010Get file properties
CServer::OnGetInfo0x10001011Get current RAT configuration
CServer::OnSetInfo0x10001012Update and save RAT configuration file
CServer::OnZipDownload0x10001013Download a directory or file as a compressed Zip file
CServer::OnTerminate0x10001014Flush configuration to disk and hibernate until next wake up
(Data)0x10001015Data
(JobSuccess)0x10001016Job succeeded
(JobFailed)0x10001017Job failed
GetServiceName0x10001018Return current service name
CleanupAndExit0x10001019Remove persistence, configuration file, and terminate RAT
RecvMsg0x1000101aForce C2 check-in
RunAs0x1000101bSpawn a process under the user token of given Windows Terminal Services session
–0x1000101c–
WriteRandomData0x1000101dWrite random data to file handle
CServer::OnInjectShellcode0x1000101eInject shellcode into process ID
Table 4: ThemeForestRAT command IDs and their descriptions

Note that the symbol names in Table 4 that start with CServer:: are from the debug symbols and the other names are deduced based on analysis of the command.

Shellcode Injection

On Windows, the CServer::OnInjectShellcode command injects shellcode into a given process ID using NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory and RtlCreateUserThread Windows API calls. The shellcode is encrypted using the same algorithm used in PerfhLoader (see Listing 1). In the macOS and Linux samples we have analysed, this command is defined as an empty stub.

RomeoGolf’s Little Brother

In 2016, Novetta released a detailed report called Operation Blockbuster29, in which a Novetta-led coalition of security companies analysed malware samples from multiple cybersecurity incidents. The investigation linked the 2014 Sony Pictures attack to the Lazarus Group and revealed that the same actor had been behind numerous other attacks against government, military, and commercial targets using related malware since 2009.

Operation Blockbuster’s malware report describes RomeoGolf, a RAT that resembles ThemeForestRAT in several ways:

  • Uses the temporary folder Z802056, although not used in ThemeForestRAT, is still created
  • Overlapping command IDs and functionality
  • Same unique identifier generation using 4 calls to rand()
  • Configuration file with extension *.inf on Windows
  • Timestomping of the configuration file based on mspaint.exe
  • Two signalling threads for USB and RDP events

Figure 10 shows the RomeoGolf startup logic for generating its bot ID and two signalling threads that is identical to ThemeForestRAT (see Figure 5).

Figure 10: RomeoGolf startup creates two signalling threads, comparable to ThemeForestRAT (see Figure 5).

As can be seen in Table 5, the functionality to detect and copy data from newly attached logical drives has been removed in ThemeForestRAT, while leaving the temporary directory creation intact. Also, the thread to check for new RDP sessions has been extended in ThemeForestRAT to optionally spawn up to ten extra configured commands under the user of the active physical console session.

RomeoGolfThemeForestRAT
Compilation dateFri Oct 11 01:20:48 2013Thu Sep 07 06:40:40 2023
Known configuration filecrkdf32.infnetraid.inf
Configuration file timestomped tomspaint.exemspaint.exe
USB thread logic1. Creates %TEMP%\Z802056
2. Checks for newly attached drives and copies data to above folder
3. Signal on newly attached drives
1. Creates %TEMP%\Z802056
RDP thread logic1. Signal on new active RDP sessions
1. Start configured commands under the user of the new active console session
2. Signal on new active RDP session if configured
C2 communicationFake TLSHTTP(S)
Highest known command id0x100010130x1000101e
Table 5: Differences and similarities between RomeoGolf and ThemeForestRAT

While RomeoGolf used Fake TLS30 and its own custom server for its C2 communications, ThemeForestRAT uses the HTTP protocol and shared hosting for its C2 servers.

Onto the next stage with RemotePE

In the 2024 incident response case, we observed the actor cleaning up PondRAT and ThemeForestRAT, to deploy a more advanced RAT, which we named RemotePE. RemotePE is retrieved from a C2 server by RemotePELoader. RemotePELoader is encrypted on disk using Window’s Data Protection API (DPAPI) and is loaded by DPAPILoader. Using DPAPI enables environmental keying and makes it difficult to recover the original payload without access to the machine. DPAPILoader was made persistent through a created Windows service.

Figure 10: RemotePELoader check-in request to retrieve RemotePE payload

In Figure 10, we show a RemotePELoader check-in request used to retrieve RemotePE from the C2 server. RemotePE is written in C++ and is more advanced and elegant. We think that the actor uses this more sophisticated RAT for interesting or high-value targets that require a higher degree of operational security. Interestingly, it too uses the file renaming strategy PondRAT and POOLRAT Windows samples implement, except it skips the last random iteration.

We will publish a more thorough analysis of RemotePE in a future blogpost.

Summary

This blog is about a Lazarus subgroup that we have encountered multiple times during incident response engagements. This is a capable, patient, financially motivated actor who remains a legitimate threat.

We first discussed an incident response case from 2024, where this actor impersonated employees of trading companies to establish contact with potential victims. Though the method of achieving initial access remains unknown, we suspect a Chrome zero-day was used.

After initial access, two RATs were used in combination: PondRAT and ThemeForestRAT. Though PondRAT has already been discussed, there are no public analyses of ThemeForestRAT at the time of writing. For persistence, phantom DLL loading was used in conjunction with a custom loader called PerfhLoader.

PondRAT is a primitive RAT that provides little flexibility, however, as an initial payload it achieves its purpose. It has similarities with POOLRAT/SimpleTea. For more complex tasks, the actor uses ThemeForestRAT, which has more functionality and stays under the radar as it is loaded into memory only.

Lastly, we found the actor replaced ThemeForestRAT and PondRAT with the more advanced RemotePE. A detailed analysis of RemotePE will be published in the near future. So, stay tuned!

In Table 6 and 7, we list indicators of compromise related to the incident response cases we investigated and other artifacts we link to this actor.

Incident Response Support

If you have any questions or need assistance based on these findings, please contact Fox-IT CERT at cert@fox-it.com. For urgent matters, call 0800-FOXCERT (0800-3692378) within the Netherlands, or +31152847999 internationally to reach one of our incident responders.

Indicators of Compromise

TypeIndicatorComment
net.domaincalendly[.]liveFake calendly.com
net.domainpicktime[.]liveFake picktime.com
net.domainoncehub[.]coFake oncehub.com
net.domaingo.oncehub[.]coFake oncehub.com
net.domaindpkgrepo[.]comPotentially related to Chrome exploitation
net.domainpypilibrary[.]comUnknown, visited by msiexec.exe shortly after dpkgrepo[.]com
net.domainpypistorage[.]comUnknown, connection seen under SessionEnv service
net.domainkeondigital[.]comLPEClient server, connection seen under SessionEnv service
net.domainarcashop[.]orgPondRAT C2
net.domainjdkgradle[.]comPondRAT C2
net.domainlatamics[.]orgPondRAT C2
net.domainlmaxtrd[.]comThemeForestRAT C2
net.domainpaxosfuture[.]comThemeForestRAT C2
net.domainwww[.]plexisco[.]comThemeForestRAT C2
net.domainftxstock[.]comThemeForestRAT C2
net.domainwww[.]natefi[.]orgThemeForestRAT C2
net.domainnansenpro[.]comThemeForestRAT C2
net.domainaes-secure[.]netRemotePE payload delivery and C2
net.domainazureglobalaccelerator[.]comRemotePE payload delivery and C2
net.domainazuredeploypackages[.]netUnknown, connection seen via injected process
net.ip144.172.74[.]120Fast Reverse Proxy server
net.ip192.52.166[.]253Used as parameter for Quasar
file.path%TEMP%\tmpntl.datWindows keylogger output file path
file.pathC:\Windows\Temp\TMP01.datWindows keylogger error file path
file.namenetraid.infThemeForestRAT Windows configuration filename
file.path/var/crash/cupsThemeForestRAT Linux configuration file path
file.path/private/etc/imapThemeForestRAT macOS configuration file path
file.path/private/etc/krb5d.confPOOLRAT macOS configuration file path, CISA 2021 report
file.path/etc/apdl.cfPOOLRAT Linux configuration file path
file.path%SystemRoot%\system32\apdl.cfPOOLRAT Windows configuration file path
file.path/tmp/xweb_log.mdPOOLRAT, PondRAT Linux libcurl error log file path
file.nameperfh011.datEncrypted payload loaded by PerfhLoader
file.namehsu.datFilename actor used for SysInternals ADExplorer output
file.namepfu.datFilename actor used for SysInternals Handle viewer output
file.namefpc.datDropped Fast Reverse Proxy configuration filename
file.namefp.exeDropped Fast Reverse Proxy executable
file.nametsvipsrv.dllDLL phantom loaded by actor (SessionEnv)
file.namewlbsctrl.dllDLL phantom loaded by actor (IKEEXT)
file.nameadepfx.exeFilename actor used for legitimate SysInternals ADExplorer
file.namehd.exeFilename actor used for legitimate SysInternals Nthandle.exe
file.namemsnprt.exeFilename actor uses for Proxymini, open-source socks proxy
file.path%LocalAppData%\IconCache.logOutput path for custom browser credentials and cookies dumper based on Mimikatz
file.path/private/etc/pdpastemacOS keylogger file path
file.path/private/etc/xmemmacOS keylogger output file path
file.path/private/etc/tls3macOS screenshotter output directory
file.path%LocalAppData%\Microsoft\Software\CacheWindows screenshotter output directory
file.pathc:\windows\system32\cmui.exeThemida-packed Quasar
Table 6: Indicators of Compromise linked to actor, without hashes
digest.sha256Comment
24d5dd3006c63d0f46fb33cbc1f576325d4e7e03e3201ff4a3c1ffa604f1b74aFast Reverse Proxy v0.32.1, also observed by Mandiant in the 3CX supply chain attack
4715e5522fc91a423a5fcad397b571c5654dc0c4202459fdca06841eba1ae9b3PerfhLoader
8c3c8f24dc0c1d165f14e5a622a1817af4336904a3aabeedee3095098192d91fPerfhLoader
f4d8e1a687e7f7336162d3caed9b25d9d3e6cfe75c89495f75a92ca87025374bPOOLRAT Windows
85045d9898d28c9cdc4ed0ca5d76eceb457d741c5ca84bb753dde1bea980b516POOLRAT Linux
5e40d106977017b1ed235419b1e59ff090e1f43ac57da1bb5d80d66ae53b1df8POOLRAT macOS (CISA 2021 report)
c66ba5c68ba12eaf045ed415dfa72ec5d7174970e91b45fda9ebb32e0a37784aThemeForestRAT Windows
ff32bc1c756d560d8a9815db458f438d63b1dcb7e9930ef5b8639a55fa7762c9ThemeForestRAT Linux
cc4c18fefb61ec5b3c69c31beaa07a4918e0b0184cb43447f672f62134eb402bThemeForestRAT macOS
6510d460395ca3643133817b40d9df4fa0d9dbe8e60b514fdc2d4e26b567dfbdPondRAT Windows
973f7939ea03fd2c9663dafc21bb968f56ed1b9a56b0284acf73c3ee141c053cPondRAT Linux
f0321c93c93fa162855f8ea4356628eef7f528449204f42fbfa002955a0ba528PondRAT macOS
4f6ae0110cf652264293df571d66955f7109e3424a070423b5e50edc3eb43874DPAPILoader
aa4a2d1215f864481994234f13ab485b95150161b4566c180419d93dda7ac039DPAPILoader
159471e1abc9adf6733af9d24781fbf27a776b81d182901c2e04e28f3fe2e6f3DPAPILoader
7a05188ab0129b0b4f38e2e7599c5c52149ce0131140db33feb251d926428d68RemotePELoader (decrypted from disk)
37f5afb9ed3761e73feb95daceb7a1fdbb13c8b5fc1a2ba22e0ef7994c7920efRemotePE
59a651dfce580d28d17b2f716878a8eff8d20152b364cf873111451a55b7224dWindows keylogger
3c8f5cc608e3a4a755fe1a2b099154153fb7a88e581f3b122777da399e698ccaWindows screenshotter
d998de6e40637188ccbb8ab4a27a1e76f392cb23df5a6a242ab9df8ee4ab3936macOS keylogger (getkey)
e4ce73b4dbbd360a17f482abcae2d479bc95ea546d67ec257785fa51872b2e3fmacOS screenshotter (getscreen)
1a051e4a3b62cd2d4f175fb443f5172da0b40af27c5d1ffae21fde13536dd3e1macOS clipboard logger (pdpaste)
9dddf5a1d32e3ba7cc27f1006a843bfd4bc34fa8a149bcc522f27bda8e95db14Proxymini tool, opensource SOCKS proxy tool
2c164237de4d5904a66c71843529e37cea5418cdcbc993278329806d97a336a5Themida-packed Quasar
Table 7: SHA256 hashes of tools used by the actor

YARA rules

import "pe"

rule Lazarus_DPAPILoader_Hunting {
  meta:
    description = "Hunting rule to detect DPAPILoader, a loader used to load RemotePE."
    author      = "Fox-IT / NCC Group"

  strings:
    $msg_1 = "[!] Could not allocate memory at the desired base!\n"
    $msg_2 = "[!] Virtual section size is out ouf bounds: "
    $msg_3 = "[!] Invalid relocDir pointer\n"
    $msg_4 = "[-] Not supported relocations format at %d: %d\n"
    $msg_5 = "[!] Cannot fill imports into 32 bit PE via 64 bit loader!\n"

  condition:
    any of them and pe.imports("Crypt32.dll", "CryptUnprotectData")
}

rule Lazarus_RemotePE_C2_strings {
  meta:
    description = "RemotePE strings used for C2."
    author      = "Fox-IT / NCC Group"

  strings:
    $a = "MicrosoftApplicationsTelemetryDeviceId" wide ascii xor
    $b = "armAuthorization" wide ascii xor
    $c = "ai_session" wide ascii xor

  condition:
    uint16(0) == 0x5A4D and all of them
}

rule Lazarus_RemotePE_class_strings {
  meta:
    description = "RemotePE class strings."
    author      = "Fox-IT / NCC Group"

  strings:
    $a = "IMiddleController" ascii wide xor
    $b = "IChannelController" ascii wide xor
    $c = "IConfigProfile" ascii wide xor
    $d = "IKernelModule" ascii wide xor

  condition:
    all of them
}

rule Lazarus_PerfhLoader_XOR_key {
  meta:
    description = "XOR key used for shellcode obfuscation."
    author      = "Fox-IT / NCC Group"

  strings:
    $mov_1  = { C7 [1-3] 00 01 02 03 }
    $mov_2  = { C7 [1-3] 04 05 06 07 }
    $mov_3  = { C7 [1-3] 08 09 0A 0B }
    $mov_4  = { C7 [1-3] 0C 0D 0E 0F }
    $init_1 = { 41 8D ?? FD 41 8D ?? F9 }

  condition:
    all of them
}

rule Lazarus_ThemeForestRAT_C2_strings {
  meta:
    description = "ThemeForestRAT strings used for C2."
    author      = "Fox-IT / NCC Group"

  strings:
    $themeforest = "ThemeForest_%s" ascii wide
    $thumb       = "Thumb_%s" ascii wide
    $param_code  = "code" ascii wide
    $param_fn    = "fn" ascii wide
    $param_ldf   = "ldf" ascii wide

  condition:
    all of them
}

rule Lazarus_ThemeForestRAT_RC4_key {
  meta:
    description = "ThemeForest RC4 key used for config file."
    author      = "Fox-IT / NCC Group"

  strings:
    $rc4_key     = { 20 1A 19 2D 83 8F 48 53 E3 00 }
    $rc4_key_mov = { 20 1A 19 2D [2-8] 83 8F 48 53 [2-10] E3 00 }

  condition:
    any of them
}

References

  •  

Three Lazarus RATs coming for your cheese

Authors: Yun Zheng Hu and Mick Koomen

A Telegram from Pyongyang

Introduction

In the past few years, Fox-IT and NCC Group have conducted multiple incident response cases involving a Lazarus subgroup that specifically targets organizations in the financial and cryptocurrency sector. This Lazarus subgroup overlaps with activity linked to AppleJeus1, Citrine Sleet2, UNC47363, and Gleaming Pisces4. This actor uses different remote access trojans (RATs) in their operations, known as PondRAT5, ThemeForestRAT and RemotePE. In this article, we analyse and discuss these three.

First, we describe an incident response case from 2024, where we observed the three RATs. This gives insights into the tactics, techniques, and procedures (TTPs) of this actor. Then, we discuss PondRAT, ThemeForestRAT and RemotePE, respectively.

PondRAT received quite some attention last year, we give a brief overview of the malware and document other similarities between PondRAT and POOLRAT (also known as SimpleTea) that have not yet been publicly documented. Secondly, we discuss ThemeForestRAT, a RAT that has been in use for at least six years now, but has not yet been discussed publicly. These two malware families were used in conjunction, where PondRAT was on disk and ThemeForestRAT seemed to only run in memory.

Lastly, we briefly describe RemotePE, a more advanced RAT of this group. We found evidence that the actor cleaned up PondRAT and ThemeForestRAT artifacts and subsequently installed RemotePE, potentially signifying a next stage in the attack. We cannot directly link RemotePE to any public malware family at the time of this writing.

In all cases, the actor used social engineering as an initial access vector. In one case, we suspect a zero-day might have been used to achieve code execution on one of the victim’s machines. We think this highlights their advanced capabilities, and with their history of activity, also shows their determination.

A Telegram from Pyongyang

In 2024, Fox-IT investigated an incident at an organisation in decentralized finance (DeFi). There, an employee’s machine was compromised through social engineering. From there, the actor performed discovery from inside the network using different RATs in combination with other tools, for example, to harvest credentials or proxy connections. Afterwards, the actor moved to a stealthier RAT, likely signifying a next stage in the attack.

In Figure 1, we provide an overview of the attack chain, where we highlight four phases of the attack:

  1. Social engineering: the actor impersonates an existing employee of a trading company on Telegram and sets up a meeting with the victim, using fake meeting websites.
  2. Exploitation: the victim machine gets compromised and shortly afterwards PondRAT is deployed. We are uncertain how the compromise was achieved, though we suspect a Chrome zero-day vulnerability was used.
  3. Discovery: the actor uses various tooling to explore the victim network and observe daily activities.
  4. Next phase: after three months, the actor removes PerfhLoader, PondRAT and ThemeForestRAT and deploys a more advanced RAT, which we named RemotePE.
Figure 1: Overview of the attack chain from a 2024 incident response case involving a Lazarus subgroup

Social Engineering

We found traces matching a social engineering technique previously described by SlowMist6. This social engineering campaign targets employees of companies active in the cryptocurrency sector by posing as employees of investment institutions on Telegram.

This Lazarus subgroup uses fake Calendly and Picktime websites, including fake websites of the organisations they impersonate. We found traces of two impersonated employees of two different companies. We did not observe any domains linked to the “Access Restricted” trick as described by SlowMist. In Figure 2, you can see a Telegram message from the actor, impersonating an existing employee of a trading company. Looking up the impersonated person, showed that the person indeed worked at the trading company.

Figure 2: Lazarus subgroup impersonating an employee at a trading company interested in the cryptocurrency sector

From the forensic data, we could not establish a clear initial access vector. We suspect a Chrome zero-day exploit was used. Although, we have no actual forensic data to back up this claim, we did notice changes in endpoint logging behaviour. Around the time of compromise, we noted a sudden decrease in the logging of the endpoint detection agent that was running on the machine. Later, Microsoft published a blogpost7, describing Citrine Sleet using a zero-day Chrome exploit to launch an evasive rootkit called FudModule8, which could explain this behaviour.

Persistence with PerfhLoader

The actor leveraged the SessionEnv service for persistence. This existing Windows service is vulnerable to phantom DLL loading9. A custom TSVIPSrv.dll can be placed inside the %SystemRoot%\System32\ directory, which SessionEnv will load upon startup. The actor placed its own loader in this directory, which we refer to as PerfhLoader. Persistence was ensured by making the service start automatically at reboot using the following command:

sc config sessionenv start=auto

The actor also modified the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SessionEnv\RequiredPrivileges registry key by adding SeDebugPrivilege and SeLoadDriverPrivilege privileges. These elevated privileges enable loading kernel drivers, which can bypass or disable Endpoint Detection and Response (EDR) tools on the compromised system.

Figure 3: PerfhLoader loaded through SessionEnv service via Phantom DLL Loading which in turn loads PondRAT or POOLRAT

In a case from 202010, this actor used the IKEEXT service for phantom DLL loading, writing PerfhLoader to the path %SystemRoot%\System32\wlbsctrl.dll. The vulnerable VIAGLT64.SYS kernel driver (CVE-2017-16237) was also used to gain SYSTEM privileges.

PerfhLoader is a simple loader that reads a file with a hardcoded filename (perfh011.dat) from its current directory, decrypts its contents, loads it into memory and executes it. In all observed cases, both PerfhLoader and the encrypted DLL were in the %SystemRoot%\System32\ folder. Normally, perfhXXX.dat files located in this folder contain Windows Performance Monitor data, which makes it blend in with normal Windows file names.

The cipher used to encrypt and decrypt the payload uses a rolling XOR key, we denote the implementation in Python code in Listing 1.

def crypt_buf(data: bytes) -> bytes:
    xor_key = bytearray(range(0x10))
    buf = bytearray(data)
    for idx in range(len(buf)):
        a = xor_key[(idx + 5) & 0xF]
        b = xor_key[(idx - 3) & 0xF]
        c = xor_key[(idx - 7) & 0xF]
        xor_byte = a ^ b ^ c
        buf[idx] ^= xor_byte
        xor_key[idx & 0xF] = xor_byte
 
    return bytes(buf)

Listing 1: Python implementation of the XOR cipher used by PerfhLoader

The decrypted content contains a DLL that PerfhLoader loads into memory using the Manual-DLL-Loader project11. Interestingly, PondRAT uses this same project for DLL loading.

Discovery

After establishing a foothold, the actor deployed various tools in combination with the RATs described earlier. These included both custom tooling and publicly available tools. Table 1 lists some of the tools we recovered that the actor used.

ToolTool OriginDescription
ScreenshotterActorA tool that takes periodic screenshots and stores them locally
KeyloggerActorA Windows keylogger that writes user keystrokes to a file
Chromium browser dumperActorA browser dump tool that dumps Chromium-based browser cookies and credentials
MidProxyActorProxy tool
Mimikatz12PublicWindows secrets dumper
Proxy Mini13PublicProxy tool
frpc14PublicFast reverse proxy client
Table 1: Tools observed during incident response case (public and actor-developed)

Interestingly, the Fast Reverse Proxy client we found was the same client found in the 3CX compromise by Mandiant15. This client is version 0.32.116 and is from 2020, which is remarkable. We also found traces of a Themida-packed version of Quasar17, a malware family we did not see this Lazarus subgroup use before.

The actor used PondRAT in combination with ThemeForestRAT for roughly three months, to afterwards clean up and install the more sophisticated RAT called RemotePE. We will now discuss these three RATs.

PondRAT

PondRAT is a simple RAT, which its authors seem to refer to as “firstloader”, based on the compilation metadata string objc_firstloader that is present in the macOS samples.

In our case, PondRAT was the initial access payload used to deploy other types of malware, including ThemeForestRAT. Judging from network data, apart from ThemeForestRAT activity, we observed significant activity to the PondRAT C2 server, indicating it was not just used for its loader functionality. In the incident response case from 2020 we encountered POOLRAT in combination with ThemeForestRAT. This could indicate that PondRAT is a successor of POOLRAT.

Overview

PondRAT is a straightforward RAT that allows an operator to read and write files, start processes and run shellcode. It has already been described by some vendors. As far as we know, the earliest sample is from 2021, referenced in a CISA article18. Based on PondRAT’s user-agent, we also noticed that PondRAT was used in an AppleJeus campaign Volexity wrote about19 (MSI file with hash 435c7b4fd5e1eaafcb5826a7e7c16a83). 360 Threat Intelligence Center wrote about PondRAT as well20, linking it to Lazarus and later writing about it being distributed through Python Package Index (PyPI) packages21. Vipyr Security wrote22 about malware that was dropped through malicious Python packages distributed through PyPI, which turned out to be PondRAT. Unit42 published an analysis23 of the RAT, referring to it as PondRAT and showing similarities between PondRAT and another RAT used by Lazarus: POOLRAT.

As described by Unit42, there are similarities between POOLRAT and PondRAT. There is overlap in function and class naming and both families check for successful responses in a similar way.

POOLRAT has more functionality than PondRAT. For example, POOLRAT has a configuration file for C2 servers, can timestomp24 files, can move files around, functionalities that PondRAT lacks. We think this is because there is no need for more functionality if its main function is to load other malware, allowing for a smaller code base and less maintenance.

Command and Control

PondRAT communicates over HTTP(S) with a hardcoded C2 server. Messages sent between the malware and the server are XOR-ed first and then Base64-encoded. For XORing it uses the hex-encoded key 774C71664D5D25775478607E74555462773E525E18237947355228337F433A3B.

Figure 4: PondRAT check-in request

Figure 4 contains an example check-in request to the C2 server. The tuid parameter contains the bot ID, control indicates the request type, and the payload parameter contains the encrypted check-in information. In this case, control is set to fconn, indicating it is a bot check-in, matching with the corresponding function name FConnectProxy(). When receiving a server reply starting with OK, PondRAT fetches a command from the server. For at least one Linux and macOS variant, the parameter names and string values consisted of scrambled letters, e.g. lkjyhnmiop instead of tuid and odlsjdfhw instead of fconn.

Commands

PondRAT has basic commands, such as reading and writing files and executing programs. Table 2 lists all commands and their names from the symbol data. When a bot command is executed, the response includes both the original command ID and a status code indicating either success (0x89A) or failure (0x89B).

Command ID / Status codeSymbol nameDescription
0x892csleepSleep
0x893MsgDownRead file
0x894MsgUpWrite file
0x895Ping
0x896Load PE from C2 in memory
0x897MsgRunLaunch process
0x898MsgCmdExecute command through the shell
0x899Exit
0x89aStatus code indicating command succeeded
0x89bStatus code indicating command failed
0x89cRun shellcode in process
Table 2: PondRAT command IDs and their descriptions

Windows

Only the Windows samples we analysed had support for commands 0x896 and 0x89C. The DLL loading functionality seems to be based on the open-source project “Manual-DLL-Loader”25. As a sidenote, we analysed another POOLRAT Windows sample that used the “SimplePELoader” project26.

POOLRAT’s Little Brother

As mentioned by Palo Alto’s Unit42, PondRAT has similarities with POOLRAT. There is overlap in XOR keys, function naming and class naming. However, there are more similarities. Firstly, the Windows versions of PondRAT and POOLRAT use the format string %sd.e%sc "%s > %s 2>&1" for launching a shell command. Format strings have been discussed in the past27 and this specific format string was linked to Operation Blockbuster Sequel. Furthermore, PondRAT has a peculiar way of generating its bot ID, see the decompiled code below.

Figure 5: Bot ID generation for PondRAT (left) and POOLRAT (right)

Figure 5 shows how PondRAT and POOLRAT compute their bot ID. For PondRAT, tuid is the bot ID. It computes two parts of a 32-bit integer, that are split in two based on the bit_shift variable. Some of the POOLRAT samples compute the bot ID in a similar manner. The sample 6f2f61783a4a59449db4ba37211fa331 has symbol information available and contains a function named GenerateSessionId() that has this same logic.

More similarities can be found as part of the C2 protocol. PondRAT provides feedback to commands issued by the C2 server by returning the command ID concatenated with the status code. POOLRAT uses the same concept, see Figure 6.

Figure 6: Command status concatenation for PondRAT (left) and POOLRAT (right)

Another similarity can be found when comparing the Windows versions of POOLRAT and PondRAT. When running a Shell command (command ID 0x898) with PondRAT, the Windows version creates a temporary file with the prefix TLT in which it saves the command output. Then, it reads the file and sends the contents back to the C2 server and subsequently removes it. However, the way it removes the temporary file is remarkable.

It generates a buffer with random bytes and overwrites the file contents with it. Then, it renames the file 27 times, replacing all letters with only A’s, then B’s, etc. and with the last iteration renames all letters with random uppercase letters. For instance, when the file C:\Windows\Temp\tlt1bd8.tmp is deleted, it would first be renamed to C:\Windows\Temp\AAAAAAA.AAA, then to C:\Windows\Temp\BBBBBBB.BBB, and lastly to something like VYLDVAP.XQA. POOLRAT’s Windows version has the same functionality, see Figure 7.

Figure 7: Windows file name generation for PondRAT (left) and POOLRAT (right)

These similarities show that apart from variable data and symbol names, PondRAT is similar to POOLRAT in coding concepts as well. This further strengthens the connection between the two.

Summary

PondRAT is a simple RAT. Judging from the symbol data of macOS samples, its authors seem to refer to the malware as firstloader, a RAT that targets all three major operating systems. In our case, we observed it in combination with social engineering campaigns, whereas others have seen PondRAT being dropped through malicious software packages. Despite being simple in nature, it seems to do the job, given the frequency in which it is used. Judging from past incidents we investigated, PondRAT is a successor of POOLRAT.

Run, ThemeForest, Run!

In two incident response cases we found traces of a different RAT being used in conjunction with POOLRAT or PondRAT. We named it ThemeForestRAT, based on the substring ThemeForest which it uses in its C2 protocol. It is written in C++ and contains class names such as CServer, CJobManager, CSocketEx, CZipper and CUsbMan. ThemeForestRAT has more functionalities compared to PondRAT and POOLRAT.

In an earlier incident response case in 2020, we observed ThemeForestRAT in combination with POOLRAT. In the case from 2024, we observed it together with PondRAT. Its continued activity over at least five years demonstrates that ThemeForestRAT remains a relevant and capable tool for this actor. Besides Windows, we have observed Linux and macOS versions of the malware.

We believe that on Windows, this RAT is injected and executed in memory only, for example via PondRAT, or a dedicated loader, and is used as stealthier second-stage RAT with more functionality. The fact there are no direct samples of ThemeForestRAT on VirusTotal indicates it is quite successful in staying under the radar.

Overview

On startup, ThemeForestRAT attempts to read the configuration file from disk. When absent, it generates a unique bot ID and uses the hardcoded C2 configuration settings in the binary to create the configuration file.

Interestingly, the Windows variant creates two Windows events and accompanying threads that are used for signalling purposes (see Figure 8). However, the first thread related to the class CUsbMan only creates the temporary directory Z802056 and returns, this turned out to be legacy code as we will describe later.

The second thread monitors for new Remote Desktop (RDP) sessions and notifies the main thread when one is detected. Additionally, the thread checks for new physical console sessions and can optionally spawn extra commands under this session if this is enabled in the configuration.

Figure 8: ThemeForestRAT startup code creating two Windows events and threads for signalling

After creating these two threads it hibernates before connecting to the C2 server. The default hibernation period is three minutes but when it runs for the first time it checks in immediately. There are two cases where ThemeForestRAT wakes up from hibernation, either the hibernation period has passed, or one of the two events is signalled.

When it wakes up from hibernation it randomly selects a C2 server from its list and attempts to establish a connection. Upon receiving a response:OK acknowledgment, it downloads a 4-byte file that must decrypt to the 32-bit constant 0x20191127 to establish a valid C2 session. If this fails it will retry a different C2 and start over again, when the list of servers is exhausted it will go back into hibernation and try again later.

If it succeeds in establishing a C2 session, ThemeForestRAT sends basic system information including its wake-up reason to the C2 server, and the operator can now interact with the RAT as it keeps polling for new commands. When the operator sends an OnTerminate or OnSleep command (see Table 4), the C2 session ends, and the RAT goes back to hibernation.

struct SystemInfoWindows   // sizeof=0x478
{
    uint32  job_id;        // 0x10005 = Windows
    wchar   bot_id[20];
    wchar   hostname[64];
    wchar   whoami[50];
    uint32  dwMajorVersion;
    uint32  dwMinorVersion;
    uint32  dwPlatformId;
    uint16  padding1;
    wchar   ip_address[20];
    wchar   timezone[50];
    wchar   gpu[50];
    wchar   memory[50];
    uint16  padding2;
    uint32  wakeup_reason; // 0 = hibernation, 1 = USB, 2 = RDP
    wchar   os_version[256];
};

struct SystemInfoPOSIX     // sizeof=0x478
{
    uint32  job_id;        // 0x20005 = POSIX
    char    bot_id[16];
    char    unused1[24];
    char    hostname[128];
    char    username[114];
    char    ip_address[40];
    char    timezone[100];
    char    arch[100];
    char    memory[100];
    char    unused2[6];
    char    os_version[512];
};

Listing 2: ThemeForestRAT system information structure that is sent after establishing a C2 session

Listing 2 shows the structure definitions that ThemeForestRAT uses for sending system information when establishing a C2 session. The job_id field indicates the OS type, 0x10005 for Windows, and 0x20005 for both Linux and macOS as they share the same structure.

Configuration

The configuration file of ThemeForestRAT is encrypted with RC4 using the hex-encoded key 201A192D838F4853E300 and contains the following settings:

  • 64-bit unique bot ID
  • List of ten C2 server URLs
  • Command interpreter, for example cmd.exe (not used)
  • List of optional commands to execute under the user of the active console session (Windows only, empty by default)
  • Matching array to enable the optional console command
  • Last check-in timestamp
  • Hibernation time between C2 sessions in minutes, default value is 3
  • C2 callback settings, for example to immediately check in on a new active RDP connection

The configuration can be parsed using the C structure definition from Listing 3.

struct ThemeForestC2Config
{
    uint64  bot_id;
    wchar   urls[10][1024];
    wchar   shell[1024];
    wchar   wts_console_cmdline[10][1024];
    char    wts_console_cmdline_enabled[10];
    uint32  last_checkin_epoch;
    uint32  configured_hibernate_minutes;
    uint32  active_hibernate_minutes;
    uint16  callback_settings;
};

Listing 3: ThemeForestRAT configuration structure definition for Windows

The configuration path that the RAT reads from disk is hardcoded. On macOS and Linux, this is an absolute path, while on Windows it looks in the current working directory where the RAT is launched. In Table 3 we list the observed configuration paths and hardcoded configuration file sizes for ThemeForestRAT.

Operating systemThemeForestRAT configuration file on diskFile size
Windowsnetraid.inf43048 bytes
Linux/var/crash/cups43044 bytes
macOS/private/etc/imap43044 bytes
Table 3: Observed ThemeForestRAT configuration paths and their file sizes on Windows, Linux and macOS

Command and Control

ThemeForestRAT communicates over HTTP(S). The filenames it uses for retrieving commands from the C2 server are prefixed with ThemeForest_. The response data is sent back to the operator as a file prefixed with Thumb_, see Figure 6. On Windows it uses the Ryeol Http Client28 library for HTTP communications, and on macOS and Linux it uses libcurl. ThemeForestRAT has a single hardcoded C2 in the binary, but its configuration can be updated by sending the SetInfo command.

Figure 9: ThemeForestRAT sending encrypted system information to C2 server on initial check-in

Commands

In terms of command functionality, ThemeForestRAT supports over twenty commands, at least twice as much as PondRAT. The Linux and macOS versions contain debug symbols, which allows us to map the command IDs to function names where available.

Symbol nameCommand IDDescription
ListDrives0x10001000Get list of drives
CServer::OnFileBrowse0x10001001Get directory listing
CServer::OnFileCopy0x10001002Copy file from source to destination on victim machine
CServer::OnFileDelete0x10001003Delete a file
FileDeleteSecure0x10001004Delete a file securely
CServer::OnFileUpload0x10001005Open a file for writing on victim machine
CServer::FileDownload0x10001006Download file from victim machine
Run0x10001007Execute a command and return the exit code
CServer::OnChfTime0x10001008Timestomp file based on another file on disk
–0x10001009–
CServer::OnTestConn0x1000100aTest TCP connection to host and port
CServer::OnCmdRun0x1000100bRun command in background and return output
CServer::OnSleep0x1000100cHibernate for X seconds, this will also be saved in the configuration file
CServer::OnViewProcess0x1000100dGet process listing
CServer::OnKillProcess0x1000100eKill process by process ID
–0x1000100f–
CServer::OnFileProperty0x10001010Get file properties
CServer::OnGetInfo0x10001011Get current RAT configuration
CServer::OnSetInfo0x10001012Update and save RAT configuration file
CServer::OnZipDownload0x10001013Download a directory or file as a compressed Zip file
CServer::OnTerminate0x10001014Flush configuration to disk and hibernate until next wake up
(Data)0x10001015Data
(JobSuccess)0x10001016Job succeeded
(JobFailed)0x10001017Job failed
GetServiceName0x10001018Return current service name
CleanupAndExit0x10001019Remove persistence, configuration file, and terminate RAT
RecvMsg0x1000101aForce C2 check-in
RunAs0x1000101bSpawn a process under the user token of given Windows Terminal Services session
–0x1000101c–
WriteRandomData0x1000101dWrite random data to file handle
CServer::OnInjectShellcode0x1000101eInject shellcode into process ID
Table 4: ThemeForestRAT command IDs and their descriptions

Note that the symbol names in Table 4 that start with CServer:: are from the debug symbols and the other names are deduced based on analysis of the command.

Shellcode Injection

On Windows, the CServer::OnInjectShellcode command injects shellcode into a given process ID using NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory and RtlCreateUserThread Windows API calls. The shellcode is encrypted using the same algorithm used in PerfhLoader (see Listing 1). In the macOS and Linux samples we have analysed, this command is defined as an empty stub.

RomeoGolf’s Little Brother

In 2016, Novetta released a detailed report called Operation Blockbuster29, in which a Novetta-led coalition of security companies analysed malware samples from multiple cybersecurity incidents. The investigation linked the 2014 Sony Pictures attack to the Lazarus Group and revealed that the same actor had been behind numerous other attacks against government, military, and commercial targets using related malware since 2009.

Operation Blockbuster’s malware report describes RomeoGolf, a RAT that resembles ThemeForestRAT in several ways:

  • Uses the temporary folder Z802056, although not used in ThemeForestRAT, is still created
  • Overlapping command IDs and functionality
  • Same unique identifier generation using 4 calls to rand()
  • Configuration file with extension *.inf on Windows
  • Timestomping of the configuration file based on mspaint.exe
  • Two signalling threads for USB and RDP events

Figure 10 shows the RomeoGolf startup logic for generating its bot ID and two signalling threads that is identical to ThemeForestRAT (see Figure 5).

Figure 10: RomeoGolf startup creates two signalling threads, comparable to ThemeForestRAT (see Figure 5).

As can be seen in Table 5, the functionality to detect and copy data from newly attached logical drives has been removed in ThemeForestRAT, while leaving the temporary directory creation intact. Also, the thread to check for new RDP sessions has been extended in ThemeForestRAT to optionally spawn up to ten extra configured commands under the user of the active physical console session.

RomeoGolfThemeForestRAT
Compilation dateFri Oct 11 01:20:48 2013Thu Sep 07 06:40:40 2023
Known configuration filecrkdf32.infnetraid.inf
Configuration file timestomped tomspaint.exemspaint.exe
USB thread logic1. Creates %TEMP%\Z802056
2. Checks for newly attached drives and copies data to above folder
3. Signal on newly attached drives
1. Creates %TEMP%\Z802056
RDP thread logic1. Signal on new active RDP sessions
1. Start configured commands under the user of the new active console session
2. Signal on new active RDP session if configured
C2 communicationFake TLSHTTP(S)
Highest known command id0x100010130x1000101e
Table 5: Differences and similarities between RomeoGolf and ThemeForestRAT

While RomeoGolf used Fake TLS30 and its own custom server for its C2 communications, ThemeForestRAT uses the HTTP protocol and shared hosting for its C2 servers.

Onto the next stage with RemotePE

In the 2024 incident response case, we observed the actor cleaning up PondRAT and ThemeForestRAT, to deploy a more advanced RAT, which we named RemotePE. RemotePE is retrieved from a C2 server by RemotePELoader. RemotePELoader is encrypted on disk using Window’s Data Protection API (DPAPI) and is loaded by DPAPILoader. Using DPAPI enables environmental keying and makes it difficult to recover the original payload without access to the machine. DPAPILoader was made persistent through a created Windows service.

Figure 10: RemotePELoader check-in request to retrieve RemotePE payload

In Figure 10, we show a RemotePELoader check-in request used to retrieve RemotePE from the C2 server. RemotePE is written in C++ and is more advanced and elegant. We think that the actor uses this more sophisticated RAT for interesting or high-value targets that require a higher degree of operational security. Interestingly, it too uses the file renaming strategy PondRAT and POOLRAT Windows samples implement, except it skips the last random iteration.

We will publish a more thorough analysis of RemotePE in a future blogpost.

Summary

This blog is about a Lazarus subgroup that we have encountered multiple times during incident response engagements. This is a capable, patient, financially motivated actor who remains a legitimate threat.

We first discussed an incident response case from 2024, where this actor impersonated employees of trading companies to establish contact with potential victims. Though the method of achieving initial access remains unknown, we suspect a Chrome zero-day was used.

After initial access, two RATs were used in combination: PondRAT and ThemeForestRAT. Though PondRAT has already been discussed, there are no public analyses of ThemeForestRAT at the time of writing. For persistence, phantom DLL loading was used in conjunction with a custom loader called PerfhLoader.

PondRAT is a primitive RAT that provides little flexibility, however, as an initial payload it achieves its purpose. It has similarities with POOLRAT/SimpleTea. For more complex tasks, the actor uses ThemeForestRAT, which has more functionality and stays under the radar as it is loaded into memory only.

Lastly, we found the actor replaced ThemeForestRAT and PondRAT with the more advanced RemotePE. A detailed analysis of RemotePE will be published in the near future. So, stay tuned!

In Table 6 and 7, we list indicators of compromise related to the incident response cases we investigated and other artifacts we link to this actor.

Incident Response Support

If you have any questions or need assistance based on these findings, please contact Fox-IT CERT at cert@fox-it.com. For urgent matters, call 0800-FOXCERT (0800-3692378) within the Netherlands, or +31152847999 internationally to reach one of our incident responders.

Indicators of Compromise

TypeIndicatorComment
net.domaincalendly[.]liveFake calendly.com
net.domainpicktime[.]liveFake picktime.com
net.domainoncehub[.]coFake oncehub.com
net.domaingo.oncehub[.]coFake oncehub.com
net.domaindpkgrepo[.]comPotentially related to Chrome exploitation
net.domainpypilibrary[.]comUnknown, visited by msiexec.exe shortly after dpkgrepo[.]com
net.domainpypistorage[.]comUnknown, connection seen under SessionEnv service
net.domainkeondigital[.]comLPEClient server, connection seen under SessionEnv service
net.domainarcashop[.]orgPondRAT C2
net.domainjdkgradle[.]comPondRAT C2
net.domainlatamics[.]orgPondRAT C2
net.domainlmaxtrd[.]comThemeForestRAT C2
net.domainpaxosfuture[.]comThemeForestRAT C2
net.domainwww[.]plexisco[.]comThemeForestRAT C2
net.domainftxstock[.]comThemeForestRAT C2
net.domainwww[.]natefi[.]orgThemeForestRAT C2
net.domainnansenpro[.]comThemeForestRAT C2
net.domainaes-secure[.]netRemotePE payload delivery and C2
net.domainazureglobalaccelerator[.]comRemotePE payload delivery and C2
net.domainazuredeploypackages[.]netUnknown, connection seen via injected process
net.ip144.172.74[.]120Fast Reverse Proxy server
net.ip192.52.166[.]253Used as parameter for Quasar
file.path%TEMP%\tmpntl.datWindows keylogger output file path
file.pathC:\Windows\Temp\TMP01.datWindows keylogger error file path
file.namenetraid.infThemeForestRAT Windows configuration filename
file.path/var/crash/cupsThemeForestRAT Linux configuration file path
file.path/private/etc/imapThemeForestRAT macOS configuration file path
file.path/private/etc/krb5d.confPOOLRAT macOS configuration file path, CISA 2021 report
file.path/etc/apdl.cfPOOLRAT Linux configuration file path
file.path%SystemRoot%\system32\apdl.cfPOOLRAT Windows configuration file path
file.path/tmp/xweb_log.mdPOOLRAT, PondRAT Linux libcurl error log file path
file.nameperfh011.datEncrypted payload loaded by PerfhLoader
file.namehsu.datFilename actor used for SysInternals ADExplorer output
file.namepfu.datFilename actor used for SysInternals Handle viewer output
file.namefpc.datDropped Fast Reverse Proxy configuration filename
file.namefp.exeDropped Fast Reverse Proxy executable
file.nametsvipsrv.dllDLL phantom loaded by actor (SessionEnv)
file.namewlbsctrl.dllDLL phantom loaded by actor (IKEEXT)
file.nameadepfx.exeFilename actor used for legitimate SysInternals ADExplorer
file.namehd.exeFilename actor used for legitimate SysInternals Nthandle.exe
file.namemsnprt.exeFilename actor uses for Proxymini, open-source socks proxy
file.path%LocalAppData%\IconCache.logOutput path for custom browser credentials and cookies dumper based on Mimikatz
file.path/private/etc/pdpastemacOS keylogger file path
file.path/private/etc/xmemmacOS keylogger output file path
file.path/private/etc/tls3macOS screenshotter output directory
file.path%LocalAppData%\Microsoft\Software\CacheWindows screenshotter output directory
file.pathc:\windows\system32\cmui.exeThemida-packed Quasar
Table 6: Indicators of Compromise linked to actor, without hashes
digest.sha256Comment
24d5dd3006c63d0f46fb33cbc1f576325d4e7e03e3201ff4a3c1ffa604f1b74aFast Reverse Proxy v0.32.1, also observed by Mandiant in the 3CX supply chain attack
4715e5522fc91a423a5fcad397b571c5654dc0c4202459fdca06841eba1ae9b3PerfhLoader
8c3c8f24dc0c1d165f14e5a622a1817af4336904a3aabeedee3095098192d91fPerfhLoader
f4d8e1a687e7f7336162d3caed9b25d9d3e6cfe75c89495f75a92ca87025374bPOOLRAT Windows
85045d9898d28c9cdc4ed0ca5d76eceb457d741c5ca84bb753dde1bea980b516POOLRAT Linux
5e40d106977017b1ed235419b1e59ff090e1f43ac57da1bb5d80d66ae53b1df8POOLRAT macOS (CISA 2021 report)
c66ba5c68ba12eaf045ed415dfa72ec5d7174970e91b45fda9ebb32e0a37784aThemeForestRAT Windows
ff32bc1c756d560d8a9815db458f438d63b1dcb7e9930ef5b8639a55fa7762c9ThemeForestRAT Linux
cc4c18fefb61ec5b3c69c31beaa07a4918e0b0184cb43447f672f62134eb402bThemeForestRAT macOS
6510d460395ca3643133817b40d9df4fa0d9dbe8e60b514fdc2d4e26b567dfbdPondRAT Windows
973f7939ea03fd2c9663dafc21bb968f56ed1b9a56b0284acf73c3ee141c053cPondRAT Linux
f0321c93c93fa162855f8ea4356628eef7f528449204f42fbfa002955a0ba528PondRAT macOS
4f6ae0110cf652264293df571d66955f7109e3424a070423b5e50edc3eb43874DPAPILoader
aa4a2d1215f864481994234f13ab485b95150161b4566c180419d93dda7ac039DPAPILoader
159471e1abc9adf6733af9d24781fbf27a776b81d182901c2e04e28f3fe2e6f3DPAPILoader
7a05188ab0129b0b4f38e2e7599c5c52149ce0131140db33feb251d926428d68RemotePELoader (decrypted from disk)
37f5afb9ed3761e73feb95daceb7a1fdbb13c8b5fc1a2ba22e0ef7994c7920efRemotePE
59a651dfce580d28d17b2f716878a8eff8d20152b364cf873111451a55b7224dWindows keylogger
3c8f5cc608e3a4a755fe1a2b099154153fb7a88e581f3b122777da399e698ccaWindows screenshotter
d998de6e40637188ccbb8ab4a27a1e76f392cb23df5a6a242ab9df8ee4ab3936macOS keylogger (getkey)
e4ce73b4dbbd360a17f482abcae2d479bc95ea546d67ec257785fa51872b2e3fmacOS screenshotter (getscreen)
1a051e4a3b62cd2d4f175fb443f5172da0b40af27c5d1ffae21fde13536dd3e1macOS clipboard logger (pdpaste)
9dddf5a1d32e3ba7cc27f1006a843bfd4bc34fa8a149bcc522f27bda8e95db14Proxymini tool, opensource SOCKS proxy tool
2c164237de4d5904a66c71843529e37cea5418cdcbc993278329806d97a336a5Themida-packed Quasar
Table 7: SHA256 hashes of tools used by the actor

YARA rules

import "pe"

rule Lazarus_DPAPILoader_Hunting {
  meta:
    description = "Hunting rule to detect DPAPILoader, a loader used to load RemotePE."
    author      = "Fox-IT / NCC Group"

  strings:
    $msg_1 = "[!] Could not allocate memory at the desired base!\n"
    $msg_2 = "[!] Virtual section size is out ouf bounds: "
    $msg_3 = "[!] Invalid relocDir pointer\n"
    $msg_4 = "[-] Not supported relocations format at %d: %d\n"
    $msg_5 = "[!] Cannot fill imports into 32 bit PE via 64 bit loader!\n"

  condition:
    any of them and pe.imports("Crypt32.dll", "CryptUnprotectData")
}

rule Lazarus_RemotePE_C2_strings {
  meta:
    description = "RemotePE strings used for C2."
    author      = "Fox-IT / NCC Group"

  strings:
    $a = "MicrosoftApplicationsTelemetryDeviceId" wide ascii xor
    $b = "armAuthorization" wide ascii xor
    $c = "ai_session" wide ascii xor

  condition:
    uint16(0) == 0x5A4D and all of them
}

rule Lazarus_RemotePE_class_strings {
  meta:
    description = "RemotePE class strings."
    author      = "Fox-IT / NCC Group"

  strings:
    $a = "IMiddleController" ascii wide xor
    $b = "IChannelController" ascii wide xor
    $c = "IConfigProfile" ascii wide xor
    $d = "IKernelModule" ascii wide xor

  condition:
    all of them
}

rule Lazarus_PerfhLoader_XOR_key {
  meta:
    description = "XOR key used for shellcode obfuscation."
    author      = "Fox-IT / NCC Group"

  strings:
    $mov_1  = { C7 [1-3] 00 01 02 03 }
    $mov_2  = { C7 [1-3] 04 05 06 07 }
    $mov_3  = { C7 [1-3] 08 09 0A 0B }
    $mov_4  = { C7 [1-3] 0C 0D 0E 0F }
    $init_1 = { 41 8D ?? FD 41 8D ?? F9 }

  condition:
    all of them
}

rule Lazarus_ThemeForestRAT_C2_strings {
  meta:
    description = "ThemeForestRAT strings used for C2."
    author      = "Fox-IT / NCC Group"

  strings:
    $themeforest = "ThemeForest_%s" ascii wide
    $thumb       = "Thumb_%s" ascii wide
    $param_code  = "code" ascii wide
    $param_fn    = "fn" ascii wide
    $param_ldf   = "ldf" ascii wide

  condition:
    all of them
}

rule Lazarus_ThemeForestRAT_RC4_key {
  meta:
    description = "ThemeForest RC4 key used for config file."
    author      = "Fox-IT / NCC Group"

  strings:
    $rc4_key     = { 20 1A 19 2D 83 8F 48 53 E3 00 }
    $rc4_key_mov = { 20 1A 19 2D [2-8] 83 8F 48 53 [2-10] E3 00 }

  condition:
    any of them
}

References

  •  

Three Lazarus RATs coming for your cheese

Authors: Yun Zheng Hu and Mick Koomen

A Telegram from Pyongyang

Introduction

In the past few years, Fox-IT and NCC Group have conducted multiple incident response cases involving a Lazarus subgroup that specifically targets organizations in the financial and cryptocurrency sector. This Lazarus subgroup overlaps with activity linked to AppleJeus1, Citrine Sleet2, UNC47363, and Gleaming Pisces4. This actor uses different remote access trojans (RATs) in their operations, known as PondRAT5, ThemeForestRAT and RemotePE. In this article, we analyse and discuss these three.

First, we describe an incident response case from 2024, where we observed the three RATs. This gives insights into the tactics, techniques, and procedures (TTPs) of this actor. Then, we discuss PondRAT, ThemeForestRAT and RemotePE, respectively.

PondRAT received quite some attention last year, we give a brief overview of the malware and document other similarities between PondRAT and POOLRAT (also known as SimpleTea) that have not yet been publicly documented. Secondly, we discuss ThemeForestRAT, a RAT that has been in use for at least six years now, but has not yet been discussed publicly. These two malware families were used in conjunction, where PondRAT was on disk and ThemeForestRAT seemed to only run in memory.

Lastly, we briefly describe RemotePE, a more advanced RAT of this group. We found evidence that the actor cleaned up PondRAT and ThemeForestRAT artifacts and subsequently installed RemotePE, potentially signifying a next stage in the attack. We cannot directly link RemotePE to any public malware family at the time of this writing.

In all cases, the actor used social engineering as an initial access vector. In one case, we suspect a zero-day might have been used to achieve code execution on one of the victim’s machines. We think this highlights their advanced capabilities, and with their history of activity, also shows their determination.

A Telegram from Pyongyang

In 2024, Fox-IT investigated an incident at an organisation in decentralized finance (DeFi). There, an employee’s machine was compromised through social engineering. From there, the actor performed discovery from inside the network using different RATs in combination with other tools, for example, to harvest credentials or proxy connections. Afterwards, the actor moved to a stealthier RAT, likely signifying a next stage in the attack.

In Figure 1, we provide an overview of the attack chain, where we highlight four phases of the attack:

  1. Social engineering: the actor impersonates an existing employee of a trading company on Telegram and sets up a meeting with the victim, using fake meeting websites.
  2. Exploitation: the victim machine gets compromised and shortly afterwards PondRAT is deployed. We are uncertain how the compromise was achieved, though we suspect a Chrome zero-day vulnerability was used.
  3. Discovery: the actor uses various tooling to explore the victim network and observe daily activities.
  4. Next phase: after three months, the actor removes PerfhLoader, PondRAT and ThemeForestRAT and deploys a more advanced RAT, which we named RemotePE.
Figure 1: Overview of the attack chain from a 2024 incident response case involving a Lazarus subgroup

Social Engineering

We found traces matching a social engineering technique previously described by SlowMist6. This social engineering campaign targets employees of companies active in the cryptocurrency sector by posing as employees of investment institutions on Telegram.

This Lazarus subgroup uses fake Calendly and Picktime websites, including fake websites of the organisations they impersonate. We found traces of two impersonated employees of two different companies. We did not observe any domains linked to the “Access Restricted” trick as described by SlowMist. In Figure 2, you can see a Telegram message from the actor, impersonating an existing employee of a trading company. Looking up the impersonated person, showed that the person indeed worked at the trading company.

Figure 2: Lazarus subgroup impersonating an employee at a trading company interested in the cryptocurrency sector

From the forensic data, we could not establish a clear initial access vector. We suspect a Chrome zero-day exploit was used. Although, we have no actual forensic data to back up this claim, we did notice changes in endpoint logging behaviour. Around the time of compromise, we noted a sudden decrease in the logging of the endpoint detection agent that was running on the machine. Later, Microsoft published a blogpost7, describing Citrine Sleet using a zero-day Chrome exploit to launch an evasive rootkit called FudModule8, which could explain this behaviour.

Persistence with PerfhLoader

The actor leveraged the SessionEnv service for persistence. This existing Windows service is vulnerable to phantom DLL loading9. A custom TSVIPSrv.dll can be placed inside the %SystemRoot%\System32\ directory, which SessionEnv will load upon startup. The actor placed its own loader in this directory, which we refer to as PerfhLoader. Persistence was ensured by making the service start automatically at reboot using the following command:

sc config sessionenv start=auto

The actor also modified the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\SessionEnv\RequiredPrivileges registry key by adding SeDebugPrivilege and SeLoadDriverPrivilege privileges. These elevated privileges enable loading kernel drivers, which can bypass or disable Endpoint Detection and Response (EDR) tools on the compromised system.

Figure 3: PerfhLoader loaded through SessionEnv service via Phantom DLL Loading which in turn loads PondRAT or POOLRAT

In a case from 202010, this actor used the IKEEXT service for phantom DLL loading, writing PerfhLoader to the path %SystemRoot%\System32\wlbsctrl.dll. The vulnerable VIAGLT64.SYS kernel driver (CVE-2017-16237) was also used to gain SYSTEM privileges.

PerfhLoader is a simple loader that reads a file with a hardcoded filename (perfh011.dat) from its current directory, decrypts its contents, loads it into memory and executes it. In all observed cases, both PerfhLoader and the encrypted DLL were in the %SystemRoot%\System32\ folder. Normally, perfhXXX.dat files located in this folder contain Windows Performance Monitor data, which makes it blend in with normal Windows file names.

The cipher used to encrypt and decrypt the payload uses a rolling XOR key, we denote the implementation in Python code in Listing 1.

def crypt_buf(data: bytes) -> bytes:
    xor_key = bytearray(range(0x10))
    buf = bytearray(data)
    for idx in range(len(buf)):
        a = xor_key[(idx + 5) & 0xF]
        b = xor_key[(idx - 3) & 0xF]
        c = xor_key[(idx - 7) & 0xF]
        xor_byte = a ^ b ^ c
        buf[idx] ^= xor_byte
        xor_key[idx & 0xF] = xor_byte
 
    return bytes(buf)

Listing 1: Python implementation of the XOR cipher used by PerfhLoader

The decrypted content contains a DLL that PerfhLoader loads into memory using the Manual-DLL-Loader project11. Interestingly, PondRAT uses this same project for DLL loading.

Discovery

After establishing a foothold, the actor deployed various tools in combination with the RATs described earlier. These included both custom tooling and publicly available tools. Table 1 lists some of the tools we recovered that the actor used.

ToolTool OriginDescription
ScreenshotterActorA tool that takes periodic screenshots and stores them locally
KeyloggerActorA Windows keylogger that writes user keystrokes to a file
Chromium browser dumperActorA browser dump tool that dumps Chromium-based browser cookies and credentials
MidProxyActorProxy tool
Mimikatz12PublicWindows secrets dumper
Proxy Mini13PublicProxy tool
frpc14PublicFast reverse proxy client
Table 1: Tools observed during incident response case (public and actor-developed)

Interestingly, the Fast Reverse Proxy client we found was the same client found in the 3CX compromise by Mandiant15. This client is version 0.32.116 and is from 2020, which is remarkable. We also found traces of a Themida-packed version of Quasar17, a malware family we did not see this Lazarus subgroup use before.

The actor used PondRAT in combination with ThemeForestRAT for roughly three months, to afterwards clean up and install the more sophisticated RAT called RemotePE. We will now discuss these three RATs.

PondRAT

PondRAT is a simple RAT, which its authors seem to refer to as “firstloader”, based on the compilation metadata string objc_firstloader that is present in the macOS samples.

In our case, PondRAT was the initial access payload used to deploy other types of malware, including ThemeForestRAT. Judging from network data, apart from ThemeForestRAT activity, we observed significant activity to the PondRAT C2 server, indicating it was not just used for its loader functionality. In the incident response case from 2020 we encountered POOLRAT in combination with ThemeForestRAT. This could indicate that PondRAT is a successor of POOLRAT.

Overview

PondRAT is a straightforward RAT that allows an operator to read and write files, start processes and run shellcode. It has already been described by some vendors. As far as we know, the earliest sample is from 2021, referenced in a CISA article18. Based on PondRAT’s user-agent, we also noticed that PondRAT was used in an AppleJeus campaign Volexity wrote about19 (MSI file with hash 435c7b4fd5e1eaafcb5826a7e7c16a83). 360 Threat Intelligence Center wrote about PondRAT as well20, linking it to Lazarus and later writing about it being distributed through Python Package Index (PyPI) packages21. Vipyr Security wrote22 about malware that was dropped through malicious Python packages distributed through PyPI, which turned out to be PondRAT. Unit42 published an analysis23 of the RAT, referring to it as PondRAT and showing similarities between PondRAT and another RAT used by Lazarus: POOLRAT.

As described by Unit42, there are similarities between POOLRAT and PondRAT. There is overlap in function and class naming and both families check for successful responses in a similar way.

POOLRAT has more functionality than PondRAT. For example, POOLRAT has a configuration file for C2 servers, can timestomp24 files, can move files around, functionalities that PondRAT lacks. We think this is because there is no need for more functionality if its main function is to load other malware, allowing for a smaller code base and less maintenance.

Command and Control

PondRAT communicates over HTTP(S) with a hardcoded C2 server. Messages sent between the malware and the server are XOR-ed first and then Base64-encoded. For XORing it uses the hex-encoded key 774C71664D5D25775478607E74555462773E525E18237947355228337F433A3B.

Figure 4: PondRAT check-in request

Figure 4 contains an example check-in request to the C2 server. The tuid parameter contains the bot ID, control indicates the request type, and the payload parameter contains the encrypted check-in information. In this case, control is set to fconn, indicating it is a bot check-in, matching with the corresponding function name FConnectProxy(). When receiving a server reply starting with OK, PondRAT fetches a command from the server. For at least one Linux and macOS variant, the parameter names and string values consisted of scrambled letters, e.g. lkjyhnmiop instead of tuid and odlsjdfhw instead of fconn.

Commands

PondRAT has basic commands, such as reading and writing files and executing programs. Table 2 lists all commands and their names from the symbol data. When a bot command is executed, the response includes both the original command ID and a status code indicating either success (0x89A) or failure (0x89B).

Command ID / Status codeSymbol nameDescription
0x892csleepSleep
0x893MsgDownRead file
0x894MsgUpWrite file
0x895Ping
0x896Load PE from C2 in memory
0x897MsgRunLaunch process
0x898MsgCmdExecute command through the shell
0x899Exit
0x89aStatus code indicating command succeeded
0x89bStatus code indicating command failed
0x89cRun shellcode in process
Table 2: PondRAT command IDs and their descriptions

Windows

Only the Windows samples we analysed had support for commands 0x896 and 0x89C. The DLL loading functionality seems to be based on the open-source project “Manual-DLL-Loader”25. As a sidenote, we analysed another POOLRAT Windows sample that used the “SimplePELoader” project26.

POOLRAT’s Little Brother

As mentioned by Palo Alto’s Unit42, PondRAT has similarities with POOLRAT. There is overlap in XOR keys, function naming and class naming. However, there are more similarities. Firstly, the Windows versions of PondRAT and POOLRAT use the format string %sd.e%sc "%s > %s 2>&1" for launching a shell command. Format strings have been discussed in the past27 and this specific format string was linked to Operation Blockbuster Sequel. Furthermore, PondRAT has a peculiar way of generating its bot ID, see the decompiled code below.

Figure 5: Bot ID generation for PondRAT (left) and POOLRAT (right)

Figure 5 shows how PondRAT and POOLRAT compute their bot ID. For PondRAT, tuid is the bot ID. It computes two parts of a 32-bit integer, that are split in two based on the bit_shift variable. Some of the POOLRAT samples compute the bot ID in a similar manner. The sample 6f2f61783a4a59449db4ba37211fa331 has symbol information available and contains a function named GenerateSessionId() that has this same logic.

More similarities can be found as part of the C2 protocol. PondRAT provides feedback to commands issued by the C2 server by returning the command ID concatenated with the status code. POOLRAT uses the same concept, see Figure 6.

Figure 6: Command status concatenation for PondRAT (left) and POOLRAT (right)

Another similarity can be found when comparing the Windows versions of POOLRAT and PondRAT. When running a Shell command (command ID 0x898) with PondRAT, the Windows version creates a temporary file with the prefix TLT in which it saves the command output. Then, it reads the file and sends the contents back to the C2 server and subsequently removes it. However, the way it removes the temporary file is remarkable.

It generates a buffer with random bytes and overwrites the file contents with it. Then, it renames the file 27 times, replacing all letters with only A’s, then B’s, etc. and with the last iteration renames all letters with random uppercase letters. For instance, when the file C:\Windows\Temp\tlt1bd8.tmp is deleted, it would first be renamed to C:\Windows\Temp\AAAAAAA.AAA, then to C:\Windows\Temp\BBBBBBB.BBB, and lastly to something like VYLDVAP.XQA. POOLRAT’s Windows version has the same functionality, see Figure 7.

Figure 7: Windows file name generation for PondRAT (left) and POOLRAT (right)

These similarities show that apart from variable data and symbol names, PondRAT is similar to POOLRAT in coding concepts as well. This further strengthens the connection between the two.

Summary

PondRAT is a simple RAT. Judging from the symbol data of macOS samples, its authors seem to refer to the malware as firstloader, a RAT that targets all three major operating systems. In our case, we observed it in combination with social engineering campaigns, whereas others have seen PondRAT being dropped through malicious software packages. Despite being simple in nature, it seems to do the job, given the frequency in which it is used. Judging from past incidents we investigated, PondRAT is a successor of POOLRAT.

Run, ThemeForest, Run!

In two incident response cases we found traces of a different RAT being used in conjunction with POOLRAT or PondRAT. We named it ThemeForestRAT, based on the substring ThemeForest which it uses in its C2 protocol. It is written in C++ and contains class names such as CServer, CJobManager, CSocketEx, CZipper and CUsbMan. ThemeForestRAT has more functionalities compared to PondRAT and POOLRAT.

In an earlier incident response case in 2020, we observed ThemeForestRAT in combination with POOLRAT. In the case from 2024, we observed it together with PondRAT. Its continued activity over at least five years demonstrates that ThemeForestRAT remains a relevant and capable tool for this actor. Besides Windows, we have observed Linux and macOS versions of the malware.

We believe that on Windows, this RAT is injected and executed in memory only, for example via PondRAT, or a dedicated loader, and is used as stealthier second-stage RAT with more functionality. The fact there are no direct samples of ThemeForestRAT on VirusTotal indicates it is quite successful in staying under the radar.

Overview

On startup, ThemeForestRAT attempts to read the configuration file from disk. When absent, it generates a unique bot ID and uses the hardcoded C2 configuration settings in the binary to create the configuration file.

Interestingly, the Windows variant creates two Windows events and accompanying threads that are used for signalling purposes (see Figure 8). However, the first thread related to the class CUsbMan only creates the temporary directory Z802056 and returns, this turned out to be legacy code as we will describe later.

The second thread monitors for new Remote Desktop (RDP) sessions and notifies the main thread when one is detected. Additionally, the thread checks for new physical console sessions and can optionally spawn extra commands under this session if this is enabled in the configuration.

Figure 8: ThemeForestRAT startup code creating two Windows events and threads for signalling

After creating these two threads it hibernates before connecting to the C2 server. The default hibernation period is three minutes but when it runs for the first time it checks in immediately. There are two cases where ThemeForestRAT wakes up from hibernation, either the hibernation period has passed, or one of the two events is signalled.

When it wakes up from hibernation it randomly selects a C2 server from its list and attempts to establish a connection. Upon receiving a response:OK acknowledgment, it downloads a 4-byte file that must decrypt to the 32-bit constant 0x20191127 to establish a valid C2 session. If this fails it will retry a different C2 and start over again, when the list of servers is exhausted it will go back into hibernation and try again later.

If it succeeds in establishing a C2 session, ThemeForestRAT sends basic system information including its wake-up reason to the C2 server, and the operator can now interact with the RAT as it keeps polling for new commands. When the operator sends an OnTerminate or OnSleep command (see Table 4), the C2 session ends, and the RAT goes back to hibernation.

struct SystemInfoWindows   // sizeof=0x478
{
    uint32  job_id;        // 0x10005 = Windows
    wchar   bot_id[20];
    wchar   hostname[64];
    wchar   whoami[50];
    uint32  dwMajorVersion;
    uint32  dwMinorVersion;
    uint32  dwPlatformId;
    uint16  padding1;
    wchar   ip_address[20];
    wchar   timezone[50];
    wchar   gpu[50];
    wchar   memory[50];
    uint16  padding2;
    uint32  wakeup_reason; // 0 = hibernation, 1 = USB, 2 = RDP
    wchar   os_version[256];
};

struct SystemInfoPOSIX     // sizeof=0x478
{
    uint32  job_id;        // 0x20005 = POSIX
    char    bot_id[16];
    char    unused1[24];
    char    hostname[128];
    char    username[114];
    char    ip_address[40];
    char    timezone[100];
    char    arch[100];
    char    memory[100];
    char    unused2[6];
    char    os_version[512];
};

Listing 2: ThemeForestRAT system information structure that is sent after establishing a C2 session

Listing 2 shows the structure definitions that ThemeForestRAT uses for sending system information when establishing a C2 session. The job_id field indicates the OS type, 0x10005 for Windows, and 0x20005 for both Linux and macOS as they share the same structure.

Configuration

The configuration file of ThemeForestRAT is encrypted with RC4 using the hex-encoded key 201A192D838F4853E300 and contains the following settings:

  • 64-bit unique bot ID
  • List of ten C2 server URLs
  • Command interpreter, for example cmd.exe (not used)
  • List of optional commands to execute under the user of the active console session (Windows only, empty by default)
  • Matching array to enable the optional console command
  • Last check-in timestamp
  • Hibernation time between C2 sessions in minutes, default value is 3
  • C2 callback settings, for example to immediately check in on a new active RDP connection

The configuration can be parsed using the C structure definition from Listing 3.

struct ThemeForestC2Config
{
    uint64  bot_id;
    wchar   urls[10][1024];
    wchar   shell[1024];
    wchar   wts_console_cmdline[10][1024];
    char    wts_console_cmdline_enabled[10];
    uint32  last_checkin_epoch;
    uint32  configured_hibernate_minutes;
    uint32  active_hibernate_minutes;
    uint16  callback_settings;
};

Listing 3: ThemeForestRAT configuration structure definition for Windows

The configuration path that the RAT reads from disk is hardcoded. On macOS and Linux, this is an absolute path, while on Windows it looks in the current working directory where the RAT is launched. In Table 3 we list the observed configuration paths and hardcoded configuration file sizes for ThemeForestRAT.

Operating systemThemeForestRAT configuration file on diskFile size
Windowsnetraid.inf43048 bytes
Linux/var/crash/cups43044 bytes
macOS/private/etc/imap43044 bytes
Table 3: Observed ThemeForestRAT configuration paths and their file sizes on Windows, Linux and macOS

Command and Control

ThemeForestRAT communicates over HTTP(S). The filenames it uses for retrieving commands from the C2 server are prefixed with ThemeForest_. The response data is sent back to the operator as a file prefixed with Thumb_, see Figure 6. On Windows it uses the Ryeol Http Client28 library for HTTP communications, and on macOS and Linux it uses libcurl. ThemeForestRAT has a single hardcoded C2 in the binary, but its configuration can be updated by sending the SetInfo command.

Figure 9: ThemeForestRAT sending encrypted system information to C2 server on initial check-in

Commands

In terms of command functionality, ThemeForestRAT supports over twenty commands, at least twice as much as PondRAT. The Linux and macOS versions contain debug symbols, which allows us to map the command IDs to function names where available.

Symbol nameCommand IDDescription
ListDrives0x10001000Get list of drives
CServer::OnFileBrowse0x10001001Get directory listing
CServer::OnFileCopy0x10001002Copy file from source to destination on victim machine
CServer::OnFileDelete0x10001003Delete a file
FileDeleteSecure0x10001004Delete a file securely
CServer::OnFileUpload0x10001005Open a file for writing on victim machine
CServer::FileDownload0x10001006Download file from victim machine
Run0x10001007Execute a command and return the exit code
CServer::OnChfTime0x10001008Timestomp file based on another file on disk
–0x10001009–
CServer::OnTestConn0x1000100aTest TCP connection to host and port
CServer::OnCmdRun0x1000100bRun command in background and return output
CServer::OnSleep0x1000100cHibernate for X seconds, this will also be saved in the configuration file
CServer::OnViewProcess0x1000100dGet process listing
CServer::OnKillProcess0x1000100eKill process by process ID
–0x1000100f–
CServer::OnFileProperty0x10001010Get file properties
CServer::OnGetInfo0x10001011Get current RAT configuration
CServer::OnSetInfo0x10001012Update and save RAT configuration file
CServer::OnZipDownload0x10001013Download a directory or file as a compressed Zip file
CServer::OnTerminate0x10001014Flush configuration to disk and hibernate until next wake up
(Data)0x10001015Data
(JobSuccess)0x10001016Job succeeded
(JobFailed)0x10001017Job failed
GetServiceName0x10001018Return current service name
CleanupAndExit0x10001019Remove persistence, configuration file, and terminate RAT
RecvMsg0x1000101aForce C2 check-in
RunAs0x1000101bSpawn a process under the user token of given Windows Terminal Services session
–0x1000101c–
WriteRandomData0x1000101dWrite random data to file handle
CServer::OnInjectShellcode0x1000101eInject shellcode into process ID
Table 4: ThemeForestRAT command IDs and their descriptions

Note that the symbol names in Table 4 that start with CServer:: are from the debug symbols and the other names are deduced based on analysis of the command.

Shellcode Injection

On Windows, the CServer::OnInjectShellcode command injects shellcode into a given process ID using NtOpenProcess, NtAllocateVirtualMemory, NtWriteVirtualMemory and RtlCreateUserThread Windows API calls. The shellcode is encrypted using the same algorithm used in PerfhLoader (see Listing 1). In the macOS and Linux samples we have analysed, this command is defined as an empty stub.

RomeoGolf’s Little Brother

In 2016, Novetta released a detailed report called Operation Blockbuster29, in which a Novetta-led coalition of security companies analysed malware samples from multiple cybersecurity incidents. The investigation linked the 2014 Sony Pictures attack to the Lazarus Group and revealed that the same actor had been behind numerous other attacks against government, military, and commercial targets using related malware since 2009.

Operation Blockbuster’s malware report describes RomeoGolf, a RAT that resembles ThemeForestRAT in several ways:

  • Uses the temporary folder Z802056, although not used in ThemeForestRAT, is still created
  • Overlapping command IDs and functionality
  • Same unique identifier generation using 4 calls to rand()
  • Configuration file with extension *.inf on Windows
  • Timestomping of the configuration file based on mspaint.exe
  • Two signalling threads for USB and RDP events

Figure 10 shows the RomeoGolf startup logic for generating its bot ID and two signalling threads that is identical to ThemeForestRAT (see Figure 5).

Figure 10: RomeoGolf startup creates two signalling threads, comparable to ThemeForestRAT (see Figure 5).

As can be seen in Table 5, the functionality to detect and copy data from newly attached logical drives has been removed in ThemeForestRAT, while leaving the temporary directory creation intact. Also, the thread to check for new RDP sessions has been extended in ThemeForestRAT to optionally spawn up to ten extra configured commands under the user of the active physical console session.

RomeoGolfThemeForestRAT
Compilation dateFri Oct 11 01:20:48 2013Thu Sep 07 06:40:40 2023
Known configuration filecrkdf32.infnetraid.inf
Configuration file timestomped tomspaint.exemspaint.exe
USB thread logic1. Creates %TEMP%\Z802056
2. Checks for newly attached drives and copies data to above folder
3. Signal on newly attached drives
1. Creates %TEMP%\Z802056
RDP thread logic1. Signal on new active RDP sessions
1. Start configured commands under the user of the new active console session
2. Signal on new active RDP session if configured
C2 communicationFake TLSHTTP(S)
Highest known command id0x100010130x1000101e
Table 5: Differences and similarities between RomeoGolf and ThemeForestRAT

While RomeoGolf used Fake TLS30 and its own custom server for its C2 communications, ThemeForestRAT uses the HTTP protocol and shared hosting for its C2 servers.

Onto the next stage with RemotePE

In the 2024 incident response case, we observed the actor cleaning up PondRAT and ThemeForestRAT, to deploy a more advanced RAT, which we named RemotePE. RemotePE is retrieved from a C2 server by RemotePELoader. RemotePELoader is encrypted on disk using Window’s Data Protection API (DPAPI) and is loaded by DPAPILoader. Using DPAPI enables environmental keying and makes it difficult to recover the original payload without access to the machine. DPAPILoader was made persistent through a created Windows service.

Figure 10: RemotePELoader check-in request to retrieve RemotePE payload

In Figure 10, we show a RemotePELoader check-in request used to retrieve RemotePE from the C2 server. RemotePE is written in C++ and is more advanced and elegant. We think that the actor uses this more sophisticated RAT for interesting or high-value targets that require a higher degree of operational security. Interestingly, it too uses the file renaming strategy PondRAT and POOLRAT Windows samples implement, except it skips the last random iteration.

We will publish a more thorough analysis of RemotePE in a future blogpost.

Summary

This blog is about a Lazarus subgroup that we have encountered multiple times during incident response engagements. This is a capable, patient, financially motivated actor who remains a legitimate threat.

We first discussed an incident response case from 2024, where this actor impersonated employees of trading companies to establish contact with potential victims. Though the method of achieving initial access remains unknown, we suspect a Chrome zero-day was used.

After initial access, two RATs were used in combination: PondRAT and ThemeForestRAT. Though PondRAT has already been discussed, there are no public analyses of ThemeForestRAT at the time of writing. For persistence, phantom DLL loading was used in conjunction with a custom loader called PerfhLoader.

PondRAT is a primitive RAT that provides little flexibility, however, as an initial payload it achieves its purpose. It has similarities with POOLRAT/SimpleTea. For more complex tasks, the actor uses ThemeForestRAT, which has more functionality and stays under the radar as it is loaded into memory only.

Lastly, we found the actor replaced ThemeForestRAT and PondRAT with the more advanced RemotePE. A detailed analysis of RemotePE will be published in the near future. So, stay tuned!

In Table 6 and 7, we list indicators of compromise related to the incident response cases we investigated and other artifacts we link to this actor.

Incident Response Support

If you have any questions or need assistance based on these findings, please contact Fox-IT CERT at cert@fox-it.com. For urgent matters, call 0800-FOXCERT (0800-3692378) within the Netherlands, or +31152847999 internationally to reach one of our incident responders.

Indicators of Compromise

TypeIndicatorComment
net.domaincalendly[.]liveFake calendly.com
net.domainpicktime[.]liveFake picktime.com
net.domainoncehub[.]coFake oncehub.com
net.domaingo.oncehub[.]coFake oncehub.com
net.domaindpkgrepo[.]comPotentially related to Chrome exploitation
net.domainpypilibrary[.]comUnknown, visited by msiexec.exe shortly after dpkgrepo[.]com
net.domainpypistorage[.]comUnknown, connection seen under SessionEnv service
net.domainkeondigital[.]comLPEClient server, connection seen under SessionEnv service
net.domainarcashop[.]orgPondRAT C2
net.domainjdkgradle[.]comPondRAT C2
net.domainlatamics[.]orgPondRAT C2
net.domainlmaxtrd[.]comThemeForestRAT C2
net.domainpaxosfuture[.]comThemeForestRAT C2
net.domainwww[.]plexisco[.]comThemeForestRAT C2
net.domainftxstock[.]comThemeForestRAT C2
net.domainwww[.]natefi[.]orgThemeForestRAT C2
net.domainnansenpro[.]comThemeForestRAT C2
net.domainaes-secure[.]netRemotePE payload delivery and C2
net.domainazureglobalaccelerator[.]comRemotePE payload delivery and C2
net.domainazuredeploypackages[.]netUnknown, connection seen via injected process
net.ip144.172.74[.]120Fast Reverse Proxy server
net.ip192.52.166[.]253Used as parameter for Quasar
file.path%TEMP%\tmpntl.datWindows keylogger output file path
file.pathC:\Windows\Temp\TMP01.datWindows keylogger error file path
file.namenetraid.infThemeForestRAT Windows configuration filename
file.path/var/crash/cupsThemeForestRAT Linux configuration file path
file.path/private/etc/imapThemeForestRAT macOS configuration file path
file.path/private/etc/krb5d.confPOOLRAT macOS configuration file path, CISA 2021 report
file.path/etc/apdl.cfPOOLRAT Linux configuration file path
file.path%SystemRoot%\system32\apdl.cfPOOLRAT Windows configuration file path
file.path/tmp/xweb_log.mdPOOLRAT, PondRAT Linux libcurl error log file path
file.nameperfh011.datEncrypted payload loaded by PerfhLoader
file.namehsu.datFilename actor used for SysInternals ADExplorer output
file.namepfu.datFilename actor used for SysInternals Handle viewer output
file.namefpc.datDropped Fast Reverse Proxy configuration filename
file.namefp.exeDropped Fast Reverse Proxy executable
file.nametsvipsrv.dllDLL phantom loaded by actor (SessionEnv)
file.namewlbsctrl.dllDLL phantom loaded by actor (IKEEXT)
file.nameadepfx.exeFilename actor used for legitimate SysInternals ADExplorer
file.namehd.exeFilename actor used for legitimate SysInternals Nthandle.exe
file.namemsnprt.exeFilename actor uses for Proxymini, open-source socks proxy
file.path%LocalAppData%\IconCache.logOutput path for custom browser credentials and cookies dumper based on Mimikatz
file.path/private/etc/pdpastemacOS keylogger file path
file.path/private/etc/xmemmacOS keylogger output file path
file.path/private/etc/tls3macOS screenshotter output directory
file.path%LocalAppData%\Microsoft\Software\CacheWindows screenshotter output directory
file.pathc:\windows\system32\cmui.exeThemida-packed Quasar
Table 6: Indicators of Compromise linked to actor, without hashes
digest.sha256Comment
24d5dd3006c63d0f46fb33cbc1f576325d4e7e03e3201ff4a3c1ffa604f1b74aFast Reverse Proxy v0.32.1, also observed by Mandiant in the 3CX supply chain attack
4715e5522fc91a423a5fcad397b571c5654dc0c4202459fdca06841eba1ae9b3PerfhLoader
8c3c8f24dc0c1d165f14e5a622a1817af4336904a3aabeedee3095098192d91fPerfhLoader
f4d8e1a687e7f7336162d3caed9b25d9d3e6cfe75c89495f75a92ca87025374bPOOLRAT Windows
85045d9898d28c9cdc4ed0ca5d76eceb457d741c5ca84bb753dde1bea980b516POOLRAT Linux
5e40d106977017b1ed235419b1e59ff090e1f43ac57da1bb5d80d66ae53b1df8POOLRAT macOS (CISA 2021 report)
c66ba5c68ba12eaf045ed415dfa72ec5d7174970e91b45fda9ebb32e0a37784aThemeForestRAT Windows
ff32bc1c756d560d8a9815db458f438d63b1dcb7e9930ef5b8639a55fa7762c9ThemeForestRAT Linux
cc4c18fefb61ec5b3c69c31beaa07a4918e0b0184cb43447f672f62134eb402bThemeForestRAT macOS
6510d460395ca3643133817b40d9df4fa0d9dbe8e60b514fdc2d4e26b567dfbdPondRAT Windows
973f7939ea03fd2c9663dafc21bb968f56ed1b9a56b0284acf73c3ee141c053cPondRAT Linux
f0321c93c93fa162855f8ea4356628eef7f528449204f42fbfa002955a0ba528PondRAT macOS
4f6ae0110cf652264293df571d66955f7109e3424a070423b5e50edc3eb43874DPAPILoader
aa4a2d1215f864481994234f13ab485b95150161b4566c180419d93dda7ac039DPAPILoader
159471e1abc9adf6733af9d24781fbf27a776b81d182901c2e04e28f3fe2e6f3DPAPILoader
7a05188ab0129b0b4f38e2e7599c5c52149ce0131140db33feb251d926428d68RemotePELoader (decrypted from disk)
37f5afb9ed3761e73feb95daceb7a1fdbb13c8b5fc1a2ba22e0ef7994c7920efRemotePE
59a651dfce580d28d17b2f716878a8eff8d20152b364cf873111451a55b7224dWindows keylogger
3c8f5cc608e3a4a755fe1a2b099154153fb7a88e581f3b122777da399e698ccaWindows screenshotter
d998de6e40637188ccbb8ab4a27a1e76f392cb23df5a6a242ab9df8ee4ab3936macOS keylogger (getkey)
e4ce73b4dbbd360a17f482abcae2d479bc95ea546d67ec257785fa51872b2e3fmacOS screenshotter (getscreen)
1a051e4a3b62cd2d4f175fb443f5172da0b40af27c5d1ffae21fde13536dd3e1macOS clipboard logger (pdpaste)
9dddf5a1d32e3ba7cc27f1006a843bfd4bc34fa8a149bcc522f27bda8e95db14Proxymini tool, opensource SOCKS proxy tool
2c164237de4d5904a66c71843529e37cea5418cdcbc993278329806d97a336a5Themida-packed Quasar
Table 7: SHA256 hashes of tools used by the actor

YARA rules

import "pe"

rule Lazarus_DPAPILoader_Hunting {
  meta:
    description = "Hunting rule to detect DPAPILoader, a loader used to load RemotePE."
    author      = "Fox-IT / NCC Group"

  strings:
    $msg_1 = "[!] Could not allocate memory at the desired base!\n"
    $msg_2 = "[!] Virtual section size is out ouf bounds: "
    $msg_3 = "[!] Invalid relocDir pointer\n"
    $msg_4 = "[-] Not supported relocations format at %d: %d\n"
    $msg_5 = "[!] Cannot fill imports into 32 bit PE via 64 bit loader!\n"

  condition:
    any of them and pe.imports("Crypt32.dll", "CryptUnprotectData")
}

rule Lazarus_RemotePE_C2_strings {
  meta:
    description = "RemotePE strings used for C2."
    author      = "Fox-IT / NCC Group"

  strings:
    $a = "MicrosoftApplicationsTelemetryDeviceId" wide ascii xor
    $b = "armAuthorization" wide ascii xor
    $c = "ai_session" wide ascii xor

  condition:
    uint16(0) == 0x5A4D and all of them
}

rule Lazarus_RemotePE_class_strings {
  meta:
    description = "RemotePE class strings."
    author      = "Fox-IT / NCC Group"

  strings:
    $a = "IMiddleController" ascii wide xor
    $b = "IChannelController" ascii wide xor
    $c = "IConfigProfile" ascii wide xor
    $d = "IKernelModule" ascii wide xor

  condition:
    all of them
}

rule Lazarus_PerfhLoader_XOR_key {
  meta:
    description = "XOR key used for shellcode obfuscation."
    author      = "Fox-IT / NCC Group"

  strings:
    $mov_1  = { C7 [1-3] 00 01 02 03 }
    $mov_2  = { C7 [1-3] 04 05 06 07 }
    $mov_3  = { C7 [1-3] 08 09 0A 0B }
    $mov_4  = { C7 [1-3] 0C 0D 0E 0F }
    $init_1 = { 41 8D ?? FD 41 8D ?? F9 }

  condition:
    all of them
}

rule Lazarus_ThemeForestRAT_C2_strings {
  meta:
    description = "ThemeForestRAT strings used for C2."
    author      = "Fox-IT / NCC Group"

  strings:
    $themeforest = "ThemeForest_%s" ascii wide
    $thumb       = "Thumb_%s" ascii wide
    $param_code  = "code" ascii wide
    $param_fn    = "fn" ascii wide
    $param_ldf   = "ldf" ascii wide

  condition:
    all of them
}

rule Lazarus_ThemeForestRAT_RC4_key {
  meta:
    description = "ThemeForest RC4 key used for config file."
    author      = "Fox-IT / NCC Group"

  strings:
    $rc4_key     = { 20 1A 19 2D 83 8F 48 53 E3 00 }
    $rc4_key_mov = { 20 1A 19 2D [2-8] 83 8F 48 53 [2-10] E3 00 }

  condition:
    any of them
}

References

  1. https://securelist.com/operation-applejeus/87553/ ↩︎
  2. https://www.microsoft.com/en-us/security/blog/2024/08/30/north-korean-threat-actor-citrine-sleet-exploiting-chromium-zero-day/ ↩︎
  3. https://cloud.google.com/blog/topics/threat-intelligence/3cx-software-supply-chain-compromise ↩︎
  4. https://unit42.paloaltonetworks.com/threat-assessment-north-korean-threat-groups-2024/ ↩︎
  5. https://unit42.paloaltonetworks.com/gleaming-pisces-applejeus-poolrat-and-pondrat/ ↩︎
  6. https://slowmist.medium.com/analysis-of-north-korean-hackers-targeted-phishing-scams-on-telegram-872db3f7392b ↩︎
  7. https://www.microsoft.com/en-us/security/blog/2024/08/30/north-korean-threat-actor-citrine-sleet-exploiting-chromium-zero-day/ ↩︎
  8. https://decoded.avast.io/janvojtesek/lazarus-and-the-fudmodule-rootkit-beyond-byovd-with-an-admin-to-kernel-zero-day/ ↩︎
  9. https://posts.specterops.io/lateral-movement-scm-and-dll-hijacking-primer-d2f61e8ab992 ↩︎
  10. https://www.nccgroup.com/us/how-the-lazarus-group-targets-fintech/ ↩︎
  11. https://github.com/adamhlt/Manual-DLL-Loader ↩︎
  12. https://github.com/ParrotSec/mimikatz ↩︎
  13. https://aluigi.altervista.org/mytoolz.htm ↩︎
  14. https://github.com/fatedier/frp ↩︎
  15. https://cloud.google.com/blog/topics/threat-intelligence/3cx-software-supply-chain-compromise ↩︎
  16. https://github.com/fatedier/frp/releases/tag/v0.32.1 ↩︎
  17. https://github.com/quasar/Quasar/releases/tag/v1.3.0.0 ↩︎
  18. https://www.cisa.gov/news-events/cybersecurity-advisories/aa21-048a ↩︎
  19. https://www.volexity.com/blog/2022/12/01/buyer-beware-fake-cryptocurrency-applications-serving-as-front-for-applejeus-malware/ ↩︎
  20. https://c.m.163.com/news/a/HQVV9MTS0538B1YX.html ↩︎
  21. https://mp.weixin.qq.com/s?__biz=MzUyMjk4NzExMA%3D%3D&mid=2247499462&idx=1&sn=7cc55f3cc2740e8818648efbec21615f ↩︎
  22. https://vipyrsec.com/research/elf64-rat-malware/ ↩︎
  23. https://unit42.paloaltonetworks.com/gleaming-pisces-applejeus-poolrat-and-pondrat/ ↩︎
  24. https://attack.mitre.org/techniques/T1070/006/ ↩︎
  25. https://github.com/adamhlt/Manual-DLL-Loader ↩︎
  26. https://github.com/nettitude/SimplePELoader/ ↩︎
  27. https://www.welivesecurity.com/2018/04/03/lazarus-killdisk-central-american-casino/ ↩︎
  28. https://www.codeproject.com/Articles/7828/CHttpClient-A-Helper-Class-Using-WinInet ↩︎
  29. https://github.com/CyberMonitor/APT_CyberCriminal_Campagin_Collections/blob/master/2016/2016.02.24.Operation_Blockbuster/Operation-Blockbuster-RAT-and-Staging-Report.pdf ↩︎
  30. https://attack.mitre.org/techniques/T1001/003/ ↩︎

  •  

Attributing CryptoCore Attacks Against Crypto Exchanges to LAZARUS (North Korea)

CryptoCore is an attack campaign against crypto-exchange companies that has been ongoing for three years and was discovered by ClearSky researchers. This cybercrime campaign is focused mainly on the theft of cryptocurrency wallets, and we estimate that the attackers have already made off with hundreds of millions of dollars. This campaign was also reported by additional companies and organizations, including JPCERT/CC[1], NTT Security[2] and F-SECURE[3]. The campaign is also known as CryptoMimic, Dangerous Password and Leery Turtle. In this report we attributed this campaign to a specific actor – North Korea’s LAZARUS APT Group, known also as Hidden Cobra.

Read the full report: Attributing CryptoCore Attacks Against Crypto Exchanges to LAZARUS (North Korea)

In this report, we based our attribution with two stages of research:

  1. First stage– connecting all research documents to the same campaign:  a comparative study of all the research documents trying to prove they are all referring to the same campaign.
  2. Second stage – Attribution to Lazarus: We adopted F-SECURE’s attribution to LAZARUS. Then we reaffirmed this attribution by comparing the attack tools  found in this campaign  to other Lazarus campaigns  and found strong similarities.

Our research shows a MEDIUM-HIGH likelihood that Lazarus group, a  North-Korean, state-sponsored APT group, is attacking crypto exchanges all over the world and in Israel for at least three years. This group is has successfully hacked into numerous companies and organizations around the world for many years. Until recently this group was not known to attack Israeli targets.

We would like to thank NTT Security Japan for sharing malware samples with us, and for their feedback on this research.


[1] https://blogs.jpcert.or.jp/en/2019/07/spear-phishing-against-cryptocurrency-businesses.html

[2] https://vblocalhost.com/uploads/VB2020-Takai-etal.pdf

[3] https://labs.f-secure.com/assets/BlogFiles/f-secureLABS-tlp-white-lazarus-threat-intel-report2.pdf

  •  
❌