Today I Learned - kernel.modules_disabled

Table of Contents

Introduction

The kernel.modules_disabled parameter is a security feature in the Linux kernel that prevents the loading and unloading of kernel modules. This setting is particularly useful for hardening a system against certain types of attacks, such as attempts to load malicious kernel modules (think rootkits) or manipulate the system at a low level. Mandiant recently published a blog post where they found, among other toolings used by the attackers, REPTILE. REPTILE is an open-source Linux rootkit, implemented as a loadable kernel module (LKM), that provides backdoor access to a system. Source: Cloaked and Covert: Uncovering UNC3886 Espionage Operations

By default, this setting is turned off:

# sysctl -a | grep modules
kernel.modules_disabled = 0

Hello, DFIR Kernel Module

We will create a simple kernel module to showcase the kernel feature modules_disabled in action. We saved the code below under the name dfir.c in our home directory. The module is as simple as it can get: When the module is loaded, the string “Hello, DFIR!” will be logged inside the kernel’s message buffer. Subsequently, when the module exists, the string “Goodbye, DFIR!” will be logged.

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

// Called when the module is loaded
static int __init dfir_init(void) {
    printk(KERN_INFO "Hello, DFIR!\n");
    return 0;
}

static void __exit dfir_exit(void) {
    printk(KERN_INFO "Goodbye, DFIR!\n");
}

module_init(dfir_init);
module_exit(dfir_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Stephan Berger");
MODULE_DESCRIPTION("Simple Kernel Module for teaching Linux hardening techniques");
MODULE_VERSION("0.1");

Next, We create a file called Makefile in the same directory as our dfir.c source file:

obj-m += dfir.o

all:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules

clean:
    make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

And we compile our kernel module:

make

We see a lot of output, but the main thing to focus on is the filename of our compiled Kernel module, dfir.ko:

make -C /lib/modules/6.8.0-31-generic/build M=/root modules
make[1]: Entering directory '/usr/src/linux-headers-6.8.0-31-generic'
warning: the compiler differs from the one used to build the kernel
  The kernel was built by: x86_64-linux-gnu-gcc-13 (Ubuntu 13.2.0-23ubuntu4) 13.2.0
  You are using:           gcc-13 (Ubuntu 13.2.0-23ubuntu4) 13.2.0
  CC [M]  /root/dfir.o
  MODPOST /root/Module.symvers
  CC [M]  /root/dfir.mod.o
  LD [M]  /root/dfir.ko
  BTF [M] /root/dfir.ko
Skipping BTF generation for /root/dfir.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-6.8.0-31-generic'

We load the module with the command insmod:

# insmod dfir.ko

Checking the logs (the command dmesg displays the message buffer of the kernel) if we see a successful load from our module - and yes, looks about right:

# dmesg | tail
[..]
[19323.400599] Hello, DFIR!

Same for unloading with the command rmmod:

# rmmod dfir
# dmesg | tail
[..]
[19388.285645] Goodbye, DFIR!

Hardening

As stated above, the kernel setting modules_disabled is not enabled by default:

# cat /proc/sys/kernel/modules_disabled
0

Let’s enable the feature to see the impact of it:

# echo 1 > /proc/sys/kernel/modules_disabled

Once set, all subsequent attempts to load or unload kernel modules will fail. This change is permanent for the running kernel session and cannot be reverted without a reboot. Ensure all necessary modules are loaded before enabling this setting, as any attempt to load additional modules post-enablement will fail. As we see here, it’s not possible anymore to load our kernel module:

# insmod dfir.ko
insmod: ERROR: could not insert module dfir.ko: Operation not permitted

And switching the flag back is not possible too, only after a reboot of the machine:

# sysctl -w kernel.modules_disabled=0
sysctl: setting key "kernel.modules_disabled": Invalid argument

And the unloading of already loaded modules is also not possible:

# sysctl -w kernel.modules_disabled=1
kernel.modules_disabled = 1

# rmmod dfir
rmmod: ERROR: ../libkmod/libkmod-module.c:856 kmod_module_remove_module() could not remove 'dfir': Operation not permitted
rmmod: ERROR: could not remove module dfir: Operation not permitted

Conclusion

The kernel.modules_disabled parameter in the Linux kernel provides a robust security feature by preventing the loading and unloading of kernel modules, thereby hardening the system against low-level attacks, such as those involving rootkits. However, by default, this setting is disabled.