Sitemap
rico9

rico9

Find me | Challenge Forensic Hard du HACKDAY

9 min readJan 26, 2026

--

Intitulé :

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

My little brother has been playing with the family computer again. I don’t know what he did, but he opened a black window with lots of green text and started banging away at the keyboard, saying he was “coding a video game”.

Now I can’t find the files anywhere.

— — — — — — — — — — — — — — — — — — — — — — — — — — — — — —

1. Préparation et conversion de l’image

Pour commencer, nous avions un fichier image.qcow2. Qcow est un format d’image disque utilisé par QEMU (logiciel de machine virtuelle). Cela signifie QEMU Copy On Write et c’est un format de fichier image disque qui a pour but d’optimiser la taille occupée par le fichier sur le disque.

Les données sont donc compressées, il faut les convertir avec l’utilitaire qemu-img pour en obtenir un fichier raw (on passe alors d’un fichier de 2 Go à 20 Go). L’avantage de cette conversion c’est qu’on obtient une copie bit à bit, utile pour faire une analyse plus précise.

qemu-img convert -f qcow2 -O raw image.qcow2 image_extraite.raw -p

2. Analyse initiale avec Autopsy

Ensuite, on peut faire ingérer ce fichier raw à Autopsy afin de naviguer dedans plus facilement.

On peut voir qu’on a affaire à un système Linux et ses 4 partitions :

Différentes partitions du disque

Dans le volume 1, on retrouve le MBR (pour Master Boot Record). On y trouve notamment la table des partitions et le bootloader. Il n’y a pas de système de fichiers ici, d’où le “Unallocated”.

Dans le volume 2 on retrouve la partition principale avec l’OS et les fichiers utilisateurs.

Dans le volume 3 on retrouve la Swap. C’est une extension de la mémoire RAM sur le disque. Typiquement, quand la RAM est pleine, Linux déplace des morceaux de données ici.

Dans le dernier volume on retrouve l’espace vide à la fin du disque.

J’ai commencé à fouiller dans l’OS par le dossier utilisateur, dossier dans lequel j’ai trouvé une première trace intéressante :

Press enter or click to view image in full size

J’ai également vérifié le bash_history qui était complètement vide. Pourtant, l’énoncé mentionne :

“ but he opened a black window with lots of green text and started banging away at the keyboard”

Il devrait donc y avoir des traces de ces commandes.

A partir de là, j’ai fouillé un peu partout, mais je n’ai rien trouvé. Du moins, rien de vraiment louche.

Cependant, en analysant le disque sur Autopsy, nous pouvons accéder au File Slack de .bash_history :

Press enter or click to view image in full size

Le File Slack qu’on observe est l’espace vide qui reste à la fin du bloc alloué au fichier. Le disque écrit par blocs de taille fixe (souvent 4096 octets).

Comparaison entre la taille logique, physique et l’espace Slack (https://www.open.edu/openlearn/digital-computing/introducing-computing-and-it/content-section-5.4)

Si un fichier (comme le nouveau .bash_history vide) ne fait que 10 octets, le système lui alloue quand même un bloc entier de 4096 octets. Les 4086 octets restants dans ce bloc ne sont pas effacés, ils conservent ce qui était écrit sur le disque avant.

Dans notre cas, Autopsy a récupéré ce Slack Space et les fragments de la session précédente qui n’ont pas été écrasés par le nouveau fichier vide.
On comprend ici que l’attaquant a volontairement écrasé l’historique (via la commande echo "" > .bash_history). Cependant, la majorité des anciennes données est restée physiquement sur le disque, bien qu’elle ne soit plus liée au nom du fichier.

Puisque le fichier visible est vide mais que l’activité a eu lieu, nous devons chercher des traces de l’ancien fichier historique qui aurait été supprimé.

3. Fonctionnement des inodes

Sur Linux un fichier est divisé en deux parties :

  • 1) Son nom
    C’est une entrée dans un répertoire. Les répertoires sont des tables à deux colonnes, où la colonne 1 est le nom du fichier et la colonne 2 est le numéro d’inode.
    Par exemple, dans votre répertoire “Bureau”, vous avez 2 fichiers nommés Test1 et Test2.
    Test1 possède le numéro d’inode 1234 et Test2 possède le numéro d’inode 6789.
    Pour connaître le numéro d’inode d’un fichier il suffit de faire :
    “ls -i Test1”
  • 2) Son numéro d’inode
    Le numéro d’inode pointe vers la table d’inode. L’inode contient les métadonnées d’un fichier, telles que la propriété, les autorisations d’accès, les heures d’accès, de modification et de changement, etc. Plus important encore, l’inode contient les informations permettant de trouver le fichier sur le disque.
Fonctionnement de la table d’inode (https://www.formatux.fr/formatux-fondamentaux/module-040-systeme-de-fichiers/index.html)

Une autre information importante est le nombre de liens, car il est possible de pointer vers l’inode à partir de plusieurs endroits (on appelle ça des hard link). Si vous supprimez un fichier en utilisant rm, alors seule l'entrée du répertoire est supprimée et le nombre de liens de l'inode est décrémenté. Ce n'est que s'il atteint 0 que l'espace disque associé à l'inode est libéré pour une réutilisation ultérieure.

Reprenons notre exemple du répertoire “Bureau” qui contient les fichiers Test1 et Test2. Le compteur de liens pour chacun de ces fichiers est de 1.
Maintenant, si on fait la commande ln (pour link, qui sert à créer des liens entre des fichiers), on donne alors un autre nom à un même contenu physique sur le disque (un même inode) :

ln Test1 Test3

Ainsi, le fichier Test3 aura aussi l’inode 1234 et le nombre de hard links pour l’inode 1234 passera à 2.

Maintenant que nous avons compris qu’il y avait une vraie volonté de masquer l’historique de commande, on peut aller vérifier manuellement si un inode orphelin qui contiendrait notre historique existe. Des inodes orphelins sont la preuve qu’un fichier a été supprimé mais que le contenu sur le disque est toujours là.

Get rico9’s stories in your inbox

Join Medium for free to get updates from this writer.

Ainsi, si notre utilisateur a supprimé un fichier, il n’apparaît plus dans le répertoire mais son contenu lui, est potentiellement toujours présent sur le disque.

4. Investigation bas niveau avec Fsck et losetup

La partition Linux commence au secteur 2048 (comme nous l’avons vu dans le screenshot présentant les différentes partitions). Comme chaque secteur fait 512 octets, l’offset est égal à 2048 * 512 = 1 048 576 octets.

Pour aller vérifier ces informations il faut d’abord monter notre fichier raw comme un périphérique de type bloc, pour ça, il existe l’outil losetup.

Pour monter notre fichier, il faut spécifier :

  • -f -> Demande à losetup de chercher automatiquement le premier périphérique de boucle libre (par exemple /dev/loop0 ou /dev/loop1)
  • -P -> Force la lecture de la table des partitions à l’intérieur du fichier image
  • — show -> Affiche le nom du périphérique qui a été assigné
  • -o -> Définit le point de départ (l’offset) en octets
sudo losetup -fP --show -o 1048576 image_extraite.raw

Ensuite, on va pouvoir utiliser l’outil fsck (File System Consistency Check) pour vérifier et réparer le système de fichiers.

La commande à utiliser est la suivante :

sudo fsck -n /dev/loop0

L’option -n permet de dire à fsck d’effectuer l’analyse en mode lecture seule et donc d’afficher toutes les anomalies qu’il détecte sans les corriger. C’est primordial pour préserver l’intégrité des preuves.

Voici le retour de la commande :

└─# sudo fsck -n /dev/loop0
fsck from util-linux 2.38
e2fsck 1.46.5 (30-Dec-2021)
/dev/loop0 was not cleanly unmounted, check forced.
Pass 1: Checking inodes, blocks, and sizes
Inode 61789 has EXTENTS_FL flag set on filesystem without extents support.
Clear? no

Inode 61789, i_blocks is 104, should be 0. Fix? no

Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Unattached inode 61789

On observe une chose très intéressante : l’inode 61789 est orphelin, on a donc un fichier perdu qui occupe de l’espace mais n’est accessible nulle part dans l’arborescence.

5. Principe d’extent tree et utilisation de Debugfs pour corriger l’inode 61789

Pour visualiser le contenu de l’espace occupé par ce fichier supprimé on peut utiliser debugfs avec cat <numéro_d_inode> :

└─# sudo debugfs /dev/loop0                                                                    

debugfs 1.46.5 (30-Dec-2021)

debugfs: cat <61789>

cat: Corrupt extent header while reading ext2 file

Pourquoi avons-nous cette erreur et qu’est-ce qu’un extent header ?

Les systèmes de fichier ext4 utilisent généralement un extent tree. Le principe est le suivant :

  • Nous avons vu que chaque inode contenait les informations permettant de retrouver où les données d’un fichier étaient stockées sur le disque. Cette partie est gérée via les extents.
  • Dans l’inode on retrouve une structure qui contient le premier extent header et les extent index. L’inode c’est un peu comme la racine de notre arbre.
  • Chaque extent header contient diverses informations mais celle la plus importante est le “eh_depth”. Elle sert à indiquer le niveau de profondeur avant d’atteindre une feuille. Chaque feuille contient un extent qui indique les blocs de données sur le disque.
  • Les extent index pointent toujours vers un bloc sur le disque qui contient lui-même un extent header.
  • Ainsi, même si un fichier est fragmenté sur le disque, l’organisation des extents sous forme d’arbre permet au système de fichiers de localiser les blocs physiques correspondant aux blocs logiques du fichier et de les lire dans le bon ordre.
Schéma du fonctionnement de l’extent tree (https://www.datarecoverytools.co.uk/2009/11/16/learn-more-about-ext4/)

Dans notre cas, fsck a relevé le fait que l’inode 61789 était marqué comme utilisant des extents mais que le système de fichiers lui, ne les supporte pas :

Inode 61789 has EXTENTS_FL flag set on filesystem without extents support.

Il faut donc réparer cet inode pour qu’il n’utilise plus les extents. Pour ça, on lance debugfs dans cet ordre :

  • On active le mode écriture avec -w
  • On modifie l’inode (modify_inode <numéro_d_inode>)
  • On fixe 0 au champ “File flags” (qui valait anciennement 0x80000 pour EXTENTS_FL)
┌──(root㉿kali)-[/media/sf_find_me_files]
└─# sudo debugfs -w /dev/loop0
debugfs 1.46.5 (30-Dec-2021)
debugfs: modify_inode <61789>
Mode [0100644]
User ID [0]
Group ID [1000]
Size [4096]
Creation time [0]
Modification time [0]
Access time [0]
Deletion time [0]
Link count [1]
Block count high [0]
Block count [104]
File flags [0x80000] 0
Generation [0x0]
File acl [0]
High 32bits of size [0]
Fragment address [0]
Direct Block #0 [27265]
Direct Block #1 [26044]
Direct Block #2 [12514]
Direct Block #3 [25755]
Direct Block #4 [36379]
Direct Block #5 [23855]
Direct Block #6 [44988]
Direct Block #7 [26898]
Direct Block #8 [0]
Direct Block #9 [0]
Direct Block #10 [0]
Direct Block #11 [0]
Indirect Block [73555]
Double Indirect Block [0]
Triple Indirect Block [0]

Une fois le flag mis à 0, debugfs ne cherchera plus de “header d’extent” et essaiera de lire les blocs comme un fichier classique.

Il suffit maintenant de lire les blocs de l’inode orphelin avec cette commande :

debugfs:  cat <61789>

HACKDAY{

Dernier petit problème : Le système s’arrête de lire après 4096 octets, mais il nous manque les données qui suivent pour avoir le flag en entier. Pour régler ce problème on peut fixer la taille de l’inode avec debugfs. Il suffit d’augmenter cette valeur :

debugfs:  modify_inode <61789>
Mode [0100644]
User ID [0]
Group ID [1000]
Size [4096] 10000
Creation time [0]
Modification time [0]
Access time [0]
Deletion time [0]
Link count [1]
Block count high [0]
Block count [104]
File flags [0x0]
Generation [0x0]
File acl [0]
High 32bits of size [0]
Fragment address [0]
Direct Block #0 [27265]
Direct Block #1 [26044]
Direct Block #2 [12514]
Direct Block #3 [25755]
Direct Block #4 [36379]
Direct Block #5 [23855]
Direct Block #6 [44988]
Direct Block #7 [26898]
Direct Block #8 [0]
Direct Block #9 [0]
Direct Block #10 [0]
Direct Block #11 [0]
Indirect Block [73555]
Double Indirect Block [0]
Triple Indirect Block [0]

Puis d’afficher à nouveau les données sur le disque :

debugfs:  cat <61789>
HACKDAY{bcd0d684805c5d5024aeb5b069bc51aa4971dcfbc64551ef599c73b05d016f90}

Aymeric Foulie

--

--