現場の最前線から:メモリフォレンジックを「無効化」する攻撃者との泥沼の戦い
「メモリダンプを取ればすべてが分かる」。これはインシデントレスポンスの教科書に書かれた理想論だ。しかし、実務の最前線で我々が対峙する攻撃者は、もはやそんな甘い世界にはいない。
彼らは、我々がフォレンジックのためにメモリへアクセスしようとすると、OSのカーネル構造を改ざんし、ダンプツールをエラーに追い込み、あるいは痕跡をゼロクリアする。今日は、そんな「アンチフォレンジック技術」を仕掛けてくる攻撃者に対し、我々DFIR担当がどう立ち向かうべきか、その泥臭い現実と防衛策を解説する。
—
1. 攻撃者が使う「見えない壁」の正体
攻撃者がメモリフォレンジックを妨害する際、最もよく使われるのが「DKOM (Direct Kernel Object Manipulation)」だ。
これは、Windowsカーネル内の EPROCESS 構造体(プロセス情報を保持するリスト)のリンクを操作し、特定の悪意あるプロセスをOSの管理リストから「意図的に切り離す」手法だ。こうなると、通常のタスクマネージャーや標準的なダンプツールからは、そのプロセスが「存在しないもの」として扱われる。
さらに厄介なのが、攻撃完了後にメモリ上のペイロードをゼロクリア(ZeroMemory等)する手法だ。ダンプを取った時には、すでに痕跡は跡形もなく消えている。
—
2. 防御の要:攻撃を「検知」し「記録」する
残念ながら、メモリ操作そのものを完全に「防ぐ」ことは不可能に近い。なぜなら、管理権限を奪われた時点でOSは攻撃者の言いなりだからだ。我々ができる唯一の対抗策は、「メモリが改ざんされる前の動き」を物理的に別の場所へ逃がし続けることだ。
現場で私が強く推奨するのは、OS標準のログに頼らず、EDR(Endpoint Detection and Response)のカーネルレベル監視を導入し、さらに「改ざん不能な外部ストレージ」へログをストリームし続けることだ。
実務で使える:プロセス監視のPythonスクリプト(ヒント)
攻撃者がリンクを解除しようとする際、カーネルドライバの読み込みや、特定のAPI(NtQuerySystemInformation等)へのフックが発生する。これを監視し、異常を検知する軽量なエージェントの雛形がこれだ。
# 簡易的なプロセスリスト整合性チェック (Linux/procfsの例)
import os
def check_hidden_processes():
# /procにあるPIDと、psコマンドの結果を比較するロジック
proc_pids = set([pid for pid in os.listdir('/proc') if pid.isdigit()])
# ここでは外部から取得したプロセスリストと比較する想定
# もし /proc に存在するのに OSの管理リストから見えないなら...
# 即座に警告ログを外部SIEMに飛ばす実装が必要
print("[INFO] 整合性チェックを実行中...")
# 実際の実務では psutil ではなく、直接 /proc を走査し、
# 期待されるプロセスツリーとの乖離を検知する
# 警告トリガー
# send_alert_to_siem("Hidden process detected: Potential DKOM attack!")
if __name__ == "__main__":
check_hidden_processes()
—
3. Webアプリレベルで「痕跡」を残さないために
メモリフォレンジック以前に、そもそも攻撃者にメモリ上で動かれることを阻止せねばならない。特にPHPやPythonのようなインタプリタ言語では、メモリの使い方が杜撰になりがちだ。
セキュアな実装例:機密データの即時廃棄
攻撃者がメモリダンプを取得しても何も盗めないように、機密情報(セッションIDやパスワード)は変数に代入した後、即座にメモリから削除する意識を持つこと。
<?php
// 機密データの取り扱い:使い終わったら即座に上書き廃棄
function processSensitiveData($data) {
// 処理実行
$result = base64_encode($data);
// 処理後、メモリから機密情報を消去
// PHPのガベージコレクションを待つのではなく、明示的に上書きする
$data = str_repeat("\0", strlen($data));
unset($data);
return $result;
}
?>
—
4. 最後に:インフラ側での「盾」
メモリフォレンジックを妨害させないための最終防衛線は、攻撃者が「実行権限を保持し続ける環境」を排除することだ。NginxのWAF設定で、メモリ操作を試みるような挙動をブロックする設定例を置いておく。
# Nginx / WAF 設定例 (特定のヘッダーやペイロードの遮断)
location / {
# メモリダンプツールやデバッガに関連する特徴的なシグネチャを拒否
if ($http_user_agent ~* (winpmem|dumpit|mimikatz)) {
return 403;
}
# 攻撃者が不正なメモリ書き込みを試みる際の異常なリクエストボディを遮断
client_body_buffer_size 16k;
client_max_body_size 16k;
}
執筆後記:技術は「信頼」の上に成り立つ
メモリフォレンジックの妨害を無効化する魔法のようなツールは存在しない。あるのは、「攻撃者が必ず痕跡を残す」という前提で、多層的に監視を仕掛けているかという、我々エンジニアの執念だけだ。
DKOMで隠されようが、メモリをゼロクリアされようが、カーネルレベルのログと、ネットワーク境界での通信ログ、そしてEDRの記録を突き合わせれば、必ずどこかに綻びが見つかる。
面倒かもしれない。だが、その「泥臭い整合性チェック」こそが、企業を守る最後の砦になる。次回のインシデントハンドリングでは、ぜひ「メモリダンプを取って終わり」ではなく、「メモリを改ざんさせない・隠させない」という観点で設計を見直してみてほしい。
コメント