スラックスペースとファイルシステムアーティファクトの分析 は何をしますか?
使用するタイミング
- ファイルシステムのスラックスペースに隠されたデータや残存データを検索する場合
- NTFSマスターファイルテーブル(MFT)エントリを分析して削除ファイルのメタデータを取得する場合
- USN変更ジャーナルからファイル操作を再構築する場合
- データやマルウェアを隠すために使用される代替データストリーム(ADS)を検出する場合
- 標準的なファイル復旧では不十分な、深いフォレンジック分析が必要な場合
前提条件
- NTFSファイルシステムのフォレンジックディスクイメージ
- The Sleuth Kit(TSK)ツール: istat, icat, fls, blkls, blkstat
- MFTECmd(Eric Zimmerman): MFT解析用
- MFTExplorer: 対話型MFT分析用
- NTFS構造の理解(MFT, $UsnJrnl, $LogFile, ADS)
- PythonとanalyzeMFTまたはmftライブラリ: 自動解析用
ワークフロー
ステップ 1: NTFSファイルシステムアーティファクトの特定と抽出
# パーティションレイアウトの特定
mmls /cases/case-2024-001/images/evidence.dd
# 主要なNTFSシステムファイルの抽出
# $MFT - マスターファイルテーブル
icat -o 2048 /cases/case-2024-001/images/evidence.dd 0 > /cases/case-2024-001/ntfs/MFT
# $UsnJrnl:$J - USN変更ジャーナル
icat -o 2048 /cases/case-2024-001/images/evidence.dd 62-128 > /cases/case-2024-001/ntfs/UsnJrnl_J
# $LogFile - トランザクションログ
icat -o 2048 /cases/case-2024-001/images/evidence.dd 2 > /cases/case-2024-001/ntfs/LogFile
# ボリュームからすべてのスラックスペースを抽出
blkls -s -o 2048 /cases/case-2024-001/images/evidence.dd > /cases/case-2024-001/ntfs/slack_space.raw
# ファイルシステム情報の取得
fsstat -o 2048 /cases/case-2024-001/images/evidence.dd | tee /cases/case-2024-001/ntfs/fs_info.txt
ステップ 2: マスターファイルテーブル(MFT)の分析
# MFTECmd(Eric Zimmerman)でMFTを解析
MFTECmd.exe -f "C:\cases\ntfs\MFT" --csv "C:\cases\analysis\" --csvf mft_analysis.csv
# analyzeMFT(Python)で解析
pip install analyzeMFT
analyzeMFT.py -f /cases/case-2024-001/ntfs/MFT \
-o /cases/case-2024-001/analysis/mft_analysis.csv \
-c
# PythonによるカスタムMFT分析
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()
# 削除ファイルを復旧対象としてフラグ付け
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
})
# タイムスタンプ改ざんの検出 (MFT更新時刻 != $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日の差
suspicious_files.append({
'filename': filename,
'si_modified': str(si_modified),
'fn_modified': str(fn_modified),
'delta': str(si_modified - fn_modified)
})
print(f"=== 削除ファイル(復旧可能なメタデータ) ===")
print(f"合計: {len(deleted_files)}")
for f in deleted_files[:20]:
print(f" [{f['modified']}] {f['filename']} ({f['size']} バイト)")
print(f"\n=== タイムスタンプ改ざんの可能性 ===")
print(f"疑わしい合計: {len(suspicious_files)}")
for f in suspicious_files[:10]:
print(f" {f['filename']}: $SI={f['si_modified']}, $FN={f['fn_modified']} (差: {f['delta']})")
PYEOF
ステップ 3: スラックスペース内の隠しデータの分析
# スラックスペースから文字列を検索
strings -a /cases/case-2024-001/ntfs/slack_space.raw > /cases/case-2024-001/analysis/slack_strings.txt
# スラックスペース内の特定パターンを検索
grep -iab "password\|secret\|confidential\|credit.card\|ssn" \
/cases/case-2024-001/ntfs/slack_space.raw > /cases/case-2024-001/analysis/slack_keywords.txt
# 個別ファイルのスラックを分析
python3 << 'PYEOF'
import struct
# ファイルスラックの構成:
# 1. RAMスラック: ファイル終了点から次のセクタ境界までのバイト(RAM内容またはゼロで埋められる)
# 2. ドライブスラック: ファイル最終セクタ以降のクラスタ内の残りセクタ
# 特定のMFTエントリのスラックを分析
# Sleuth Kitを使用して特定ファイルのファイルスラックを取得
import subprocess
# ファイル詳細の取得
result = subprocess.run(
['istat', '-o', '2048', '/cases/case-2024-001/images/evidence.dd', '14523'],
capture_output=True, text=True
)
print(result.stdout)
# 出力にはデータランが表示されます - 最後のクラスタにスラックデータが含まれている可能性があります
# スラックサイズの計算: (割り当てサイズ - ファイルサイズ) バイト
PYEOF
# スラックスペース内のファイルシグネチャ(埋め込みファイル)を検索
foremost -t jpg,pdf,zip -i /cases/case-2024-001/ntfs/slack_space.raw \
-o /cases/case-2024-001/carved/slack_carved/
# bulk_extractorを使用してスラック内の構造化データを検索
bulk_extractor -o /cases/case-2024-001/analysis/bulk_extract/ \
/cases/case-2024-001/ntfs/slack_space.raw
ステップ 4: USN変更ジャーナルの解析
# MFTECmdでUSNジャーナルを解析
MFTECmd.exe -f "C:\cases\ntfs\UsnJrnl_J" --csv "C:\cases\analysis\" --csvf usn_journal.csv
# Python USNジャーナル解析
pip install pyusn
python3 << 'PYEOF'
import struct
import csv
from datetime import datetime, timedelta
def parse_usn_record(data, offset):
"""単一の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をdatetimeに変換
ts = datetime(1601, 1, 1) + timedelta(microseconds=timestamp // 10)
# 理由フラグをデコード
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
# ジャーナルを解析
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 # ゼロをスキップ
# 削除イベントでフィルタリング
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"USNレコード総数: {len(records)}")
print(f"ファイル作成: {len(creations)}")
print(f"ファイル削除: {len(deletions)}")
print(f"ファイル名変更: {len(renames)}")
print("\n=== 最近の削除 ===")
for r in deletions[-20:]:
print(f" [{r['timestamp']}] 削除: {r['filename']} (MFT#{r['mft_entry']})")
# 完全なジャーナルを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
ステップ 5: 代替データストリームの検出と分析
# イメージ内のすべての代替データストリームをリスト
find /mnt/evidence -exec getfattr -d {} \; 2>/dev/null | grep -i "ads\|zone\|stream"
# Sleuth Kitを使用してADSを検索
fls -r -o 2048 /cases/case-2024-001/images/evidence.dd | grep ":" | \
tee /cases/case-2024-001/analysis/ads_list.txt
# 特定のADSコンテンツを抽出
# 形式: icat イメージ inode:ads名
icat -o 2048 /cases/case-2024-001/images/evidence.dd 14523:hidden_stream \
> /cases/case-2024-001/analysis/extracted_ads.bin
# Zone.Identifierストリーム(ダウンロード元追跡)を確認
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のコンテンツは以下を示す:
# [ZoneTransfer]
# ZoneId=3 (3 = インターネット。ファイルがダウンロードされたことを示す)
# ReferrerUrl=https://malicious-site.com/payload.exe
# HostUrl=https://cdn.malicious-site.com/payload.exe
主要な概念
| 概念 | 説明 |
|---|---|
| ファイルスラック | ファイル終了点とクラスタ境界の間の未使用領域で、残存データを含む |
| RAMスラック | ファイル終了点からセクタ境界までのスラック部分(歴史的にRAM内容で埋められる) |
| MFT ($MFT) | マスターファイルテーブル - すべてのファイルのエントリを持つNTFSメタデータデータベース |
| USNジャーナル ($UsnJrnl) | NTFS上のすべてのファイル/ディレクトリ変更を記録する変更ジャーナル |
| 代替データストリーム | ファイルごとに複数のデータストリームを可能にするNTFS機能(隠しストレージ) |
| $STANDARD_INFORMATION | ユーザーモードアプリケーションによって変更可能なタイムスタンプを持つMFT属性 |
| $FILE_NAME | カーネルによってのみ変更可能なタイムスタンプを持つMFT属性 |
| タイムスタンプ改ざん | ファイルタイムスタンプを変更して検出を回避するアンチフォレンジック技術 |
ツールとシステム
| ツール | 目的 |
|---|---|
| MFTECmd | Eric ZimmermanのMFTおよびUSNジャーナルパーサー、CSV出力対応 |
| MFTExplorer | 対話型MFT分析用GUIツール |
| analyzeMFT | CSV/JSON出力対応のPython MFTパーサー |
| The Sleuth Kit | ファイルシステムフォレンジックツールキット(fls, icat, blkls, istat) |
| bulk_extractor | スラックスペースを含む生データからの特徴抽出 |
| NTFS Log Tracker | $LogFileトランザクションレコード解析ツール |
| streams.exe | NTFS代替データストリームをリストするSysinternalsツール |
| Plaso | MFTとUSNジャーナルを解析するスーパータイムラインツール |
一般的なシナリオ
シナリオ1: タイムスタンプ改ざんによるアンチフォレンジック検出 MFTエントリ内の$STANDARD_INFORMATIONタイムスタンプと$FILE_NAMEタイムスタンプを比較し、$SIタイムスタンプが$FNタイムスタンプより古いファイル(通常の操作では不可能)をフラグ付けし、タイムスタンプが改ざんされたファイルを意図的な操作の証拠として特定し、他のタイムライン証拠と相関させる。
シナリオ2: 代替データストリーム内の隠しデータ 標準のZone.Identifierを超えてファイルに添付されたADSをスキャンし、ADSコンテンツを抽出して分析し、ADSに保存された隠し実行可能ファイルやドキュメントを確認し、ADS作成をユーザーアクティビティタイムラインと相関させ、証拠として文書化する。
シナリオ3: MFTからの削除ファイル復旧 非アクティブ(削除済み)エントリのMFTを解析し、削除ファイルのファイル名、サイズ、タイムスタンプを抽出し、データクラスタが上書きされていない場合はicatを使用してファイル内容を復旧し、削除された証拠ファイルのリストを作成し、USNジャーナル削除イベントと相関させる。
シナリオ4: USNジャーナルからのファイルアクティビティ復元 調査期間のUSN変更ジャーナルを解析し、ファイル作成、変更、名前変更、削除イベントを特定し、ファイル操作の順序を再構築し、データステージングの証拠(作成、コピー、圧縮、削除のパターン)を検出し、アンチフォレンジックファイルワイピングを特定する。
出力形式
ファイルシステムアーティファクト分析:
ボリューム: NTFS (パーティション2, 465 GB)
クラスタサイズ: 4096 バイト
MFT分析:
総エントリ数: 456,789
アクティブファイル: 234,567
削除エントリ: 12,345 (うち8,901は復旧可能なメタデータ)
タイムスタンプ改ざんファイル: 23 (SI/FN不一致を検出)
USNジャーナル:
解析レコード数: 2,345,678
日付範囲: 2024-01-01~2024-01-20
ファイル作成: 45,678
ファイル削除: 23,456
ファイル名変更: 12,345
代替データストリーム:
検出ADS総数: 1,234
Zone.Identifier: 890 (ダウンロードファイル)
カスタム/疑わしいADS: 5 (隠しデータ検出)
スラックスペース:
総スラック容量: 12.3 GB
キーワードヒット: 45 (パスワード、クレジットカード)
スラックからカービングされたファイル: 23
疑わしい発見:
- タイムスタンプ改ざんされた23ファイル
- データが隠されたADSを持つ5ファイル
- USNは2024-01-18に大量削除を示す(アンチフォレンジック)
…
[翻訳のため省略]