I’m writing a return to libc attack for my systems security class. First, the vulnerable code:
//vuln.c
#include <stdio.h>
#include <stdlib.h>
int loadconfig(void){
char buf[1024];
sprintf(buf, "%s/.config", getenv("HOME"));
return 0;
}
int main(int argc, char **argv){
loadconfig();
return 0;
}
I want to use a return to libc attack. Compiling and debugging the program:
$ gcc -g -fno-stack-protector -o vuln vuln.c
$ gdb vuln
(gdb) break loadconfig
(gdb) run
Reached breakpoint blah blah blah.
(gdb) p $ebp
$1 = (void *) 0xbfffefb0
(gdb) p system
$2 = {<text variable, no debug info>} 0x0016db20 <system>
(gdb) p exit
$3 = {<text variable, no debug info>} 0x001639e0 <exit>
(gdb) x/2000s $esp
...
0xbffff5af: "SHELL=/bin/bash"
To execute the attack, I want to overflow the buffer into loadconfig‘s return address (aka $esp+4), replacing it with the return address for system, then the return address for exit (since system expects a real return address), then the command name (the address of SHELL=/bin/bash plus 6, to trim the SHELL= part). This should be possible by crafting a $HOME environment variable of 1024 characters of crap, then the little-endian address of system, exit, and /bin/bash.
However, with every computer I’ve tried, system gets loaded at an address that starts with 0x00, which will null terminate the string that sprintf is reading and stop the attack dead. Is there some way to force libc to load elsewhere in memory, or am I misinterpreting the attack?
For reference, I’m running an Ubuntu Server 11.10 virtual machine in VirtualBox (Windows host), with gcc version 4.6.1 and gdb version 7.3-2011.08. edit: ASLR is disabled, and I compiled with -fno-stack-protector to remove the canary. Since I’m not executing anything from the stack, I don’t need to execstack it.
The act of mapping important libc function to addresses which contain a NULL byte is called ASCII armoring.
This protection is part of RedHat Exec-shield which is currently enabled on recent ubuntu distros link
To disable it you have to run as root:
sysctl -w kernel.exec-shield=0
as explained here
By the way you can find interesting material about how to bypass ASCII armoring here on exploit-db