おい、ちょっと手を止めてこっちを向いてくれ。
今、うちのSOCで入ったばかりのインシデントの初期解析が終わったところだ。標的型攻撃の初期潜伏から、見事なまでにメモリ上だけで痕跡を消そうとしたマルウェアの挙動を追っていたんだが……攻撃者は「プロセスを終了し、ネットワークコネクションを切断すれば、揮発性メモリの解析なんて怖くない」とでも思っていたらしい。
だが、甘い。我々は物理メモリ(RAM)のダンプだけで戦っているわけじゃない。
今回は、インシデントレスポンスの現場で「見落とされがちだが、最強の証拠の山」である、 pagefile.sys(ページファイル)と hiberfil.sys(ハイバネーションファイル)を使ったフォレンジックの極意を伝授しよう。
教科書通りの「Volatile Memory is everything(揮発性メモリがすべてだ)」なんて言葉を信じていたら、重大なインシデントで見事に足元をすくわれるぞ。
—
1. なぜ物理メモリだけでは不十分なのか?
インシデントが起きたとき、多くのジュニアアナリストは真っ先に DumpIt や LiME のようなツールを叩いて、現在の物理メモリをファイルとして吸い出そうとする。もちろん、それは正しい初動だ。
だが、考えてみてほしい。
対象のサーバーや端末が数時間前にスリープ状態に入っていたり、高負荷によってメモリ不足に陥り、重要なプロセスやデータがディスクへページアウト(退避)されていたらどうなる?
当然、現在の物理メモリ(RAM)上にはその痕跡は残っていない。すでにディスクの奥底へ追いやりられているからだ。
ここで登場するのが、Windowsが裏でこっそり管理している以下の2つの巨大なファイルだ。
pagefile.sys: 物理メモリが足りなくなった際、一時的にデータを退避させる仮想記憶のストレージ。hiberfil.sys: PCを休止状態(ハイバネーション)にする際、その時点の物理メモリの全内容をそのままディスクに書き出すファイル。
攻撃者がどれだけ巧妙に痕跡を隠蔽しようとも、過去にメモリ上に展開されたクレデンシャル、復号鍵、インジェクションされたシェルコードの断片は、このディスク上のスワップ領域や休止ファイルに残酷なほど生々しくこびり付いている。これを拾わずして、真のインシデントレスポンスとは言えない。
—
2. 攻撃者が仕掛ける罠と、フォレンジックでの回収手法
攻撃者はしばしば、メモリ上の機密情報を狙い撃ちにする。例えば、Webアプリケーションやバックエンドサービスが誤ってメモリ上に平文のパスワードやAPIトークンを保持している場合、攻撃者はそれをダンプして持ち去ろうとする。
ここでは、実務で遭遇する「メモリダンプから機密情報を漁る攻撃」のシミュレーションと、それを暴くためのフォレンジック手順を解説しよう。
攻撃シナリオ:メモリ常駐型クレデンシャル抽出
攻撃者は、侵入したWindowsサーバー上で管理者権限を取得すると、プロセス内のメモリを直接読み取るカスタムスクリプトや、有名なオープンソースのダンプツールを用いて lsass.exe やカスタムプロセスのメモリ空間をファイルに書き出そうとする。
もし、開発者がセキュアなコーディングを怠り、以下のようにメモリ空間(あるいはアプリケーションの変数)に平文のデータベース接続情報を保持していたらどうなるか。
# 【危険な実装例】メモリや設定ファイル周辺に平文クレデンシャルを露出させるアンチパターン
import os
class DatabaseConnector:
def __init__(self):
# 脆弱性: 平文のパスワードやシークレットがそのままメモリ空間に常駐する
self.db_host = "internal-db.corp.local"
self.db_user = "sa_admin"
self.db_password = "SuperSecretPassword123!" # 攻撃者にダンプされると即座に終了する値
def connect(self):
print(f"Connecting to {self.db_host} with user {self.db_user}...")
if __name__ == "__main__":
connector = DatabaseConnector()
connector.connect()
# 意図的にプロセスを維持し、メモリ上にクレデンシャルを残す
input("Press Enter to exit...")
このPythonスクリプトが稼働している最中に、あるいは終了した直後に、ページファイルやハイバネーションファイルにはこの SuperSecretPassword123! という文字列が綺麗に残り続ける。揮発性メモリが消え去った後でも、だ。
—
3. 実践:pagefile.sys と hiberfil.sys の解析手順
では、現場でこれらをどう回収し、解析するのか。手順はこうだ。
ステップ1: 証拠の保全(Acquisition)
絶対にライブな状態のまま解析対象のディスクに直接触れてはならない。ライブ環境でのフォレンジックは証拠隠滅のリスクがある。
理想的には、対象マシンの電源を物理的に切るか、ネットワークから完全に隔離した上で、ハードディスクを取り外し、フォレンジックワークステーションにセキュアに接続してイメージ(dd や E01 形式)を取得する。
特に hiberfil.sys はルートディレクトリに隠しファイルとして存在するため、通常のファイルコピーツールではアクセス拒否されることがある。DFIR専用のブータブルUSB(FTK ImagerやCAINEなど)を用いて安全に吸い出すのが鉄則だ。
ステップ2: Volatility 3 を用いた解析
回収した pagefile.sys や hiberfil.sys は、そのままではただのゴミの塊に見える。これをメモリフォレンジックフレームワークである Volatility 3 に食わせることで、構造化されたデータとして蘇らせる。
例えば、ハイバネーションファイルを通常のメモリイメージに変換、あるいは直接指定して解析するには、次のようなコマンドを実行する。
# ハイバネーションファイルからプロセス一覧を抽出するVolatily 3のコマンド例
python3 vol.py -f /path/to/hiberfil.sys windows.pslist.PsList
# ページファイルから特定のキーワード(例: パスワードやURL)をカルブ(切り出し)する
python3 vol.py -f /path/to/pagefile.sys windows.strings.Strings --pattern "SuperSecretPassword"
この解析によって、数日前にシャットダウンされたサーバーのメモリ上に存在したネットワークコネクション、隠しプロセス、さらには入力されたコマンド履歴までがすべて丸裸になる。攻撃者がどれだけ「ログを消した」「プロセスを消した」と言っても、ページファイルという名の「記憶のゴミ捨て場」が全てを語ってくれるわけだ。
—
4. 完全に防御するための『セキュアな設計・設定』
フォレンジックで証拠を掴むのは我々の仕事だが、そもそもインシデントを起こさない、あるいは万が一侵入されても機密情報をメモリ上に残さないための設計がエンジニアの腕の見せ所だ。
以下の対策を、今すぐインフラとアプリケーションのコードに反映させてくれ。
A. OSレベルの設定:シャットダウン時のページファイルクリア
Windowsはデフォルトの設定だと、シャットダウン時にページファイルをクリアしない。つまり、電源を切っても過去の機密情報がディスク上に残り続ける。これをGPO(グループポリシー)またはレジストリで強制的にクリアさせる設定に変える。
# 【PowerShell】シャットダウン時にページファイルをゼロクリアするレジストリ設定
# 攻撃者が物理的にディスクを抜いた場合でも、ページファイルからの情報漏洩を防ぎます
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management" -Name "ClearPageFileAtShutdown" -Value 1
*※注意: この設定を有効にすると、システムのシャットダウンに若干時間がかかるようになるが、セキュリティと可用性のトレードオフとしては当然呑むべきコストだ。*
B. アプリケーションレベルの設定:メモリ上での機密情報の即座の破棄
先ほどのPythonの例のように、平文のパスワードや暗号化キーをいつまでもメモリ上に保持するようなコードは書くな。もしどうしてもメモリ上に展開する必要があるなら、使い終わった瞬間にメモリ領域を上書き・クリア(あるいはガベージコレクションの強制など)する意識を持て。
セキュアな実装のサンプルとして、機密情報を扱った直後にメモリから安全に消去するパターンを見てみよう。
# 【セキュアな実装例】機密情報を扱った直後に明示的にメモリ上の変数を上書き・解放する
import ctypes
import gc
class SecureDatabaseConnector:
def __init__(self, host, user, password):
self.db_host = host
self.db_user = user
# パスワードを保持
self._db_password = password
def connect(self):
print(f"Connecting to {self.db_host} as {self.db_user}...")
# 接続処理(実際にはここでコネクションを確立)
def close(self):
# 対策: 使い終わったら即座にメモリ上の機密データをダミー値で上書きする
if hasattr(self, '_db_password') and self._db_password:
# 可能な限り文字列のミュータブルなバッファを書き換える
self._db_password = "0" * len(self._db_password)
del self._db_password
# ガベージコレクションを明示的に呼び出し、参照を完全に消去する
gc.collect()
print("Secure cleanup completed: Credentials wiped from memory references.")
if __name__ == "__main__":
# 安全なインスタンス化と即座の破棄
connector = SecureDatabaseConnector("internal-db.corp.local", "sa_admin", "SuperSecretPassword123!")
try:
connector.connect()
finally:
# 例外が発生した場合でも必ず機密情報を消去する
connector.close()
—
5. チーフからのまとめ
メモリフォレンジックの世界は奥が深い。物理メモリという「今この瞬間の真実」だけでなく、pagefile.sys や hiberfil.sys という「過去の記憶の残骸」まで漁れるようになって初めて、一人前のDFIRエンジニアと言える。
攻撃者は常に我々の盲点を突いてくる。「メモリは再起動すれば消える」という神話は捨てろ。ディスクの隅々にまで目を光らせ、痕跡を逃さない鋭い洞察力と、そもそも痕跡を残さない堅牢なコード・インフラ設計を両立させること。それが、プロのセキュリティエンジニアの仕事だ。
さあ、手を動かして、今のシステムのページファイル設定とアプリのメモリ管理を見直してくれ。頼んだぞ。
コメント