「消えない」侵入者:WMIイベントサブスクリプションを悪用した永続化をメモリから暴く
現場でインシデント対応をしていると、OSを再起動しても、パスワードを変えても、数日後には必ず「戻ってくる」悪意ある存在に頭を抱えることがよくある。多くのエンジニアは「バックドアの実行ファイルが残っているのでは?」とディスクを血眼になって探すが、真のプロフェッショナルは「メモリ上の見えない設定」を疑う。
今日語るのは、WMI(Windows Management Instrumentation)イベントサブスクリプションを悪用した、極めて狡猾な永続化手法と、それをメモリフォレンジックで特定する泥臭いテクニックだ。
—
なぜ攻撃者はWMIを選ぶのか?
WMIはWindowsの管理機能の心臓部だ。「システム起動から5分後」「特定のプロセスが終了した瞬間」といった条件(Event Filter)と、その際に実行する動作(Event Consumer)を紐付けて登録できる。
問題は、これがファイルレスに実行可能であるという点だ。攻撃者はpowershell.exe等から__EventFilterや__EventConsumerといったWMIクラスを操作し、悪意あるスクリプトをリポジトリ(OBJECTS.DATA)に潜り込ませる。一度登録されれば、OSの再起動のたびにWMIサービスが自律的に悪意あるコードをメモリ上で展開する。これでは、どんなにアンチウイルスでファイルを探しても見つかるはずがない。
メモリから「死角」を暴き出す
この攻撃を特定するには、揮発性メモリの解析が不可欠だ。Volatility 3のようなツールを使い、WMIに関連するオブジェクトの不整合を探す必要がある。
特に注目すべきは、以下のプラグインだ。
1. windows.wmi: 登録されているイベントフィルターとコンシューマーを列挙する。不審なコマンドライン引数(Base64エンコードされたPowerShellや、外部への通信を伴うコマンド)が潜んでいないか徹底的に確認する。
2. windows.handles: wmiprvse.exeプロセスが保持しているハンドルを確認する。不自然なパイプ接続や、本来触るはずのないファイルへのアクセスがないかを追う。
「怪しい」と思ったら、メモリダンプから関連する__EventConsumerのCommandLineTemplateプロパティを抽出してほしい。そこに「なぜそんなコマンドが?」という文字列が見えた瞬間が、インシデントレスポンスの正念場だ。
—
「永続化」を許さない:防御のためのセキュアな設計
この攻撃を完全に防ぐには、「WMIを制限する」というアプローチが現実的だ。必要のない端末でWMIの遠隔操作を許可してはならない。
1. PowerShellによるWMI権限の厳格化
攻撃者の足掛かりを最小化するため、特定の管理者以外がWMIリポジトリを編集できないよう、WMIの名前空間権限を設定する。
# 管理者として実行
# WMI名前空間(root/subscription)へのアクセス権を特定のユーザーグループのみに制限する例
$acl = Get-WmiObject -Namespace root\subscription -Class __SystemSecurity
$sd = $acl.GetSecurityDescriptor().Descriptor
# ここで適切なAccessMask(セキュリティ識別子)を指定し、許可されたユーザー以外を排除する
# ※誤った設定はシステム管理を破壊するため、必ず検証環境でテストすること
2. 環境変数と実行ポリシーによるガード
Webアプリケーションサーバーなど、WMIを使用しない環境では、そもそもWMI関連の依存サービスを無効化するか、PowerShellの実行ポリシーを厳格化しておくべきだ。
# Nginxの設定例:攻撃者がWMIを叩くための入り口(Web Shell等)を塞ぐ
# ディレクトリトラバーサルやコマンド注入を防ぐためのヘッダー設定
server {
listen 80;
# 不審なリクエストをブロックするセキュリティヘッダーの付与
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
location / {
# 実行可能ファイルのアップロードやディレクトリ内での実行を制限
location ~* \.(php|php5|exe|ps1)$ {
deny all;
}
}
}
3. アプリケーション層での徹底防御(PHP例)
Webアプリケーションがコマンドインジェクションを受け、そこからWMIを操作されるケースが最も多い。ユーザー入力をそのまま実行するような実装は論外だ。
<?php
// 危険:ユーザー入力を直接実行するようなコードは絶対に書かない
// $input = $_GET['cmd'];
// system($input);
// 推奨:入力値のホワイトリスト検証を徹底する
$allowed_commands = ['status', 'uptime'];
$input = $_POST['action'];
if (in_array($input, $allowed_commands)) {
// 許可されたコマンドのみを安全に実行する
// shell_execではなく、より制限されたAPIを利用する
echo "Current System Status: OK";
} else {
// ログに記録し、管理者にアラートを飛ばす
error_log("不正なコマンド試行が検出されました: " . $input);
die("Security Violation");
}
?>
—
現場のエンジニアへ:教訓
メモリ上のWMI永続化は、「見えない場所」に潜む典型例だ。だが、どんなに隠れようとしても、OSが動くためにメモリ上に「設定(オブジェクト)」を展開しなければならない、という物理法則からは逃げられない。
「ログに何も出ていないから安全」は、プロのエンジニアが口にしてはいけない言葉だ。 違和感があれば、その違和感の正体をメモリの深淵まで潜って突き止めてほしい。それが私たちの仕事であり、守るべきシステムへの誠意だ。
次に現場で怪しい挙動を見つけたら、まずはメモリダンプをとり、WMIの構成を疑うこと。そこからが、本当の戦いの始まりだ。
コメント