Device drivers are an essential part in modern Unix-like systems to handle operations on physical devices, from hard disks and printers to digital cameras and Bluetooth speakers. The surge of new hardware, particularly on mobile devices, introduces an explosive growth of device drivers in system kernels. Many such drivers are provided by third-party developers, which are susceptible to security vulnerabilities and lack proper vetting. Unfortunately, the complex input data structures for device drivers render traditional analysis tools, such as fuzz testing, less effective, and so far, research on kernel driver security is comparatively sparse.
DIFUZE is an interface-aware fuzzing tool to automatically generate valid inputs and trigger the execution of the kernel drivers. It leverage static analysis to compose correctly-structured input in the userspace to explore kernel drivers. DIFUZE is fully automatic, ranging from identifying driver handlers, to mapping to device file names, to constructing complex argument instances. DIFUZE can effectively identify kernel driver bugs, and reports previously unknown vulnerabilities, including flaws that lead to arbitrary code execution.
Unlike userspace applications, for which several vulnerability mitigation techniques are available and used, the kernels of modern operating systems are relatively vulnerable to attack despite available protections. As a result, as vulnerabilities in userspace applications become rarer, attackers turn their focus on the kernel. For example, over the last three years, the share of Android vulnerabilities that are in kernel code increased from 4% (in 2014) to 39% (in 2016), highlighting the need for techniques to detect and eliminate kernel bugs.
The kernel can itself be split into two types of code: core kernel code and device drivers. The former is accessed through the system call (syscall) interface, allowing a user to open files (the open() system call), execute programs (the execve() system call), and so on. The latter, on POSIX-compliant systems (such as Linux/Android and FreeBSD/iOS which cover over 98% of the mobile phone market), are typically accessed via the ioctl interface. This interface, implemented as a specific system call, allows for the dispatch of input to be processed by a device driver. According to Google, 85% of the bugs reported against the Android kernel (which is a close fork of Linux) are in driver code written by third-party device vendors. With the continually growing number of mobile devices in use, and with the criticality of their security, automated approaches to identify vulnerabilities in device drivers before they can be exploited by attackers are critical.
While automatic analysis of the system call interface has been thoroughly explored by related work, ioctls have been neglected. This is because, while interaction with syscalls follows a Session J2: Fun with Fuzzing CCS’17, October 30-November 3, 2017, Dallas, TX, USA 2123 unified, well-defined specification, interaction with ioctls varies depending on the device driver in question. Specifically, the ioctl interface comprises structured arguments for each of a set of valid commands, with both the commands and the data structures being driver-dependent. While this has security implications (i.e., pointers, dynamically-sized fields, unions, and sub-structures in these structures increase the chance of a vulnerability resulting from the mis-parsing of the structure), it also makes these devices hard to analyze. Any automated analysis of such devices must be interface-aware, in the sense that, to be effective, it must interact with ioctls using the command identifiers and data structures expected by them.
DIFUZE is a novel combination of techniques to enable interface-aware fuzzing, and facilitate the dynamic exploration of the ioctl interface provided by device drivers. DIFUZE performs an automated static analysis of kernel driver code to recover their specific ioctl interface, including the valid commands and associated data structures. It uses this recovered interface to generate inputs to ioctl calls, which can be dispatched to the kernel from userspace programs. These inputs match the commands and structures used by the driver, enabling efficient and deeper exploration of the ioctls. The recovered interface allows the fuzzer to make meaningful choices when mutating the data: i.e., typed fields like pointers, enums, and integers should not be handled as simply a sequence of bytes. DIFUZE stresses assumptions made by the drivers in question and exposes serious security vulnerabilities.