Article

    Cyber News / Article / Linux rootkits explained – Part 2: Loadable kernel modules

    Linux rootkits explained – Part 2: Loadable kernel modules
    Av
    Avigayil Mechtinger-2023-10-24

    Linux rootkits explained – Part 2: Loadable kernel modules

    Part 2 dives into the world of LKMs (Loadable Kernel Modules) and kernel-space rootkits to explore what LKMs are, how attackers abuse them, and how to detect them.

    In theprevious part of this series, we looked at the LD_PRELOAD user-space rootkit. We learned how these rootkits work and provided best practices for detecting them on your operating system.

    In Linux (and other Unix-like operating systems), system memory is divided into two distinct domains: user space and kernel space. These spaces represent different areas of memory and serve different purposes, providing a fundamental separation between user-level applications and the operating system's core functionality (the kernel). The separation between the two spaces enhances system stability, security, and overall performance.

    In part 2 of this series, we will explore theLKM (Loadable Kernel Modules) kernel-space rootkit.This rootkit technique has been utilized by different threat actors in the wild, including:

    TeamTNT,which used theDiamorphineopen-source LKM rootkit to hide cryptomining process.

    Winnti group,(APT 41) which usedadore-ngandsuterusuLKM rootkits to conceal different malicious activity.

    In this blog, we'll examine what loadable kernel modules are, how attackers abuse this capability, provide examples of usage in the wild, and explain how to detect it.

    The Linux kernel is the core of the operating system that manages system resources and provides essential services to other parts of the operating system and applications. Loadable kernel modules are pieces of code that can be dynamically loaded into the Linux kernel to extend its functionality without the need to recompile the kernel or even reboot. For example, when you need to handle a new type of filesystem that is not supported by the kernel, you may need to load a specific kernel module designed to provide support for that filesystem type.

    Loadable kernel modules are designed to be loadable at runtime, allowing adaptation of the kernel to different hardware configurations and supporting various devices and features without recompiling or modifying the main kernel code.

    Linux provides various commands to manage kernel modules which are part of thekmodutility.  These commands include:

    insmod: used to manually insert a kernel module into the running kernel.

    rmmod: used to unload (remove) a kernel module.

    modprobe: an advanced module management tool that not only loads modules but also handles module dependencies, automatically loading related modules when needed.

    lsmod: used to list all loaded kernel modules. It operates by reading information from the /proc/modules file and querying the /sys/module/ directory for details on each module.

    Typically, users don't directly invokekmod, as it is primarily used by package managers and system tools to handle kernel modules efficiently.

    Three relevant files and directories are:

    /lib/modules/ -contains kernel modules and related files specific to different kernel versions installed on the system. Each subdirectory within /lib/modules/ corresponds to a particular kernel version and contains the following components. It allows the operating system to keep different kernel versions and their associated modules separate, making it easier to switch between kernel versions when needed.

    /proc/modules- this virtual file provides a list of currently loaded kernel modules. Each line in this file represents a single loaded module and contains information about that module, including its name, size, and usage count.

    /sys/module/ -this virtual directory provides information about the currently loaded kernel modules. Each loaded module has its own directory under /sys/modules/, and within each module's directory, there are different files containing information about the module. This directory allows user-space processes, tools, and administrators to access information about loaded kernel modules and their properties at runtime. Browsehereto learn more about the structure of this directory.

    Before we dive into how attackers abuse LKMs, it is important to understand what syscalls and kernel functions are.

    When a user space program needs to perform tasks that require interaction with the kernel (e.g., reading a file, creating a network socket, managing processes), it has to ask the kernel to perform these actions. Syscalls act as aninterfacebetween user-space and kernel-space, allowing the kernel to execute the requested operation on behalf of the user program. Browse theLinux syscalls manual pagefor more information about syscalls.

    Syscalls are a way to call a function in the kernel from user-space, but the vast majority of kernel code is not exposed as syscalls, and instead used internally by the kernel to perform various tasks related to managing system resources and maintaining the overall operation of the operating system. They are not part of the standardized interface that user programs can access through syscalls.

    Example:

    Runstrace ls:

    In the snippet above, we can see the usage of thegetdents64(get directory entries)syscall. This syscall is used for retrieving directory entries from a directory. It is primarily used by programs that need to read the contents of a directory, includinglsandps. In this case, we executedlson an empty directory so that is the reason this syscall received 2 entries (the default.and..).

    filldiris a function in the kernel and is called fromfs/readdir.c. It is responsible for filling directory entries (file names and metadata) into a directory buffer during directory listing.getdentssystem call is defined in thefs/readdir.cand uses thefilldirfunction (seefs/readdirsource code).

    In theprevious partof this series, we mentioned that rootkits are often used to hide malicious activity by hooking execution flow. While in the user-space attackers can, for example, overwrite libc functions for this purpose, hijacking the control flow in the kernel space will come into practice by hooking kernel functions or syscalls.

    The usage of LKM rootkits presents certain constraints for threat actors. These constraints can be divided intopermissionsandportability(compilation):

    Permissions

    Containers:unlike user-mode rootkits, kernel module rootkits require access to the system kernel on the host machine. Threat actors that compromise a container will have the capability to load kernel modules if either of these are true:

    The container is privileged

    The container has the SYS_MODULE capability

    The attacker’s controlled thread has the SYS_MODULE capability

    Virtual machine/host:the threat actor must have root privileges or ability to execute a process with SYS_MODULE capability.

    In addition, security mechanisms such asseccompandAppArmorcan be employed to restrict the actions of processes, including the prevention of interactions with kernel modules. Furthermore, systems` kernels can be compiled without module loading capability at all.

    Portability

    Kernel modules must be compiled using the specific kernel headers compatible with the target system's kernel version. In addition, kernel functions and objects vary between kernel versions and architecture. Consequently, for each unique kernel version, a distinct module compilation may be required. This complexity poses a challenge for attackers, as they cannot just drop and load a pre-compiled.ko(kernel object file) file. Instead, they must either compile the module directly on the target system or on a system that matches the kernel headers of the target system.

    While it is considered best practice, it's important to note that there might be alternative methods to avoid the necessity of a full compilation when loading a kernel module.

    Once threat actors are able to insert a malicious LKM, they have complete control over the kernel space (hence, over the entire machine), and they can abuse different features in the kernel.

    Let's list some of thecommonmethods used by attackers to hook kernel functions: Syscall table modification, Kprobes (kernel probes), Ftrace, and VFS (Virtual File System) manipulation.

    We will detail each method in high-level and reference an open-source LKM rootkit project that leverages it. Exploring these rootkit projects can help understand how these methods come into practice by threat actors.

    The syscall table is a data structure used by the Linux kernel to manage system calls. It serves as a lookup table that contains pointers to the functions responsible for handling specific system calls. When a user-space program makes a system call, the kernel uses this table to find the appropriate handler function and execute the requested system call.

    With complete control over the kernel space, it is possible to alter this table and manipulate handler pointer values. Attackers can hook any system call by saving the old handler value and adding their own handler to the table.

    An open-source LKM rootkit project that leverages this method:Diamorphine.

    Kprobes are a dynamic instrumentation feature in the Linux kernel that allows developers to insert custom code (probes) at specific points within the kernel's code paths. These probes are designed to be used for debugging, profiling, tracing, and gathering runtime information about the kernel's behavior without requiring modification of the actual kernel code.

    Kprobes work by attaching a probe handler function to a chosen point in the kernel's code. When that specific code path is executed, the probe handler function is invoked. By placing a kprobe on a sensitive kernel function, attackers can execute their code whenever that function is called.

    An open-source LKM rootkit project that leverages this method:Reptile(leverageskhook).

    Ftrace is a built-in tracing framework within the Linux kernel that provides tools and infrastructure for collecting and analyzing distinct types of runtime information about the kernel's behavior and performance. It is designed to help developers and system administrators understand how the kernel operates and identify performance bottlenecks, debugging issues, and more.

    Ftrace allows users to trace specific kernel functions. Attackers can use this feature to hook and intercept the execution of kernel functions.

    An open-source LKM rootkit project that leverages this method:Ftrace-hook.

    The VFS is a key component of Unix-like operating systems, it provides a filesystem interface to user-space programs by enabling syscalls such as open(), stat(), read(), write(), and chmod(). The VFS abstracts and unifies access to different filesystems, allowing various filesystem implementations to coexist. VFS is a family of data structures representing the common file model. The four primary object types of the VFS are:

    Superblock object - represents a specific mounted filesystem.

    Inode object - represents a specific file (e.g regular files, directories, FIFOs and sockets).

    This object holds the field of inode operations (i_op) structure. Inode operations are a set of low-level functions provided by the filesystem layer in an operating system to manipulate and interact with files and directories. This includes – lookup(), rename(), mkdir(), unlink() andmore(note that the structure varies between kernel versions).

    Dentry - represents a directory entry, a single component of a path.

    File object - represents an open file as associated with a process.

    This object holds the field of file operations (f_op) structure. File operations are functions that define how files can be manipulated when they are open for reading, writing, or other forms of access. This includes –read(),write(), mmap(), fsync(), andmore(note that the structure varies between kernel versions).

    In high level, each of these objects holds a pointer to the next object, with the following node structure: file object -> dentry object -> inode object -> superblock object. Browseherefor more information about VFS.

    Attackers can hook into function pointers associated with specific filesystems, such as therootandprocand replace them with their own function pointers. For example, replace thereaddirfile operation function pointer (see file operation structure forolder kernel versions).

    An open-source LKM rootkit project that leverages this method:adore-ngandsuterusu.

    As mentioned, thegetdentssyscall is used by programs such aslsandpsthat read the contents of a directory as part of their flow. This syscall is commonly hooked as part of LKM rootkits. It is also worth noting that attackers commonly hook thefilldir(orfillonedir) kernel function, therefore hookingfilldiris a lower-level hooking for the same purpose.

    Let’s create a kernel module that hooks thegetdents64syscall using the syscall table modificationmethod to hide files named "malicious_file", compile it, and load it.

    Important:

    Inserting and removing kernel modules can break the kernel. Make sure to run this demo on a disposable, non-production, non-critical environment where you can afford to lose all the data.

    This demo will work for kernel versions between4.16.0 and 5.7.0and X86/ X86_64 architecture.

    1. Create a working directory under /tmp:mkdir /tmp/test-lkm-rootkit && cd /tmp/test-lkm-rootkit

    2. Install relevant packages, including kernel headers that match your kernel:

    * Foraptbased machines: runapt install -y build-essential libncurses-dev linux-headers-$(uname -r)* Foryumbased machines runyum install -y kernel-devel-$(uname -r) && yum –y groupinstall 'Development Tools'

    3. Create aMakefileand copy the following content:

    4. Create a file named lkmdemo.c and copy the module code below (the code is based onDiamorphinerootkit):

    5. Runmaketo create a.kofile.

    6. Create a file named malicious_filetouch malicious_file.

    7. Runlson the working directory and see themalicious_filefile in the output.

    8. Load the kernel moduleinsmod lkmdemo.ko.

    9. Runlsagain, and we will see that nowmalicious_fileis hidden from the output.

    10. Runlsmodand see lkmdemo in the output.

    11. Unload the modulermmod lkmdemo.

    Loadable Kernel Module (LKM) rootkits have been detected in numerous real-world threats. Notably, many of the documented attacks have leveraged open-source rootkits. However, this doesn't necessarily imply that the majority of LKM rootkits in the wild originate from open-source projects. Due to the inherent difficulty in detecting loaded kernel modules, it is suspected that there may be undisclosed threats that remain undocumented.

    TeamTNTgroup:Diamporphinerootkit.

    TeamTNTusedDiamorphineto hide cryptomining process(es) in different campaignssince August 2020and in more recent campaigns such asKiss-a-Dogwhich was attributed to this group.

    Melofeemalware /Campaign targeting vulnerable Fortinet services/Campaign targeting South Korean companies:Reptilerootkit.

    Reptileis a powerful rootkit that besides concealing malicious activity, it provides backdoor capability. It was recently documented as being part of different Chinese attributed threats;a campaign targeting vulnerable Fortinet services,Melofeemalware, anda campaign targeting South Korean companies.Interestingly, ASEC, the company that published the latter campaign, presented similarities between artifacts found in this campaign toMelofeemalware.

    Winntigroup (APT 41) /RedXormalware /Syslogkmalware:Adore-ng,Suterusurootkits.

    Adore-ngwas initially documented as part of theWinnti group (APT 41)toolset. Although being an old rootkit (last commit was 8 years ago) that suits for old kernel versions (hence, legacy systems), its usage was observed in a more recent malware,RedXor,which was attributed to a Chinese threat actor. In June 2022, Avast has reported a new rootkit,Syslogkwhich is heavily based on adore-ng.

    Skidmapmalware.

    Skidmapmalware uses LKM rootkits to hide cryptomining activity. In the latest report bytrustwaveof this evolving threat, Skidmap has been seen targeting vulnerable Redis instances.

    LKM rootkits are leveraged by attackers to intercept different kernel-level functions, increasing the complexity of investigating compromised systems. The following techniques can help identify the presence of such rootkits:

    Compare the runtime filesystem to the image snapshot. If there is a difference, these files might be part of an attack hidden from certain commands.

    Once a kernel module is loaded, theinit_module(orfinit_module) syscall is called. If you are using a runtime detection tool, make sure it alerts you on this event.

    Utilize tools likeunhide. Unhide uses different brute force techniques to detect concealed processes.

    Moreover, the Wiz runtime sensor detects LKM attacks. See the following example of an alert detailing the demo kernel module being loaded:

    Learn more about theWiz runtime sensor.

    To minimize the possibility of being attacked with LKM rootkit within your environment, we recommend considering the following steps:

    Make sure that you are not using privileged containers or containers with the SYS_MODULE capabilityif they are not needed. You can use Wiz to detect such containers viaWiz’s Security GraphorCloud Configuration Rules.

    Minimize internet-facing services.

    Ensure applications do not haveexcessivecapabilities, and avoid using root userpermissionswhere not needed.

    Use access control mechanisms such asAppArmorandSElinuxto limit which processes and users can load and interact with kernel modules.

    Use secure boot. Secure boot is a feature that ensures that only signed and trusted components, including kernel modules, can be loaded during the system boot process. It prevents the loading of unauthorized modules.

    Loadable Kernel Module (LKM) rootkits leverage different kernel features to hook kernel functions.  Attackers that successfully load LKM rootkits have full control over the resource, concealing malicious activity causing detection to be challenging.

    In this post, we provided an intro to this rootkit method. We detailed what kernel modules are used for and how they are abused by threat actors. We listed examples of the usage of this rootkit in the wild and provided best practices on how to detect this type of rootkit.

    Stay tuned for part 3 of this series, where we will delve into eBPF rootkits.

    This blog post was written by Wiz Research, as part of our ongoing mission to analyze threats to the cloud, build mechanisms that prevent and detect them, and fortify cloud security strategies.

    In its 2023 Frost Radar™ analysis, Frost & Sullivan identified the top companies in the Global Cloud Workload Protection Platform (CWPP) market. Wiz is honored to be included on the list!

    Wiz recently talked with security expert Clint Gibler about the many uses of AI in the cybersecurity space.

    Wiz is proud to announce the opening of its data center in Mumbai, India

    Get a personalized demo

    ©2026Wiz, Inc.

    StatusPrivacy PolicyTerms of UseModern Slavery StatementCookie Settings

    Original source