Ce cas pratique porte sur un scénario d’investigation classique du forensic pédagogique1 : une clé USB (RHINOUSB.dd) et plusieurs captures réseau (rhino.log, rhino2.log, rhino3.log) sont fournies, avec pour question centrale de savoir si l’une et les autres sont liées, et ce qu’elles révèlent d’un transfert de fichiers suspect. L’intérêt de l’exercice tient moins à la conclusion — assez vite acquise — qu’à la méthode : croiser deux sources indépendantes pour bâtir une preuve robuste plutôt qu’une simple présomption.

Retrouver des photos que l’outil automatique a manquées

Le point de départ est déroutant : les traces réseau, une fois les objets exportés, livrent plusieurs images (rhino1.jpg, rhino3.jpg et son doublon, rhino4.jpg, rhino5.gif), plus une archive contraband.zip contenant rhino2.jpg, protégée par un mot de passe alors inconnu. Sur la clé USB, en revanche, PhotoRec ne détecte aucune image — alors qu’un examen manuel confirme leur présence.

Ce désaccord entre l’outil et la réalité est le cœur méthodologique de l’exercice : un outil de carving automatique repose sur des heuristiques (structure de répertoire, continuité des blocs) qui échouent lorsque le layout du support est atypique. Il ne faut jamais prendre l’absence de résultat d’un outil pour une absence de données.

Retrouver les JPEG à la main

La parade consiste à revenir aux signatures binaires elles-mêmes plutôt qu’à la structure de fichiers. Le format JPEG/JFIF s’ouvre par un en-tête caractéristique et se referme par le marqueur FF D9 ; il suffit de les chercher directement dans l’image brute.

Un premier repérage grossier localise les occurrences de la chaîne JFIF :

rg --text "JFIF" work/RHINOUSB.dd

Le carving proprement dit automatise ensuite l’extraction, en bornant chaque image entre son en-tête et son marqueur de fin :

from pathlib import Path
import re
 
data = Path('work/RHINOUSB.dd').read_bytes()
start_pattern = re.compile(b'\xff\xd8\xff\xe0\x00\x10JFIF')
end = b'\xff\xd9'
 
for i, m in enumerate(start_pattern.finditer(data), 1):
    s = m.start()
    e = data.find(end, s)
    if e == -1:
        continue
    e += len(end)
    out = Path(f'output/carved_{i}.jpg')
    out.write_bytes(data[s:e])
    print(i, hex(s), hex(e), out, len(out.read_bytes()))

Sept images en ressortent, dont une de 230 665 octets — une taille qui, on le verra, n’est pas anodine. identify permet d’en vérifier rapidement les dimensions. Autopsy, interrogé en parallèle, retrouve les mêmes fichiers par la même logique de signatures : la confirmation par un second outil, indépendant du script maison, renforce la fiabilité du résultat.

Reconstituer le transfert réseau

En parallèle, l’examen des captures réseau avec tshark reconstitue qui a parlé à qui, et de quoi il retourne.

tshark -r work/rhino.log --export-objects ftp-data,output/ftp_objects
tshark -r work/rhino2.log --export-objects ftp-data,output/ftp_objects
tshark -r work/rhino2.log --export-objects http,output/ftp_objects
tshark -r work/rhino3.log --export-objects ftp-data,output/ftp_objects
tshark -r work/rhino3.log --export-objects http,output/ftp_objects

Le client FTP (137.30.122.253) s’adresse au serveur 137.30.120.40. En filtrant les commandes d’authentification, les identifiants employés apparaissent en clair — rappel, s’il en fallait un, de ce que coûte un FTP non chiffré :

tshark -r work/rhino.log -Y 'ftp.request.command == "PASS"' \
  -T fields -e frame.number -e ftp.request.arg

L’utilisateur gnome s’authentifie avec le mot de passe gnome123. Filtrer ensuite les commandes de transfert révèle l’essentiel de l’activité : STOR rhino1.jpg, STOR rhino3.jpg (à deux reprises), STOR contraband.zip côté FTP, et côté HTTP, le téléchargement de rhino4.jpg, rhino5.gif et, plus inquiétant, d’un exécutable rhino.exe.

Faire tenir la corrélation

Reste la question qui donne son sens à tout l’exercice : la clé USB et les traces réseau désignent-elles le même épisode ? La réponse ne repose pas sur une intuition thématique — « ce sont les mêmes photos de rhinocéros » — mais sur une mesure précise : le fichier carved_6.jpg, extrait par carving de la clé USB, pèse exactement 230 665 octets, soit la taille exacte de rhino2.jpg attendu dans contraband.zip côté réseau.

Une correspondance de taille binaire, sur un fichier compressé dont le contenu n’a jamais été comparé directement (le mot de passe de l’archive restant inconnu), constitue une preuve bien plus solide qu’une ressemblance visuelle : deux JPEG de contenus différents n’ont, sauf coïncidence extraordinaire, aucune raison de faire exactement le même nombre d’octets.

La clé USB livre par ailleurs deux fichiers texte récupérables directement (gumbo1.txt, gumbo2.txt, via tsk_recover), qui confirment qu’elle n’est pas un support « vide » malgré l’échec initial de PhotoRec.

Ce que l’exercice enseigne

Trois leçons méritent d’être retenues au-delà du cas particulier. D’abord, ne jamais s’arrêter au verdict d’un seul outil automatique — le carving par signature, effectué à la main ou via Autopsy, a retrouvé ce que PhotoRec avait manqué. Ensuite, privilégier les preuves mesurables (taille, hash) aux ressemblances qualitatives pour établir une corrélation entre deux sources. Enfin, une investigation forensic gagne toujours à croiser plusieurs sources indépendantes — ici le support physique et la capture réseau — car c’est leur convergence, plus que chacune isolément, qui emporte la conviction.

Footnotes

  1. Scénario inspiré du jeu de données pédagogique « Rhino Hunt », largement utilisé en formation à l’investigation numérique.