メモリフォレンジックの「証拠能力」を担保せよ:現場で泣かないためのチェーン・オブ・カストディ
インシデント発生時、君たちが最初に行うのは「サーバーの再起動」だろうか?もしそうなら、その瞬間に君たちは「事件の真相」を永遠に葬り去っていることになる。
メモリ(RAM)には、ディスクには残らない「生きている攻撃」の痕跡が詰まっている。ファイルレスマルウェアのペイロード、復号化されたパスワード、そして攻撃者のコマンド履歴。しかし、それを解析して「法廷で戦える証拠」にするためには、単にダンプを取ればいいという話ではない。今日は、プロの現場で必須となる証拠保全の作法(Chain of Custody)について、現場の泥臭い教訓を交えて解説する。
—
1. 「証拠能力」を殺す最大のミス:一貫性の欠如
メモリフォレンジックにおいて最も恐ろしいのは、取得したデータが「改ざんされていないこと」を証明できないことだ。法廷や監査において、「そのダンプファイル、後から君が書き換えたんじゃないのか?」と突っ込まれたとき、論理的に反論できなければ証拠は無価値になる。
証拠保全の鉄則は以下の3つだ。
1. 最小限の侵害(Minimizing Intrusion): ダンプ取得ツールすらもメモリ上の既存データを書き換える可能性がある。信頼できる静的なバイナリのみを使うこと。
2. ハッシュ値による完全性の証明: 取得した瞬間にMD5やSHA-256を算出し、記録する。
3. 厳密な記録(Chain of Custody): 「誰が、いつ、どのツールで、どの環境から」取得したか、その全行程をログに残す。
—
2. 実務:メモリ取得と証拠保全の手順
現場では、信頼できるポータブルなツール(LiMEやAVMLなど)を使用する。今回はクラウドネイティブ環境を想定し、Microsoftが公開しているLinux用メモリ取得ツールAVMLを例に挙げる。
証拠保全の自動化スクリプト(Bash)
単にコマンドを叩くだけでなく、ハッシュ値を生成し、メタデータを残すラッパーを作るのがプロのやり方だ。
#!/bin/bash
# メモリ保全用セキュアスクリプト
# 使用前にAVMLバイナリのハッシュ値を確認しておくこと
OUTPUT_FILE="memory_dump_$(date +%Y%m%d_%H%M%S).raw"
LOG_FILE="evidence_log.txt"
# 1. 取得開始時刻と環境の記録
echo "--- Evidence Collection Start ---" >> $LOG_FILE
echo "Timestamp: $(date)" >> $LOG_FILE
echo "Hostname: $(hostname)" >> $LOG_FILE
echo "Kernel: $(uname -a)" >> $LOG_FILE
# 2. メモリダンプ取得(AVML利用)
./avml --compress $OUTPUT_FILE
# 3. 証拠のハッシュ算出(完全性の証明)
sha256sum $OUTPUT_FILE >> $LOG_FILE
echo "--- Evidence Collection End ---" >> $LOG_FILE
# ここで生成されたログとファイルをオフラインのセキュアなストレージへ転送する
—
3. なぜ「インメモリ攻撃」が防げないのか?
攻撃者は、ディスク上に痕跡を残さないfilelessな手法を好む。例えば、Webアプリの脆弱性を突いてメモリ上でpythonコードを実行し、リバースシェルを張るようなケースだ。
脆弱なPHPコードの例(絶対に書いてはいけない)
system()関数やexec()関数をユーザー入力と組み合わせることは、フォレンジックの対象を作る行為に等しい。
// 危険な例:ユーザー入力を直接コマンドに渡している
$cmd = $_GET['cmd'];
system($cmd); // ここでメモリ上に悪意あるプロセスが常駐する
これを防ぐには、「そもそもOSコマンドを実行させない」のが最大の防御だ。どうしても必要な場合は、徹底的なホワイトリスト制限をかける。
セキュアな実装(Python: 外部コマンドの安全な呼び出し)
subprocessモジュールを使う場合でも、シェルを介さない(shell=False)のが鉄則だ。
import subprocess
def run_safe_command(user_input):
# リスト形式で引数を渡すことで、コマンドインジェクションを物理的に封じる
# shell=False にすることでシェル経由の攻撃を無効化する
try:
result = subprocess.run(
["/usr/bin/safe_binary", "--arg", user_input],
capture_output=True,
text=True,
check=True,
shell=False
)
return result.stdout
except subprocess.CalledProcessError as e:
# ここでログを残すことが事後解析の鍵になる
print(f"Attack attempt or error: {e}")
return None
—
4. 最後に:エンジニアへの提言
インシデントレスポンスは、事件が起きてから始まるのではない。「調査しやすい環境を平時から作っているか」で勝負は決まる。
- ログの外部転送: メモリをダンプする前にサーバーがクラッシュしたら終わりだ。ログは即座に外部のSIEMへ転送せよ。
- Immutableな設計: コンテナベースの運用なら、侵害されたコンテナを捨てて、ハッシュ値を計算した上で証拠としてイメージ化する手順を自動化しておけ。
フォレンジックは芸術ではない。科学だ。誰がやっても同じ結果が出る手順(再現性)と、それが正当であることの証明(完全性)。これさえ守れば、君たちはどんな攻撃者にも後れを取らない。
次のインシデントで慌てないために、今日からChain of Custodyの記録をノートに取る癖をつけてほしい。現場からは以上だ。
コメント