Récupérer des preuves de fichiers supprimés avec le USN journal

Quand un fichier a été supprimé, sorti de la corbeille et que l'entrée MFT a été recyclée — le USN journal a souvent encore son nom, son parent et sa timeline. Un guide pour extraire ces preuves.

Par 8 min de lecture

La question la plus courante que les analystes forensiques reçoivent des parties prenantes non techniques est une variante de « peux-tu prouver que ce fichier a existé ? ». Quand le fichier a été supprimé du disque, la corbeille vidée, et qu'assez de temps a passé pour que l'entrée $MFT ait été réutilisée pour autre chose, la réponse dépend presque toujours de ce que le USN journal se rappelle encore. Le journal est, selon mon expérience, le témoin le plus durable pour « ce fichier a-t-il jamais existé » une fois que tout le reste a été effacé.

Ce billet passe en revue ce que le journal préserve des fichiers supprimés, comment extraire ces preuves, et les cas où le journal est ta dernière ligne.

Ce que le journal garde après suppression

Quand un fichier est supprimé sur NTFS, trois choses se passent :

  1. L'entrée $FILE_NAME dans le répertoire parent est délinkée. L'INDEX_ALLOCATION du répertoire est réécrit sans elle.
  2. L'entrée MFT est marquée inutilisée mais pas mise à zéro. Le prochain fichier alloué à cette entrée écrase les data runs, attributs et timestamps.
  3. Le USN journal reçoit un enregistrement FileDelete | Close nommant le fichier, sa référence parent, son numéro et séquence d'entrée MFT, et le timestamp de suppression.

L'enregistrement journal est le plus résilient des trois. L'entrée de répertoire disparaît dès que le parent est réécrit — parfois en secondes sur un volume actif. L'entrée MFT peut être réutilisée dès qu'un nouveau fichier est créé — parfois en minutes. L'enregistrement journal reste dans un ring buffer qui ne boucle qu'après des jours ou des semaines.

Pour les fichiers supprimés il y a des jours, le journal te donne typiquement :

  • Le nom de fichier comme feuille UTF-16 LE. Juste la feuille, pas de chemin.
  • Le numéro de référence MFT du répertoire parent. Combiné à une $MFT parsée il se résout en chemin.
  • Le timestamp de suppression en FILETIME UTC.
  • Le numéro et la séquence d'entrée MFT que le fichier occupait. Utile pour croiser des artefacts carvés et des images avant de $LogFile.
  • Une trace de cycle de vie. Les enregistrements FileCreate, DataExtend, BasicInfoChange et rename sur la même FileReferenceNumber te disent quand le fichier a été créé, comment il a été écrit et si ses timestamps ont été modifiés avant suppression.

Un tuple à quatre champs — référence MFT, nom de fichier, référence parent, FILETIME de suppression — plus un événement de création de plus tôt dans le même journal suffit en général à satisfaire un legal hold ou à ancrer une enquête plus profonde. La plupart du temps tu obtiens aussi l'enregistrement FileCreate du même $J, ce qui te donne l'âge du fichier et un timestamp de création indépendant de $STANDARD_INFORMATION.

Un workflow de récupération minimal

Avec un journal parsé et une $MFT parsée (voir le guide d'extraction pour les deux) :

  1. Filtrer par FileDelete dans la fenêtre temporelle d'intérêt. Trier par timestamp. C'est ta liste maîtresse de suppressions à trier.
  2. Grouper par FileReferenceNumber pour toute suppression qui compte. Tu obtiens l'historique complet du fichier : création, écritures, paires de rename, changements d'attribut et finalement la suppression.
  3. Résoudre le chemin parent via $MFT. Le parser sur cette page le fait automatiquement quand les deux fichiers sont fournis. Sans $MFT tu t'arrêtes au nom feuille.
  4. Croiser $LogFile si la suppression est très récente. Il peut encore contenir l'image « avant » de l'entrée de répertoire, donnant un second témoin indépendant de l'existence du fichier.

La combinaison « référence MFT, nom de fichier, chemin parent, timestamp de création, timestamp de suppression, trace de cycle de vie complète » suffit habituellement à écrire un rapport défendable.

Quand le chemin parent ne peut pas être résolu

Parfois l'entrée MFT parent a elle-même été recyclée au moment où tu regardes — courant sur les hôtes à filesystem chargé, en particulier les serveurs de fichiers et les runners CI. Le journal te donne toujours le nom de fichier et le numéro de référence parent, mais $MFT ne sait plus à quel dossier cette référence pointait.

Trois plans B débloquent généralement le chemin :

Des enregistrements RenameNewName antérieurs sur la même référence parent. Groupe tous les enregistrements journal par référence parent. Tout RenameNewName antérieur dont le nouveau parent correspond à ta référence non résolue, et dont le propre chemin se résout, te dit quel dossier cette référence était.

Entrées d'index de $LogFile. Le log de récupération de crash contient des images avant des mises à jour d'INDEX_ALLOCATION. libfsntfs de Joachim Metz et LogFileParser de Jonas Schicht les extraient. Sur une suppression assez récente, l'image avant de l'entrée de répertoire est encore dans $LogFile et se résout en chemin.

Fichiers frères dans la même référence parent. Groupe tous les enregistrements journal par référence parent. Si l'un des frères a été renommé dans un répertoire dont l'entrée MFT est encore valide, pivote via lui : l'ancien parent au moment du rename est ton dossier inconnu.

Quand aucun ne marche, le parser fera émerger le fichier comme « nom : notes.docx, parent : 1234-5 » sans chemin résolu. C'est intentionnel. Inventer un chemin que tu ne peux pas vérifier est pire qu'admettre qu'il en manque un.

« Effacé » ne veut pas dire parti

Les outils de shredding — secure delete de CCleaner, Eraser, BleachBit, sdelete — écrasent le contenu du fichier puis le suppriment. Ils laissent une trace journal plus bruyante, pas plus silencieuse :

  • Plusieurs enregistrements DataOverwrite | Close sur le fichier avant son FileDelete | Close indiquent un écrasement de contenu intentionnel. Une suppression normale ne produit pas d'écrasement.
  • Un FileCreate plus DataOverwrite plus FileDelete sur le même fichier en quelques secondes est la signature canonique de destruction de preuves.
  • Là où le shredder a aussi essuyé l'espace libre en écrivant puis supprimant des fichiers de remplissage, ces fichiers de remplissage ont leurs propres séquences create/extend/overwrite/delete — un essaim d'enregistrements autour du moment de la destruction.

Le mapping MITRE ATT&CK est T1485 Data Destruction pour l'écrasement de contenu et T1070.004 File Deletion pour la suppression elle-même. Sur un hôte où tu suspectes une destruction délibérée de preuves, le journal te donne souvent un timestamp plus serré que tout autre artefact.

Et le contenu du fichier

Le USN journal ne porte jamais de contenu. Seulement des métadonnées sur les opérations. Si tu as besoin des octets de retour, le journal te pointe vers les endroits où chercher :

  • Le numéro d'entrée MFT du fichier supprimé. Si l'entrée n'a pas été réutilisée, $MFT tient encore les data runs et le contenu peut être récupérable via icat de The Sleuth Kit ou X-Ways.
  • Les images avant de $LogFile pour les suppressions très récentes.
  • Volume Shadow Copies (VSS) s'ils existent sur l'hôte. Le fichier supprimé peut vivre dans un snapshot précédent. vssadmin list shadows sur un hôte vif ou vshadowmount de libyal sur une image les feront émerger.
  • Stores spécifiques à une application. La corbeille en ligne OneDrive, le dossier Office « Autosaved » sous \Users\<u>\AppData\Roaming\Microsoft\Office\UnsavedFiles\, les caches navigateur (l'outillage de forensique navigateur couvre ça), les copies staged Outlook OST/PST.
  • Le pagefile et un dump RAM si le fichier a été ouvert assez récemment.

Le rôle du journal est de te dire *qu'*un fichier a existé et quand il a été supprimé. La récupération de contenu est en aval.

Limites pratiques

Fenêtre du ring buffer. Les fichiers supprimés avant que le journal ne passe sur le plus vieux enregistrement que tu peux voir sont partis de cet artefact. Sors $LogFile et les shadow copies en compléments.

Opérations qui ne touchent pas le journal. Les changements de chiffrement au niveau filesystem qui ne mettent à jour que des attributs, les écritures de régions sparse qui ne touchent que la carte d'allocation, et certaines manipulations de reparse-point NTFS peuvent ne pas produire d'enregistrements DataExtend ou DataOverwrite. Le journal voit ce que NTFS choisit de loguer.

FileDelete sans FileCreate précédent. Signifie que le fichier a été créé avant le plus vieil enregistrement actuel du journal. Son cycle de vie dans $J est partiel, mais le timestamp de suppression et la référence parent ancrent toujours un rapport.

Partages bind-mount ou volume-mount. Si les données vivent sur un filesystem distant, le journal sur ton hôte ne connaît que les handles locaux. Sors le journal du filesystem qui héberge réellement.

Lectures complémentaires

  • Microsoft Learn — Change Journals couvre la surface API et le cycle de vie qui produit les enregistrements FileDelete.
  • Pages man de The Sleuth Kit — icat et fls pour la récupération de contenu contre des entrées MFT encore allouées.
  • SANS DFIR — le Windows Forensic Analysis poster met le journal en contexte avec $LogFile, Volume Shadow Copies et autres artefacts de récupération de suppression sur une seule page.
  • libfsntfs de Joachim Metz — la référence ouverte pour l'extraction d'images avant $LogFile quand la suppression est fraîche et l'entrée de répertoire n'a pas encore été écrasée ailleurs.