I understand the principles of exploiting a classical stack-based buffer-overflow, and now I want to practice it. Therefore I wrote the following test-application:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
void public(char *args) {
char buff[12];
memset(buff, 'B', sizeof(buff));
strcpy(buff, args);
printf("\nbuff: [%s] (%p)(%d)\n\n", &buff, buff, sizeof(buff));
}
void secret(void) {
printf("SECRET\n");
exit(0);
}
int main(int argc, char *argv[]) {
int uid;
uid = getuid();
// Only when the user is root
if (uid == 0)
secret();
if (argc > 1) {
public(argv[1]);
}
else
printf("Kein Argument!\n");
}
When the user which starts the program is root, the method secret()
is being called, otherwise, the method public(...)
is being called.
I am using debian-gnome x64, so I had to compile it specifically to x86 to get x86-assembly (which I know better than x64).
I compiled the program with gcc: gcc ret.c -o ret -m32 -g -fno-stack-protector
Target:
I want to call the method secret()
without being a root-user. {To do that I have to overwrite the R
eturn I
nstruction P
ointer (RIP
) with the address of the function secret()
}
Vulnerability:
The method public(...)
copies the program-args with the unsafe strcpy()
method into the char-array
buff. So it is possible to overwrite data on the stack, when the user starts the program with an arg > 11, where arg should be the length of the string-arg.
Required Information:
- The address of the function
secret()
. - The address of the first buffer's first element. Due to
ASCII
-Encoding I know that eachchar
has a size of1 byte
, so that the buffer's last element is12 bytes
ahead the first element. - The address of the
RIP
, because I have to overwrite itsecret()
s address. - OPTIONAL: It also helps to know the address of the
S
afedF
rameP
ointer (SFP
).
Methodical approach:
- Load the program into
gdb
:gdb -q ret
. - To get an overview of the full stack-frame of the method
public(...)
I have to set a breakpoint there, where thefunction-epilogue
starts. This is at the enclosing brace}
at line11
. - Now I have to run the program with a valid arg:
run A
. At the breakpoint, I now want to view the stack-frame.
(gdb) info frame 0 Stack frame at 0xffffd2f0: eip = 0x804852d in public (ret.c:11); saved eip = 0x804858c called by frame at 0xffffd330 source language c. Arglist at 0xffffd2e8, args: args=0xffffd575 "A" Locals at 0xffffd2e8, Previous frame's sp is 0xffffd2f0 Saved registers: ebp at 0xffffd2e8, eip at 0xffffd2ec
Because from that I can gather the following information:
- The
RIP
is located at0xffffd2ec
and contains the address0x804858c
which contains the instruction0x804858c <main+61>: add $0x10,%esp
. - The
SFP
is located at0xffffd2e8
. Now I need the address, where the
secret()
-function starts:(gdb) print secret $2 = {void (void)} 0x804852f
- The
Last, but not least I get the buffer's address:
(gdb) print/x &buff $4 = 0xffffd2d4
To sum it up:
RIP
is at0xffffd2ec
.SFP
is at0xffffd2e8
.buff
is at0xffffd2d4
.
This means that I would have to run the program with 0xffffd2ec
- 0xffffd2d4
+ 0x04
= 28 bytes
(= char
s).
So, to exploit it I'd have to run the program with an arg which is 28 bytes
long whereas the last 4 bytes
contain the address of the function secret()
(and pay attention to little-endian-ordering):
(gdb) run `perl -e '{print "A"x24; print "\xec\d2\ff\ff"; }'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/patrick/Projekte/C/I. Stack_Overflow/ret `perl -e '{print "A"x24; print "\xec\d2\ff\ff"; }'`
buff: [AAAAAAAAAAAAAAAAAAAAAAAA�d2
f
f] (0xffffd2b4)(12)
Program received signal SIGSEGV, Segmentation fault.
0x0c3264ec in ?? ()
Two questions are rising up:
Why is it not working. This example is basically from an older book I'm reading. But theoretically it should work so I think....
Why is between
buff
and theSFP
a8-byte
gap? What does this memory-area contain?
EDIT: That's a download-link to the binary.