A crypto module loading vulnerability
Loading a module into a running kernel is rather invasive, which is part of why the operation is restricted in various ways. For the most part, it requires root privileges (or the CAP_SYS_MODULE capability) to load a module, but there are exceptions. Some modules get automatically loaded when a new piece of hardware is plugged in, a new filesystem type is mounted, or a new kernel cryptographic algorithm is needed. In the latter case, however, unprivileged users could cause any module in the official module directory to get loaded by exploiting a hole in the crypto subsystem—at least until recently.
The problem was actually discovered almost two years ago when Mathias Krause pointed it out in a thread about a similar bug with mount and user namespaces. In that case, the root user in a user namespace (who might be a regular user in the top-level namespace) could mount a filesystem using the -t (type) option and pass any module name as the parameter. That would cause the kernel's module-loading logic to load the module, even if it wasn't a filesystem. Krause noted that the same was true for the crypto subsystem.
The mount problem was fixed shortly after it was reported, but a fix for the crypto bug evidently slipped through the cracks. Neither of these bugs allowed unprivileged users to load arbitrary (i.e. attacker-controlled) modules, which would be a much more severe problem, but even being able to load unexpected modules from the standard location (normally under /lib/modules) can lead to various vulnerabilities including privilege escalation—full system compromise, essentially.
Since kernel modules have intimate access to the kernel, vulnerabilities in modules have been exploited in the past. But if an administrator believes that regular users cannot load certain modules, they may be less inclined to update their kernel to fix a problem in an "irrelevant" module. There is also the risk of unknown or zero-day module vulnerabilities. The risk of any of that may be fairly low, but there are reasons that module loading is restricted to certain users.
The usual fix (and the one that was applied for filesystems) is to prefix the user-supplied module name with a subsystem-specific string; for filesystems, "fs-" was used. All filesystems that can be built as modules got a MODULE_ALIAS() using the prefix. The request_module() call for filesystems was modified to prepend the prefix, which means that the kernel would no longer load just any module as a filesystem type, it would need to actually be a filesystem module.
Obviously, the same scheme can be applied to the crypto subsystem, but there were a few wrinkles, as Krause outlined in a G+ post. First off, the vulnerability is a bit more easily accessed than the mount and user-namespace flaw. Any user-space program that binds to an AF_ALG socket (which provides a netlink-based user-space interface to the crypto subsystem) can specify the type of cipher that it wishes to use. If that cipher is not present, the kernel will try to load a module of that name. Since there are no restrictions on the name, any module name can be passed and it will be loaded.
Modifying all of the cryptographic algorithms that can be built as modules to add a "crypto-" prefix, while changing the crypto module–loading code to do the same, is the obvious path forward. Kees Cook made that change, but there was a problem with the fix. Crypto ciphers can also specify a mode, so the AES cipher in electronic codebook (ECB) mode would be specified as "ecb(aes)". Cook's original fix would allow any module to be specified for the mode (e.g. "vfat(aes)") and it would get loaded.
That led to a second patch from Cook, but that was missing some needed crypto module aliases. A patch from Krause added the necessary aliases.
But there was still one more (non-kernel) bug that was found in this process. The kernel turns the module-loading job over to the modprobe user-space utility, which finds the module file, reads it in, and uses the init_module() system call to add it to the kernel. As it turns out, Krause was using a BusyBox-based system to test the patches. He discovered that BusyBox's modprobe effectively uses the basename of the module name passed to it. That means everything up through the final "/" is ignored. A request for "/vfat" gets turned into a request for "crypto-/vfat", but the BusyBox modprobe ignores the first part and happily loads the vfat module—which takes us back to square one.
That problem affected more than just crypto (in fact, Krause doesn't mention crypto in the bug report, presumably because Cook's patches had not yet been merged). He noted two other commands that would load modules when they shouldn't:
# ifconfig /usbserial up
# mount -t /snd_pcm none /
In both cases a prefix is used ("netdev-" and "fs-", respectively) to avoid
this kind of problem, but BusyBox effectively ignored them. BusyBox
maintainer Denys Vlasenko fixed the bug one day after Krause reported it.
There were some fits and starts along the way, but those bugs are fixed
now, as Krause noted:
The kernel bug has been around since 2011, when the AF_ALG interface to the crypto subsystem was introduced in 2.6.38. The bugs were assigned three separate CVE numbers: CVE-2013-7421 for the original bug Krause pointed out in 2013, CVE-2014-9644 for the "vfat(aes)" variation, and CVE-2014-9645 for the BusyBox modprobe bug. The kernel fixes are included in the mainline and will be released with 3.19; backports to the stable kernels may be coming as well.
While it is not a particularly critical bug, letting unprivileged users
mess with kernel internals is certainly something to be avoided. But it
has languished for nearly two years since its discovery, which is kind of
surprising. Disabling module loading (either at boot or in the kernel
config) is one fairly easy mitigation technique, though it may not be an
option for some types of systems (especially desktops and laptops).
| Index entries for this article | |
|---|---|
| Kernel | Cryptography |
| Kernel | Modules |
| Kernel | Security/Vulnerabilities |
| Security | Linux kernel/Modules |
