This challenge introduces the concept of control flow hijacking. It’s another heap based buffer overflow. The overflow isn’t hard to find, just look at the only two strcpy calls and you’ll notice neither of them do any bounds checking.
Finding the Exploit
I wasn’t immediately sure what to do with this one so I did something very intelligent… I typed ./heap1 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA in GDB just to see what would happen. Sure enough, it crashed. However, what caught my attention was that I controlled the destination address of the strcpy:
This brought me to the conclusion that the overflow from argv into i1->name must allow me to overwrite the destination address of i2->name. Once I thought about it, that makes sense. The heap grows from low addresses towards high addresses. i1 is declared first so it is likely at a lower address in the heap than i2. Therefore, when the overflow occurs (which flows from low to high), we overwrite information from i2. To confirm this suspicion I went through some disassembly.
I placed breakpoints at 0x080484e8 and 0x08048517, which correspond to the allocations allotted for the name variable. At these locations I knew eax would contain a pointer into the heap where memory had been allocated. I then ran the program with my As again. Sure enough my suspicions were confirmed:
This next part was a bit more tricky. I wanted to know what the offset was from our buffer at 0x0804a018 to the pointer to i2->name. This requires a bit of knowledge of structs. I placed a breakpoint at 0x08048517 knowing that the return value would be in eax and that the pointer returned must ultimately end in eax. Now, what I expect to see if I am correct is a pointer to a value of 2. I would see this because the beginning of the struct for i2 should have an integer value of 2. I went ahead and tried that.
Indeed we see our value of 2. This means that at address 0x0804a028+4 is the address of our i2->name pointer. Now I know the offset from the beginning of my buffer to the pointer of interest is 0x0804a02c-0x0804a018=20.
The only thing left is the address of winner which I just found with p winner. The address is 0x08048494.
At this juncture, I figured I would just use a classic overwrite of the return address, which in this scenario is stable. I simply stepped into the strcpy function to see what the address of the return address would be. When the return is made, it can return into the winner function.
Now that I know the return address is 0xbffff7dc, I fired up my exploit.
Sure enough it worked!