Jan-Philipp Litza

Linux kernel module whitelisting

In the last weeks, several high severity vulnerabilities in the Linux kernel have been exposed (most notably Copy Fail and Dirty Frag), leading to local privilege escalations (LPE).

What they share is that they exploit kernel modules that are not totally obscure but definitely not loaded by default on every installation. So to reduce attack surface, it would be natural to only allow loading modules that are actually desired to be loaded.

I'm in no way the first person to have that idea. Fedora has a feature proposal dated 2010 to introduce whitelisting. But sadly, nobody ever did.

So what I did was write a small script that whitelists all modules currently loaded and then aliases all others to /bin/true, effectively preventing them from being loaded.

#!/bin/sh

set -eu

OUTDIR="${1:-/etc/modprobe.d}"

if [ "$#" -lt 2 ]; then
	awk '{print $1}' /proc/modules | xargs -r -n1 "$0" "$OUTDIR"
	cat > "$OUTDIR/whitelist.conf" <<-EOF
	alias * blacklisted
	install blacklisted /bin/true
	EOF
	exit 0
fi

MODULE="$2"
OUTFILE="$OUTDIR/whitelist-$MODULE.conf"

exec > "$OUTFILE"

echo "alias $MODULE $MODULE"
export MODULE
modinfo "$MODULE" | awk '$1 == "alias:" {printf "alias %s %s\n", $2, ENVIRON["MODULE"]}'
modinfo "$MODULE" | awk '$1 == "depends:" {print $2}' | tr -d '\n' | xargs -r -d, -n1 "$0" "$OUTDIR"

What the script does is that, for each loaded module, it adds a file /etc/modprobe.d/whitelist-$MODULE.conf containing the lines alias $MODULE $MODULE and alias $ALIAS $MODULE, and after that was done for all loaded modules and their dependencies, it adds another file /etc/modprobe.d/whitelist.conf containing the lines alias * blacklisted and install blacklisted /bin/true.

There are several important aspects:

  1. Whitelisting has to be performed for the modules as well as all their aliases. If you only whitelist ext4 but not its alias fs-ext4, mounting ext4 filesystems will fail because the kernel tries to autoload the module by its alias.
  2. Dependencies have to be whitelisted as well.
  3. Trying to load any non-whitelisted module will fail silently, without any error message. This is due to the handling of aliases in modprobe: Since both alias directives match – the whitelisting alias modname modname as well as the blacklisting alias * blacklisted – modprobe tries to load both modules. So if I had used install blacklisted /bin/false instead, loading whitelisted modules would have exited with an error code as well.
  4. Keeping this module whitelist up to date is tedious work. Especially for distribution upgrades, removing the functionality altogether (with rm /etc/modprobe.d/whitelist*.conf) and re-running it afterwards is probably best. But strange error messages might pop up in other places as well. You can later add an additional whitelist entry for $MODULE by calling the script with the parameters /etc/modprobe.d $MODULE.