カーネルの深淵を覗く:ドライバ改ざんによる「見えない脅威」を暴く技術
現場でインシデント対応をしていると、OSの管理ツールが「正常」と表示しているにもかかわらず、裏で何かが動いているという不気味な事案に遭遇することがあります。ログは消され、プロセスは隠蔽されている。そんな時、私たちが最後に頼るのは「メモリ」です。
今日は、攻撃者がOSの核心部であるカーネル領域に潜り込み、Driver Objectを改ざんして自身の存在を隠蔽する手法と、それを検知するためのフォレンジック的アプローチについて話しましょう。
1. なぜ「カーネルモード」が狙われるのか?
ユーザーランド(通常のアプリ層)で動くマルウェアは、EDRやタスクマネージャの監視下にあるため、簡単に特定できます。しかし、カーネルモードで動作するドライバ(.sysファイル)は、OSそのものと同等の権限を持ちます。
攻撃者は、正規のドライバリスト(PsLoadedModuleListなど)から自身の存在をアンリンク(接続解除)することで、標準的なAPI呼び出しから自身のモジュールを消し去ります。これが「ルートキット」の常套手段です。OSの標準的なツールがリストを見に行っても、そこには攻撃者のドライバは存在しないように見えるわけです。
2. 攻撃手法の核心:Driver Objectの改ざん
Windows環境では、各ドライバは DRIVER_OBJECT 構造体としてメモリ上に存在します。攻撃者は、以下のような手順で足跡を消します。
1. リストからの切り離し: PsLoadedModuleList 内のダブルリンクリスト(Flink, Blink)を書き換え、自身のノードをスキップさせる。
2. 名前の偽装: DriverName を ntoskrnl.exe や tcpip.sys といった正規のドライバ名に書き換える。
3. コールバックの乗っ取り: MajorFunction 配列を改ざんし、特定のシステム呼び出しを自身の関数にフック(転送)する。
これを行われると、OSは正規のドライバとして偽物をロードし続け、監視ツールは「正常」と誤認します。
3. 実践:Volatilityを用いたメモリフォレンジック
現場では、ライブレスポンスで取得したメモリダンプを Volatility Framework を使って解析するのが鉄則です。特にカーネルドライバの異常を検知するには、以下のプラグインを使い分けます。
# 1. 読み込まれているドライバのリストを出力
python vol.py -f memory.dmp windows.modules
# 2. Driver Objectを直接スキャン(リスト改ざん検知に有効)
python vol.py -f memory.dmp windows.driverscan
# 3. リストとスキャン結果の差分を確認し、不審なエントリを特定する
windows.modules がOSのリストを信頼して表示するのに対し、windows.driverscan はメモリ上の DRIVER_OBJECT シグネチャを直接力技で探します。この2つの結果に「差分」がある場合、それはほぼ100%、悪意ある改ざんが起きています。
4. 完全に防御するための設計ルール
カーネルレベルの攻撃を防ぐには、侵入後の対策よりも「侵入させない(または無効化させる)」環境作りが重要です。
A. コード署名の強制(Kernel Mode Code Signing)
未署名、あるいは不正な署名のドライバがロードされないよう、グループポリシーで設定を固めます。
- GPO設定:
コンピューターの構成>管理用テンプレート>システム>ドライバーのインストール>デバイスドライバーのコード署名を「有効」かつ「ブロック」に設定。
B. ハイパーバイザー保護の有効化
現代のWindowsには、カーネル自体を保護する仕組みがあります。
- HVCI (Hypervisor-Protected Code Integrity): メモリ整合性を保護し、カーネル領域の改ざんを防ぎます。これが有効であれば、そもそも不正なドライバのロード自体が拒否されます。
C. Pythonによる異常検知の自動化(イメージ)
もし自社で独自の監視エージェントを構築するなら、psutil やカーネルAPIをラップして、定期的にドライバリストの整合性をチェックするスクリプトを走らせるのが有効です。
import psutil
def check_for_suspicious_drivers():
"""
ロードされたドライバの署名を検証し、
署名情報が欠落しているものを抽出する簡易的なロジック
"""
# 注意: 実際にはWindows APIのEnumDeviceDriversと
# GetModuleFileNameEx等を使用する必要がある
print("[*] ドライバリストの整合性をチェック中...")
# 疑似コード:全てのロード済みモジュールをスキャン
# 署名チェックロジックをここに組み込む(WinVerifyTrust API等を使用)
suspicious_list = ["driver_x.sys", "hidden_mod.sys"] # 仮の検知結果
if suspicious_list:
# 管理者へアラートを送信する等の処理
send_alert_to_soc(f"異常なドライバを検知: {suspicious_list}")
else:
print("[+] 正常: カーネル領域に異常なし")
def send_alert_to_soc(message):
# 実際の実務ではSIEMやSlack/TeamsのWebHookへ通知を送る
print(f"[ALERT] {message}")
if __name__ == "__main__":
check_for_suspicious_drivers()
最後に:エンジニアが持つべき「疑いの目」
「OSの管理ツールが正常と言っているから大丈夫」。この慢心が、過去数多くのインシデントを招いてきました。
メモリフォレンジックの本質は、OSが提示する「きれいな顔」を信じず、その裏側に隠された「生のデータ」を読み解くことにあります。皆さんの環境で、もし理由のつかないCPU使用率のスパイクや、説明のつかないネットワーク通信が見られたら、それはカーネルが悲鳴を上げているサインかもしれません。
次にインシデントが発生した際、慌てて電源を落とす前に、まずはメモリをダンプし、その深淵を覗く勇気を持ってください。それが、プロのDFIRエンジニアへの第一歩です。
コメント