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 Return Instruction Pointer (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 eachcharhas a size of1 byte, so that the buffer's last element is12 bytesahead 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
SafedFramePointer (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-epiloguestarts. 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 0xffffd2ecBecause from that I can gather the following information:
- The
RIPis located at0xffffd2ecand contains the address0x804858cwhich contains the instruction0x804858c <main+61>: add $0x10,%esp. - The
SFPis 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 = 0xffffd2d4To sum it up:
RIPis at0xffffd2ec.SFPis at0xffffd2e8.buffis at0xffffd2d4.
This means that I would have to run the program with 0xffffd2ec - 0xffffd2d4 + 0x04 = 28 bytes (= chars).
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
buffand theSFPa8-bytegap? What does this memory-area contain?
EDIT: That's a download-link to the binary.