Privilege Escalation Attack - Linux (CVE-2017-0358)
This article is a deep dive on CVE-2017-0358. The article starts with an introduction to the attack and then explains the basics of the components present in the attack and finally details the vulnerability and exploit implementation.
Introduction
CVE-2017-0358
is a privilege escalation attack on Linux
reported by Jann Horn of Google Project Zero.
Jann Horn exploited a design pitfall found in
NTFS-3G
to escalate the privilege.
NTFS-3G is an open source cross-platform implementation of
the Microsoft Windows
NTFS file system
with read-write support.
It comes pre-installed in most of the Linux distros. The package
uses FUSE
(Filesystem in Userspace) to mount NTFS partitions.
If the FUSE module is not already supported by the kernel, NTFS-3G attempts
to load fuse using
Components
NTFS-3G
NTFS-3G is a read-write NTFS driver for Linux distros (A module that will help you read and write to windows files using Linux). It provides support for NTFS file systems of all the windows versions starting from Windows XP to Windows 10. The package is widely used and is available for embedded systems as well.
If your computer dual boots to Windows and Linux, it is most probable that you have handled Windows files using Linux, and if so, you have already used NTFS-3G!
Linux kernel module
Linux Kernel modules are pieces of code that can be loaded and unloaded into the kernel upon demand. They extend the functionality of the kernel without the need to reboot the system. For example, when you plug in a mouse to your computer, the corresponding device driver needs to be loaded into the kernel. This device driver can be considered as a kernel module. It loads into the kernel without the need of any restart and extends the functionality of the system to make use of the device plugged in.
A custom kernel module is recommended to have 3 important functions in it.
-
Module init (
__init ): The function that should be called while loading the module. The return value of this function is considered to be a flag. If the module loading is a failure, the function is expected to return an error tomodprobe . -
Module exit (
__exit ): The function that will be called while unloading the module from kernel. This function is supposed to clean the kernel by unsetting all settings that were made for the module. - Module param: A function that lets you pass parameters to the module.
In Linux,
Modprobe
FUSE
FUSE or Filesystem in Userspace is a Linux software interface that allows the user to write their own file systems without editing the kernel code.
For example, let's say you like to have a filesystem were any ".csv" file that you save will be automatically converted to a spreadsheet and any ".doc" file stored will be converted to pdf etc… This can be realized by writing a custom filesystem using FUSE. In fact, NTFS-3G is built upon FUSE.
Vulnerability
When NTFS-3G is invoked to mount an NTFS partition, it seeks the help
of FUSE module to do the same. Firstly, it reads
Exploit Mechanism
Instead of loading a malicious module into the kernel, in this example
, we obtain a binary which can create
1. Create a malicious kernel module
First, we create a binary (say
Now, create a malicious module that will be loaded into the kernel.
The malicious module will have an
uid=0, gid=0 - read & execute permissions for everyone
2. Settings the stage for modprobe
We set the following environment variable to mislead
MODPROS="-C malicious_config_dir -d malicious_module_dir"
The variable has two flags in it:
-C : This sets the configuration directory for themodprobe . We set it's value to point to our malicious configuration folder.-d : This sets the directory to load the module from. We set it to the directory containing our malicious module.
3. Modprobe configuration
In the malicious configuration file (step 2.1), we set two configurations.
We set
alias fuse rootmod options rootmod suidfile_fd=suidfile_fd
Now when the
4. Deny access to /proc/filesystems
In Linux, the number of files that can be opened at a time is limited by
the number set in
Note: We will open all the essential files required for the
script beforehand so that we don't run out of file descriptors.
This is the reason why we passed the file descriptor for
Note: This has some practical difficulties, they are discussed later.
5. Call to NTFS-3G
Let's list what we have done till now:
- Created the malicious kernel module.
Its__init will set the required permissions for ourrootshell binary. - Created config file for
modprobe .
The configurations will askmodprobe to loadrootmod instead offuse . -
Set environmental variable.
This will help us load config file and source directory tomodprobe when it is called. -
We exhausted all file descriptors in the system.
So that any attempt to read/proc/filesystems will be a failure.
At this point, we call NTFS-3G asking it to mount a dummy module.
NTFS-3G will try to read
As mentioned in 5.3, environmental variable set will now provide
malicious configurations to
Some practical difficulties
There are some practical difficulties in exhausting the file descriptors.
In Linux, the number of files openable by a process is limited and this limit is below the total number of file descriptors allowed. As a work around, we will have to fork the process which is opening the files multiple times so that the total number of files opened by all forked processes exceed the limit.
Before trying to mount any files, NTFS-3G will check if the files are
already mounted or not. For this, NTFS-3G will read
Proposed solution
Both
The end