OverTheWire: Bandit – 0 à 8

Cela fait un moment que je n’ai pas écrit d’article sur la sécurité informatique, et je suis récemment retombé sur les wargames d’OverTheWire.

Ces derniers consistent en une série de niveaux, de difficulté croissante. On se connecte généralement en SSH avec le premier utilisateur dont le mot de passe est donné, et, de faille en faille, on monte les échelons et change d’utilisateur jusqu’à la fin.

Bandit est le plus simple des wargames proposés par OverTheWire. Si l’aspect sécurité y est relativement peu présent, on peut néanmoins y apprendre énormément sur les systèmes UNIX et les différentes commandes de bases. Une description est fournie pour chaque niveau. La lecture de ces dernières est quasi indispensable pour la majorité des exercices.

Je vous recommande d’essayer de le résoudre par vous-même avant de regarder mes solutions : il y a souvent plusieurs façons de finir un niveau et votre méthode sera peut-être différente de la mienne !

Cet article est le premier d’une série de quatre :

Niveau 0

On se connecte au premier niveau avec l’utilisateur bandit0 et le mot de passe bandit0 :

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

On m’explique dans la description du niveau que le mot de passe pour le niveau suivant est dans un fichier appelé readme.

En premier lieu, j’utilise ls pour lister les fichiers présents dans le dossier courant. Après avoir confirmé la présence du fichier readme, j’utilise cat pour en lire le contenu :

bandit0@bandit:~$ ls
readme

bandit0@bandit:~$ cat readme
[[PASSWORD]]

Pour fermer la session SSH et passer au niveau suivant, j’utilise exit :

bandit0@bandit:~$ exit
$

Niveau 1

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

La description du niveau m’indique que le prochain mot de passe est dans un fichier dont le nom n’est composé que d’un trait d’union : -. En effet :

bandit1@bandit:~$ ls -l
-

Si j’essaye de lire ce fichier avec cat -, je constate non seulement que rien ne s’affiche, mais qu’en plus le programme ne se ferme pas et que j’ai besoin d’utiliser ^C pour en sortir.

^C (Ctrl+C) représente le caractère de contrôle End of Text (ETX). Il est interprété comme une commande d’arrêt par votre terminal qui se chargera d’envoyer un signal d’interruption (SIGINT) au processus en cours – ici cat. C’est en réponse à ce signal que cat se termine.

Pourquoi ce comportement ? Tout d’abord, il faut comprendre qu’une grande partie des outils de base Linux – les coreutils, dont cat fait partie – adhèrent aux normes POSIX, un ensemble de normes qui vise depuis 1988 à standardiser la façon d’intéragir avec les programmes informatiques.

Il se trouve que POSIX spécifie qu’il convient d’utiliser l’entrée standard (stdin) lorsqu’un programme attend un nom de fichier en argument et qu’on lui passe un trait d’union -. En clair, cela signifie que lorsque je lance cat -, le programme m’affichera le texte que je tape.

Pour afficher ce fichier, il faut spécifier le nom - d’une autre façon. Je peux par exemple utiliser le chemin complet de ce fichier :

bandit1@bandit:~$ cat /home/bandit1/-
[[PASSWORD]]

Une autre solution serait d’utiliser ./- ; le point . est utilisé pour dénommer le dossier courant, ici /home/bandit1.

Niveau 2

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

Je vous passe les détails, ici le but consiste à lire un fichier dont le nom comporte des espaces – j’utilise l’option -1 ici pour être sûr de n’afficher qu’un fichier par ligne :

bandit2@bandit:~$ ls -1
spaces in this filename
bandit2@bandit:~$ cat spaces in this filename
cat: spaces: No such file or directory
cat: in: No such file or directory
cat: this: No such file or directory
cat: filename: No such file or directory

L’espace entre des mots est interprétée par notre shell – ici Bash – comme une séparation entre des arguments. Il exécute cat en lui passant les quatre arguments spaces, in, this, et filename, ce qui en pratique lui demande de concaténer les quatre fichiers spaces, in, this, et filename.

Il existe divers moyens de forcer Bash à ne pas interpréter les espaces. Je peux soit passer le nom du fichier entre des guillemets, soit échapper chaque espace avec un antislash \ :

bandit2@bandit:~$ cat "spaces in this filename"
[[PASSWORD]]

bandit2@bandit:~$ cat spaces\ in\ this\ filename
[[PASSWORD]]

Niveau 3

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

Ici, le fichier contenant le mot de passe est dans le dossier inhere. Cependant, ce dernier semble vide :

bandit3@bandit:~$ ls inhere/

En réalité il n’en est rien. En passant l’option -a à ls, je peux forcer l’affichage d’un fichier appelé .hidden :

bandit3@bandit:~$ ls -a inhere/
.  ..  .hidden

Une convention UNIX est de considérer comme cachés les fichiers dont le nom commence par un point .. Ils n’apparaissent pas par défaut dans les résultats de ls. Notez également l’affichage des fichiers . et .. dans les résultats, qui représentent respectivement le dossier courant – comme je vous l’ai montré dans le niveau 1 – et le dossier parent.

Je peux ensuite afficher la solution avec cat :

bandit3@bandit:~$ cat inhere/.hidden
[[PASSWORD]]

Niveau 4

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

Dans le niveau 4, je retrouve un dossier inhere qui cette fois contient dix fichiers :

bandit4@bandit:~$ ls -1 inhere/
-file00
-file01
-file02
-file03
-file04
-file05
-file06
-file07
-file08
-file09

J’essaye d’afficher le contenu du premier fichier :

bandit4@bandit:~$ cat inhere/-file00
/`2ғ%rL~5g

La description du niveau m’indique que le mot de passe est contenu dans le seul fichier directement lisible. Je peux soit essayer de lire un à un tous les fichiers jusqu’à trouver le bon, soit en profiter pour vous introduire le programme file qui sert à déterminer le type d’un fichier en fonction de son contenu :

bandit4@bandit:~$ file inhere/*
inhere/-file00: data
inhere/-file01: data
inhere/-file02: data
inhere/-file03: data
inhere/-file04: data
inhere/-file05: data
inhere/-file06: data
inhere/-file07: ASCII text
inhere/-file08: data
inhere/-file09: data

L’astérisque * utilisée dans inhere/* est décomposée par Bash pour représenter les différents fichiers non cachés dans le dossier inhere. Les arguments effectivement reçus par file sont donc inhere/-file00, inhere/-file01, etc.

Je remarque qu’un seul fichier – -file07 – est lisible, les autres étant de type data, valeur retournée par file lorsque le fichier contient des données binaires, non lisibles ou non identifiée.

bandit4@bandit:~$ cat inhere/-file07
[[PASSWORD]]

Niveau 5

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

Je rencontre une fois de plus un dossier inhere qui cette fois contient multitude d’autres sous-dossiers. Le but est de retrouver dans toute cette hiérarchie un fichier avec les caractéristiques suivantes :

  • il doit être lisible par un humain – comprendre « contenir des données affichables » ;
  • sa taille doit faire très exactement 1033 octets ;
  • il ne doit pas être exécutable.

Le programme find peut être utilisé pour fouiller récursivement une arborescence de fichiers. Moultes options sont possibles pour filtrer les résultats, et je me sers des fonctionnalités fournies par -size et -executable :

bandit5@bandit:~$ find inhere/ -size 1033c -not -executable
inhere/maybehere07/.file2

Décomposons cet appel :

  • find : le nom du programme ;
  • inhere/ : le dossier que je veux explorer ;
  • -size 1033c : le filtre -size, auquel je passe l’argument 1033c – le c est l’unité et signifie ici « octets » ;
  • -not -executable : deux filtres, -not pour inverser le filtre suivant et -executable pour normalement n’afficher que les fichiers exécutables.

Il se trouve qu’un seul fichier correspond à ces deux conditions, et je n’ai pas besoin d’ajouter une vérification pour la première caractéristique « lisible par un humain ». Ce filtre n’est en outre pas proposé directement par find.

Je me permets d’ouvrir une parenthèse ici. Vous pouvez si vous le souhaitez passer directement au niveau suivant.

J’aurais pu utiliser une combinaison du filtre -exec et de grep pour plus de précision :

bandit5@bandit:~$ find inhere/ \
  -size 1033c \
  -not -executable \
  -exec find {} + | grep text
inhere/maybehere07/.file2: ASCII text, with very long lines

Décomposons une nouvelle fois.

-exec permet d’exécuter un programme en fonction des résultats. Lorsque la commande suivant -exec se termine par ;, cette commande sera exécutée pour chaque résultat, le substitut {} étant remplacé par le nom du fichier trouvé. En revanche, lorsque la commande se termine par + comme ici, elle est exécutée une fois pour tous les résultats, passés sous la forme de plusieurs arguments.

Prenons un exemple. Étant donné l’arborescence suivante :

$ ls -1 example
file00
file01

$ cat example/file00
3
5
1

$ cat example/file01
2
8
0

Avec ; – que je dois échapper avec un antislash \ – le programme sort est exécuté pour chacun des fichiers :

$ find example -type f -exec sort {} \;
1
3
5
0
2
8

Avec +, sort n’est exécuté qu’une fois :

$ find example -type f -exec sort {} +
0
1
2
3
5
8

Poursuivons : le pipe | est utilisé pour passer la sortie de find à grep. grep est un programme qui permet de filtrer un fichier en n’affichant que les lignes qui correspondent à un motif : grep text filtre la sortie de find et n’affiche que les lignes contenant text.

Enfin, j’affiche le contenu de inhere/maybehere07/.file2 :

bandit5@bandit:~$ cat inhere/maybehere07/.file2
[[PASSWORD]]

Niveau 6

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

Ce niveau est assez similaire au précédent. Je dois trouver un fichier avec les propriétés suivantes :

  • appartient à l’utilisateur bandit7 ;
  • appartient au groupe bandit6 ;
  • a une taille d’exactement 33 octets.

Sous Linux, un fichier appartient à un utilisateur et à un groupe. Vous pouvez afficher ces permissions avec l’argument -l de ls. Par exemple, dans le dossier /home/bandit1, on retrouve le fameux fichier - qui appartient à l’utilisateur bandit2 et au groupe bandit1.

bandit6@bandit:~$ ls -l /home/bandit1
total 4
-rw-r----- 1 bandit2 bandit1 33 May  7 20:14 -

J’utilise une fois de plus find avec les options idoines :

bandit6@bandit:~$ find / \
  -type f \
  -user bandit7 \
  -group bandit6 \
  -size 33c 2> /dev/null
/var/lib/dpkg/info/bandit7.password

Certains dossiers système m’étant interdits, j’utilise 2> /dev/null pour rediriger la sortie d’erreurs vers /dev/null et ainsi masquer les erreurs de permissions afin de n’afficher que les résultats utiles.

bandit6@bandit:~$ cat /var/lib/dpkg/info/bandit7.password
[[PASSWORD]]

Niveau 7

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

Je dois maintenant trouver le mot de passe du huitième niveau, qui se trouve dans le fichier data.txt à côté du mot millionth. Inspectons tout d’abord ce fichier :

bandit7@bandit:~$ du -sh data.txt
4.0M    data.txt

bandit7@bandit:~$ head data.txt
binning WnfnFPqkuhl2nwHBohzn2C4L5W0gwcLq
abuts   v8PAwDdkGDdp5NsJ7ZFM5A7TJ5MkYDbm
fathead wBhCy0fqvbQdexz5kMKBtGoSWgXw7s0H
attacks 3GzwnGiZnBDdVuHivJk1pEfOOYu7uOTa
lopping H9hzviFp1QO4WF8EzcQNl5MDz5r1bzUC
tyrannosaurus   WxtYXVar4sgInHp7YUpTzOjdUw1Ww0x8
reservists      QDidoX6BN1MDTi0QwA6Vt82L9Rb64cm3
atrophy's       mSpCwP9VgcGRn1SCD8R9bb9cPBl2yqkW
bolt's  726RB3lt2RmeCtbWEQ8lhUAxVBJfepy0
Klondikes       wVh3ILxQAsKg8WNnFHp8GxtnSu213GbR

head est un programme très utile pour avoir un aperçu d’un fichier, il en affiche par défaut les dix premières lignes.

data.txt est un fichier de quatre mégaoctets contenant pour chaque ligne un mot aléatoire et un mot de passe.

Je peux une fois de plus utiliser grep pour n’afficher que la ligne contenant millionth :

bandit7@bandit:~$ grep millionth data.txt
millionth       [[PASSWORD]]

Niveau 8

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

Derechef face à un fichier data.txt, le but est de trouver une aiguille dans une botte de foin. Plus précisément, il s’agit de trouver la seule ligne qui n’est pas en double dans ce fichier.

Je peux utiliser uniq, qui permet de n’afficher que les lignes non dupliquées grâce à l’option -u. Cependant, cela ne fonctionne que si ces lignes sont triées. J’utilise alors sort pour cela :

bandit8@bandit:~$ sort data.txt | uniq -u
[[PASSWORD]]

… et ensuite ?

Vous retrouverez les solutions des niveaux 9 à 17 dans l’article suivant.