@port139 Blog

基本的にはデジタル・フォレンジックの技術について取り扱っていますが、記載内容には高確率で誤りが含まれる可能性があります。

Velociraptor 調査の舞台裏:Windows.Triage.Targets の解析とカスタマイズ

※ご注意:この記事は生成AIによって作成されています。

記載内容の正確性については細心の注意を払っておりますが、生成AIの特性上、ハルシネーション(事実に基づかない情報の生成)が含まれる可能性があります。提示されたスクリプトや手順を実務で利用される際は、必ず事前に十分な検証を行ってください。

※つまり、ほぼ全て生成AIに書いてもらった記事になるので、微妙な点もありますが「へぇ」くらいで読んでいただければ。


Velociraptor 調査の舞台裏:Windows.Triage.Targets の解析とカスタマイズ

デジタルフォレンジック調査において、証跡収集のスピードと網羅性は非常に重要です。その中核を担う Velociraptor の Windows.Triage.Targets アーティファクトについて、内部構造の解析から独自ルールを反映したカスタマイズまでの一連の手法を整理しました。

最新版の入手とルールの確認

Windows.Triage.Targets はコミュニティによって継続的にアップデートされています。最新の定義内容は公式サイトで確認可能です。

  • 最新版のダウンロード: Velociraptor Triage Repository から最新のアーティファクトを取得できます。

  • ルールの可視化: 公式サイト上では、YAML内で符号化されている具体的なルールの内容(収集パスやパラメータ)をブラウザ上で直接確認できます。

符号化された定義:GlobTable

YAML ファイル内の export セクションには、ルール定義が符号化された GlobTable 変数が含まれています。

GlobTable の記述サンプル

LET GlobTable <= gunzip(string=base64decode(string="H4sIAAAAAAAC/9y9a2/jONYu+n3/CiOYA5ztV2XUbbp79j..."))

この Base64 データには、数千行に及ぶターゲット定義が GZIP 圧縮して格納されており、YAML の巨大化を防ぎつつ効率的なスキャンを実現しています。

実運用での活用例:EDR の検知を回避するカスタマイズ

デフォルトの YAML を編集したいケースとして、例えば RegistryHivesSystem を利用してレジストリファイルの取得を行う場合が挙げられます。

通常、この設定には SAM ファイルの取得が含まれます。しかし、調査対象機器で EDR が稼働している場合、SAM へのアクセスが不審な挙動として検知され、データ取得が妨げられる ケースがあります。

このようなシナリオでは、本記事で紹介する手法を用いて CSV ファイルを編集し、SAM を取得する項目を削除 することで、セキュリティ製品の干渉を避けつつ、他の必要なレジストリ情報のみを安全に収集するカスタマイズツールの作成が可能になります。


1. ルール定義の抽出(decode_glob.py)

YAML内の GlobTable をデコードして CSV 形式で出力します。これにより、収集対象を正確に把握できます。

スクリプト:decode_glob.py

import re
import base64
import gzip
import argparse
import os
import sys

def main():
    parser = argparse.ArgumentParser(description="Velociraptor YAML から GlobTable をデコードします。")
    parser.add_argument("-i", "--input", required=True, help="入力 YAML ファイルのパス")
    parser.add_argument("-o", "--output", default="decoded_glob_table.csv", help="出力 CSV ファイル名")

    args = parser.parse_args()

    if not os.path.exists(args.input):
        print(f"Error: {args.input} not found.")
        sys.exit(1)

    try:
        with open(args.input, 'r', encoding='utf-8') as f:
            content = f.read()

        pattern = r'LET GlobTable <= .*?base64decode\(string="([^"]+)"\)'
        match = re.search(pattern, content, re.DOTALL)

        if not match:
            print("Error: GlobTable data not found in YAML.")
            sys.exit(1)

        b64_str = match.group(1)
        compressed_data = base64.b64decode(b64_str)
        decompressed_data = gzip.decompress(compressed_data)

        with open(args.output, 'wb') as f_out:
            f_out.write(decompressed_data)

        print(f"Success: Decoded CSV saved to {os.path.abspath(args.output)}")

    except Exception as e:
        print(f"An error occurred: {e}")
        sys.exit(1)

if __name__ == "__main__":
    main()

実行コマンド例

# デフォルト名(decoded_glob_table.csv)で出力する場合
python decode_glob.py -i Windows.Triage.Targets.yaml

# 出力ファイル名を指定する場合
python decode_glob.py -i Windows.Triage.Targets.yaml -o triage_list.csv

パース結果のサンプル:

| Target | Rule | Glob | Ref |

| :--- | :--- | :--- | :--- |

| 1Password | 1Password_Backup_Databases | Users\*\AppData\Local\1password\backups\1Password10.sqlite | |

| 1Password | 1Password_Database | Users\*\AppData\Local\1password\data\1Password10.sqlite | |

| 1Password | 1Password_Logs | Users\*\AppData\Local\1password\logs\*.log | |


2. カスタムルールの反映(update_yaml.py)

抽出・編集した CSV を再び YAML へ書き戻し、現場の要件に最適化されたカスタム版を作成します。

スクリプト:update_yaml.py

import base64
import gzip
import re
import argparse
import sys
import os

def update_yaml(yaml_path, csv_path, output_path):
    if not os.path.exists(csv_path) or not os.path.exists(yaml_path):
        print("エラー: 入力ファイルが見つかりません。")
        sys.exit(1)

    try:
        with open(csv_path, 'rb') as f:
            csv_data = f.read()
        compressed = gzip.compress(csv_data)
        new_b64 = base64.b64encode(compressed).decode('utf-8')

        with open(yaml_path, 'r', encoding='utf-8') as f:
            yaml_content = f.read()

        pattern = r'(LET GlobTable <= .*?base64decode\(string=")([^"]+)("\))'
        if not re.search(pattern, yaml_content, re.DOTALL):
            print("エラー: YAML内で置換対象が見つかりません。")
            return

        new_yaml_content = re.sub(pattern, rf'\1{new_b64}\3', yaml_content, flags=re.DOTALL)

        with open(output_path, 'w', encoding='utf-8', newline='\n') as f:
            f.write(new_yaml_content)

        print(f"成功: カスタムYAMLを保存しました: {output_path}")

    except Exception as e:
        print(f"反映中にエラーが発生しました: {e}")

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("-y", "--yaml", required=True, help="元のYAMLファイル")
    parser.add_argument("-c", "--csv", required=True, help="編集済みのCSVファイル")
    parser.add_argument("-o", "--output", default="Custom.Windows.Triage.Targets.yaml", help="出力先YAML名")
    args = parser.parse_args()
    update_yaml(args.yaml, args.csv, args.output)

実行コマンド例

# 編集した triage_list.csv の内容を反映し、新しいYAMLを作成
python update_yaml.py -y Windows.Triage.Targets.yaml -c triage_list.csv -o Custom.Triage.Targets.yaml

注意事項

本記事で紹介したスクリプトはあくまで動作例(サンプル)です。実際のフォレンジック調査やインシデント対応で利用される場合には、事前にテスト環境などで十分な検証を行い、データの整合性や動作に問題がないことを確認した上でご利用ください。