1

Suppose I have a vulnerable SUID program belonging to the user Bob, which is executable by all users.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[])
{
  const char* s = getenv("USER"); // for debugging
  printf("%s\n", s); // for debugging

  char cmd[256] = "/home/bob/hello.sh $USER"; //hello.sh prints "Hello world"
  execl("/bin/bash", "bash", "-p", "-c", cmd, NULL);

  return 0;
}

I want to use the fact that the command it runs has the USER environment variable appended to it, to read a file that only Bob can see. For example, if I set USER to ";cat /home/bob/secret-file"

env USER=";cat /home/bob/secret-file" ./program

This does not run and only returns:

;cat /home/bob/secret-file
Hello world

The second command never runs, despite the USER environment variable changing.

Without editing the C code, how can I run a command such as 'cat' using the USER environment variable to run it with bob's permissions (because the program is SUID) and view files only bob can see.

1 Answers1

1

What you want to do is impossible. This has nothing to do with C or SUID and everything to do with how the shell expands variables.

Parsing input to the shell into individual shell commands happens before expanding environment variables. The contents of the variable are not interpreted according to shell syntax (otherwise foo='$foo' ; : $foo would be an infinite loop). This means interpolating a variable into a command line can never split it into multiple separate command lines. If the variable is unquoted, it only means the variable's contents may be split on whitespace into multiple arguments to the command. This may or may not create some kind of exploitable vulnerability, but it will not be direct shell injection.

If you replace your hello.sh script with this:

#!/bin/sh
i=1
for arg in "$@"; do
    echo "argument #$i: [[$arg]]"
    i="$(expr "$i" + 1)"
done

you will notice that it is invoked with first argument ;cat and second argument /home/bob/secret-file. The shell does not interpret the ; as a command separator, it just passes it along.

user3840170
  • 1,832