mcpField
A
analyzing-windows-lnk-files-for-artifacts

analyzing-windows-lnk-files-for-artifacts

github.com/mukul975セキュリティ35.2k216 回インストール8 週間前に更新
リポジトリ

analyzing-windows-lnk-files-for-artifacts について

Windows の LNK ショートカットファイルとジャンプリストを分析するフォレンジック用スキルです。解析したショートカットのメタデータから、ファイルアクセス、リムーバブルメディアやネットワーク共有の利用、関連タイムラインを再構成します。

主な機能

  1. 01証拠から LNK ファイルを収集
  2. 02ショートカットを CSV または JSON に解析
  3. 03対象パスとタイムスタンプを確認
  4. 04リムーバブルメディアとネットワーク先を特定
  5. 05タイムラインでショートカットデータを照合

活用シーン

  1. 01ユーザーの文書アクセス履歴を再構成
  2. 02USB ドライブからアクセスされたファイルを調査
  3. 03ネットワーク共有へのアクセスを確認
  4. 04永続化に使われたスタートアップショートカットを調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mukul975/anthropic-cybersecurity-skills --skill analyzing-windows-lnk-files-for-artifacts

インストール後、Claude Code で/analyzing-windows-lnk-files-for-artifactsを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

LICENSE
references/
api-reference.md
scripts/
agent.py

詳細

アーティファクトを探すための Windows LNK ファイルの分析

使用する場面

  • Windows のショートカットファイルからユーザーのファイルアクセス履歴を復元するとき
  • アクセスされたファイル、ネットワーク共有、リムーバブルメディアを追跡するとき
  • 特定の文書をユーザーが開いたことを立証する調査中
  • ファイルアクセスを他のタイムラインアーティファクトと関連付けるとき
  • リモートシステムまたは USB デバイス上でアクセスされたパスを特定するとき

前提条件

  • フォレンジックイメージから LNK ファイル(Recent、Desktop、Quick Launch)にアクセスできること
  • 分析用の LECmd(Eric Zimmerman)、python-lnk、または LnkParser
  • LNK ファイルの構造(Shell Link Binary 形式)に関する理解
  • Windows システム上の LNK ファイルの保存場所に関する知識
  • 分析ツールがインストールされたフォレンジックワークステーション

ワークフロー

ステップ 1:フォレンジックイメージから LNK ファイルを収集する
# Mount forensic image
mount -o ro,loop,offset=$((2048*512)) /cases/case-2024-001/images/evidence.dd /mnt/evidence

mkdir -p /cases/case-2024-001/lnk/{recent,desktop,startup,custom}

# Copy Recent items LNK files (primary source)
cp /mnt/evidence/Users/*/AppData/Roaming/Microsoft/Windows/Recent/*.lnk \
   /cases/case-2024-001/lnk/recent/ 2>/dev/null

# Copy automatic destinations (Jump Lists)
cp /mnt/evidence/Users/*/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/*.automaticDestinations-ms \
   /cases/case-2024-001/lnk/recent/ 2>/dev/null

# Copy custom destinations (pinned Jump List items)
cp /mnt/evidence/Users/*/AppData/Roaming/Microsoft/Windows/Recent/CustomDestinations/*.customDestinations-ms \
   /cases/case-2024-001/lnk/custom/ 2>/dev/null

# Copy Desktop shortcuts
cp /mnt/evidence/Users/*/Desktop/*.lnk /cases/case-2024-001/lnk/desktop/ 2>/dev/null

# Copy Startup folder shortcuts (persistence)
cp /mnt/evidence/Users/*/AppData/Roaming/Microsoft/Windows/Start\ Menu/Programs/Startup/*.lnk \
   /cases/case-2024-001/lnk/startup/ 2>/dev/null
cp "/mnt/evidence/ProgramData/Microsoft/Windows/Start Menu/Programs/Startup"/*.lnk \
   /cases/case-2024-001/lnk/startup/ 2>/dev/null

# Find all LNK files on the system
find /mnt/evidence/ -name "*.lnk" -type f 2>/dev/null > /cases/case-2024-001/lnk/all_lnk_locations.txt

# Count and hash
ls /cases/case-2024-001/lnk/recent/ | wc -l
sha256sum /cases/case-2024-001/lnk/recent/*.lnk > /cases/case-2024-001/lnk/lnk_hashes.txt 2>/dev/null
ステップ 2:LECmd で LNK ファイルを解析する
# Using Eric Zimmerman's LECmd (Windows or via Mono)
# Process all LNK files in a directory
LECmd.exe -d "C:\cases\lnk\recent\" --csv "C:\cases\analysis\" --csvf lnk_analysis.csv

# Process a single LNK file with verbose output
LECmd.exe -f "C:\cases\lnk\recent\document.pdf.lnk"

# Process Jump List files
JLECmd.exe -d "C:\cases\lnk\recent\" --csv "C:\cases\analysis\" --csvf jumplist_analysis.csv

# Output includes:
# - Source file path
# - Target path (file that was accessed)
# - Target creation, modification, access timestamps
# - LNK creation and modification timestamps
# - Working directory
# - Command line arguments
# - Volume serial number and label
# - Drive type (Fixed, Removable, Network)
# - Machine ID (NetBIOS name)
# - MAC address (from tracker database)
# - File size of target
ステップ 3:Python で LNK ファイルを解析する
pip install LnkParse3

python3 << 'PYEOF'
import LnkParse3
import os, json, csv
from datetime import datetime

lnk_dir = '/cases/case-2024-001/lnk/recent/'
results = []

for filename in sorted(os.listdir(lnk_dir)):
    if not filename.lower().endswith('.lnk'):
        continue

    filepath = os.path.join(lnk_dir, filename)
    try:
        with open(filepath, 'rb') as f:
            lnk = LnkParse3.lnk_file(f)
            info = lnk.get_json()

            parsed = {
                'lnk_file': filename,
                'target_path': '',
                'working_dir': '',
                'arguments': '',
                'target_created': '',
                'target_modified': '',
                'target_accessed': '',
                'file_size': '',
                'drive_type': '',
                'volume_serial': '',
                'volume_label': '',
                'machine_id': '',
                'mac_address': '',
            }

            # Extract header timestamps
            header = info.get('header', {})
            parsed['target_created'] = str(header.get('creation_time', ''))
            parsed['target_modified'] = str(header.get('modified_time', ''))
            parsed['target_accessed'] = str(header.get('accessed_time', ''))
            parsed['file_size'] = str(header.get('file_size', ''))

            # Extract link info
            link_info = info.get('link_info', {})
            if link_info:
                local_path = link_info.get('local_base_path', '')
                network_path = link_info.get('common_network_relative_link', {}).get('net_name', '')
                parsed['target_path'] = local_path or network_path

                vol_info = link_info.get('volume_id', {})
                if vol_info:
                    parsed['drive_type'] = str(vol_info.get('drive_type', ''))
                    parsed['volume_serial'] = str(vol_info.get('drive_serial_number', ''))
                    parsed['volume_label'] = str(vol_info.get('volume_label', ''))

            # Extract string data
            string_data = info.get('string_data', {})
            parsed['working_dir'] = str(string_data.get('working_dir', ''))
            parsed['arguments'] = str(string_data.get('command_line_arguments', ''))

            # Extract tracker data (machine ID and MAC)
            extra = info.get('extra', {})
            tracker = extra.get('DISTRIBUTED_LINK_TRACKER_BLOCK', {})
            if tracker:
                parsed['machine_id'] = str(tracker.get('machine_id', ''))
                parsed['mac_address'] = str(tracker.get('mac_address', ''))

            results.append(parsed)

            # Print summary
            print(f"\n{filename}")
            print(f"  Target: {parsed['target_path']}")
            print(f"  Modified: {parsed['target_modified']}")
            print(f"  Drive: {parsed['drive_type']} (Serial: {parsed['volume_serial']})")
            if parsed['machine_id']:
                print(f"  Machine: {parsed['machine_id']}")

    except Exception as e:
        print(f"  Error parsing {filename}: {e}")

# Write results to CSV
with open('/cases/case-2024-001/analysis/lnk_analysis.csv', 'w', newline='') as f:
    writer = csv.DictWriter(f, fieldnames=results[0].keys() if results else [])
    writer.writeheader()
    writer.writerows(results)

print(f"\n\nTotal LNK files parsed: {len(results)}")
PYEOF
ステップ 4:調査上の価値を分析する
# Identify files accessed from removable media
python3 << 'PYEOF'
import csv

with open('/cases/case-2024-001/analysis/lnk_analysis.csv') as f:
    reader = csv.DictReader(f)

    print("=== FILES ACCESSED FROM REMOVABLE MEDIA ===\n")
    removable = []
    network = []

    for row in reader:
        if 'DRIVE_REMOVABLE' in row.get('drive_type', '').upper() or \
           'removable' in row.get('drive_type', '').lower():
            removable.append(row)
            print(f"  {row['target_modified']} | {row['target_path']} | Vol: {row['volume_serial']}")

        if 'network' in row.get('drive_type', '').lower() or \
           row.get('target_path', '').startswith('\\\\'):
            network.append(row)

    print(f"\n=== FILES ACCESSED FROM NETWORK SHARES ===\n")
    for row in network:
        print(f"  {row['target_modified']} | {row['target_path']}")

    print(f"\nRemovable media files: {len(removable)}")
    print(f"Network share files: {len(network)}")

    # Check for unique machines (tracker data)
    machines = set()
    for row in [*removable, *network]:
        if row.get('machine_id'):
            machines.add(row['machine_id'])
    if machines:
        print(f"\nMachine IDs found: {machines}")
PYEOF

# Check Startup folder LNK files for persistence
echo "=== STARTUP FOLDER SHORTCUTS (PERSISTENCE) ===" > /cases/case-2024-001/analysis/startup_persistence.txt
for lnk in /cases/case-2024-001/lnk/startup/*.lnk; do
    python3 -c "
import LnkParse3
with open('$lnk', 'rb') as f:
    lnk = LnkParse3.lnk_file(f)
    info = lnk.get_json()
    target = info.get('link_info', {}).get('local_base_path', 'Unknown')
    args = info.get('string_data', {}).get('command_line_arguments', '')
    print(f'  $(basename $lnk): {target} {args}')
" >> /cases/case-2024-001/analysis/startup_persistence.txt 2>/dev/null
done

主な概念

概念説明
Shell Link(.lnk)対象パス、タイムスタンプ、メタデータを含む Windows ショートカットファイル形式
対象のタイムスタンプショートカットが指すファイルの作成日時、更新日時、アクセス日時
ボリュームシリアル番号対象ファイルが存在するドライブボリュームの一意の識別子
マシン IDDistributed Link Tracking サービスによって埋め込まれる NetBIOS 名
MAC アドレスLNK ファイルを作成したマシンのネットワークアダプターの MAC アドレス
Jump Listsアプリケーションごとの最近使用したファイルとピン留めされたファイルの一覧(埋め込み LNK データを含む)
Automatic Destinations最近開いたファイルのシステム管理 Jump List エントリ
Custom Destinations手動で削除されるまで保持される、ユーザーがピン留めした Jump List 項目

ツールとシステム

ツール用途
LECmdCSV/JSON 出力に対応した、Eric Zimmerman のコマンドライン LNK ファイルパーサー
JLECmdEric Zimmerman の Jump List パーサー
LnkParse3プログラムから LNK ファイルを分析する Python ライブラリ
lnk_parser代替の Python LNK 解析ツール
AutopsyLNK ファイル分析モジュールを備えたフォレンジックプラットフォーム
KAPELNK および Jump List アーティファクトを自動収集
Plasoスーパータイムラインの作成に使う、LNK ファイルパーサー搭載のタイムラインツール
LNK ExplorerLNK ファイルを対話形式で調査する GUI ツール

よくあるシナリオ

シナリオ 1:USB ドライブ経由のデータ持ち出し Recent フォルダーの LNK ファイルを分析し、リムーバブルドライブ上の対象を探します。ボリュームシリアル番号を USBSTOR レジストリエントリと関連付け、USB デバイスからアクセスされたファイルの一覧を作成し、リムーバブルドライブから開かれた文書を特定して、ファイルのコピー時刻と関連付けます。

シナリオ 2:スタートアップショートカット経由のマルウェア永続化 Startup フォルダーの LNK ファイルを調べ、悪意のある対象を探します。対象のパスと引数にエンコードされたコマンドや疑わしい実行ファイルがないか確認し、対象ファイルの存在を確かめて内容を調査し、作成日時を最初の侵害時刻と関連付けます。

シナリオ 3:ネットワーク共有へのアクセス調査 ネットワークパス(UNC 対象)を持つ LNK ファイルで絞り込み、アクセスされたネットワーク共有とアクセス時刻を特定し、マシン ID を既知の社内システムと関連付けます。通常業務以外で機密ファイルサーバーにアクセスしていないか確認し、コンプライアンス調査用のアクセス履歴を作成します。

シナリオ 4:法的手続きに向けた文書アクセスのタイムライン Recent フォルダーにあるすべての LNK ファイルを抽出し、ユーザーがアクセスした文書を時系列で一覧にします。案件に関連する特定のファイルを特定し、ファイルが開かれた時刻を示す対象のタイムスタンプを提示して、メールや通信のタイムラインと関連付けます。

出力形式

LNK File Analysis Summary:
  User Profile: suspect_user
  Total LNK Files: 234 (Recent: 198, Desktop: 23, Startup: 5, Other: 8)

  File Access Statistics:
    Local drive (C:):    156 files
    Removable media:     23 files (3 unique volume serials)
    Network shares:      15 files (\\server01, \\fileserver)
    Other drives:        4 files

  Machine IDs Found: DESKTOP-ABC123, LAPTOP-XYZ789
  MAC Addresses: AA:BB:CC:DD:EE:FF, 11:22:33:44:55:66

  Removable Media Access:
    Volume Serial 1234-ABCD:
      2024-01-15 14:32 - E:\Confidential\financial_report.xlsx
      2024-01-15 14:45 - E:\Confidential\customer_database.csv
      2024-01-15 15:00 - E:\Projects\source_code.zip

  Startup Persistence:
    updater.lnk -> C:\ProgramData\svc\updater.exe (SUSPICIOUS)
    OneDrive.lnk -> C:\Users\...\OneDrive.exe (Legitimate)

  Timeline: /cases/case-2024-001/analysis/lnk_analysis.csv

コメント