現場の最前線から:Process Hollowingという「静かな侵入者」を暴く
インシデントレスポンスの現場で、最も胃が痛くなる瞬間は「プロセスリスト上は正規のプログラムが動いているのに、その挙動が全くの別物であるとき」です。
今日解説するのは、攻撃者が好んで用いる隠蔽手法の金字塔、Process Hollowing(プロセス・ハロウィング)です。これは、正規のプロセス(例えば svchost.exe や explorer.exe)をサスペンド状態で立ち上げ、そのメモリ空間を丸ごと悪意のあるコードに差し替えて実行させる手法です。
「タスクマネージャーで見ても怪しいプロセスはいない」。これが彼らの狙いです。しかし、どんなに巧妙に隠れても、OSのメモリ管理という物理法則からは逃げられません。今日は、この泥臭い戦い方について紐解いていきましょう。
—
Process Hollowingの盲点:VAD(Virtual Address Descriptor)の不整合
攻撃者が WriteProcessMemory や NtMapViewOfSection を駆使してメモリを書き換えた際、Windowsのメモリ管理機構であるVADツリーには「不自然な痕跡」が残ります。
通常、実行ファイルとしてロードされた領域は、メモリ上でも「ファイル backed(ファイルに紐付いている)」な状態です。しかし、Hollowingによって無理やり書き換えられた領域は、この紐付けが切れているか、あるいはページ属性(RWX: Read/Write/Execute)が異常な値を示します。
特に、メモリセクションが PAGE_EXECUTE_READWRITE になっている領域は、フォレンジック担当者からすれば「そこに何かある」と叫んでいるようなものです。
—
現場で役立つ検知・防御の実践的アプローチ
「どうやって検知するのか?」という問いに対して、エンドポイントセキュリティの現場では、単なるシグネチャベースの検知ではなく、メモリの異常な状態監視が鍵となります。
1. EDR/Sysmonを活用した監視ルール
まずは Sysmon を導入しているなら、以下のイベントIDを監視するのが鉄則です。
- Event ID 8 (CreateRemoteThread): プロセス間でのスレッド作成。Hollowingの過程で頻発します。
- Event ID 10 (ProcessAccess):
PROCESS_VM_WRITEやPROCESS_VM_OPERATION権限を要求する怪しい動き。
2. Pythonによるメモリ解析(Volatility 3の活用)
もし、侵害の疑いがあるマシンからメモリダンプを取得できたなら、Volatility 3 を使ってVADの不整合を突くのが最も確実です。以下のコマンドは、保護属性が怪しい領域を炙り出すための布石です。
# ボラティリティでメモリダンプを解析し、RWX属性を持つプロセスを抽出する
python3 vol.py -f memory.dmp windows.vadyarascan --pid <疑わしいPID>
—
セキュアなアプリケーション設計のための教訓
「Process HollowingはOSレベルの攻撃だからアプリ開発者には関係ない」と思っていませんか? 違います。攻撃の起点は、多くの場合、Webサーバー上の脆弱性(コマンドインジェクションやRCE)を足がかりにした「初期侵入」です。
アプリケーションの実行権限を最小化し、攻撃者が「メモリを書き換えるための権限」を持てないようにすることが、最大の防御となります。
Nginx/Dockerによる実行権限の制限設定
Webサーバーのコンテナを実行する際、ルートユーザーで動かすのは論外です。以下のような Dockerfile の記述で、攻撃者が権限昇格を行う難易度を跳ね上げましょう。
# ルート権限で動かさないためのベストプラクティス
FROM php:8.2-fpm
# 非特権ユーザーを作成
RUN groupadd -r appuser && useradd -r -g appuser appuser
# 必要最小限の権限のみ付与
USER appuser
# プロセスが外部からメモリを操作しにくいよう、必要なパスのみ書き込み許可
# 実行バイナリのパスには書き込み権限を与えないこと!
PHPにおける脆弱性対策の基本
RCEを防ぐことが、結果としてHollowingを許さない最短ルートです。system() や exec() を無効化するのは当然として、入力値のバリデーションを徹底してください。
<?php
// 安全なコマンド実行のヒント:外部入力を直接渡さない
// escapeshellarg() を使っても限界があるため、そもそもユーザー入力を受け取らない設計にするのが正解
$userInput = $_POST['filename'];
// 不正なパス指定を許さない(パス・トラバーサル対策)
if (preg_match('/[^a-zA-Z0-9_\-\.]/', $userInput)) {
die("不正な入力です。");
}
// ユーザー入力をコマンドに直接結合しない設計を徹底すること
// どうしても必要な場合は、ホワイトリスト形式で実行可能なコマンドを制限する
?>
—
最後に:フォレンジックの視点を持つエンジニアであれ
Process Hollowingのような攻撃は、防御側からすれば「見えない敵」との戦いです。しかし、攻撃者もCPUやメモリという物理的な制約からは逃れられません。
日頃から「自分が書いたこのコードは、メモリ上でどう見えるのか?」「この設定は、攻撃者にさらなる足場を与えていないか?」と自問自答してください。「異常」を「異常」として認識できる知識こそが、最終的にインシデントを防ぐ最強の武器になります。
現場からは以上です。次のデプロイで、より堅牢なシステムを構築できることを期待しています。
コメント