メモリの「空白」を覗く:VADツリー解析で暴くシェルコードの正体
現場でインシデント対応をしていると、よく「巧妙に隠蔽されたマルウェアが見つからない」という相談を受けます。攻撃者は今や、ディスクに痕跡を残さない「ファイルレス攻撃」を常套手段としています。
彼らが使っているのは、Windowsの VirtualAlloc や VirtualProtect を悪用し、正規プロセスのメモリ空間にシェルコードを潜り込ませる手法です。プロセスのメモリマップをただ眺めるだけでは、この「寄生」には気づけません。ここで重要になるのが VAD (Virtual Address Descriptor) ツリー の解析です。
1. VADツリーとは何か? なぜ攻撃者がここを狙うのか
Windowsは、プロセスが使用するメモリ領域を管理するためにVADというデータ構造を使っています。OSはこれを使って「そのアドレス範囲が読み書き可能なのか、実行可能なのか」を判断しています。
攻撃者がシェルコードをインジェクションする際、彼らはメモリを確保し、PAGE_EXECUTE_READWRITE (RWX) といった強力な権限を付与します。通常のDLLや実行ファイルはファイルシステム上のパスと紐付いていますが、攻撃者のコードはメモリ上に直接展開されるため、「ファイル名を持たない、しかし実行権限を持つ領域」としてVAD上に浮き彫りになります。
我々DFIRの現場では、Volatilityなどのツールを使ってこのVADを走査し、「正体不明の実行可能領域」を特定することで、侵入の起点を見つけ出します。
2. インジェクションの盲点:攻撃者はどう動くか
攻撃者は、脆弱性のあるWebアプリケーション経由で悪意のあるバイト列を送り込み、それを VirtualAlloc で実行可能メモリ領域にコピーします。
典型的な攻撃フローは以下の通りです:
1. メモリ確保: VirtualAlloc でRWX属性のメモリを確保。
2. 書き込み: WriteProcessMemory 等でシェルコードを注入。
3. 実行: CreateRemoteThread 等で、そのメモリ番地から実行を開始。
このプロセスを阻止するには、そもそも「任意のメモリ領域に実行権限を与えない」という強固な防御姿勢が必要です。
3. 【実戦的防御】メモリエグゼキューションを封じる実装
開発者がアプリケーションレイヤーで直接メモリを操作することは稀ですが、Node.jsのネイティブアドオンや、PHPの拡張機能開発、あるいはセキュアなコンテナ設計において、この知識は不可欠です。
特に、コンテナ環境での脆弱性攻撃を防ぐため、システムコールを制限することは極めて有効な防御策です。Dockerで seccomp を利用し、memfd_create やメモリ保護属性の変更を制限する設定例を紹介します。
セキュアなDocker設定(seccompプロファイル)
攻撃者がシェルコードを実行するために行う mprotect や mmap の不正利用をブロックするためのJSON設定です。
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["mprotect", "mmap"],
"action": "SCMP_ACT_ERRNO",
"args": [
{
"index": 2,
"value": 7,
"op": "SCMP_CMP_MASKED_EQ",
"comment": "PROT_EXEC(4) | PROT_WRITE(2) | PROT_READ(1) のようなRWXを禁止"
}
]
}
]
}
4. アプリケーション層での検証:不正な入力のフィルタリング
Webアプリ開発において、攻撃者がシェルコードを送り込むための「足場」を作らせないことが最優先です。以下のPHPコードは、リクエストデータの中にバイナリ形式のシェルコードを想起させるような不審なバイトシーケンスが含まれていないか、簡易的かつ厳格にバリデーションする例です。
<?php
/**
* リクエスト入力の検証:シェルコード特有のNOPスレッドやバイト列を検知
*/
function is_suspicious_input($data) {
// 典型的なシェルコードのNOPスレッジ (\x90) が連続していないか
if (preg_match('/(\x90){10,}/', $data)) {
return true;
}
// システムコールを呼び出すための特定バイト列の検知(例: x86/x64の特有パターン)
// 実際の運用ではWAFのシグネチャと併用することを強く推奨します
return false;
}
$input = $_POST['payload'] ?? '';
if (is_suspicious_input($input)) {
// インシデントとしてログに記録し、管理者に即時通知
error_log("Security Alert: Potential shellcode injection attempt detected.");
http_response_code(403);
exit("Forbidden");
}
?>
最後に:現場のアナリストからのアドバイス
VAD解析のような高度なフォレンジック手法は、インシデント発生時の「最後の砦」です。しかし、本当に優秀なエンジニアは、「メモリの中身を解析しなくてもいい環境」を設計するエンジニアです。
1. DEP/ASLRの徹底: OS標準の防御機能を絶対に無効化しない。
2. 特権の最小化: コンテナやプロセスに ptrace などのデバッグ権限を与えない。
3. Egress通信の監視: 万が一シェルコードが動いても、外部へのC2通信をファイアウォールで遮断する。
メモリの奥深くにある「不自然な空白」を見つける技術を磨きつつ、同時にその空白が生まれないような堅牢なアーキテクチャを構築してください。それが、我々セキュリティエンジニアの真の仕事です。
コメント