メモリの深淵を覗く:LSASSという名の「黄金の鍵」と、その痕跡を追うDFIRの流儀
インシデントレスポンスの現場で、攻撃者が最初に到達する「聖杯」は何だと思う? それはドメインコントローラーへの権限昇格でも、バックドアの設置でもない。lsass.exe(Local Security Authority Subsystem Service)のメモリ空間だ。
ここには、システムにログインしたユーザーの平文パスワード、NTLMハッシュ、そしてKerberosチケットが、まるで宝箱のように整理されて格納されている。Mimikatzが世に出てから10年以上が経過したが、この「メモリからクレデンシャルをかすめ取る」というゲームのルールは、根本的には変わっていない。今回は、この泥沼のような領域を、アーキテクトの視点から紐解いていこう。
—
1. LSASSの脆弱性:なぜ「仕様」は「欠陥」へと転じるのか
lsass.exeがこれほどまでに狙われるのは、Windowsの設計思想そのものに起因している。認証プロトコル(SSP: Security Support Provider)を統合的に管理するため、このプロセスは特権プロセスとしてメモリ上にクレデンシャルをキャッシュし続ける必要がある。
攻撃者は MiniDumpWriteDump APIを悪用し、lsass.exe のメモリをまるごとダンプする。あるいは、最近の高度な攻撃では、ディスクに書き出さずにメモリ上で直接 lsass.exe のハンドルを操作し、パッチを当ててメモリ内の平文パスワードを抽出する手法も一般的だ。
防衛の盲点:EDRの検知を回避する「ライブメモリ操作」
多くのEDRは、プロセスのダンプ(ReadProcessMemory 等のAPI呼び出し)をフックして検知する。しかし、カーネルモードドライバを悪用して直接物理メモリにアクセスする攻撃者の前では、ユーザーモードの監視など無力に等しい。
—
2. メモリフォレンジックにおける「決定的瞬間」の特定
インシデント発生時、我々アナリストが最初に行うのは、揮発性データの保全だ。Volatility 3などのツールを使い、特定のプラグインでクレデンシャルの断片を探す。
痕跡の特定:インジケータとしての lsass.exe
以下の Volatility のコマンドは、メモリ上のダンプから疑わしい挙動を抽出する際の基本だが、我々はさらに一歩先を見ている。
# プロセスツリーを解析し、lsass.exe が奇妙な親プロセスから起動されていないか確認
python3 vol.py -f memory.dmp windows.pstree
# メモリ内のモジュールを列挙し、署名されていない DLL がインジェクトされていないか確認
# 多くの攻撃者はここでインジェクションを行う
python3 vol.py -f memory.dmp windows.ldrmodules
攻撃者が Mimikatz を実行した際、最も顕著な痕跡は「異常なスレッドの実行」と「メモリ保護属性の変更」にある。VirtualAllocEx で確保された領域に PAGE_EXECUTE_READWRITE が設定されていれば、それはほぼ間違いなく悪意あるインジェクションの兆候だ。
—
3. 次世代アーキテクチャ:クレデンシャルを「メモリから排除する」
これからの防御層は、「検知」から「構造的な隔離」へシフトしなければならない。
A. Credential Guard の強制適用
Windows 10/11 以降で利用可能な Credential Guard は、仮想化技術(VBS: Virtualization-Based Security)を用いて lsass.exe からクレデンシャルを分離する。これにより、たとえ lsass.exe のメモリをダンプされても、そこには暗号化されたデータしか存在しない。
B. 管理者特権の細分化(Privileged Access Workstation)
「ドメイン管理者」が通常の端末でメールを開くような運用は、今すぐやめるべきだ。管理作業は、物理的に分離された、あるいはホスト環境から隔離されたセキュアなPAW(Privileged Access Workstation)からのみ行うこと。
—
4. コードレベルでの対策:ガードレイルの設計
アプリケーション層において、プロンプトインジェクションやメモリ破壊によるクレデンシャル漏洩を防ぐためには、入力の検証(バリデーション)だけでは不十分だ。メモリ安全性の高い言語(Rustなど)への移行と、機密情報のメモリ内ライフサイクル管理を徹底する必要がある。
以下は、機密情報をメモリ上で扱う際の「最低限守るべき」設計パターンの擬似コードだ。
// メモリ上にパスワードを保持する際は、必ずゼロクリアを徹底する
void secure_cleanup(char* buffer, size_t size) {
if (buffer != NULL) {
// SecureZeroMemory を使用して、コンパイラの最適化による削除を防ぐ
SecureZeroMemory(buffer, size);
free(buffer);
}
}
// クレデンシャル処理のガードレイル実装例
bool authenticate_user(const char* input_pass) {
// 入力値はメモリのスタック領域ではなく、ヒープに確保して即座に破棄する
char* sensitive_data = (char*)malloc(MAX_PASS_LEN);
strncpy(sensitive_data, input_pass, MAX_PASS_LEN);
// ... 認証処理 ...
secure_cleanup(sensitive_data, MAX_PASS_LEN); // 確実にメモリを消去
return auth_result;
}
—
最後に:フォレンジックの真髄
メモリフォレンジックは、単なるツールの操作ではない。それは、攻撃者がシステムとどのように対話し、どのメモリ領域に執着したかという「思考の足跡」を辿る行為だ。
もしあなたが今、インシデントの真っ只中にいるのであれば、まずは lsass.exe が「いつ」「どのプロセスによって」アクセスされたのか、監査ログのID 4663(オブジェクトアクセス)と、EDRのプロセス監視ログを突き合わせてほしい。そこに、あなたの組織が次に守るべき答えがあるはずだ。
防御側の視点とは、攻撃者が最も嫌がる「見えない壁」を、システムの中に構築し続けることである。技術の深淵を恐れず、常に一歩先を読み続けよう。
コメント