Guide CTF : Wonderland de TryHackMe
- Mia Combeau
- Cybersécurité
- 28 mai 2023
Table des matières
Wonderland est un défi de capture du drapeau (CTF, “capture the flag” en anglais), créé par NinjaJc01 et disponible gratuitement sur TryHackme. Dans ce guide CTF, nous allons tomber dans le terrier du pentesting et atterrir dans un étrange pays des merveilles, celui de la cybersécurité !
Informations du CTF
Nom : Wonderland
Difficulté : Moyenne
Publié le : 05/05/2020
Auteur : NinjaJc01
URL : https://tryhackme.com/room/wonderland
Sans révéler les solutions de ce défi, nous allons ici explorer de façon méthodique les processus d’énumération, d’exploitation et d’escalade des privilèges nécessaires pour réussir Wonderland de TryHackMe. Avec une machine virtuelle Linux pour cible, nous serons amenés à brute-forcer un site web, à trouver des identifiants pour se connecter via SSH, à détourner une librairie Python, à dé-compiler un programme et à exploiter une vulnérabilité de configuration.
Comment prendre part au défi Wonderland de TryHackMe ?
Pour prendre part au défi Wonderland, il faut créer un compte gratuit sur TryHackMe et se rendre à l’adresse suivante : https://tryhackme.com/room/wonderland
. Le bouton “Start Machine” créera une nouvelle machine virtuelle dont on nous fournira l’adresse IP. C’est cette machine que nous devons exploiter afin de trouver les drapeaux et remporter la victoire.
Remarquons que l’adresse IP qu’on nous fournit une fois la machine démarrée commence par 10.10.x.x
. C’est une adresse privée, ce qui indique que la machine est sur un réseau privé : celui de TryHackMe. On ne pourra donc y accéder que depuis ce même réseau privé.
TryHackMe propose deux moyens d’accéder à leur réseau :
- AttackBox : une machine virtuelle Ubuntu hébergée dans le réseau et accessible depuis notre navigateur. Pour la lancer, il suffit de cliquer sur le bouton bleu “Start AttackBox”. La machine apparaît alors dans la fenêtre du navigateur. Celle-ci est équipée de tous les outils dont nous aurons besoin.
- OpenVPN : Ceux qui souhaitent travailler depuis leur propre machine peuvent, en alternative, télécharger la configuration OpenVPN de TryHackMe. Ensuite, il suffit de lancer la commande
sudo openvpn /chemin/vers/fichier.ovpn
dans un terminal. Attention à ne pas fermer cette fenêtre pour ne pas interrompre la connexion ! Pour vérifier qu’on est bien connecté au réseau privé, on peut lancer la commandeping 10.10.10.10
. Si on reçoit une réponse, c’est qu’on est bien connecté. En utilisant cette méthode, il est évidemment recommandé d’utiliser une machine virtuelle afin de ne pas compromettre son propre système d’exploitation. ParrotOS et Kali Linux sont de bons choix en termes de systèmes d’exploitation : tous deux ont les outils nécessaires aux audits intrusifs que nous devons effectuer.
Dans cet article, nous utiliserons le terme “machine locale” pour faire référence à la machine avec laquelle nous attaquons, qu’elle soit déployée dans le navigateur ou non. De même, nous utiliserons “machine Wonderland” pour désigner la cible de notre attaque.
Maintenant que nous nous sommes connectés au réseau TryHackMe depuis notre machine locale et que nous avons lancé la machine Wonderland, penchons-nous sur l’exploitation de cette dernière.
Énumérer Wonderland
La première étape de tout audit intrusif (“penetration testing” ou “pentesting” en anglais), c’est l’énumération. L’énumération, c’est le procédé par lequel on découvre d’éventuels vecteurs d’attaque qui peuvent nous mener à établir une connexion au système ciblé et l’exploiter. Lors de cette étape, on va chercher à en apprendre le plus possible sur notre cible : ses ports, ses services, ces utilisateurs, et toute autre information qui pourrait nous être utile.
Ping : tester la connexion à Wonderland
Commençons par un petit test non-intrusif : envoyer un petit paquet ICMP (“Internet Control Message Protocol”) avec l’outil ping
pour voir si elle nous répond. Au moment de la rédaction de cet article, l’adresse IP de ma machine Wonderland était 10.10.11.241
, mais tout le monde n’aura pas la même !
ping -c 3 10.10.11.241
Ici, l’option -c
suivi d’un chiffre nous permet de spécifier combien de fois envoyer le paquet. Sans cette spécification, ping
ne cessera pas d’envoyer ses paquets à moins d’être interrompu avec ctrl-c
.
Si l’on reçoit une réponse de la machine ciblée, cela veut dire qu’elle est bel et bien en ligne et accessible depuis notre système. Par contre, si l’on ne reçoit aucune réponse, ce n’est pas pour autant qu’elle est inaccessible. Elle pourrait bien être protégée par un pare-feu qui inhibe ses réponses par mesure de sécurité.
Dans le cas d’une non-réponse, vérifions tout de même si nous sommes bien connectés au réseau TryHackMe en lançant un ping 10.10.10.10
. TryHackme nous garantit que cette machine-là répond aux paquets ICMP lorsqu’on est correctement connecté au réseau.
Que la machine Wonderland réponde ou non, il nous faut de toutes façons des tests plus poussés pour l’analyser.
Nmap : tester les ports et services de Wonderland
Ce qu’on cherche à ce stade, c’est des ports par lesquels se connecter à la machine cible. Toute connexion entre deux ordinateurs se fait via un port. L’outil nmap
(“network mapping” ou “cartographie du réseau” en français) est parfait pour ceci.
Muni de l’adresse IP qu’on lui fournit, nmap
va pouvoir tester chaque port de notre cible afin de déterminer lesquels sont ouverts, fermés ou protégés par un pare-feu. Mieux, il sait même identifier les services qui y opèrent ainsi que détecter leurs versions et systèmes d’exploitations dans certains cas. Pour nous fournir ces informations, nmap
envoie des paquets à chaque port possible et imaginable et interprète les réponses qu’il reçoit. Laçons-le contre cette IP :
nmap -vv -sV 10.10.11.241 | tee nmap.log
Décrivons rapidement les options de cette commande :
-vv
augmente la verbosité de la sortie denmap
. Comme nous souhaitons avoir autant d’informations que possible, c’est toujours une bonne idée de l’inclure.-sV
indique ànmap
de tenter de déterminer le service qui opère sur les ports ouverts qu’il découvre
Nous redirigeons la sortie de nmap
vers un autre programme tee
, qui prend en paramètres un fichier. L’outil tee
permet à la fois d’afficher la sortie de la commande précédente sur la sortie standard (le terminal) et de la sauvegarder dans un fichier, nmap.log
. De cette façon, on pourra y retourner au besoin, sans avoir à relancer l’analyse nmap
. En effet, l’analyse peut prendre plusieurs minutes, vu que nmap
teste chaque port de la cible, et qu’une machine en a généralement 65 536 !
L’option -vv
a été omise dans cette commande par souci de brièveté.
Le résultat nous indique que la machine Wonderland a deux ports ouverts :
- le port 22, qui est le port par défaut du protocole SSH (“secure shell”). C’est une bonne nouvelle : SSH nous permettra sans doute d’accéder à la machine à distance via notre terminal, sous condition de trouver des identifiants.
- le port 80, qui est le port par défaut du protocole HTTP. Cette machine gère donc un site web, qu’on pourra peut-être exploiter, ou du moins explorer.
Grâce à l’analyse de nmap
, on sait aussi que la machine Wonderland tourne sous Ubuntu Linux.
Port 80 : le site Wonderland
Allons tout d’abord voir ce site internet, puisque dans identifiant, il n’y a pas grand espoir de pouvoir se connecter via SSH. Dans notre navigateur, nous pouvons simplement mettre l’adresse IP de la cible dans la barre de navigation.
À première vue, le site est très simple : uniquement du texte et une image. Il n’y a pas de liens ou de menu.
On peut aller voir le code source de la page, au cas où il y aurait des informations cachées. Chaque navigateur est différent, mais en général, il suffit de faire un clic droit sur la page et de sélectionner “voir le code source”. Manque de chance, on n’y trouve rien de suspect.
Mais le code source n’est pas le seul endroit où on pourrait cacher quelque chose…
Stéganographie : extraire des messages cachés dans des images
La stéganographie est une pratique voisine à la cryptographie. Elle consiste à dissimuler des informations à l’intérieur d’un autre message ou objet. On peut par exemple cacher un message, une image, une vidéo ou tout autre fichier à l’intérieur d’un autre fichier, d’une autre vidéo, d’une autre image ou d’un autre message.
L’image qu’on nous présente sur le site de la Wonderland semble tout à fait ordinaire, mais pourrait-elle cacher un message ? Téléchargeons l’image avec wget
pour en avoir le cœur net :
wget http://10.10.11.241/img/white_rabbit_1.jpg
Le programme steghide
est un outil parmi d’autres qui permet d’extraire un message d’un fichier .jpeg
, .bmp
, .wav
ou .au
, s’il y en a un. Tentons cela :
steghide extract -sf white_rabbit_1.jpg
On nous demande une phrase secrète. Comme on n’en a pas, appuyons directement entrée sans rien taper avant. steghide
nous informe qu’il a extrait les données vers un nouveau fichier, hint.txt
.
cat hint.txt
Ah, un indice ! Comme sur le site web, on nous invite à suivre le lapin. Mais l’espacement des lettres de “rabbit” est pour le peu intriguant… On a maintenant un indice, mais rien d’autre.
Nous avons trouvé quelque chose de caché sur la page, mais qu’en est-il du reste du site ? Il n’y a aucun lien, mais peut-être y a-t-il tout de même d’autres pages ou d’autres répertoires…
Gobuster : trouver les répertoires d’un site web
Il y a plusieurs outils pour tenter de découvrir tous les fichiers et répertoires d’un site web, et parmi eux, gobuster
. Cet outil nous permet entres autres, de brute-forcer la découverte d’URIs (fichiers et dossiers) ou de sous-domaines d’un site web.
Pour cela, on doit lui fournir d’adresse IP ou l’URL d’un site web ainsi qu’un dictionnaire de mots à essayer. Ensuite, gobuster
se charge d’ajouter chaque mot du dictionnaire à l’adresse du site et de faire une requête au serveur. Si le serveur répond avec un code correct, gobuster
nous indique que la page existe. Voyons s’il nous trouve quelque chose :
gobuster dir -u 10.10.11.241 -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt | tee gobuster.log
Regardons les options de cette commande de plus près :
dir
(“directory”) indique àgobuster
qu’on cherche à trouver des sous-répertoires, et non des sous-domaines par exemple.-u
signale que le prochain paramètre sera l’URL ou l’adresse IP du site à découvrir.-w
annonce que le paramètre suivant est le dictionnaire de mots à utiliser. Ici, on utilise un petit dictionnaire de mots fréquemment utilisés pour les sous-répertoires. Bien évidement, un dictionnaire plus long permet plus de précision et de découvertes, au prix d’un plus long temps de traitement. Il y a beaucoup de dictionnaires disponibles en ligne, la collection SecLists est populaire de par son exhaustivité.
Et comme toujours, c’est une bonne idée de conserver le résultat de gobuster
dans un fichier, par exemple gobuster.log
, pour ne pas avoir à relancer le processus qui peut prendre du temps.
On voit que gobuster
a trouvé quelques sous-répertoires valides sur Wonderland, dont /img
, /poem
et /r
. Allons donc visiter l’adresse 10.10.11.241/img
qui contient l’image sur la page d’accueil qu’on a déjà analysée mais aussi quelques autres images. Nous pouvons les télécharger et les analyser aussi. La page 10.10.11.241/poem
, contient, on l’aura deviné… un poème ! Cependant, le sous-dossier /r
semble particulièrement intriguant, allons donc voir 10.10.11.241/r
.
On y trouve encore une miette de l’histoire d’ Alice au Pays des Merveilles, mais pas grand chose d’autre. Le code source ne révèle rien non plus.
Retournons donc à notre cher gobuster
et lançons une nouvelle analyse sur ce sous-répertoire, au cas-où :
gobuster dir -u 10.10.11.241/r -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt | tee gobuster-2.log
Voilà un répertoire qu’on avait pas trouvé avant : /a
! Par défaut, gobuster
ne tente pas de chercher les sous-répertoires des sous-répertoires, cela prendrait très longtemps. Il faut dont toujours penser à relancer gobuster
pour les sous-répertoires si besoin est.
Visitons donc l’adresse 10.10.11.241/r/a
dans notre navigateur. La page existe et on peut y lire la prochaine ligne de l’histoire. Il n’y a rien d’autre d’intéressant sur cette page.
Maintenant, on pourrait retourner de nouveau voir si gobuster
peut nous trouver un autre sous-répertoire. Mais nos pouvoirs de déduction viennent à notre secours : avec l’indice de l’image stéganographique, “Follow the r a b b i t”, on se rend vite compte dans quelle direction on s’en va ! Clairement, on suivre le /r/a/b/b/i/t
.
Voir le code source : lire l’HTML d’une page web
Quand on arrive sur la page 10.10.11.241/r/a/b/b/i/t
, on y trouve encore un morceau de l’histoire et une image. On peut tenter d’extraire des informations dissimulées dans cette dernière s’il y en a. Mais jetons d’abord un œil au code source.
Que trouvons-nous ici ? Un paragraphe invisible contenant un identifiant en clair, celui d’Alice ! Il est écrit dans le format très courant : utilisateur:mot_de_passe
.
Se connecter à Wonderland via SSH
Maintenant qu’on a un identifiant, on devrait pouvoir prendre pied dans le système de la machine Wonderland. Comme il n’y a aucune page de connexion sur ce site web, l’identifiant sert sans doute à se connecter via le port SSH.
Consultons notre sauvegarde de notre analyse nmap
. On y trouve le port 22 qui a un service SSH. Comme 22 est le port par défaut de SSH, on n’aura pas besoin de spécifier l’option -p 22
dans notre commande :
Saisissons le mot de passe qu’on a trouvé dans le code source. Nous voilà connecté à l’utilisateur Alice !
À ce stade, il est possible de trouver le drapeau user.txt
qu’il nous faut. L’explication sera en blanc sur blanc ci-dessous, pour ceux qui ne souhaitent pas déduire l’emplacement de ce drapeau à partir de l’indice sur la page Wonderland de TryHackMe. Il y aura toujours l’occasion de trouver ce drapeau plus tard.
L’indice sur la question user.txt
sur la page Wonderland de TryHackMe nous dit “Everything is upside down here” : “tout est à l’envers, ici”. Quand on regarde dans son répertoire, on voit qu’Alice a un fichier root.txt
qu’elle ne peut pas lire. Renversons cette idée : peut-être root a-t-il un fichier user.txt
qu’Alice peut lire ? Alice ne peut pas se déplacer dans le répertoire root ni même regarder ce qu’il contient. Mais si elle a les permissions de lecture pour ce fichier en particulier, est-ce qu’elle ne pourrait pas faire un cat /root/user.txt
?
Escalader les privilèges dans Wonderland
Maintenant qu’on a un accès à la machine Wonderland, notre but est d’escalader les privilèges jusqu’à pouvoir devenir root, le super-utilisateur du système.
En lançant la commande ls -l
on peut voir qu’Alice a un fichier root.txt
, mais malheureusement, ce fichier ne lui appartient pas. Elle n’a pas les permissions nécessaires pour lire le drapeau qui s’y trouve. On trouve aussi ici un script intéressant en Python, walrus_and_the_carpenter.py
.
Si l’on cd ..
vers le dossier /home
, on peut aussi voir qu’il y a 3 utilisateurs, Alice, Rabbit et Hatter. Bien sûr, Alice n’a pas les permissions d’aller explorer ce qu’il y a dans leurs répertoires.
Justement, voyons les limites des permissions d’Alice avec sudo
. Le programme sudo
est un moyen sécurisé de permettre à un utilisateur autorisé de lancer certaines commandes avec les permissions root.
sudo -l
Ici, l’option -l
indique à sudo
de nous faire une liste des commandes autorisées et interdites pour l’utilisateur qui l’invoque, donc dans ce cas, Alice. Le résultat nous montre quelque chose de très intéressant :
Alice a donc la permission d’utiliser sudo
pour lancer le script qui se trouve dans son répertoire en tant que l’utilisateur Rabbit. Cela veut dire que si on arrive à insérer du code malicieux dans ce script, et qu’on le lance avec sudo
en spécifiant l’utilisateur Rabbit, on pourrait potentiellement accéder au shell de Rabbit. En voilà un bon vecteur d’escalade de privilèges ! Penchons-nous donc sur ce script.
Exploitation du script en Python
Malheureusement, Alice n’a pas les privilèges d’écriture pour directement modifier le script en Python. Mais avec quelques recherches sur l’escalade des privilèges à l’aide d’un script en Python, on tombe vite sur l’idée de détournement des librairies.
En examinant le fichier /home/alice/walrus_and_the_carpenter.py
, on peut voir la première instruction : import random
. Il importe donc le module random.py
de la librairie Python. Mais comment Python fait-il pour trouver le fichier à importer ? Nous allons pouvoir répondre à cette question avec la commande suivante :
python3 -c 'import sys; print(sys.path)'
On demande ici à Python3 d’exécuter le code qui suit l’option -c
. Le code importe la librairie système pour pouvoir imprimer sys.path
, un tableau de chemins où pourrait se trouver les modules de ses librairies.
Python cherche dans chacun de ces chemins, de gauche à droite, en espérant tomber sur le bon module à importer. Le résultat de cette commande nous montre que tout d’abord, Python regarde dans le répertoire ''
. Ça ne semble pas être grand chose mais c’est une très bonne nouvelle : ''
indique le répertoire actuel, celui dans lequel le script lui-même se trouve !
C’est l’occasion pour nous de créer notre propre script random.py
dans le même répertoire que walrus_and_the_carpenter.py
( /home/alice/
). Lors de l’exécution du script, Python cherchera random.py
en premier lieu dans le répertoire actuel ( /home/alice
). Il tombera sur notre script, qu’il se contentera d’importer tel quel sans aller chercher plus loin. Il exécutera donc notre version malicieuse de random.py
plutôt que le module de la librairie Python. Mais qu’allons-nous bien pouvoir y mettre ? On a mille et une options, dont deux qu’on va explorer ci-dessous.
Option 1 : obtenir un shell avec /bin/bash
L’option la plus simple est probablement de tenter d’obtenir un shell avec le code suivant dans notre script random.py
:
import os
os.system('/bin/bash')
Puis, on peut ensuite exécuter la commande sudo
en spécifiant l’utilisateur Rabbit avec l’option -u
:
sudo -u rabbit /usr/bin/python3.6 /home/alice/walrus_and_the_carpenter.py
Et voilà, le tour est joué ! Comme on peut le voir dans l’invite des commandes et le confirmer en lançant la commande id
ou encore whoami
, on est maintenant Rabbit !
Option 2 : autoriser une nouvelle clef SSH
Une autre option serait d’ajouter notre clef SSH publique dans le fichier des clefs autorisées de l’utilisateur Rabbit. Cela nous permettra de nous connecter à l’utilisateur Rabbit via SSH sans devoir fournir de mot de passe.
Tout d’abord, générons une paire de clefs SSH dans notre machine locale. Nous allons générer des clefs de type RSA puisque touts les systèmes ne sont pas garantis de pouvoir gérer les clefs plus récentes et plus efficaces, de type ed25519.
ssh-keygen -t rsa
Nous avons maintenant une paire de clefs SSH dans notre répertoire ~/.ssh
: une privée qu’on ne va jamais partager, et une publique, qu’on veut mettre chez Rabbit sur Wonderland.
Toujours depuis notre machine locale, copions la clef publique (jamais la clef privée !) vers le répertoire d’Alice sur la machine Wonderland à l’aide de scp
, l’outil de copie sécurisé :
scp ~/.ssh/id_rsa.pub [email protected]:id_rsa.pub
Après avoir saisi le mot de passe d’Alice, le fichier devrait apparaître dans son répertoire /home/alice
. Maintenant que la clef est prête, on va pouvoir exploiter le script en Python pour l’ajouter à /home/rabbit/.ssh/authorized_keys
.
De retour dans la machine Wonderland, créons notre script random.py
dans le même répertoire que le script /home/alice/walrus_and_the_carpenter.py
. On le fera tout d’abord exécuter cette commande :
mkdir -p /home/rabbit/.ssh && cat /home/alice/id_rsa.pub >> /home/rabbit/.ssh/authorized_keys
Avec cette commande, on crée le dossier /home/rabbit/.ssh
au cas où il n’existe pas déjà. Ensuite, on ajoute le contenu de la clef au fichier /home/rabbit/.ssh/authorized_keys
. Si ce fichier n’existe pas, il sera créé.
Mais ce n’est pas tout. Il nous faut lancer une deuxième commande pour gérer les permissions dans le cas où on doit créer le répertoire .ssh
et/ou le fichier authorized_keys
. Pour des raisons de sécurité, SSH rejette la connexion sans mot de passe si le dossier .ssh
a des permissions supérieures à 700 ( rwx------
). De même si le fichier authorized_keys
a des permissions plus importantes que 600 ( rw-------
). La commande sera donc :
chmod 700 /home/rabbit/.ssh && chmod 600 /home/rabbit/.ssh/authorized_keys
Mettons ces deux commandes dans notre script random.py
:
import os
os.system('mkdir -p /home/rabbit/.ssh && cat /home/alice/id_rsa.pub >> /home/rabbit/.ssh/authorized_keys')
os.system('chmod 700 /home/rabbit/.ssh && chmod 600 /home/rabbit/.ssh/authorized_keys')
print('Exploit successful: ssh key injected.')
Enfin, on peut exécuter la commande sudo
en spécifiant l’utilisateur Rabbit avec l’option -u
:
sudo -u rabbit /usr/bin/python3.6 /home/alice/walrus_and_the_carpenter.py
Comme le script a été exécuté en tant que Rabbit et non Alice, et que Rabbit a naturellement les permissions de lecture et d’écriture dans son propre répertoire, notre script ne devrait pas rencontrer d’erreur. Pour s’en assurer, déconnectons-nous de l’utilisateur Alice avec la commande exit
, puis tentons de nous reconnecter à l’utilisateur Rabbit :
Parfait, on ne nous demande pas de mot de passe pour la connexion SSH !
Quoiqu’un peu plus complexe que l’option précédente, cette méthode peut être particulièrement utile puisqu’elle nous crée, de fait, une porte dérobée. Si on perd la connexion à notre machine Wonderland, par exemple, on pourra se reconnecter sans devoir repasser par Alice. Dans les CTFs plus compliqués, laisser ce genre de clef fonctionnera comme une sauvegarde de notre progrès pour ne pas avoir à relancer un exploit en plusieurs étapes, selon la difficulté d’obtention du shell.
Exploitation du programme teaParty
Le seul fichier qu’on trouve dans le répertoire de Rabbit est ce qui semble a première vue être un exécutable nommé teaParty
. Déterminons tout d’abord son type avec l’outil file
et puis lançons-le pour voir ce qu’il fait.
On nous dit que le chapelier fou arrivera probablement l’heure suivant l’heure actuelle. Quand on appuie sur entrée, le programme se termine en un joli segfault. Il est peu probable que le chapelier fou nous laisse entrer même au bout d’une heure comme le suggère le programme !
Le type de ce fichier est extrêmement prometteur, par contre. setuid
, c’est un paramétrage qui autorise un utilisateur à exécuter un processus ou un fichier avec les permissions de son propriétaire. Le propriétaire de teaParty
étant root, ceci est sans aucun doute un vecteur d’attaque. Notons aussi les permissions de ce fichier : rws
et non le plus courant rwx
. La permission s
nous indique déjà même sans avoir lancé la commande file
, que ce fichier est de type setuid
.
Tentons de dé-compiler le programme teaParty
pour voir son code source. On ne sait pas dans quelle langue il a été écrit, mais le binaire devrait pouvoir être retraduit en Assembleur, puis en C. Pour cela, retournons sur notre machine locale pour y copier le fichier teaParty
afin de l’analyser.
scp [email protected]:teaParty .
Pour dé-compiler le programme, nous allons utiliser Ghidra, un programme de rétro-ingénierie des logiciels. Toujours sur notre machine locale, lançons Ghidra et créons un nouveau projet en sélectionnant son menu File > New Project...
. Le projet est vide, mais on peut y importer le programme à analyser en sélectionnant le menu File > Import File...
. Évidemment, on va choisir teaParty
. On peut laisser toutes les options par défaut.
Maintenant, on peut cliquer droit sur teaParty
et sélectionner Open with... > CodeBrowser
:
Ghidra va nous demander si on souhaite analyser le programme, cliquons “yes”. Maintenant, on peut voir le code en langage assembleur, ce qui n’est pas très aisé à lire pour nous les humains… Tentons de traduire la fonction main
en C.
Pour cela, ouvrons la fenêtre dé-compilation si elle ne l’est pas déjà en sélectionnant le menu Window > Decompiler
. Puis, dans le menu Symbol Tree
à gauche de la fenêtre, trouvons la fonction main
. Si l’on clique dessus, le code source en C s’affiche dans la fenêtre décompilation.
Regardons ce code de plus près :
La première chose dont on s’aperçoit c’est que le segfault n’est même pas un vrai segfault ! Et il n’y a pas non plus de compte à rebours, donc aucun espoir que le Hatter arrive à l’heure qu’on nous dit !
Le programme teaParty
définit en premier lieu l’identifiant de l’utilisateur avec setuid
à 0x3eb
en hexadécimal. Une petite conversion nous indique 1003 en décimal. Sur la machine Wonderland, on peut lancer la commande id 1003
pour découvrir qu’il s’agit de Hatter. C’est donc en réalité lui qui va exécuter le reste de ce processus, et non root ou Rabbit.
Remarquons aussi que pour afficher la date et l’heure, teaParty
fait appel au programme date
, mais sans en spécifier le chemin absolu ( /bin/date
). De ce fait, au lieu de pouvoir directement exécuter /bin/date
, le shell va devoir chercher pour le trouver. Il va d’abord consulter la variable PATH de son environnement pour récupérer la liste des répertoires où pourrait se trouver un programme. L’absence de chemin absolu nous permet donc de créer notre propre programme nommé date
qui pourra contenir des commandes malicieuses. Ensuite, il suffira d’ajouter le répertoire dans lequel se trouve notre programme à la variable d’environnement PATH.
On est dans la même situation que chez Alice avec le script en Python : on a un programme vulnérable qui peut être exécuté avec les privilèges d’un autre utilisateur. Un programme qui fait appel à un script externe de manière non sécurisée. Nos options d’exploitation sont donc identiques, au final.
Créons d’abord notre propre script date
, qu’on peut mettre dans le répertoire /tmp
, par exemple.
Option 1 : obtenir un shell avec /bin/bash
Pour obtenir un shell, nous allons simplement mettre la commande suivante dans /tmp/date
:
#!/bin/bash
/bin/bash
Il ne faudra pas oublier de rendre ce script Bash exécutable avec la commande :
chmod +x /tmp/date
Puis, ajoutons le répertoire dans lequel se trouve notre script (ici, /tmp
) au début de la variable d’environnement PATH pour que notre shell puisse le trouver en premier :
export PATH=/tmp:$PATH
Et voilà ! Nous sommes maintenant Hatter. Dans /home/hatter
, nous trouverons un fichier password.txt
qui contient son mot de passe en clair.
Option 2 : autoriser notre clef SSH
Comme précédemment pour accéder à Rabbit, nous pouvons mettre les deux mêmes commandes dans notre /tmp/date
:
#!/bin/bash
mkdir -p /home/hatter/.ssh && cat /home/alice/id_rsa.pub >> /home/hatter/.ssh/authorized_keys
chmod 700 /home/hatter/.ssh && chmod 600 /home/hatter/.ssh/authorized_keys
N’oublions pas de rendre ce script exécutable :
chmod +x /tmp/date
Puis, pour que le shell trouve notre date
avant /bin/date
, ajoutons le répertoire dans lequel se trouve notre script (ici, /tmp
) au début de la variable d’environnement PATH :
export PATH=/tmp:$PATH
Enfin, on va pouvoir exécuter de nouveau le programme teaParty
.
On peut maintenant se déconnecter de Rabbit avec exit
et se reconnecter en tant que Hatter sans avoir à renseigner de mot de passe :
Parfait, nous sommes maintenant Hatter. Son répertoire /home/hatter
contient un fichier password.txt
qui contient son mot de passe, mais avec cette méthode, on n’en aura pas besoin.
LinPEAS : énumération des vulnérabilités
En examinant les fichiers et les privilèges de l’utilisateur Hatter, on ne trouve pas à première vue de vecteurs d’attaque flagrants. Faisons donc appel à un script d’énumération automatique pour nous montrer les possibilités d’escalade de privilèges sur la machine Wonderland.
LinPEAS (“Linux Privilege Escalation Awesome Script”) est facile d’utilisation et nous indique clairement les vulnérabilités qu’il trouve. Il affiche en rouge les configurations suspectes, et les signale en rouge sur jaune lorsqu’elles sont garanties à 99% d’être un vecteur d’escalade de privilèges.
Téléchargeons donc le script sur notre machine locale :
curl -L https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh > linpeas.sh
N’oublions pas de le rendre exécutable :
chmod +x linpeas.sh
Puis, copions ce fichier vers Hatter sur Wonderland. Si on a mis notre clef publique chez Hatter, scp
ne nous demandera pas de mot de passe, mais dans le cas contraire, on devrait avoir trouvé le mot de passe dans son fichier /home/hatter/password.txt
.
scp linpeas.sh [email protected]:linpeas.sh
De retour sur la machine Wonderland, exécutons le script en sauvegardant les résultats dans un fichier linpeas.log
:
./linpeas.sh | tee linpeas.log
Quelque part au milieu de tous ces résultats, on remarquera quelque chose en rouge sur jaune :
Il y a une mauvaise configuration des programmes /usr/bin/perl5.26.1
et /usr/bin/perl
. Comme le teaParty
dans Rabbit, ils ont les capacités setuid
! Voilà quelque chose qu’on va pouvoir exploiter.
Exploitation de la configuration Perl
Quelques recherches sur le Perl et particulièrement l’escalade de privilèges avec Perl nous révèlent la commande à utiliser pour obtenir un shell root :
/usr/bin/perl -e 'use POSIX (setuid); POSIX::setuid(0); exec "/bin/bash";'
Ici, on lance /usr/bin/perl
avec l’option -e
qui nous permet d’ensuite spécifier le bout de code à exécuter. Les instructions qui suivent sont :
use POSIX (setuid);
: on déclare l’utilisation desetuid
,POSIX::setuid(0);
: on indique l’identifiant numérique de l’utilisateur root, 0,exec "/bin/bash";
: on exécute le shell Bash.
Après avoir exécuté la commande, on obtient un shell root ! On peut maintenant aller voir les drapeaux :
# cat ~root/user.txt
# cat ~alice/root.txt
Victoire !
Conclusion du CTF Wonderland
Le CTF Wonderland de TryHackme souligne l’importance des configurations de permissions et la susceptibilité de setuid
dans un système Linux.
L’exploitation de la librairie du script Python pour obtenir le shell de Rabbit n’était possible que par une mauvaise configuration de sudo
qui permettait à Alice d’exécuter ce script en tant que Rabbit. Cela met l’accent sur l’importance de la configuration correcte de sudo
.
Le programme teaParty
était sensible de par sa capacité à définir un identifiant avec setuid
. Et le fait qu’il fasse appel à un programme externe sans spécifier un chemin absolu l’a rendu vulnérable à notre manipulation de son environnement. L’utilisation inappropriée de setuid
pose donc un risque lorsque l’exécutable n’est pas soigneusement conçu.
Enfin, la mauvaise configuration de Perl qui lui donnait la capacité setuid
nous a permis de changer d’identifiant utilisateur comme bon nous semblait. Cette capacité à manipuler l’identifiant de l’utilisateur d’un processus en cours est un risque sécuritaire. Elle ne devrait pas être accordée aux exécutables à la légère.
Merci à NinjaJc01 pour ce défi intéressant et très éducatif !
Une autre astuce à partager, une question à poser, ou une découverte intéressante à propos des vulnérabilités exposées dans Wonderland de TryHackMe ? Je serai ravie de lire et de répondre à chaque commentaire. Bon CTF !
Sources et lectures supplémentaires
- NinjaJc01, Wonderland [tryhackme.com]
- Outils :
- ghidra [ghidra-sre.org]
- gobuster [github.com]
- linPEAS [github.com]
- nmap (1) [man]
- perl (1) [man]
- ping (8) [man]
- scp (1) [man]
- ssh (1) [man]
- steghide [sourceforge.net]
- strings (1) [man]
- Samuel Whang, Privilege Escalation: Hijacking Python Library [medium.com]
- Linux Programmer’s Manual, Capabilities (7) [man]
- Wikipédia, setuid [wikipedia.org]
- The BlackBerry Cylance Threat Research Team, Code Analysis With Ghidra: An Introduction [blogs.blackberry.com]
- Raj Chandel, Linux for Pentester: Perl Privilege Escalation [hackingarticles.in]