Analyse de l'espace libre et des artefacts du système de fichiers
Quand l'utiliser
- Lors de la recherche de données cachées ou résiduelles dans l'espace libre du système de fichiers
- Pour analyser les entrées de la table de fichiers maîtres (MFT) NTFS pour les métadonnées de fichiers supprimés
- Lors de la reconstruction des opérations sur les fichiers à partir du journal des modifications USN
- Pour détecter les flux de données alternatifs (ADS) utilisés pour cacher des données ou des logiciels malveillants
- Lors d'une analyse forensique approfondie nécessitant un examen au-delà de la récupération standard de fichiers
Prérequis
- Image de disque forensique avec un système de fichiers NTFS
- Outils de la trousse Sleuth Kit (TSK) : istat, icat, fls, blkls, blkstat
- MFTECmd (Eric Zimmerman) pour le parsing de la MFT
- MFTExplorer pour l'analyse interactive de la MFT
- Compréhension des structures NTFS (MFT, $UsnJrnl, $LogFile, ADS)
- Python avec les bibliothèques analyzeMFT ou mft pour un parsing automatisé
Flux de travail
Étape 1 : Identifier et extraire les artefacts du système de fichiers NTFS
# Déterminer la disposition des partitions
mmls /cases/case-2024-001/images/evidence.dd
# Extraire les fichiers système NTFS clés
# $MFT - Table de fichiers maîtres
icat -o 2048 /cases/case-2024-001/images/evidence.dd 0 > /cases/case-2024-001/ntfs/MFT
# $UsnJrnl:$J - Journal des modifications USN
icat -o 2048 /cases/case-2024-001/images/evidence.dd 62-128 > /cases/case-2024-001/ntfs/UsnJrnl_J
# $LogFile - Journal des transactions
icat -o 2048 /cases/case-2024-001/images/evidence.dd 2 > /cases/case-2024-001/ntfs/LogFile
# Extraire tout l'espace libre du volume
blkls -s -o 2048 /cases/case-2024-001/images/evidence.dd > /cases/case-2024-001/ntfs/slack_space.raw
# Obtenir les informations du système de fichiers
fsstat -o 2048 /cases/case-2024-001/images/evidence.dd | tee /cases/case-2024-001/ntfs/fs_info.txt
Étape 2 : Analyser la table de fichiers maîtres (MFT)
# Parser la MFT avec MFTECmd (Eric Zimmerman)
MFTECmd.exe -f "C:\cases\ntfs\MFT" --csv "C:\cases\analysis\" --csvf mft_analysis.csv
# Parser avec analyzeMFT (Python)
pip install analyzeMFT
analyzeMFT.py -f /cases/case-2024-001/ntfs/MFT \
-o /cases/case-2024-001/analysis/mft_analysis.csv \
-c
# Analyse personnalisée de la MFT avec Python
python3 << 'PYEOF'
from mft import PyMft
import csv
mft = PyMft(open('/cases/case-2024-001/ntfs/MFT', 'rb').read())
deleted_files = []
suspicious_files = []
for entry in mft.entries():
if entry is None:
continue
filename = entry.get_filename()
if filename is None:
continue
is_deleted = not entry.is_active()
is_directory = entry.is_directory()
created = entry.get_created_timestamp()
modified = entry.get_modified_timestamp()
mft_modified = entry.get_mft_modified_timestamp()
size = entry.get_file_size()
# Signaler les fichiers supprimés pour récupération
if is_deleted and not is_directory and size > 0:
deleted_files.append({
'filename': filename,
'size': size,
'created': str(created),
'modified': str(modified),
'entry_number': entry.entry_number
})
# Détection de timestomping (heure modifiée MFT != heure modifiée $SI)
si_modified = entry.get_si_modified_timestamp()
fn_modified = entry.get_fn_modified_timestamp()
if si_modified and fn_modified:
if abs((si_modified - fn_modified).total_seconds()) > 86400: # >1 jour de différence
suspicious_files.append({
'filename': filename,
'si_modified': str(si_modified),
'fn_modified': str(fn_modified),
'delta': str(si_modified - fn_modified)
})
print(f"=== FICHIERS SUPPRIMÉS (métadonnées récupérables) ===")
print(f"Total : {len(deleted_files)}")
for f in deleted_files[:20]:
print(f" [{f['modified']}] {f['filename']} ({f['size']} octets)")
print(f"\n=== TIMESTOMPING POTENTIEL ===")
print(f"Total suspects : {len(suspicious_files)}")
for f in suspicious_files[:10]:
print(f" {f['filename']} : $SI={f['si_modified']}, $FN={f['fn_modified']} (delta : {f['delta']})")
PYEOF
Étape 3 : Analyser l'espace libre pour les données cachées
# Rechercher des chaînes dans l'espace libre
strings -a /cases/case-2024-001/ntfs/slack_space.raw > /cases/case-2024-001/analysis/slack_strings.txt
# Rechercher des motifs spécifiques dans l'espace libre
grep -iab "password\|secret\|confidential\|credit.card\|ssn" \
/cases/case-2024-001/ntfs/slack_space.raw > /cases/case-2024-001/analysis/slack_keywords.txt
# Analyser l'espace libre individuel des fichiers
python3 << 'PYEOF'
import struct
# L'espace libre se compose :
# 1. Espace RAM : octets entre la fin du fichier et la prochaine limite de secteur (remplis avec le contenu RAM ou des zéros)
# 2. Espace disque : secteurs restants dans le cluster après le dernier secteur du fichier
# Analyser l'espace libre pour des entrées MFT spécifiques
# Utilisation de Sleuth Kit pour obtenir l'espace libre d'un fichier spécifique
import subprocess
# Obtenir les détails du fichier
result = subprocess.run(
['istat', '-o', '2048', '/cases/case-2024-001/images/evidence.dd', '14523'],
capture_output=True, text=True
)
print(result.stdout)
# La sortie montre les runs de données - le dernier cluster peut contenir des données libres
# Calculer la taille de l'espace libre : (taille allouée - taille du fichier) octets
PYEOF
# Rechercher des signatures de fichiers dans l'espace libre (fichiers intégrés)
foremost -t jpg,pdf,zip -i /cases/case-2024-001/ntfs/slack_space.raw \
-o /cases/case-2024-001/carved/slack_carved/
# Utiliser bulk_extractor pour trouver des données structurées dans l'espace libre
bulk_extractor -o /cases/case-2024-001/analysis/bulk_extract/ \
/cases/case-2024-001/ntfs/slack_space.raw
Étape 4 : Parser le journal des modifications USN
# Parser le journal USN avec MFTECmd
MFTECmd.exe -f "C:\cases\ntfs\UsnJrnl_J" --csv "C:\cases\analysis\" --csvf usn_journal.csv
# Parsing du journal USN en Python
pip install pyusn
python3 << 'PYEOF'
import struct
import csv
from datetime import datetime, timedelta
def parse_usn_record(data, offset):
"""Parser un seul enregistrement USN_RECORD_V2."""
if offset + 8 > len(data):
return None, offset
record_len = struct.unpack_from('<I', data, offset)[0]
if record_len < 56 or record_len > 65536 or offset + record_len > len(data):
return None, offset + 8
major_ver = struct.unpack_from('<H', data, offset + 4)[0]
if major_ver != 2:
return None, offset + record_len
mft_ref = struct.unpack_from('<Q', data, offset + 8)[0] & 0xFFFFFFFFFFFF
parent_ref = struct.unpack_from('<Q', data, offset + 16)[0] & 0xFFFFFFFFFFFF
usn = struct.unpack_from('<Q', data, offset + 24)[0]
timestamp = struct.unpack_from('<Q', data, offset + 32)[0]
reason = struct.unpack_from('<I', data, offset + 40)[0]
source_info = struct.unpack_from('<I', data, offset + 44)[0]
security_id = struct.unpack_from('<I', data, offset + 48)[0]
file_attrs = struct.unpack_from('<I', data, offset + 52)[0]
filename_len = struct.unpack_from('<H', data, offset + 56)[0]
filename_off = struct.unpack_from('<H', data, offset + 58)[0]
name = data[offset + filename_off:offset + filename_off + filename_len].decode('utf-16-le', errors='ignore')
# Convertir FILETIME Windows en datetime
ts = datetime(1601, 1, 1) + timedelta(microseconds=timestamp // 10)
# Décoder les indicateurs de raison
reasons = []
reason_flags = {
0x01: 'DATA_OVERWRITE', 0x02: 'DATA_EXTEND', 0x04: 'DATA_TRUNCATION',
0x10: 'NAMED_DATA_OVERWRITE', 0x20: 'NAMED_DATA_EXTEND',
0x100: 'FILE_CREATE', 0x200: 'FILE_DELETE', 0x400: 'EA_CHANGE',
0x800: 'SECURITY_CHANGE', 0x1000: 'RENAME_OLD_NAME', 0x2000: 'RENAME_NEW_NAME',
0x4000: 'INDEXABLE_CHANGE', 0x8000: 'BASIC_INFO_CHANGE',
0x10000: 'HARD_LINK_CHANGE', 0x20000: 'COMPRESSION_CHANGE',
0x40000: 'ENCRYPTION_CHANGE', 0x80000: 'OBJECT_ID_CHANGE',
0x100000: 'REPARSE_POINT_CHANGE', 0x200000: 'STREAM_CHANGE',
0x80000000: 'CLOSE'
}
for flag, desc in reason_flags.items():
if reason & flag:
reasons.append(desc)
record = {
'timestamp': ts.strftime('%Y-%m-%d %H:%M:%S'),
'filename': name,
'mft_entry': mft_ref,
'parent_entry': parent_ref,
'reasons': '|'.join(reasons),
'usn': usn
}
return record, offset + record_len
# Parser le journal
with open('/cases/case-2024-001/ntfs/UsnJrnl_J', 'rb') as f:
data = f.read()
records = []
offset = 0
while offset < len(data) - 8:
record, offset = parse_usn_record(data, offset)
if record:
records.append(record)
else:
offset += 8 # Sauter les zéros
# Filtrer les événements de suppression
deletions = [r for r in records if 'FILE_DELETE' in r['reasons']]
creations = [r for r in records if 'FILE_CREATE' in r['reasons']]
renames = [r for r in records if 'RENAME_NEW_NAME' in r['reasons']]
print(f"Total des enregistrements USN : {len(records)}")
print(f"Créations de fichiers : {len(creations)}")
print(f"Suppressions de fichiers : {len(deletions)}")
print(f"Renommages de fichiers : {len(renames)}")
print("\n=== SUPPRESSIONS RÉCENTES ===")
for r in deletions[-20:]:
print(f" [{r['timestamp']}] SUPPRIMÉ : {r['filename']} (MFT#{r['mft_entry']})")
# Écrire le journal complet au format CSV
with open('/cases/case-2024-001/analysis/usn_journal.csv', 'w', newline='') as f:
writer = csv.DictWriter(f, fieldnames=['timestamp', 'filename', 'mft_entry', 'parent_entry', 'reasons', 'usn'])
writer.writeheader()
writer.writerows(records)
PYEOF
Étape 5 : Détecter et analyser les flux de données alternatifs
# Lister tous les flux de données alternatifs dans l'image
find /mnt/evidence -exec getfattr -d {} \; 2>/dev/null | grep -i "ads\|zone\|stream"
# Utiliser Sleuth Kit pour trouver les ADS
fls -r -o 2048 /cases/case-2024-001/images/evidence.dd | grep ":" | \
tee /cases/case-2024-001/analysis/ads_list.txt
# Extraire le contenu spécifique d'un ADS
# Format : icat image inode:nom_ads
icat -o 2048 /cases/case-2024-001/images/evidence.dd 14523:hidden_stream \
> /cases/case-2024-001/analysis/extracted_ads.bin
# Vérifier les flux Zone.Identifier (suivi de l'origine du téléchargement)
fls -r -o 2048 /cases/case-2024-001/images/evidence.dd | grep "Zone.Identifier" | \
while read line; do
inode=$(echo "$line" | awk '{print $2}' | tr -d ':')
echo "=== $line ==="
icat -o 2048 /cases/case-2024-001/images/evidence.dd "${inode}:Zone.Identifier" 2>/dev/null
echo ""
done > /cases/case-2024-001/analysis/zone_identifiers.txt
# Le contenu de Zone.Identifier révèle :
# [ZoneTransfer]
# ZoneId=3 (3 = Internet, indique que le fichier a été téléchargé)
# ReferrerUrl=https://malicious-site.com/payload.exe
# HostUrl=https://cdn.malicious-site.com/payload.exe
Concepts clés
| Concept | Description |
|---|---|
| Espace libre (slack) | Espace inutilisé entre la fin du fichier et la limite du cluster contenant des données résiduelles |
| Espace RAM | Partie de l'espace libre allant de la fin du fichier à la limite du secteur (historiquement remplie avec la RAM) |
| MFT ($MFT) | Table de fichiers maîtres - base de données de métadonnées NTFS avec des entrées pour chaque fichier |
| Journal USN ($UsnJrnl) | Journal de changement enregistrant toutes les modifications de fichiers/répertoires sur NTFS |
| Flux de données alternatifs | Fonctionnalité NTFS permettant plusieurs flux de données par fichier (stockage caché) |
| $STANDARD_INFORMATION | Attribut MFT avec des horodatages modifiables par des applications en mode utilisateur |
| $FILE_NAME | Attribut MFT avec des horodatages modifiables uniquement par le noyau |
| Timestomping | Technique anti-forensique modifiant les horodatages des fichiers pour éviter la détection |
Outils et systèmes
| Outil | Objectif |
|---|---|
| MFTECmd | Parser MFT et journal USN d'Eric Zimmerman avec sortie CSV |
| MFTExplorer | Outil GUI interactif pour l'analyse de la MFT |
| analyzeMFT | Parser Python de la MFT avec sortie CSV/JSON |
| The Sleuth Kit | Boîte à outils forensique du système de fichiers (fls, icat, blkls, istat) |
| bulk_extractor | Extraction de caractéristiques à partir de données brutes, y compris l'espace libre |
| NTFS Log Tracker | Outil pour parser les enregistrements de transactions $LogFile |
| streams.exe | Outil Sysinternals pour lister les flux de données alternatifs NTFS |
| Plaso | Outil de super-chronologie parsant la MFT et le journal USN |
Scénarios courants
Scénario 1 : Détection anti-forensique par timestomping Comparez les horodatages $STANDARD_INFORMATION avec les horodatages $FILE_NAME dans les entrées MFT, signalez les fichiers où les horodatages $SI sont antérieurs aux horodatages $FN (impossible en fonctionnement normal), identifiez les fichiers timestompés comme preuve de manipulation délibérée, corrélez avec d'autres preuves de la chronologie.
Scénario 2 : Données cachées dans les flux de données alternatifs Analysez les ADS attachés aux fichiers au-delà du Zone.Identifier standard, extrayez le contenu des ADS pour analyse, vérifiez la présence d'exécutables ou de documents cachés dans les ADS, corrélez la création des ADS avec la chronologie de l'activité utilisateur, documentez les conclusions pour les preuves.
Scénario 3 : Reconstruction de fichiers supprimés à partir de la MFT Parsez la MFT pour les entrées inactives (supprimées), extrayez les noms, tailles et horodatages des fichiers supprimés, récupérez le contenu des fichiers avec icat si les clusters de données ne sont pas écrasés, construisez une liste de fichiers de preuve supprimés, corrélez avec les événements de suppression du journal USN.
Scénario 4 : Reconstruction de l'activité des fichiers à partir du journal USN Parsez le journal des modifications USN pour la période d'enquête, identifiez les événements de création, modification, renommage et suppression de fichiers, reconstruisez la séquence des opérations sur les fichiers, détectez les preuves de préparation de données (motif créer, copier, compresser, supprimer), identifiez le nettoyage anti-forensique des fichiers.
Format de sortie
Analyse des artefacts du système de fichiers :
Volume : NTFS (Partition 2, 465 Go)
Taille de cluster : 4096 octets
Analyse MFT :
Total des entrées : 456 789
Fichiers actifs : 234 567
Entrées supprimées : 12 345 (8 901 avec métadonnées récupérables)
Fichiers timestompés : 23 (détection de divergence SI/FN)
Journal USN :
Enregistrements parsés : 2 345 678
Plage de dates : 2024-01-01 au 2024-01-20
Créations de fichiers : 45 678
Suppressions de fichiers : 23 456
Renommages de fichiers : 12 345
Flux de données alternatifs :
Total ADS trouvés : 1 234
Zone.Identifier : 890 (fichiers téléchargés)
ADS personnalisés/suspects : 5 (données cachées détectées)
Espace libre :
Total espace libre : 12,3 Go
Correspondances de mots-clés : 45 (mots de passe, cartes de crédit)
Fichiers extraits de l'espace libre : 23
Conclusions suspectes :
- 23 fichiers avec horodatages timestompés
- 5 fichiers avec ADS cachés contenant des données
- L'USN montre une suppression massive le 2024-01-18 (anti-forensique)
…
[tronqué pour la traduction]