レジストリは嘘をつかない:DFIRの現場で明かされる「永続化」の爪痕
現場でインシデント対応をしていると、侵入した攻撃者が最も好む「隠れ家」がどこか、嫌というほど思い知らされます。それは高度なゼロデイ攻撃のコードではなく、古くからあるWindowsの「レジストリ」です。
システム管理者やエンジニアは、普段レジストリを直接触ることを避けますが、攻撃者はここを執拗に攻めます。OSが起動するたびに、ユーザーがログインするたびに、自分のバックドアを動かすための「招待状」をそこに書き込むからです。今日は、DFIRの視点から「攻撃者がレジストリに何を隠すのか」、そして「開発・運用者としてどう防御すべきか」を深掘りします。
1. 攻撃者が狙う「永続化」の聖域
フォレンジック調査の際、我々がまず SYSTEM や SOFTWARE ハイブ、そしてユーザー固有の NTUSER.DAT をダンプして確認するのは、以下の「定番」の場所です。
- Run / RunOnce キー:
HKCU\Software\Microsoft\Windows\CurrentVersion\Runは、ユーザーがログインするたびに悪意のあるバイナリやPowerShellスクリプトを走らせるための特等席です。 - Services キー:
HKLM\SYSTEM\CurrentControlSet\Servicesは、OSレベルで権限を奪うための拠点です。攻撃者はここに新しくサービスを作成するか、既存の正当なサービス(例えばwuauservなど)の実行パスを自作のDLLに書き換えます。 - UserAssist:
NTUSER.DAT内にあるこのキーは、ユーザーがGUIを通じていつ、どのアプリを何回実行したかを記録しています。攻撃者の足跡を追うための「タイムラインの証拠」がここに残ります。
2. 開発者が知っておくべき「防御」の現実
「レジストリをいじらせない」というのは、OSレベルでの権限管理の話ですが、Webアプリ開発者にとっても無関係ではありません。例えば、ファイルアップロード機能を持つWebアプリが、攻撃者の悪意ある実行ファイルをWeb公開ディレクトリやシステムディレクトリに配置し、それをレジストリの「自動実行キー」に登録させるような脆弱性を突かれるケースは後を絶ちません。
実践:セキュアなファイルアップロードのための防衛実装(Python/Flask)
ユーザーからのアップロードを許可する場合、単に保存するだけでは甘すぎます。以下のコードは、アップロードされたファイルが実行形式(.exe, .dll, .scr 等)でないかを厳格に検証し、ディレクトリトラバーサルを防ぐための実装例です。
import os
from werkzeug.utils import secure_filename
# 許可する拡張子のリスト(最小権限の原則)
ALLOWED_EXTENSIONS = {'png', 'jpg', 'pdf'}
def allowed_file(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
def save_uploaded_file(file):
# ファイル名からディレクトリトラバーサルを防ぐ
filename = secure_filename(file.filename)
# 拡張子チェック(ここを突破されるとレジストリ登録のトリガーにされる)
if not allowed_file(filename):
raise ValueError("不正なファイル形式です")
# 保存先ディレクトリはWeb公開領域から完全に隔離する
upload_path = "/var/lib/app_data/uploads/"
file.save(os.path.join(upload_path, filename))
# 【重要】保存したファイルには決して実行権限を与えないこと
os.chmod(os.path.join(upload_path, filename), 0o644)
3. インフラ・OSレベルでの鉄壁の防御:AppLockerの活用
レジストリを書き換える攻撃への最大の防御は、「実行させない」ことです。Windows環境であれば、AppLocker や Windows Defender Application Control (WDAC) を活用し、特定のディレクトリ(C:\Windows や Program Files)以外からのバイナリ実行を許可しないポリシーを適用してください。
また、もし皆さんがNginxなどを利用したWebサーバーを構築しているのであれば、設定ファイルで不要なメソッドやディレクトリへのアクセスを遮断することも重要です。
Nginxでのアクセス制御サンプル
# 特定のディレクトリ内でのスクリプト実行を禁止する
location /uploads/ {
# PHPやCGIなどが誤って実行されないようにする
location ~ \.(php|pl|py|jsp|asp|sh|cgi)$ {
deny all;
}
# 静的ファイルのみ配信するように制限
default_type application/octet-stream;
}
まとめ:フォレンジックから得られる教訓
「レジストリを監視する」ことは、侵入された後の検知には非常に有効ですが、プロのエンジニアなら「侵入を許さない設計」に注力すべきです。
1. 入力バリデーション: ファイルアップロードは最も危険な入り口です。拡張子だけでなく、ファイルヘッダ(Magic Number)を確認してください。
2. 特権の分離: アプリケーションを実行するユーザーは、レジストリやシステムディレクトリを書き換える権限を持ってはいけません。
3. 可視化: Sysmon を導入し、レジストリへの書き込みイベント(Event ID 12, 13, 14)をログ転送してください。攻撃者は必ず「キーの作成」という痕跡を残します。
後輩の皆さん、レジストリハイブの解析は骨が折れる作業ですが、そこには間違いなく真実が刻まれています。コードを書くとき、サーバーを組むとき、常に「これが攻撃者の永続化の足がかりにならないか?」と自問自答してください。それが、インシデントレスポンスの最前線で培った、唯一の「勝利の方程式」です。
コメント