Python library hijacking

Bien que j'ai déjà entendu parler et mis en pratique une élévation de privilèges via hijacking de librairie, je me suis retrouvé cette fois face à un cas que je n'avais jamais vu. Voici le scénario. Après avoir obtenu un shell en tant que user simple je me suis rendu compte que mon utilisateur pouvait run un script python en tant que root via une règle sudoers:

Pour faciliter l'explication de cet article j'ai volontairement simplifié le code source vulnérable en prenant pour cible une librairie que tout le monde connaît: requests! Voici le code du script python:

import requests

print("Sending a request to google.com")
request = requests.get("https://google.com")
print(f"Response size: {len(request.text)}")

D'emblée on peut constater que le script python import la librairie requests. Or nous savons que sous certaines circonstances il est possible d'hijacker un import de librairie. Pourquoi ? Eh bien parce que lorsque un script python importe des librairies externes, il va les chercher dans plusieurs répertoires.

Pour connaître la liste de ces répertoires il suffit d'exécuter la commande suivante:

python3 -c 'import sys; print("\n".join(sys.path))'

Qui nous donne le résultat suivant:

Comme vous pouvez le voir la première ligne de l'output est vide. En fait cela veut simplement dire que le premier répertoire qui est vérifié par python est le répertoire courant d'exécution du script.

Ainsi lorsque l'on exécute notre script, python va d'abord chercher la librairie requests dans le répertoire courant, puis dans le répertoire /usr/bin/python3.6 puis dans /usr/bin/python3.6/lib-dynload etc.

Une fois que nous connaissons ces répertoires il faudra déterminer dans lequel se trouve la librairie requests. Pour cela on pourra exécuter les lignes suivantes:

Ici on apprend donc que la librairie requests a été installée dans le répertoire /usr/lib/python3/dist-packages/requests. Que se passerait-il du coup si je créais un fichier requests.py dans le répertoire courant d'exécution du script python ? Eh bien je serai en capacité d'hijacker cette librairie et ainsi exécuter du code tout en profitant des privilèges root.

Voici la libraire factice que l'on va créer (il ne faudra pas oublier de bien nommer le fichier requests.py):

import os

print("Oh you just got hijacked bro")
os.system("/bin/bash")

Il ne nous reste plus qu'à exécuter le script vuln.py via nos droits sudoers pour obtenir un shell root:

Mais du coup est-ce qu'on pourrait hijack une librairie python sans pour autant avoir les droits d'écriture dans un de ces répertoires:

La réponse est oui... Mais sous certaines conditions. En effet pour que cela soit possible il va falloir pouvoir modifier la variable d'environnement PYTHONPATH. Or cela n'est pas possible avec la règle sudoers évoquée au début de cet article. Pour que cette technique soit exploitable il faudra que la règle sudoers soit la suivante:

Cette règle indique qu'en plus de pouvoir exécuter le script vuln.py en tant que root, nous allons aussi pouvoir modifier les variables d'environnement de notre utilisateur. Pour élever nos privilèges il nous suffira donc de créer notre librairie factice dans le répertoire /tmp, modifier la variable d'environnement PYTHONPATH pour qu'elle pointe sur le répertoire /tmp puis exécuter le script vuln.py:

Morale de l'histoire, n'ajoutez pas l'attribut SETENV sur vos règles sudoers ou alors faites le avec parcimonie.