|ntfs-3g: modprobe is executed with unsanitized environment|
|Project Member Reported by firstname.lastname@example.org, Jan 5 2017||Back to list|
ntfs-3g is installed by default e.g. on Ubuntu and comes with a setuid root program /bin/ntfs-3g. When this program is invoked on a system whose kernel does not support FUSE filesystems (detected by get_fuse_fstype()), ntfs-3g attempts to load the "fuse" module using /sbin/modprobe via load_fuse_module(). The issue is that /sbin/modprobe is not designed to run in a setuid context. As the manpage of modprobe explicitly points out: The MODPROBE_OPTIONS environment variable can also be used to pass arguments to modprobe. Therefore, on a system that does not seem to support FUSE filesystems, an attacker can set the environment variable MODPROBE_OPTIONS to something like "-C /tmp/evil_config -d /tmp/evil_root" to force modprobe to load its configuration and the module from attacker-controlled directories. This allows a local attacker to load arbitrary code into the kernel. In practice, the FUSE module is usually already loaded. However, the issue can still be attacked because a failure to open /proc/filesystems (meaning that get_fuse_fstype() returns FSTYPE_UNKNOWN) always causes modprobe to be executed, even if the FUSE module is already loaded. An attacker can cause an attempt to open /proc/filesystems to fail by exhausting the global limit on the number of open file descriptions (/proc/sys/fs/file-max). I have attached an exploit for the issue. I have tested it in a VM with Ubuntu Server 16.10. To reproduce, unpack the attached file, compile the exploit and run it: user@ubuntu:~$ tar xf ntfs-3g-modprobe-unsafe.tar user@ubuntu:~$ cd ntfs-3g-modprobe-unsafe/ user@ubuntu:~/ntfs-3g-modprobe-unsafe$ ./compile.sh make: Entering directory '/usr/src/linux-headers-4.8.0-32-generic' CC [M] /home/user/ntfs-3g-modprobe-unsafe/rootmod.o Building modules, stage 2. MODPOST 1 modules CC /home/user/ntfs-3g-modprobe-unsafe/rootmod.mod.o LD [M] /home/user/ntfs-3g-modprobe-unsafe/rootmod.ko make: Leaving directory '/usr/src/linux-headers-4.8.0-32-generic' depmod: WARNING: could not open /home/user/ntfs-3g-modprobe-unsafe/depmod_tmp//lib/modules/4.8.0-32-generic/modules.order: No such file or directory depmod: WARNING: could not open /home/user/ntfs-3g-modprobe-unsafe/depmod_tmp//lib/modules/4.8.0-32-generic/modules.builtin: No such file or directory user@ubuntu:~/ntfs-3g-modprobe-unsafe$ ./sploit looks like we won the race got ENFILE at 198088 total Failed to open /proc/filesystems: Too many open files in system yay, modprobe ran! modprobe: ERROR: ../libkmod/libkmod.c:514 lookup_builtin_file() could not open builtin file '/tmp/ntfs_sploit.u48sGO/lib/modules/4.8.0-32-generic/modules.builtin.bin' modprobe: ERROR: could not insert 'rootmod': Too many levels of symbolic links Error opening '/tmp/ntfs_sploit.u48sGO/volume': Is a directory Failed to mount '/tmp/ntfs_sploit.u48sGO/volume': Is a directory we have root privs now... root@ubuntu:~/ntfs-3g-modprobe-unsafe# id uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lxd),123(libvirt),127(sambashare),128(lpadmin),1000(user) Note: The exploit seems to work relatively reliably in VMs with multiple CPU cores, but not in VMs with a single CPU core. If you test this exploit in a VM, please ensure that the VM has at least two CPU cores. This bug is subject to a 90 day disclosure deadline. If 90 days elapse without a broadly available patch, then the bug report will automatically become visible to the public.
Jan 5 2017,
The vendor has created a patch that provides modprobe with an empty environment. The patch looks good.
Derestricting: While I don't see a new release on the vendor's website (http://www.tuxera.com/community/open-source-ntfs-3g/), this bug was posted on oss-security by Laszlo Boszormenyi (http://seclists.org/oss-sec/2017/q1/259). (Someone also posted an exploit for the bug; however, unlike my exploit, that exploit probably won't work if the FUSE kernel module is already loaded.)
@jannh -- what makes you think my exploit doesn't work when fuse is already loaded? :) Tested on standard debian 9 and works out of the box for me. Did you see the trick I used to bypass what you think wouldn't be possible? I believe you should be able to reproduce as well.
@kristian...: I just installed Debian 9 in a VM, and FUSE is not already loaded: "grep fuse /proc/filesystems" shows nothing. With e.g. Ubuntu Server 16.10, that command does print output. The slightly tricky part of my exploit is to bypass the get_fuse_fstype() check in ntfs-3g if the FUSE driver is already loaded. As far as I can tell, you don't need this bypass on a freshly booted Debian 9 machine because the FUSE driver is only loaded on demand in Debian 9. Note that the presence of /dev/fuse does not imply that the fuse module is loaded; however, attempting to open that device should trigger module autoloading if necessary. Does your exploit still work if the "fuse" module has been autoloaded (e.g. via "cat /dev/fuse")? Or does it work on any Linux system where FUSE support is compiled into the kernel (e.g. Ubuntu Server 16.10)?
Jann -- so you have also confirmed my exploit works on Debian 9 stretch, thanks! You are correct that my exploit does NOT work on other versions of debian or Ubuntu due to fuse already being loaded. I had not tested on other distros and so as you mentioned, your exploit is able to bypass that check on ubuntu, which is neat. The "trick" I was referring to was the symlink traversal and to the aliased module name, which as you correctly identified, does not actually bypass the ntfs-3g code, so I was mistaken. Your symlink and file exhaustion attack does work on Ubuntu to bypass the fuse loaded check; very nice. Thanks again for confirming everything. Looks like my exploit only works on Debian 9 stretch due to the fact that fuse is not loaded by default. If you can confirm all this that would be great. Cheers.
|► Sign in to add a comment|