現場のトリアージを変えるメモリフォレンジック:YARAルールによるプロセス空間の深淵の覗き方
インシデントレスポンスの現場において、私たちは常に時間との戦いを強いられている。巧妙化するAPTグループやランサムウェアのオペレーターは、ファイルレス攻撃、プロセス・インジェクション、そしてメモリ上だけで動作するペイロードを駆使して、従来のディスクベースのフォレンジックをあざ笑うかのように痕跡を消し去る。
「ディスクには何もない。しかし、システムは明らかに異常な挙動を示している」
この絶望的な状況を打破するための唯一の鍵が、メモリフォレンジックだ。そして、数ギガバイト、時には数百ギガバイトにも及ぶ生々しいメモリダンプの海から、わずかな悪意の断片を正確に釣り上げるための最強の武器が YARA である。
今回は、単に「YARAの使い方の説明」をなぞるのではない。サイバー犯罪者が OS のメモリ管理機構の盲点をどう突き、私たちがそれをどのように逆手に取って検出的優位性を築くのか、その低レイヤの攻防ロジックと実践的なカスタムシグネチャの設計思想を徹底的に紐解いていこう。
—
1. 低レイヤの現実:なぜメモリ上のシグネチャマッチングは一筋縄ではいかないのか
現代のオペレーティングシステム(Windows や Linux)のメモリ管理は、仮想メモリとページング機構によって高度に抽象化されている。攻撃者が用いるプロセス・ホロウイング(Process Hollowing)やアトミシティを無視したDLLインジェクションは、物理メモリ上では連続しておらず、断片化されたページフレーム(Page Frame)に散らばっている。
Volatility などのフレームワークを用いてメモリダンプを抽出し、YARAを適用する際、私たちは以下のハードルに直面する。
- 不連続なアドレス空間: プロセスが持つ仮想アドレス空間が、物理メモリ上ではページ単位でバラバラに配置されている現実。
- 難読化と暗号化: 高度なマルウェアは、メモリ上に展開された瞬間から自らのセクションを暗号化・復号しながら実行するため、静的なバイトパターンが変化する。
- カーネルモードの隠蔽: ダイレクト・カーネル・オブジェクト・マニピュレーション(DKOM)によってプロセスリストから unlink されたプロセスは、通常のプロセス列挙ではヒットせず、プールスキャン(
psscan)等を併用してメモリダンプ全体を対象にする必要がある。
だからこそ、YARAルールを書く際には、単なる文字列の羅列ではなく、「攻撃者が避けて通れないコードの構造上の特徴」や「メモリ特有のアーティファクト」を捉える必要があるのだ。
—
2. 実践的YARAルール設計:メモリ空間を射抜くカスタムシグネチャ
現場で使えるYARAルールは、ノイズ(誤検知)を極限まで削ぎ落としつつ、亜種によるバイパスを防ぐ汎用性を兼ね備えていなければならない。
以下に、メモリ上のプロセス空間において、リフレクティブDLLローダーやインジェクションされたコード片を検出するための高度なYARAルールの実例を示す。
rule Detect_Reflective_DLL_Loader_Memory {
meta:
description = "メモリダンプ内における一般的なリフレクティブDLLローダーのバイトパターン検出"
author = "SOC Analyst / DFIR Team"
date = "202X-MM-DD"
severity = "Critical"
reference = "Internal Threat Intelligence"
strings:
// x86/x64共通のAPIハッシュ解決ロジックや、PEヘッダのMZシグネチャを伴わないインジェクション領域を想定
// 典型的なリフレクティブローダーが持つプロローグとPEヘッダの兆候
$mz_header = { 4D 5A }
// リフレクティブローダーがAPI(LoadLibraryAやGetProcAddress等)を動的に解決するための典型的なスタック操作バイト列
// 例: 機能を呼び出すためにリターンアドレスを計算するポータブルなアセンブリパターン (call 0; pop reg 等)
$pos_independent_call = { E8 00 00 00 00 5B }
// メモリ上に不自然に存在するエクスポートディレクトリ名やセクション名 (.textの欠如など)
$sec_reloc = ".reloc" ascii wide
condition:
// 条件の定義:
// 1. MZヘッダが存在するが、それが通常のファイルパスと結びついていない領域(メモリマッピングの不整合)
// 2. 位置独立コード(PIC)の典型的な命令パターンが含まれている
uint16(0) == 5A4D and
$pos_independent_call and
not $sec_reloc
}
このルールの肝は、単に 4D 5A(MZヘッダ)を探すだけでなく、not $sec_reloc や位置独立コード(PIC)の挙動特性を組み込むことで、正当なアプリケーションのメモリ領域との差別化を図っている点にある。
—
3. Volatility 3 との統合:実戦におけるスキャンワークフロー
ルールを作成したら、次はインシデントレスポンスの現場でそれをどう回すかだ。Volatility 3 を用いたメモリフォレンジックにおいて、YARAスキャンは単体のプラグイン(windows.vadyara や yarascan)として実行できる。
現場のトリアージでは、全メモリを一括スキャンすると膨大な時間がかかるため、以下のステップで絞り込みを行うのが定石だ。
ステップ1: 疑わしいプロセスの特定
まずは不審なプロセスID(PID)や、親子関係の異常なプロセスをあぶり出す。
# プロセスツリーを確認し、異常なコンテキストで動作しているプロセスを特定する
python3 vol.py -f memdump.raw windows.pslist.PsList
ステップ2: 特定プロセスのアドレス空間に対するYARAスキャン
全体スキャンでI/Oネックを引き起こすのを防ぐため、疑わしいプロセスの仮想アドレス空間(VAD: Virtual Address Descriptor)に対してピンポイントでYARAを走らせる。
# 特定のPID(例: 4120)のメモリ空間に対してカスタムYARAルールを適用
python3 vol.py -f memdump.raw windows.vadyara.VadYara --yara-file custom_rules.yar --pid 4120
ここでヒットした仮想アドレス範囲から、直接ペイロードを切り出し(Dump)、静的解析およびサンドボックスでの挙動確認に回すのがプロのフォレンジックエンジニアの手法だ。
—
4. 高度な回避手法(アンチ・フォレンジック)への対抗策
サイバー犯罪者も進化している。彼らはメモリフォレンジックやYARAスキャンを妨害するため、以下のようなアンチ・フォレンジック技術を実装してくる。
1. ページパーミッションの操作(Memory Page Protection Tampering):
実行中のコードのページ属性を PAGE_EXECUTE_READWRITE から一時的に PAGE_NOACCESS や PAGE_READONLY に変更し、スキャンの瞬間に例外を発生させたり、スキャナーをクラッシュさせたりする。
2. スクランブルド・ストリングス:
YARAの文字列マッチングを回避するため、固定文字列をスタック上で動的に構築するか、簡易的なXORやロール演算で難読化してメモリ上に置く。
こうした高度な脅威に対抗するためには、YARAルール側に「ワイルドカード」や「正規表現」、そしてバイト列の変異を吸収する「Ngram」や「Bitmask」を活用した柔軟なシグネチャ設計が求められる。
例えば、単純な文字列 cmd.exe を探すのではなく、次のようにバイト単位での揺らぎを許容するパターンを組み込む。
rule Obfuscated_Command_Execution {
strings:
// "cmd.exe" がスタック上で1文字ずつ置かれるようなケースや、小刻みなXORに対応するワイルドカードパターン
$xor_cmd = { [0-4] 63 [0-4] 6D [0-4] 64 }
condition:
$xor_cmd
}
—
5. チーフホワイトハッカーからの提言:検知エンジニアリングの自動化へ
メモリフォレンジックにおけるYARAの活用は、単に「過去のインシデントを事後検証するためのツール」ではない。EDR(Endpoint Detection and Response)やXDRのカスタムルールとしてリアルタイムに組み込むことで、次世代のプロアクティブ防衛の基盤となる。
インシデントレスポンスの現場で得られた知見(TTPs)を、即座に高精度なYARAルールへ昇華させ、組織全体のメモリ空間を監視し続けること。この泥臭いフィードバックループを回せるかどうかが、セキュリティチームの真の実力を決める。
メモリの深淵には、攻撃者のすべての足跡が残されている。その足跡を正確に捉えるシグネチャを書き上げる技術こそが、現代のセキュリティアーキテクトに求められている最上位のスキルなのだ。
コメント