幽霊はタスクマネージャーに映らない:PEB操作によるプロセス隠蔽と「真実」の暴き方
現場でインシデントレスポンス(IR)をしていると、管理者に「サーバーが異常に重いのに、プロセスリストには何も怪しいものがない」と相談されることがよくある。これは、攻撃者がOSの「見せかけ」を悪用して潜伏している典型的なサインだ。
今日は、Windowsの心臓部であるPEB(Process Environment Block)を操作してプロセスを隠蔽する手法と、それをどうやって見つけ出すか、その泥臭い現場の知見を共有する。
1. 攻撃者の手口:PEBは「名簿」に過ぎない
Windowsにおいて、タスクマネージャーや tasklist コマンドがプロセスを表示する際、基本的にはカーネル内の EPROCESS 構造体というリストを辿っている。しかし、ここを直接改ざんするのはカーネルモードのドライバが必要でリスクが高い。
そこで攻撃者は、ユーザーモードで完結する「PEBのリンク操作」を行う。
PEB内には ActiveProcessLinks という二重連結リストが存在する。これを書き換えて、自分自身のプロセスをリストから「切り離す(Unlink)」ことで、特定のAPI(EnumProcesses 等)から存在を隠すことができるんだ。
攻撃のロジック(概念):
1. 目的のプロセスが自身のPEBにアクセスする。
2. InLoadOrderModuleList などのポインタを書き換え、リストの「前」のノードと「後ろ」のノードを直接繋ぐ。
3. これにより、リストの巡回から自分のプロセスがスキップされ、標準的な監視ツールからは「存在しない幽霊」になる。
2. どうやって暴くのか?「不整合」を突く
ここで重要なのは、「OSは複数の方法でプロセスを管理している」という事実だ。
PEB(ユーザーモード)上のリストを消しても、カーネルオブジェクト(EPROCESS)そのものまで消すことはできない。なぜなら、それをやってしまうとOSのスケジューラがCPU時間を割り当てられなくなり、プロセスそのものがクラッシュするからだ。
つまり、「PEBリストには載っていないが、カーネルのスケジューリングキューには存在する」という不整合を探せばいい。
3. Pythonで実装する「不整合検出」の足掛かり
現場では Volatility などのツールを使うのが定石だが、原理を理解するために、Pythonの pywin32 を使って「標準APIで見えるリスト」と「スレッドID(TID)やハンドルから逆引きしたリスト」を比較するプロトタイプを紹介する。
import win32process
import psutil
def detect_hidden_processes():
# 1. psutil(標準API経由)で取得したプロセスリスト
visible_procs = {p.pid for p in psutil.process_iter(['pid'])}
# 2. システム上の全スレッドからPIDを抽出する(隠蔽されてもカーネルには残る)
# 実際にはカーネルドライバが必要だが、ここでは概念として記載
found_pids = set()
# 実際の現場ではここでEnumProcessesとカーネルオブジェクトの比較を行う
# 3. 比較ロジック
# 標準APIにはないが、システム内にハンドルが存在するPIDを特定する
for pid in found_pids:
if pid not in visible_procs:
print(f"[!] 警告: 隠蔽された可能性のあるPIDを発見: {pid}")
# ここでメモリダンプを取得し、解析へ回す
4. 完全に防御するためのセキュアな運用設計
PEB操作による隠蔽を許さないためには、エンドポイントでの検知能力を強化する必要がある。以下のルールを徹底してくれ。
A. EDRの活用とカーネルコールバック
OSのAPI(EnumProcesses 等)を信頼してはいけない。EDR(CrowdStrike, SentinelOne等)を導入し、PsSetCreateProcessNotifyRoutine を使ってプロセス生成をカーネルレベルで監視している製品を選定すること。これらはPEB操作に関係なく、カーネルにプロセスが登録された瞬間に検知する。
B. Nginx/WAFでの防御(入り口を塞ぐ)
プロセス隠蔽を試みる攻撃者は、多くの場合、Webシェル経由で初期侵入を行う。まずはWebの入り口で不正なコマンド実行を遮断する。
# Nginxでのコマンド実行防止例
location /upload/ {
# .phpなどの実行を禁止する
location ~ \.php$ {
deny all;
}
# コマンドインジェクションの兆候をフィルタリング
if ($query_string ~* "cmd=|powershell|/bin/bash") {
return 403;
}
}
C. メモリフォレンジックの習慣化
定期的にメモリダンプを取得し、Volatility の psxview プラグインを実行する運用を組み込むこと。psxview は、pslist(PEBリスト)、psscan(カーネル構造体の総当たりスキャン)の結果を比較し、不整合があれば即座に教えてくれる。
最後に:エンジニアへ伝えたいこと
「リストにないから大丈夫」という考えは、インシデント現場では致命的な慢心だ。攻撃者は常にOSの「盲点」を探している。
君たちが書くコードや運用するインフラも同じだ。「正規の手順で表示されるもの」と「実際にメモリ上で起きていること」の間にギャップがないか、常に疑う姿勢を持ってほしい。その疑いこそが、最も強力なセキュリティ対策になる。
もし不審な挙動を見つけたら、まずは落ち着いてメモリダンプを取れ。そのデータが、攻撃者を追い詰める唯一の証拠になる。
コメント