MFTの深淵:削除された痕跡を掘り起こすメモリフォレンジックの最前線
サイバー攻撃の痕跡、特にファイルシステム上の「消された」証拠を追跡することは、インシデントレスポンス(IR)やデジタルフォレンジック(DFIR)における最も骨太な作業の一つだ。多くのセキュリティ担当者は、ログ解析やネットワークトラフィックの監視に注力しがちだが、攻撃者が最も頻繁に利用する、そして最も見落とされやすい「隠れ蓑」こそ、ファイルシステム、特にNTFSのMaster File Table(MFT)に潜んでいる。
本稿では、単なるMFTの構造解説に留まらず、メモリフォレンジックの観点からMFTエントリを深掘りし、ファイル属性やタイムスタンプの不整合を暴くことで、攻撃者が巧妙に隠蔽したファイルや削除された証跡を特定する実践的な手法を、セキュリティアーキテクトやチーフホワイトハッカー、テックリードといった、現場の最前線で戦う諸氏に向けて、惜しみなく解説する。
MFTの「生きた」構造:メモリ上での振る舞いを理解する
MFTは、NTFSファイルシステムの中核であり、ディスク上の全ファイルとディレクトリのエントリ(レコード)の集合体だ。各エントリは、ファイル名、サイズ、属性、セキュリティディスクリプタ、そしてデータ本体へのポインタ(あるいはデータ本体そのもの)といった、ファイルに関するメタデータを保持している。
しかし、我々がDFIRの現場で直面するのは、ディスクイメージそのものだけではない。しばしば、メモリダンプからMFTの断片や、ディスク上では削除されたとマークされていてもメモリ上に残存するMFTエントリを解析する必要に迫られる。攻撃者は、マルウェアの実行や不正なツールの使用、あるいは機密情報の窃取といった活動の痕跡を消すために、ファイルを削除するだけでなく、MFTエントリ自体を改変したり、あるいはメモリ上にその痕跡を残したままディスク上から消去したりする。
ここで重要なのは、MFTエントリが単なる静的なデータ構造ではなく、OSのメモリ管理やキャッシュ機構によって、ある程度「生きた」状態でメモリ上に存在しうるという事実だ。特に、頻繁にアクセスされるファイルや、現在アクティブなプロセスに関連するファイルのエントリは、ディスクよりもメモリ上にキャッシュされている可能性が高い。
タイムスタンプの不整合:攻撃者の「沈黙」を暴く鍵
MFTエントリには、$STANDARD_INFORMATION属性内に、以下の4つのタイムスタンプが含まれる。
- $STANDARD_INFORMATION\_TIME\_FILE\_CREATION: ファイル作成時刻
- $STANDARD\_INFORMATION\_TIME\_FILE\_MODIFICATION: ファイル最終更新時刻
- $STANDARD\_INFORMATION\_TIME\_FILE\_LAST\_ACCESS: ファイル最終アクセス時刻
- $STANDARD\_INFORMATION\_TIME\_FILE\_MFT\_MODIFICATION: MFTエントリ自体の変更時刻
攻撃者がファイルを削除したり、その存在を隠蔽したりする際、これらのタイムスタンプを操作することが頻繁に見られる。例えば、
1. 削除: ファイルを削除すると、MFTエントリは「削除済み」としてマークされ、データ領域は再利用可能となる。しかし、MFTエントリ自体は、ディスク上の別の場所で再利用されるまで、あるいはディスクのフォーマットが行われるまで、そのまま残存する可能性がある。メモリ上では、この削除済みエントリがキャッシュされていることも少なくない。
2. タイムスタンプの改変: 攻撃者は、SetFileTimeのようなAPIや、より低レベルなツールを用いて、これらのタイムスタンプを過去の日付に書き換えることで、あたかもそのファイルが長期間アクセスされていないかのように見せかけようとする。あるいは、攻撃活動が行われた日時と矛盾しないように、意図的に調整することもある。
3. MFTエントリの改変: さらに高度な攻撃者は、MFTエントリの$FILE_NAME属性に格納されているファイル名や、$STANDARD_INFORMATION属性のタイムスタンプそのものを直接書き換える。これにより、ファイルが「最初から存在しなかった」かのように見せかけることも可能だ。
これらのタイムスタンプの不整合、特に「最終更新時刻」よりも「作成時刻」が新しい、あるいは「MFT変更時刻」が「最終更新時刻」よりも古い、といった状況は、ファイルシステムに対する不正な操作が行われた強力な兆候となる。
メモリダンプからのMFT解析:実践的なアプローチ
メモリダンプからMFTエントリを解析する際には、専用のツールが不可欠となる。Volatility 3のような強力なメモリフォレンジックフレームワークは、ntfsプラグインなどを提供しており、メモリ上に存在するNTFS構造を抽出・解析するのに役立つ。
Volatility 3 を用いたMFTエントリの抽出と解析
ここでは、Volatility 3を用いて、メモリダンプからMFTエントリを抽出し、そのタイムスタンプの不整合を検出する基本的なワークフローを示す。
まず、対象となるメモリダンプファイル(例: memdump.vmem)と、そのシステムプロファイル(例: windows.2019.17763.v1)を指定して、Volatility 3を実行する。
# Volatility 3 を実行し、MFT関連の情報を抽出するコマンド例
# 'windows.2019.17763.v1' は、対象システムのプロファイル名に置き換えてください。
python vol.py -f memdump.vmem --profile windows.2019.17763.v1 ntfs.mftscan
このntfs.mftscanプラグインは、メモリ上に存在するMFTエントリをスキャンし、その情報をリストアップしてくれる。出力結果は、各エントリのMFTレコード番号、ファイル名、サイズ、そしてタイムスタンプ(UTC形式)などを含む。
# Volatility 3 (ntfs.mftscan) の出力例(簡略化)
[<MFT_ENTRY 0x0000000000000000> Name: $MFT Size: 1024 Flags: InUse Parent: 0x0
Creation Time: 2023-10-27 10:00:00 UTC
Modification Time: 2023-10-27 10:00:00 UTC
Access Time: 2023-10-27 10:00:00 UTC
MFT Modification Time: 2023-10-27 10:00:00 UTC]
[<MFT_ENTRY 0x0000000000000004> Name: $LogFile Size: 4096 Flags: InUse Parent: 0x0
Creation Time: 2023-10-27 10:05:00 UTC
Modification Time: 2023-10-27 10:05:00 UTC
Access Time: 2023-10-27 10:05:00 UTC
MFT Modification Time: 2023-10-27 10:05:00 UTC]
[<MFT_ENTRY 0x0000000000000008> Name: ImportantDocument.docx Size: 15360 Flags: InUse Parent: 0x0
Creation Time: 2023-10-26 09:30:00 UTC
Modification Time: 2023-10-27 11:00:00 UTC
Access Time: 2023-10-27 11:15:00 UTC
MFT Modification Time: 2023-10-27 11:10:00 UTC]
[<MFT_ENTRY 0x0000000000000010> Name: DeletedFile.tmp Size: 5120 Flags: InUse Parent: 0x0
Creation Time: 2023-10-25 14:00:00 UTC
Modification Time: 2023-10-25 14:05:00 UTC
Access Time: 2023-10-25 14:05:00 UTC
MFT Modification Time: 2023-10-25 14:03:00 UTC]
この出力から、各エントリのタイムスタンプを比較し、不整合がないかを確認する。例えば、DeletedFile.tmpは、MFTエントリ自体はディスク上では「削除済み」とマークされているかもしれないが、メモリ上にキャッシュされていれば、上記のように表示される可能性がある。しかし、この例ではタイムスタンプに明らかな不整合は見られない。
タイムスタンプの不整合検出スクリプト(Python)
より体系的にタイムスタンプの不整合を検出するために、Volatility 3の出力をパースし、分析するPythonスクリプトを作成する。
import datetime
def analyze_mft_timestamps(mft_entries):
"""
MFTエントリのリストを受け取り、タイムスタンプの不整合を検出する関数。
Args:
mft_entries (list): Volatility 3のntfs.mftscanプラグインから得られたMFTエントリのリスト。
各エントリは辞書形式で、'name', 'creation_time', 'modification_time',
'access_time', 'mft_modification_time' などのキーを持つことを想定。
タイムスタンプはdatetimeオブジェクトであること。
"""
print("--- MFT Timestamp Anomaly Detection ---")
anomalies_found = False
for entry in mft_entries:
name = entry.get('name', 'N/A')
creation_time = entry.get('creation_time')
modification_time = entry.get('modification_time')
access_time = entry.get('access_time')
mft_modification_time = entry.get('mft_modification_time')
# タイムスタンプが取得できない場合はスキップ
if not all([creation_time, modification_time, access_time, mft_modification_time]):
# print(f"[-] Skipping entry '{name}' due to missing timestamps.")
continue
# --- 検出ロジック ---
# 1. 作成時刻 > 更新時刻 の場合(論理的にありえない)
if creation_time > modification_time:
print(f"[!] Anomaly detected for '{name}': Creation time ({creation_time}) is after Modification time ({modification_time}).")
anomalies_found = True
# 2. 更新時刻 > アクセス時刻 の場合(通常はありえないが、特定のシナリオでは発生しうる。注意が必要)
# if modification_time > access_time:
# print(f"[!] Potential anomaly for '{name}': Modification time ({modification_time}) is after Access time ({access_time}). Investigate further.")
# anomalies_found = True
# 3. MFT変更時刻 > 更新時刻 の場合(通常はありえない)
if mft_modification_time > modification_time:
print(f"[!] Anomaly detected for '{name}': MFT Modification time ({mft_modification_time}) is after File Modification time ({modification_time}).")
anomalies_found = True
# 4. MFT変更時刻 > アクセス時刻 の場合(通常はありえない)
if mft_modification_time > access_time:
print(f"[!] Anomaly detected for '{name}': MFT Modification time ({mft_modification_time}) is after File Access time ({access_time}).")
anomalies_found = True
# --- 削除済みファイルの兆候 ---
# MFTエントリ自体が削除済みとしてマークされている場合、
# Volatilityの出力でフラグが設定されることがある。
# ここでは、エントリの存在自体が「削除済みだがメモリ上に残存」の証拠となる。
# ファイル名に「削除済み」という文字列が含まれる場合(ただし、これは攻撃者が意図的に残す可能性もある)
# if "deleted" in name.lower():
# print(f"[*] Entry '{name}' might be a deleted file, but remains in memory.")
# anomalies_found = True
if not anomalies_found:
print("[-] No significant timestamp anomalies found based on the defined rules.")
# --- Volatility 3 の出力をパースする処理(簡略化) ---
# Volatility 3 の出力を直接Pythonスクリプトに渡すのは複雑なため、
# ここではダミーデータで関数をテストします。
# 実際には、Volatilityの出力をファイルに保存し、それを読み込むか、
# VolatilityのAPIを直接利用することになります。
# ダミーのMFTエントリデータ(datetimeオブジェクトに変換済みを想定)
dummy_mft_data = [
{
'name': '$MFT',
'creation_time': datetime.datetime(2023, 10, 27, 10, 0, 0, tzinfo=datetime.timezone.utc),
'modification_time': datetime.datetime(2023, 10, 27, 10, 0, 0, tzinfo=datetime.timezone.utc),
'access_time': datetime.datetime(2023, 10, 27, 10, 0, 0, tzinfo=datetime.timezone.utc),
'mft_modification_time': datetime.datetime(2023, 10, 27, 10, 0, 0, tzinfo=datetime.timezone.utc)
},
{
'name': 'NormalFile.txt',
'creation_time': datetime.datetime(2023, 10, 26, 9, 0, 0, tzinfo=datetime.timezone.utc),
'modification_time': datetime.datetime(2023, 10, 27, 14, 30, 0, tzinfo=datetime.timezone.utc),
'access_time': datetime.datetime(2023, 10, 27, 15, 0, 0, tzinfo=datetime.timezone.utc),
'mft_modification_time': datetime.datetime(2023, 10, 27, 14, 35, 0, tzinfo=datetime.timezone.utc)
},
{
'name': 'ManipulatedFile.dat',
'creation_time': datetime.datetime(2023, 10, 20, 12, 0, 0, tzinfo=datetime.timezone.utc),
'modification_time': datetime.datetime(2023, 10, 27, 11, 0, 0, tzinfo=datetime.timezone.utc), # 攻撃者が操作した可能性
'access_time': datetime.datetime(2023, 10, 27, 11, 0, 0, tzinfo=datetime.timezone.utc), # 攻撃者が操作した可能性
'mft_modification_time': datetime.datetime(2023, 10, 27, 10, 55, 0, tzinfo=datetime.timezone.utc) # MFT自体も操作された可能性
},
{
'name': 'BadTimestampFile.log',
'creation_time': datetime.datetime(2023, 10, 27, 9, 0, 0, tzinfo=datetime.timezone.utc),
'modification_time': datetime.datetime(2023, 10, 26, 18, 0, 0, tzinfo=datetime.timezone.utc), # 作成時刻より古い(ありえない)
'access_time': datetime.datetime(2023, 10, 27, 9, 0, 0, tzinfo=datetime.timezone.utc),
'mft_modification_time': datetime.datetime(2023, 10, 27, 9, 0, 0, tzinfo=datetime.timezone.utc)
},
{
'name': 'MftModifiedAfterFile.tmp',
'creation_time': datetime.datetime(2023, 10, 27, 12, 0, 0, tzinfo=datetime.timezone.utc),
'modification_time': datetime.datetime(2023, 10, 27, 13, 0, 0, tzinfo=datetime.timezone.utc),
'access_time': datetime.datetime(2023, 10, 27, 13, 0, 0, tzinfo=datetime.timezone.utc),
'mft_modification_time': datetime.datetime(2023, 10, 27, 13, 5, 0, tzinfo=datetime.timezone.utc) # ファイル更新よりMFT変更が後(ありえない)
}
]
# ダミーデータで関数を実行
analyze_mft_timestamps(dummy_mft_data)
このスクリプトは、単純なタイムスタンプの論理的矛盾を検出する。重要なのは、creation_time > modification_timeやmft_modification_time > modification_timeといった、ファイルシステムが通常取りえない状態を検出することだ。これらの異常は、攻撃者による意図的な操作、あるいはOSのキャッシュ機構とディスクへの書き込みタイミングのずれなど、調査すべき痕跡を示唆している。
攻撃者の盲点:削除済みファイルの「幽霊」を追う
攻撃者がファイルを削除する際、MFTエントリは「削除済み」としてマークされるだけで、ディスク上のデータ自体はすぐには上書きされない。メモリフォレンジックでは、この「削除済み」とマークされたMFTエントリが、OSのメモリキャッシュに残存しているケースを狙う。
Volatility 3のntfs.mftプラグイン(mftscanとは異なり、より詳細なエントリ情報を取得)は、MFTエントリのフラグ情報などを解析できる。削除済みエントリは、$FILE_NAME属性の「PARENT\_DIR\_REF」フィールドが指すディレクトリのエントリにおいて、そのファイル名が「削除済み」としてマークされている場合がある。
さらに、$STANDARD_INFORMATION属性のタイムスタンプが全て過去の日付になっており、かつMFTエントリ自体はアクティブ(InUseフラグが立っている)である場合、これは攻撃者がファイルを削除した後に、そのMFTエントリを再利用して別のファイルを作成したが、タイムスタンプの更新を怠った、といったシナリオを示唆する。
削除済みファイルデータ本体の復元可能性
MFTエントリからファイル名やサイズ、データ本体へのポインタ(またはデータ本体そのもの)を特定できれば、たとえMFTエントリが「削除済み」とマークされていても、ディスク上の対応するクラスタがまだ上書きされていなければ、データ本体を復元できる可能性がある。メモリダンプの場合、そのデータ断片がメモリ上にキャッシュされていれば、直接抽出できることも稀にある。
しかし、攻撃者はしばしば、削除したファイルを完全に消去するために、ディスクワイパーのようなツールを使用したり、あるいはMFTエントリ自体を不正に操作して、データ本体へのポインタを破壊したりする。これらの高度な隠蔽手法に対しては、より低レイヤでのディスク構造解析や、ファイルシステムのジャーナルログ($LogFile)の解析などが組み合わされることになる。
耐量子暗号とDFIR:未来への備え
現代のサイバーセキュリティにおける重要な話題として、耐量子暗号(Post-Quantum Cryptography, PQC)への移行がある。これは、現在の公開鍵暗号が量子コンピュータによって容易に解読されるリスクに対応するためのものだ。
DFIRの観点から見ると、PQCへの移行は、暗号化された通信や保存データのフォレンジック調査に大きな影響を与える。
- 暗号化された通信の解析: 現在、TLS/SSLなどの暗号化通信は、過去のパケットをキャプチャしても、鍵がなければ復号できない。PQC時代になっても、これは変わらないだろう。しかし、将来的に、量子コンピュータで現在の暗号が破られるようになれば、過去の通信データが「復号可能」になるリスクもゼロではない。
- 保存データのフォレンジック: 攻撃者がシステムに侵入し、PQCで保護されたデータを窃取した場合、たとえ量子コンピュータが普及しても、そのデータは安全だと考えられる。しかし、DFIR担当者は、PQCアルゴリズム自体の実装上の脆弱性や、鍵管理の不備、あるいはPQC移行プロセスにおける「移行期間」の脆弱性を突く攻撃に備える必要がある。
- フォレンジックツールの進化: 将来、PQCが普及した環境でのフォレンジック調査は、より複雑になることが予想される。新しい暗号化方式に対応したツールや、鍵管理システムと連携したフォレンジック手法の開発が求められるだろう。
MFTの解析も、将来的に暗号化される可能性のあるメタデータや、ファイルシステムレベルでの暗号化(例: BitLocker)と組み合わさることで、より難解になるかもしれない。しかし、ファイルシステム構造やメモリ上の振る舞いといった根本的な原理を理解していれば、どのような暗号化が施されていても、その「隙間」を見つけ出す糸口は必ず見つかるはずだ。
生成AIにおけるガードレイル設計とMFTの類似性
近年、生成AI(Generative AI)におけるプロンプトインジェクション(Prompt Injection)攻撃が大きな問題となっている。これは、AIモデルに悪意のある指示を注入し、本来意図しない動作をさせる攻撃だ。
このプロンプトインジェクションに対する防御層、いわゆる「ガードレイル」の設計は、MFTの解析における「痕跡の隠蔽と検出」というテーマと、ある種の類似性を持っている。
- AIモデルの「MFT」: 生成AIモデルの内部状態や、学習データ、あるいはプロンプト処理の過程は、ある意味でAIの「MFT」と見なせる。攻撃者は、この「MFT」に不正なデータを注入(プロンプトインジェクション)することで、モデルの振る舞いを改変しようとする。
- ガードレイルの役割: ガードレイルは、ユーザーからの入力(プロンプト)を検証し、悪意のある指示や不適切なコンテンツをフィルタリングする役割を担う。これは、MFTエントリのタイムスタンプの整合性をチェックしたり、不正な属性を検出したりするDFIRのプロセスに似ている。
- 検知と防御のイタチごっこ: 攻撃者はガードレイルの検知ロジックを回避しようと、巧妙なプロンプトを考案する。DFIR担当者は、攻撃者が残した痕跡を分析し、新たな検知ルールや防御策を開発する。このイタチごっこは、AIセキュリティとDFIRの世界で共通して見られる構図だ。
生成AIのガードレイル設計においては、単にキーワードでフィルタリングするだけでなく、プロンプトの意図や文脈を理解し、AIモデルの内部状態を監視するような、より洗練されたアプローチが必要となる。これは、MFTのタイムスタンプの不整合を、単なる「異常」としてではなく、「攻撃者の意図」という文脈で解釈するDFIRの高度な分析能力に通じるものがある。
まとめ:痕跡は必ず残る
MFTの構造解析とメモリフォレンジックは、攻撃者が最も隠蔽したがる領域であり、かつ最も確実な証拠が眠っている場所だ。タイムスタンプの不整合、削除済みエントリの残存、そしてそれらメモリ上での振る舞いを深く理解することで、我々は攻撃者の巧妙な隠蔽工作を暴き、インシデントの真相に迫ることができる。
サイバー攻撃の技術は日々進化し、耐量子暗号への移行や生成AIといった新たな領域が次々と登場する。しかし、ファイルシステムやメモリといった低レイヤの挙動を理解し、その「痕跡」を追跡する能力は、どのような時代においても、最高峰の防衛技術と監査の基盤となる。
常に、攻撃者の視点に立ち、彼らが「消した」と信じているものが、実は「見えざるもの」としてメモリ上に息づいている可能性を忘れてはならない。その「幽霊」こそが、我々の真実を語りかけてくれるのだ。
コメント