Athena's blog

Kernel module lockdown

Athena Lilith Martin

Published on

I've thought recently about malware, especially malicious kernel modules. Obviously, to load a malicious kernel module, you have to already be root, so the system is already pretty thoroughly compromised by that point, but a module can potentially make it much harder to detect the compromise so it's definitely useful to make them harder to load.

One thing I've considered is the obvious: Load modules at boot, then refuse to load them once all the ones needed have been loaded. That's probably good for servers, but not as convenient for interactive systems where hardware that needs a new driver might be added at runtime, and even some servers have modules they only load on-demand. So, this is probably the most secure option, but it might be good to have an option that lets modules be loaded late while still making it harder to load a malicious module.

I recall that Linux has support for checking signatures on modules, but that requires an infrastructure to protect the private key and sign each module when it's built. Distributions might have that, but people who build their own kernels probably don't. But then I realized that, very often (not always, but often), with self-built kernels especially, the kernel only actually loads modules that were built from the same source tree at the same time. If we can restrict module loading to only the modules that were compiled in the same build as the base kernel, we have a fairly good tool for security, at least as long as we stick to in-tree modules only (which, I know, is a kind of big ask).

So, here's what I want to know if there's existing tooling for: During the kernel build, generate a keypair, and sign all the modules with it, then build the public key into the kernel image. At runtime, the kernel refuses to load any module not signed with that keypair. Preferably, the private key is held in memory in a short-lived process whose only job is to generate the keys, sign the modules, and write out the public key before it zeroes the private key and exits; that way, it's much harder to find a copy of the private key lying around in a kernel build directory or something.

If implemented correctly, the only practical way to load a malicious kernel module is to modify or replace the kernel to change the key or disarm the signature check, which is a change to the kernel image that can be detected more easily than a malicious *.ko file somewhere in some buried directory.

Essentially, the idea here is to say that all kernel modules are inherently part of the kernel, and you shouldn't be able to load code from outside that single kernel build into kernel space. It's restrictive, since you can't use out-of-tree modules, and it means any change to your set of modules requires a kernel reinstall to replace all the module signatures and the public key, but it does provide a relatively convenient and basically simple way for a significant subset of self-built kernel users to secure themselves against malicious kernel modules.

If you know that this tool exists, please let me know!