メモリの「空白」を覗き見る:インジェクション攻撃をVADツリーから暴く技術
現場でインシデント対応をしていると、よく「巧妙なマルウェア」という言葉を耳にする。だが、実際に対峙するのは「巧妙」なのではなく、「OSの正当な機能を悪用しただけ」のコードであることがほとんどだ。
特に、VirtualAllocEx や WriteProcessMemory を駆使したメモリインジェクションは、ディスク上にファイルの実体を残さない「ファイルレス攻撃」の代表格だ。ログを見ても何も残らない。だが、OSのメモリ構造――具体的には VAD(Virtual Address Descriptor)ツリー を覗けば、嘘はすべて暴ける。
今日は、後輩諸君が現場で「怪しい挙動」を検知した際、直ちに異常を特定し、防御するための勘所を伝授しよう。
—
1. なぜ「VADツリー」を見るのか
Windowsにおいて、各プロセスがどのメモリ領域をどのように使っているかを管理しているのがVADツリーだ。攻撃者がインジェクションを行う際、通常は以下のような「不自然なメモリ領域」を生成する。
- PAGE_EXECUTE_READWRITE (RWX): 読み込み・書き込み・実行権限がすべて付与された領域。通常のアプリケーションでは、セキュリティ上の理由から非常に珍しい。
- Mapped/Privateの不整合: ファイルベースではないはずのメモリ領域なのに、実行コードが含まれている。
攻撃者は、Reflective Loading を使い、自身のDLLをロードする関数を自前で実装してメモリ上に「パッチ」を当てる。この時、VADツリー上には「紐付けられたファイルが存在しないのに、実行権限を持つ領域」が浮かび上がる。これがインシデントの決定的な証拠だ。
—
2. 実践:インジェクションを未然に防ぐ「開発の盾」
インジェクションはクライアントサイドやOSの権限昇格を通じて侵入する。開発者が防ぐべきは、「リモートからのコード実行(RCE)を許さない」ことに尽きる。
PHPでのファイルアップロードやコマンド実行は、インジェクションの踏み台にされやすい。以下は、脆弱な関数を徹底的に排除するためのセキュアな実装例だ。
不正なコマンド実行を遮断するPHP設定
exec() や system() をむやみに叩くのは自殺行為だ。どうしても必要な場合は、入力値を厳格に検証し、シェル経由を回避する。
<?php
// PHPの危険な関数を無効化(php.iniで設定するのがベスト)
// disable_functions = exec,passthru,shell_exec,system,proc_open,popen
/**
* 外部入力を用いたコマンド実行のセキュアなラッパー
*/
function safe_execute($command, $args = []) {
// 1. コマンドのホワイトリスト化
$allowed_commands = ['/usr/bin/convert', '/usr/bin/zip'];
if (!in_array($command, $allowed_commands)) {
throw new Exception("許可されていないコマンドです。");
}
// 2. 引数のエスケープ(シェルインジェクション対策)
$escaped_args = array_map('escapeshellarg', $args);
// 3. 実行(proc_openの利用を推奨)
$full_cmd = $command . ' ' . implode(' ', $escaped_args);
// ここでログ出力を行い、後でフォレンジック可能な状態にしておく
error_log("Executing: " . $full_cmd);
return shell_exec($full_cmd);
}
—
3. Webアプリ層での防御:WAFによるインジェクション検知
Webアプリ開発者が意識すべきは、NginxとWAFの連携だ。攻撃者はインジェクションの準備段階として、ペイロードをPOSTリクエストで送り込んでくる。
ModSecurity (Nginx用WAF) 設定例
Reflective Loading のような怪しい通信パターンを検知するには、リクエスト内のエンコードされたバイナリや、不自然な長さをチェックするルールが有効だ。
# /etc/nginx/modsec/main.conf
# 攻撃者が好むペイロードのパターンをブロックする
SecRule REQUEST_BODY "@rx (?:VirtualAllocEx|WriteProcessMemory|LoadLibrary)" \
"id:100001,phase:2,deny,status:403,log,msg:'Memory Injection Attempt Detected'"
# Base64で難読化されたシェルの断片を検知
SecRule REQUEST_BODY "@rx [A-Za-z0-9+/]{50,}={0,2}" \
"id:100002,phase:2,chain,deny,status:403,log"
SecRule REQUEST_BODY "@rx (?:MZ|This program cannot be run in DOS mode)"
—
4. SOCアナリストからのアドバイス:現場のリアリティ
メモリインジェクションを防ぐための「完璧なツール」は存在しない。しかし、以下の運用ルールを徹底するだけで、攻撃の難易度は劇的に跳ね上がる。
1. EDRの導入: VirtualAllocEx などのAPI呼び出しを監視するEDRは必須だ。特に「どのプロセスがどのプロセスに書き込んだか」というプロセスツリーの追跡が重要になる。
2. 最小権限の原則: Webサーバーのユーザーに管理者権限を与えていないか? 権限がなければ、インジェクションの成功率は著しく下がる。
3. 継続的なメモリ診断: 定期的にプロセスのVADツリーをダンプし、RWX 権限を持つ領域を自動的に抽出するスクリプトを回しておくこと。
メモリは嘘をつかない。攻撃者はインジェクションを実行した瞬間、自らの足跡をメモリという「広大な砂場」に残してしまうのだ。その砂場を読み解く知識こそが、諸君を一流のエンジニアにする。
もし今日、サーバーのメモリ上で不可解な RWX 領域を見つけたら、まずはそのプロセスをダンプし、文字列調査(strings コマンド)を行ってみてほしい。そこには、攻撃者が隠した「悪意」の断片が、ありのままの姿で刻まれているはずだ。
コメント