Was macht Analysieren von Slack Space und Dateisystem-Artefakten?
Wann zu verwenden
- Bei der Suche nach versteckten oder Restdaten im Dateisystem-Slack-Space
- Zur Analyse von NTFS-Master-File-Table (MFT)-Einträgen für Metadaten gelöschter Dateien
- Bei der Rekonstruktion von Dateioperationen aus dem USN Change Journal
- Zur Erkennung von Alternate Data Streams (ADS), die zum Verstecken von Daten oder Malware verwendet werden
- Während einer tiefgehenden forensischen Analyse, die eine Untersuchung über die Standard-Dateiwiederherstellung hinaus erfordert
Voraussetzungen
- Forensisches Disk-Image mit NTFS-Dateisystem
- The Sleuth Kit (TSK) Tools: istat, icat, fls, blkls, blkstat
- MFTECmd (Eric Zimmerman) für die MFT-Parsing
- MFTExplorer für interaktive MFT-Analyse
- Verständnis der NTFS-Strukturen (MFT, $UsnJrnl, $LogFile, ADS)
- Python mit analyzeMFT oder mft-Bibliothek für automatisiertes Parsing
Arbeitsablauf
Schritt 1: NTFS-Dateisystem-Artefakte identifizieren und extrahieren
# Partitionstabelle ermitteln
mmls /cases/case-2024-001/images/evidence.dd
# Wichtige NTFS-Systemdateien extrahieren
# $MFT - Master File Table
icat -o 2048 /cases/case-2024-001/images/evidence.dd 0 > /cases/case-2024-001/ntfs/MFT
# $UsnJrnl:$J - USN Change Journal
icat -o 2048 /cases/case-2024-001/images/evidence.dd 62-128 > /cases/case-2024-001/ntfs/UsnJrnl_J
# $LogFile - Transaktionsprotokoll
icat -o 2048 /cases/case-2024-001/images/evidence.dd 2 > /cases/case-2024-001/ntfs/LogFile
# Gesamten Slack-Space des Volumes extrahieren
blkls -s -o 2048 /cases/case-2024-001/images/evidence.dd > /cases/case-2024-001/ntfs/slack_space.raw
# Dateisysteminformationen abrufen
fsstat -o 2048 /cases/case-2024-001/images/evidence.dd | tee /cases/case-2024-001/ntfs/fs_info.txt
Schritt 2: Die Master File Table (MFT) analysieren
# MFT mit MFTECmd (Eric Zimmerman) parsen
MFTECmd.exe -f "C:\cases\ntfs\MFT" --csv "C:\cases\analysis\" --csvf mft_analysis.csv
# Mit analyzeMFT (Python) parsen
pip install analyzeMFT
analyzeMFT.py -f /cases/case-2024-001/ntfs/MFT \
-o /cases/case-2024-001/analysis/mft_analysis.csv \
-c
# Benutzerdefinierte MFT-Analyse mit Python
python3 << 'PYEOF'
from mft import PyMft
import csv
mft = PyMft(open('/cases/case-2024-001/ntfs/MFT', 'rb').read())
gelöschte_dateien = []
verdächtige_dateien = []
for eintrag in mft.entries():
if eintrag is None:
continue
dateiname = eintrag.get_filename()
if dateiname is None:
continue
ist_gelöscht = not eintrag.is_active()
ist_verzeichnis = eintrag.is_directory()
erstellt = eintrag.get_created_timestamp()
geändert = eintrag.get_modified_timestamp()
mft_geändert = eintrag.get_mft_modified_timestamp()
größe = eintrag.get_file_size()
# Gelöschte Dateien zur Wiederherstellung markieren
if ist_gelöscht and not ist_verzeichnis and größe > 0:
gelöschte_dateien.append({
'dateiname': dateiname,
'größe': größe,
'erstellt': str(erstellt),
'geändert': str(geändert),
'eintragsnummer': eintrag.entry_number
})
# Timestomping erkennen (MFT-Änderungszeit != $SI-Änderungszeit)
si_geändert = eintrag.get_si_modified_timestamp()
fn_geändert = eintrag.get_fn_modified_timestamp()
if si_geändert and fn_geändert:
if abs((si_geändert - fn_geändert).total_seconds()) > 86400: # >1 Tag Differenz
verdächtige_dateien.append({
'dateiname': dateiname,
'si_geändert': str(si_geändert),
'fn_geändert': str(fn_geändert),
'delta': str(si_geändert - fn_geändert)
})
print(f"=== GELÖSCHTE DATEIEN (wiederherstellbare Metadaten) ===")
print(f"Gesamt: {len(gelöschte_dateien)}")
for f in gelöschte_dateien[:20]:
print(f" [{f['geändert']}] {f['dateiname']} ({f['größe']} Bytes)")
print(f"\n=== POTENTIELLES TIMESTOMPING ===")
print(f"Gesamt verdächtig: {len(verdächtige_dateien)}")
for f in verdächtige_dateien[:10]:
print(f" {f['dateiname']}: $SI={f['si_geändert']}, $FN={f['fn_geändert']} (Delta: {f['delta']})")
PYEOF
Schritt 3: Slack-Space auf versteckte Daten analysieren
# Slack-Space nach Zeichenketten durchsuchen
strings -a /cases/case-2024-001/ntfs/slack_space.raw > /cases/case-2024-001/analysis/slack_strings.txt
# Nach bestimmten Mustern im Slack-Space suchen
grep -iab "password\|secret\|confidential\|credit\.card\|ssn" \
/cases/case-2024-001/ntfs/slack_space.raw > /cases/case-2024-001/analysis/slack_keywords.txt
# Datei-Slack einzeln analysieren
python3 << 'PYEOF'
import struct
# Datei-Slack besteht aus:
# 1. RAM-Slack: Bytes zwischen Dateiende und nächster Sektorgrenze (mit RAM-Inhalt oder Nullen gefüllt)
# 2. Laufwerk-Slack: Verbleibende Sektoren im Cluster nach dem letzten Dateisektor
# Slack für bestimmte MFT-Einträge analysieren
# Sleuth Kit verwenden, um Datei-Slack für eine bestimmte Datei zu erhalten
import subprocess
# Dateidetails abrufen
result = subprocess.run(
['istat', '-o', '2048', '/cases/case-2024-001/images/evidence.dd', '14523'],
capture_output=True, text=True
)
print(result.stdout)
# Die Ausgabe zeigt Datenläufe - der letzte Cluster kann Slack-Daten enthalten
# Slack-Größe berechnen: (zugewiesene_größe - dateigröße) Bytes
PYEOF
# Nach Dateisignaturen im Slack-Space suchen (eingebettete Dateien)
foremost -t jpg,pdf,zip -i /cases/case-2024-001/ntfs/slack_space.raw \
-o /cases/case-2024-001/carved/slack_carved/
# bulk_extractor verwenden, um strukturierte Daten im Slack zu finden
bulk_extractor -o /cases/case-2024-001/analysis/bulk_extract/ \
/cases/case-2024-001/ntfs/slack_space.raw
Schritt 4: Das USN Change Journal parsen
# USN-Journal mit MFTECmd parsen
MFTECmd.exe -f "C:\cases\ntfs\UsnJrnl_J" --csv "C:\cases\analysis\" --csvf usn_journal.csv
# Python USN-Journal-Parsing
pip install pyusn
python3 << 'PYEOF'
import struct
import csv
from datetime import datetime, timedelta
def parse_usn_record(data, offset):
"""Parse einen einzelnen 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')
# Windows FILETIME in datetime umwandeln
ts = datetime(1601, 1, 1) + timedelta(microseconds=timestamp // 10)
# Reason-Flags dekodieren
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
# Journal parsen
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 # Nullen überspringen
# Nach Löschereignissen filtern
löschungen = [r for r in records if 'FILE_DELETE' in r['reasons']]
erstellungen = [r for r in records if 'FILE_CREATE' in r['reasons']]
umbenennungen = [r for r in records if 'RENAME_NEW_NAME' in r['reasons']]
print(f"Gesamt USN-Datensätze: {len(records)}")
print(f"Dateierstellungen: {len(erstellungen)}")
print(f"Dateilöschungen: {len(löschungen)}")
print(f"Dateiumbenennungen: {len(umbenennungen)}")
print("\n=== LETZTE LÖSCHUNGEN ===")
for r in löschungen[-20:]:
print(f" [{r['timestamp']}] GELÖSCHT: {r['filename']} (MFT#{r['mft_entry']})")
# Vollständiges Journal in CSV schreiben
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
Schritt 5: Alternate Data Streams erkennen und analysieren
# Alle Alternate Data Streams im Image auflisten
find /mnt/evidence -exec getfattr -d {} \; 2>/dev/null | grep -i "ads\|zone\|stream"
# Mit Sleuth Kit ADS finden
fls -r -o 2048 /cases/case-2024-001/images/evidence.dd | grep ":" | \
tee /cases/case-2024-001/analysis/ads_list.txt
# Bestimmten ADS-Inhalt extrahieren
# Format: icat image inode:ads_name
icat -o 2048 /cases/case-2024-001/images/evidence.dd 14523:hidden_stream \
> /cases/case-2024-001/analysis/extracted_ads.bin
# Zone.Identifier-Ströme überprüfen (Herkunftsverfolgung von Downloads)
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
# Zone.Identifier-Inhalt enthüllt:
# [ZoneTransfer]
# ZoneId=3 (3 = Internet, zeigt an, dass die Datei heruntergeladen wurde)
# ReferrerUrl=https://malicious-site.com/payload.exe
# HostUrl=https://cdn.malicious-site.com/payload.exe
Schlüsselkonzepte
| Konzept | Beschreibung |
|---|---|
| Datei-Slack | Unbenutzter Raum zwischen Dateiende und Clustergrenze, der Restdaten enthalten kann |
| RAM-Slack | Teil des Slack vom Dateiende bis zur Sektorgrenze (historisch mit RAM gefüllt) |
| MFT ($MFT) | Master File Table - NTFS-Metadatenbank mit Einträgen für jede Datei |
| USN-Journal ($UsnJrnl) | Änderungsjournal, das alle Datei-/Verzeichnisänderungen auf NTFS aufzeichnet |
| Alternate Data Streams | NTFS-Funktion, die mehrere Datenströme pro Datei ermöglicht (versteckte Speicherung) |
| $STANDARD_INFORMATION | MFT-Attribut mit Zeitstempeln, die durch Benutzermodusanwendungen geändert werden können |
| $FILE_NAME | MFT-Attribut mit Zeitstempeln, die nur durch den Kernel geändert werden können |
| Timestomping | Anti-forensische Technik, die Dateizeitstempel modifiziert, um Erkennung zu vermeiden |
Tools und Systeme
| Tool | Zweck |
|---|---|
| MFTECmd | Eric Zimmermans MFT- und USN-Journal-Parser mit CSV-Ausgabe |
| MFTExplorer | Interaktives GUI-Tool zur MFT-Analyse |
| analyzeMFT | Python MFT-Parser mit CSV/JSON-Ausgabe |
| The Sleuth Kit | Forensik-Toolkit für Dateisysteme (fls, icat, blkls, istat) |
| bulk_extractor | Feature-Extraktion aus Rohdaten einschließlich Slack-Space |
| NTFS Log Tracker | Werkzeug zum Parsen von $LogFile-Transaktionsdatensätzen |
| streams.exe | Sysinternals-Werkzeug zum Auflisten von NTFS Alternate Data Streams |
| Plaso | Super-Timeline-Werkzeug, das MFT und USN-Journal parst |
Häufige Szenarien
Szenario 1: Anti-Forensik-Erkennung durch Timestomping Vergleiche $STANDARD_INFORMATION-Zeitstempel mit $FILE_NAME-Zeitstempeln in MFT-Einträgen, markiere Dateien, bei denen $SI-Zeitstempel vor $FN-Zeitstempeln liegen (im normalen Betrieb unmöglich), identifiziere timgestompte Dateien als Beweis für bewusste Manipulation, korreliere mit anderen Timeline-Beweisen.
Szenario 2: Versteckte Daten in Alternate Data Streams Scanne nach ADS, die an Dateien über den standardmäßigen Zone.Identifier hinaus angehängt sind, extrahiere ADS-Inhalte zur Analyse, überprüfe auf versteckte ausführbare Dateien oder Dokumente in ADS, korreliere ADS-Erstellung mit der Benutzeraktivitätstimeline, dokumentiere Ergebnisse als Beweismittel.
Szenario 3: Wiederherstellung gelöschter Dateien aus der MFT Parse MFT nach inaktiven (gelöschten) Einträgen, extrahiere Dateinamen, Größen und Zeitstempel gelöschter Dateien, stelle Dateiinhalte mit icat wieder her, wenn Datencluster nicht überschrieben wurden, erstelle eine Liste gelöschter Beweisdateien, korreliere mit USN-Journal-Löschereignissen.
Szenario 4: Rekonstruktion der Dateiaktivität aus dem USN-Journal Parse das USN Change Journal für den Untersuchungszeitraum, identifiziere Dateierstellungs-, Änderungs-, Umbenennungs- und Löschungsereignisse, rekonstruiere die Abfolge der Dateioperationen, erkenne Beweise für Datenstaging (Muster: Erstellen, Kopieren, Komprimieren, Löschen), identifiziere anti-forensisches Datei-Wiping.
Ausgabeformat
Dateisystem-Artefakt-Analyse:
Volume: NTFS (Partition 2, 465 GB)
Clustergröße: 4096 Bytes
MFT-Analyse:
Gesamte Einträge: 456.789
Aktive Dateien: 234.567
Gelöschte Einträge: 12.345 (8.901 mit wiederherstellbaren Metadaten)
Timgestompte Dateien: 23 (SI/FN-Unterschied erkannt)
USN-Journal:
Geparste Datensätze: 2.345.678
Datumsbereich: 2024-01-01 bis 2024-01-20
Dateierstellungen: 45.678
Dateilöschungen: 23.456
Dateiumbenennungen: 12.345
Alternate Data Streams:
Gesamt ADS gefunden: 1.234
Zone.Identifier: 890 (heruntergeladene Dateien)
Benutzerdefinierte/verdächtige ADS: 5 (versteckte Daten erkannt)
Slack-Space:
Gesamter Slack: 12,3 GB
Keyword-Treffer: 45 (Passwörter, Kreditkarten)
Aus Slack herausgeschnittene Dateien: 23
Verdächtige Funde:
- 23 Dateien mit timgestompten Zeitstempeln
- 5 Dateien mit versteckten ADS, die Daten enthalten
- USN zeigt Massenlöschungen am 2024-01-18 (Anti-Forensik)
…
[abgeschnitten für Übersetzung]