Aller au contenu

05 · NTFS et système de fichiers

Forensique NTFS : $MFT, journal USN et $LogFile décryptés

Lire NTFS en analyste : structure des enregistrements MFT, horodatages $SI et $FN, timestomping, flags USN, $LogFile, slack $I30 et fichiers supprimés.

Publié le 14 min de lecture

NTFS conserve bien plus d'historique que les fichiers visibles par l'utilisateur : chaque fichier créé laisse un enregistrement, chaque modification est journalisée, et les noms supprimés persistent dans le slack des index. Ce guide passe en revue les fichiers de métadonnées utiles, la construction d'un enregistrement MFT, la lecture des horodatages $SI et $FN sans surinterpréter, la détection du timestomping, et la manière de combiner $MFT, journal USN, $LogFile et $I30 en une timeline du système de fichiers défendable.

Les fichiers de métadonnées NTFS qui comptent

Tout ce qui se trouve sur un volume NTFS, y compris la comptabilité interne du système de fichiers, est un fichier décrit dans la Master File Table. Les premiers enregistrements sont réservés à des fichiers système que l'API Windows masque et verrouille : il faut un outil d'accès disque brut ou une image pour les collecter.

Enregistrement MFTFichierIntérêt forensique
0$MFTUn enregistrement FILE par fichier et répertoire : noms, parents, horodatages, tailles, données résidentes
1$MFTMirrCopie des quatre premiers enregistrements seulement, pour la récupération ; pas une sauvegarde de la table
2$LogFileJournal transactionnel des changements de métadonnées, avec données redo et undo ; rétention courte
5. (racine)Répertoire racine et son index $I30
6$BitmapUn bit par cluster : alloué ou libre. Indique si les clusters d'un fichier supprimé sont encore disponibles
7$BootSecteur de boot : octets par secteur, secteurs par cluster, emplacement de la MFT, taille des enregistrements
9$SecureLe flux $SDS contient les descripteurs de sécurité partagés (SID propriétaire, ACL) ; $SDH et $SII les indexent
11$ExtendRépertoire contenant $UsnJrnl, $ObjId, $Quota, $Reparse, etc.
variable$Extend\$UsnJrnlFlux $J : enregistrements du journal de modifications. Flux $Max : paramètres de taille

Les enregistrements 3 ($Volume), 4 ($AttrDef), 8 ($BadClus) et 10 ($UpCase) complètent l'ensemble. Chaque volume NTFS possède sa propre copie de tout cela, clés USB et VHD compris : collectez volume par volume.

Anatomie d'un enregistrement MFT

L'en-tête FILE

Chaque entrée de la $MFT fait normalement 1 024 octets (4 096 sur certains disques 4Kn ; $Boot vous le dira). Dans un éditeur hexadécimal, un enregistrement valide commence en général par FILE0 : la signature FILE suivie de l'offset du tableau de séquence de mise à jour 0x30, qui correspond au caractère ASCII 0. Un enregistrement ayant échoué au contrôle d'intégrité porte BAAD.

OffsetTailleChamp
0x004Signature FILE
0x042Offset du tableau de séquence de mise à jour (fixup)
0x088Numéro de séquence $LogFile (LSN) du dernier changement
0x102Numéro de séquence
0x122Nombre de liens physiques
0x142Offset du premier attribut
0x162Flags : 0x01 en usage, 0x02 répertoire
0x184Taille utilisée de l'enregistrement
0x208Référence de l'enregistrement de base (non nulle pour les enregistrements d'extension)
0x2C4Numéro d'enregistrement (XP et suivants)

N'oubliez pas les fixups : les deux derniers octets de chaque secteur de 512 octets de l'enregistrement sont remplacés par le numéro de séquence de mise à jour et doivent être restaurés depuis le tableau avant de lire les attributs. Tout parser sérieux le fait ; le carving manuel l'oublie souvent.

Les attributs

Après l'en-tête vient une liste d'attributs, chacun avec un code de type, terminée par 0xFFFFFFFF. Ceux que l'on croise tous les jours : 0x10 $STANDARD_INFORMATION, 0x20 $ATTRIBUTE_LIST, 0x30 $FILE_NAME, 0x80 $DATA, 0x90 $INDEX_ROOT, 0xA0 $INDEX_ALLOCATION et 0xB0 $BITMAP. Un fichier peut porter plusieurs attributs $FILE_NAME : nom long, nom court DOS 8.3, et un par lien physique.

Données résidentes et non résidentes

Quand le contenu tient dans l'enregistrement, $DATA est résident : les octets sont stockés dans l'entrée MFT elle-même. Avec des enregistrements de 1 024 octets, la limite pratique tourne autour de 700 octets selon les autres attributs présents. Les données résidentes sont précieuses : petits scripts, fichiers batch, fragments de configuration et flux Zone.Identifier se lisent directement dans la $MFT, même pour des fichiers supprimés dont l'enregistrement n'a pas été réutilisé. Au-delà, le contenu est non résident : l'attribut stocke des data runs (offsets et longueurs de clusters) qui pointent dans le volume.

Réutilisation des enregistrements et référence de fichier

À la suppression d'un fichier, NTFS efface le flag « en usage » et l'enregistrement redevient disponible. À sa prochaine allocation, le numéro de séquence est incrémenté. Les autres structures désignent les fichiers par une référence de fichier de 64 bits : numéro d'entrée sur 48 bits plus numéro de séquence sur 16 bits. C'est ce couple qui distingue « entrée 81234, séquence 5 » (le fichier actuel) de « entrée 81234, séquence 4 » (ce qui s'y trouvait avant). Les enregistrements USN, les entrées $I30, les fichiers LNK et les Jump Lists stockent des références complètes : une séquence qui ne correspond pas indique de façon fiable que le fichier visé a disparu.

Horodatages $STANDARD_INFORMATION et $FILE_NAME

Les deux attributs contiennent quatre valeurs FILETIME en UTC, à une résolution de 100 nanosecondes : création, modification, changement de l'enregistrement MFT et accès, soit le jeu MACB. Ils ne sont pas maintenus de la même façon, et c'est tout l'intérêt de les comparer. La distinction $SI contre $FN se résume ainsi :

  • $SI est ce qu'affichent l'Explorateur, dir et la plupart des outils. Windows le met à jour à l'usage, et tout processus disposant d'un accès en écriture peut le modifier via SetFileTime ou NtSetInformationFile.
  • $FN est maintenu par le noyau lors de la création ou du changement de l'entrée de nom. Aucune API documentée en mode utilisateur ne permet de le modifier directement.

Comportement général sur les Windows récents (vérifiez sur le build concerné avant de vous appuyer sur une case isolée) :

Action$SI$FN
CréationLes quatre à l'heure couranteLes quatre à l'heure courante
Modification du contenuM, C (et souvent A) mis à jourGénéralement inchangé
Renommage dans le même dossierC mis à jourVarie selon la version ; souvent rafraîchi ou inchangé
Déplacement dans le volumeC mis à jourVarie ; la nouvelle entrée de nom peut prendre l'heure courante ou les valeurs $SI
CopieB, C et A nouveaux ; M hérité de la sourceTous à l'heure de la copie
Déplacement vers un autre volumeB nouveau ; M héritéTous à l'heure du déplacement

Deux conséquences pratiques. D'abord, une date de modification antérieure à la date de création est normale pour un fichier copié et ne constitue pas une falsification. Ensuite, la date de dernier accès dépend de NtfsDisableLastAccessUpdate, désactivé par défaut de Vista à Windows 10 1709 et « géré par le système » depuis 1803 : relevez la valeur avant d'interpréter le moindre horodatage A.

Détecter le timestomping

Le timestomping (MITRE T1070.006) vise presque toujours $SI, puisque c'est ce que l'API expose. Indices à rechercher, du plus fort au plus faible :

  1. Création $SI antérieure à la création $FN. Un fichier ne peut pas être créé avant sa propre entrée de nom. MFTECmd le signale par la colonne SI<FN.
  2. Fractions de seconde à zéro. Beaucoup d'outils écrivent des secondes entières, d'où des fractions .0000000. Un horodatage réel ne se termine quasiment jamais par sept zéros. MFTECmd le signale par uSecZeros.
  3. Un enregistrement BASIC_INFO_CHANGE dans le journal USN pour le fichier sans écriture de contenu correspondante, surtout peu après le FILE_CREATE.
  4. Les données undo du $LogFile qui contiennent encore les anciennes valeurs $SI, si le changement est assez récent.
  5. Une incohérence avec d'autres artefacts : un binaire « créé en 2019 » dont le numéro d'entrée MFT se trouve au milieu de fichiers créés la semaine dernière, ou que Prefetch, Amcache ou le journal USN ont vu arriver hier.

Un attaquant aguerri peut contourner le contrôle $FN, par exemple en modifiant $SI puis en déplaçant le fichier pour que la nouvelle entrée de nom hérite des fausses valeurs, ou en écrivant directement sur le volume. D'où l'importance de la corroboration par le journal USN et le $LogFile, plus que de n'importe quel flag isolé.

# $MFT complète en CSV, puis filtrage des colonnes d'anomalie dans Timeline Explorer
MFTECmd.exe -f "E:\Triage\C\$MFT" --csv E:\Out --csvf mft.csv

# Détail d'un enregistrement (numéro d'entrée issu du CSV)
MFTECmd.exe -f "E:\Triage\C\$MFT" --de 81234

Le journal USN : chaque changement, dans l'ordre

Le journal USN réside dans le flux sparse $Extend\$UsnJrnl:$J et ajoute un enregistrement à chaque modification d'un fichier ou d'un répertoire. $Max contient sa taille maximale et son delta d'allocation. Quand le journal dépasse ce maximum, les enregistrements les plus anciens sont purgés et leurs clusters se relisent comme des zéros : un $J de plusieurs gigaoctets peut ne contenir que quelques dizaines de mégaoctets de données réelles, à la fin.

Les flags Reason à connaître

Le champ Reason est un masque de bits qui s'accumule tant qu'un handle est ouvert ; l'enregistrement final porte CLOSE. Lisez la combinaison, pas un bit isolé.

FlagValeurSignification habituelle
DATA_OVERWRITE0x00000001Contenu réécrit
DATA_EXTEND0x00000002Contenu ajouté, normal pour un nouveau fichier
DATA_TRUNCATION0x00000004Fichier raccourci ou vidé
NAMED_DATA_EXTEND0x00000020Flux de données alternatif écrit, par exemple Zone.Identifier
FILE_CREATE0x00000100Fichier ou répertoire créé
FILE_DELETE0x00000200Fichier ou répertoire supprimé
SECURITY_CHANGE0x00000800ACL ou propriétaire modifié
RENAME_OLD_NAME0x00001000Nom avant un renommage ou un déplacement
RENAME_NEW_NAME0x00002000Nom après un renommage ou un déplacement
BASIC_INFO_CHANGE0x00008000Attributs ou horodatages modifiés
STREAM_CHANGE0x00200000Flux nommé ajouté ou supprimé
CLOSE0x80000000Dernier enregistrement pour ce handle

Reconstruire les chemins complets

Chaque enregistrement ne stocke que le nom du fichier et la référence du parent. Pour obtenir le chemin complet, il faut résoudre la référence parente dans la $MFT, récursivement jusqu'à la racine. Si l'entrée du parent a été réutilisée (séquence différente), le chemin est ambigu et un bon parser le signale au lieu de deviner. Il est indispensable de résoudre avec la $MFT collectée en même temps que $J.

# Parser $J en résolvant les chemins parents avec la $MFT du même volume
MFTECmd.exe -f "E:\Triage\C\$Extend\$J" -m "E:\Triage\C\$MFT" --csv E:\Out --csvf usn.csv

Motifs typiques : un téléchargement donne FILE_CREATE, DATA_EXTEND, puis NAMED_DATA_EXTEND pour le flux Zone.Identifier ; une extraction d'archive donne des dizaines de FILE_CREATE sous un même parent en une seconde ; un rançongiciel produit des paires RENAME_OLD_NAME / RENAME_NEW_NAME avec une nouvelle extension sur des milliers de fichiers.

$LogFile : les dernières minutes, avant et après

Le $LogFile est le journal transactionnel de NTFS, en général 64 Mo sur les volumes récents. Avant de modifier des métadonnées, NTFS écrit un enregistrement avec une opération redo (comment réappliquer le changement) et une opération undo (comment l'annuler), contenant souvent les octets bruts avant et après. Le $LogFile peut donc révéler les horodatages $SI d'origine d'un fichier timestompé, les noms d'une entrée d'index tout juste supprimée ou un contenu résident écrasé depuis.

Le revers, c'est la rétention. C'est un tampon circulaire consacré aux métadonnées, qui sur un disque système actif ne couvre parfois que quelques minutes à quelques heures. Collectez-le tôt et considérez-le comme un outil de précision pour les événements proches de l'acquisition, pas comme un historique long. Depuis Windows 8, le volume monté utilise la version 2.0 du Log File Service, que certains parsers anciens ne savent pas lire.

Le slack $I30 : des noms qui survivent à leurs enregistrements

Chaque répertoire stocke un index trié de ses enfants dans des attributs $I30 : $INDEX_ROOT dans l'enregistrement MFT pour les petits dossiers, puis des enregistrements INDX $INDEX_ALLOCATION quand il grossit. Chaque entrée contient une copie du $FILE_NAME de l'enfant : nom, tailles et quatre horodatages. Quand des entrées sont retirées ou que l'arbre B est rééquilibré, les anciennes entrées survivent souvent dans la partie inutilisée de l'enregistrement INDX. Parser ce slack $I30 peut révéler des fichiers supprimés d'un dossier de staging, même après réutilisation de leurs enregistrements MFT. C'est une forme de slack propre aux index de répertoires.

# Parser le $I30 d'un répertoire exporté avec un outil d'accès NTFS brut
MFTECmd.exe -f "E:\Triage\ProgramData_I30" --csv E:\Out --csvf i30.csv

Flux de données alternatifs et Zone.Identifier

Un flux de données alternatif (ADS) n'est qu'un attribut $DATA nommé. Windows s'en sert pour la Mark of the Web : fichier.exe:Zone.Identifier stocke ZoneId=3 pour les téléchargements Internet et, depuis Windows 10 avec la plupart des navigateurs, ReferrerUrl et HostUrl. Le flux étant minuscule, il est presque toujours résident, et MFTECmd affiche son contenu directement dans le CSV. Voir la fiche Zone.Identifier pour le détail des champs.

Les attaquants cachent aussi des charges dans des ADS (type evil.exe > notes.txt:hidden.exe). Elles apparaissent comme des attributs $DATA supplémentaires dans la $MFT et comme NAMED_DATA_EXTEND ou STREAM_CHANGE dans le journal USN.

# Triage à chaud : lister les flux des fichiers d'un dossier
Get-ChildItem C:\Users\Public -Recurse -File | Get-Item -Stream * |
  Where-Object Stream -ne ':$DATA' | Select-Object FileName, Stream, Length

Récupération de fichiers supprimés ou carving

Deux techniques différentes, de valeur probante différente.

  • La récupération par les métadonnées s'appuie sur un enregistrement MFT non alloué qui conserve ses data runs. Si $Bitmap indique que ces clusters sont libres et que rien ne les a réutilisés, on récupère le fichier avec son nom, son chemin et ses horodatages d'origine. Outils : The Sleuth Kit (icat), Velociraptor Windows.NTFS.Recover, la plupart des suites commerciales.
  • Le carving ignore le système de fichiers et cherche des en-têtes et des fins de fichiers dans l'espace non alloué. Il récupère du contenu sans nom ni horodatage et se heurte vite à la fragmentation des gros fichiers. Utilisez-le quand les métadonnées ont disparu, et précisez dans le rapport que le contenu carvé n'a aucune provenance système de fichiers.

Sur les SSD avec TRIM, les clusters libérés sont souvent remis à zéro rapidement : attendez-vous à moins de résultats dans les deux cas. Les clichés instantanés (VSS) sont souvent la meilleure source pour les anciennes versions d'un fichier.

Outils

  • MFTECmd (Eric Zimmerman) parse $MFT, $J, $Boot, $SDS et $I30, avec des colonnes d'anomalie pour le timestomping et l'affichage des ADS résidents. Sources et versions : github.com/EricZimmerman/MFTECmd.
  • Parsers $LogFile : LogFileParser (Joakim Schicht), NTFS Log Tracker et dfir_ntfs décodent les opérations redo et undo.
  • Velociraptor : Windows.NTFS.MFT, Windows.Forensics.Usn et Windows.NTFS.I30 fonctionnent à chaud comme hors ligne.
  • Nos parsers dans le navigateur, exécutés côté client pour que les preuves ne quittent jamais votre machine : MFT Parser, USN Parser et LogFile Parser.
  • Plaso intègre $MFT et $J dans une super timeline ; voir construire une super timeline avec Plaso.

Erreurs fréquentes

  • Voir une falsification dans tout écart $SI/$FN. $FN est souvent en retard. Seule une création $SI antérieure à la création $FN, corroborée, étaye un timestomping.
  • Parser $J avec la mauvaise $MFT. Des chemins résolus avec une $MFT plus récente ou plus ancienne seront faux partout où des entrées ont été réutilisées.
  • Ignorer les numéros de séquence. L'entrée 81234 d'aujourd'hui n'est pas forcément le fichier qu'un LNK ou un enregistrement USN désignait le mois dernier.
  • Collecter trop tard. Le $LogFile et la fin de $J sont écrasés par l'activité normale, y compris par vos propres outils de triage lancés sur le système.
  • Oublier les clichés instantanés. Des versions plus anciennes de $MFT et $J dorment souvent dans les VSS et peuvent allonger la timeline de plusieurs semaines.
  • Lire les dates d'accès sans vérifier NtfsDisableLastAccessUpdate.

Méthode de travail

  1. Collectez $MFT, $J, $Max, $LogFile, $Secure:$SDS et $Boot sur chaque volume NTFS, ainsi que dans les clichés instantanés. Voir acquisition et triage avec KAPE et Velociraptor.
  2. Parsez la $MFT en CSV. Notez les caractéristiques NTFS, la taille des enregistrements et NtfsDisableLastAccessUpdate.
  3. Parsez $J avec -m pointant vers la $MFT du même volume. Relevez le premier et le dernier horodatage USN pour connaître la fenêtre couverte.
  4. Pivotez sur la fenêtre de l'incident : filtrez l'USN sur FILE_CREATE, RENAME_*, FILE_DELETE et BASIC_INFO_CHANGE sous les parents suspects (\ProgramData\, \Users\Public\, \Windows\Temp\, Downloads des utilisateurs).
  5. Pour chaque fichier suspect, examinez la ligne $MFT : SI<FN, uSecZeros, Zone.Identifier résident, propriétaire via SecurityId et $Secure:$SDS.
  6. Parsez le $LogFile si l'activité est récente, et le $I30 des répertoires de staging pour retrouver les noms supprimés.
  7. Récupérez les fichiers supprimés par les métadonnées quand c'est possible ; ne carvez qu'en dernier recours, et étiquetez les résultats carvés.
  8. Fusionnez le tout dans une timeline unique et corrélez avec les artefacts d'exécution comme Prefetch et les journaux d'événements. Si le système de fichiers montre un nettoyage délibéré, poursuivez avec détecter l'anti-forensique Windows.

Domaine 05

NTFS et système de fichiers

Les fiches artefacts derrière ce guide : emplacement selon la version de Windows, ce que prouve chacun et les outils pour l'analyser.

Articles liés