こんにちは!SOC(セキュリティオペレーションセンター)で日々、サイバー攻撃の痕跡を追いかけているアナリストです。
今回は、インシデントレスポンスやデジタルフォレンジック(DFIR)の世界でも、特にディープでスリリングな領域である「メモリフォレンジックにおけるアンチフォレンジック技術の無効化」についてお話しします。
「なんだか難しそうな名前だな……」と思いましたか?大丈夫です!一歩ずつ、身近な例えを交えながら優しく紐解いていきますので、安心してついてきてくださいね。
—
1. 家の鍵と泥棒に例える「メモリフォレンジック」と「アンチフォレンジック」
まずは、私たちが普段やっている「メモリフォレンジック」の世界を、身の回りの防犯に例えてみましょう。
パソコンのメモリ(RAM)は、いわば「今まさに家の中で家族が広げているリビングのテーブル」のようなものです。そこには、開いているアプリのデータ、入力中のパスワード、そして侵入者がこっそり実行した怪しいプログラム(マルウェア)の足跡が、ホカホカの状態で置かれています。
セキュリティ調査員である私たちは、事件が起きたとき、この「リビングのテーブル(メモリ)」の写真をパシャリと撮影して(これをメモリダンプと言います)、証拠を集めるわけです。
しかし、頭の良い(そしてタチの悪い)サイバー攻撃者は、自分たちの足跡を残したくありません。そこで彼らは、私たちが写真を撮ろうとした瞬間にテーブルをめちゃくちゃにしたり、証拠を隠したりする技術を使います。これが「アンチフォレンジック(フォレンジック妨害)」です。
- メモリのゼロクリア: 警察(調査員)が踏み込んできた瞬間に、テーブルの上の書類をすべてシュレッダーにかける行為。
- カーネルオブジェクトのリンク解除(DKOMなど): 警察に見つからないように、怪しい人間を「家族名簿(プロセス一覧)」からこっそり消して、透明人間にしてしまう技。
今回は、この攻撃者たちのずる賢い隠蔽工作をどうやって見破り、無効化するのかを覗いていきましょう!
—
2. 攻撃者が仕掛ける「透明人間化(リンク解除)」のカラクリ
WindowsなどのOSは、動いているプログラム(プロセス)を管理するために、頭の中で「すべてのプロセスを鎖で繋いだリスト(双方向連結リスト)」を作っています。これをカーネルオブジェクトのリストと呼びます。
通常、タスクマネージャーを開くと、この鎖を順番に辿って「あ、メモ帳が動いてるな」「ブラウザがいるな」と画面に表示します。
しかし、攻撃者のマルウェアは、自分の前後のプロセスを繋いでいる「鎖」をこっそり付け替えて、自分をこのリストから切り離してしまうのです。これが「リンク解除」です。
[正常な状態]
[プロセスA] <---> [怪しいマルウェア] <---> [プロセスB]
[リンク解除後(透明人間状態)]
[プロセスA] <----------------------------> [プロセスB]
(怪しいマルウェアは鎖から外れてポツンと取り残されるが、メモリの片隅で動き続ける)
こうなると、通常のタスクマネージャーや、単純なプロセス一覧ツールでは「怪しいプロセスはいません」と表示されてしまいます。ずるいですよね。
—
3. どうやって対抗する?隠された証拠を暴く解析手法
では、私たち調査員は、この透明人間をどうやって見つけ出すのでしょうか?
答えは「鎖がダメなら、部屋中をしらみつぶしに探す(プールタグスキャンやVADツリー解析)」です。
プロセスがメモリ上に存在している以上、OSが管理するための「看板(構造体)」が必ずどこかに落ちています。リストの鎖から外されていても、メモリの特定のエリアを総当たりでスキャンすれば、「あれ? この看板、どこのリストにも繋がっていないけど、まだ動いているぞ?」と気づくことができます。
ここで、実務の現場でよく使われる、Pythonを使ったメモリ解析自動化のイメージコードを見てみましょう。Volatility(メモリフォレンジックの定番フレームワーク)を拡張して、隠されたプロセスを探す際の概念的なスクリプトです。
# -*- coding: utf-8 -*-
import sys
def detect_hidden_processes(memory_dump_path):
"""
メモリダンプを走査し、プロセスリストからリンク解除された
(=透明人間化された)隠しプロセスを検出するための疑似コードです。
"""
print(f"[*] 解析対象のメモリイメージ: {memory_dump_path}")
print("[*] 通常のプロセスリスト(ActiveProcessLinks)の走査を開始...")
# 擬似的に取得した「表向きのプロセス一覧」
visible_processes = [4, 324, 892, 1204]
print("[*] 続いて、メモリプール全体の網羅的スキャン(Pool Scanner)を実行中...")
# メモリ上からプロセス構造体(EPROCESS)の目印を総当たりで探す
# 実際には volatility の pslist / psscan プラグインの差分を取るロジックに相当します
all_found_processes = [4, 324, 892, 1204, 3142] # 3142がリストから外されていた隠しプロセス!
# 表向きのリストと、実際のメモリ上の存在を比較する
hidden_processes = [pid for pid in all_found_processes if pid not in visible_processes]
if hidden_processes:
print(f"[!] 警告: 隠蔽されたプロセス(アンチフォレンジックの痕跡)を検出しました!")
for hp in hidden_processes:
print(f" -> 孤立しているプロセスのPID: {hp} (要詳細調査)")
else:
print("[+] 隠しプロセスは検出されませんでした。")
if __name__ == "__main__":
# 実行例の呼び出し
detect_hidden_processes("sample_memory_dump.raw")
このように、「表側のリスト」と「裏側のメモリ実体」の数や差異を突き合わせることが、アンチフォレンジックを無効化する基本中の基本となります。
—
4. 現場で役立つ!インシデント発生時の心構えと対策
最後に、もし皆さんが現場で「おや、メモリダンプや解析結果がおかしいぞ」「ログが綺麗に消されているかもしれない」と気づいたときに、どう振る舞うべきかのマインドセットをお伝えします。
1. 「ない」ことを鵜呑みにしない
攻撃者がアンチフォレンジックを行っている場合、「プロセスがいない」「ログがない」という事実自体が、「そこに強力な攻撃者がいる」という最大の証拠になります。「静かすぎる環境」を疑う目を持ちましょう。
2. 複数のツールを組み合わせる
一つの解析ツールだけで判断せず、別のアルゴリズムを使ったツール(例えば、カーネルのシンボルを見る手法と、生のバイト列をパターンマッチングする手法など)を併用することで、相手の隠蔽工作をすり抜けることができます。
3. ライブレスポンスの重要性
メモリをファイルに保存(ダンプ)するプロセス自体が、マルウェアに検知されて証拠を消されるトリガーになることがあります。そのため、環境に応じた適切なライブフォレンジックツールや、ハードウェアレベルでの保全手順を日頃から検討しておくことが大切です。
一歩ずつ、こうした技術の仕組みと対抗策を学んでいけば、どんなに巧妙な隠蔽工作を行う攻撃者であっても、必ず尻尾を掴むことができます。
インシデントレスポンスの道は奥が深く、そして何よりエキサイティングです。一緒に泥臭く、確実な調査スキルを磨いていきましょう!
コメント