analyzing-windows-lnk-files-for-artifacts について
Windows の LNK ショートカットファイルとジャンプリストを分析するフォレンジック用スキルです。解析したショートカットのメタデータから、ファイルアクセス、リムーバブルメディアやネットワーク共有の利用、関連タイムラインを再構成します。
主な機能
- 01証拠から LNK ファイルを収集
- 02ショートカットを CSV または JSON に解析
- 03対象パスとタイムスタンプを確認
- 04リムーバブルメディアとネットワーク先を特定
- 05タイムラインでショートカットデータを照合
活用シーン
- 01ユーザーの文書アクセス履歴を再構成
- 02USB ドライブからアクセスされたファイルを調査
- 03ネットワーク共有へのアクセスを確認
- 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 と一緒にインストールされる参考資料やスクリプト。
LICENSEreferences/api-reference.mdscripts/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 ショートカットファイル形式 |
| 対象のタイムスタンプ | ショートカットが指すファイルの作成日時、更新日時、アクセス日時 |
| ボリュームシリアル番号 | 対象ファイルが存在するドライブボリュームの一意の識別子 |
| マシン ID | Distributed Link Tracking サービスによって埋め込まれる NetBIOS 名 |
| MAC アドレス | LNK ファイルを作成したマシンのネットワークアダプターの MAC アドレス |
| Jump Lists | アプリケーションごとの最近使用したファイルとピン留めされたファイルの一覧(埋め込み LNK データを含む) |
| Automatic Destinations | 最近開いたファイルのシステム管理 Jump List エントリ |
| Custom Destinations | 手動で削除されるまで保持される、ユーザーがピン留めした Jump List 項目 |
ツールとシステム
| ツール | 用途 |
|---|---|
| LECmd | CSV/JSON 出力に対応した、Eric Zimmerman のコマンドライン LNK ファイルパーサー |
| JLECmd | Eric Zimmerman の Jump List パーサー |
| LnkParse3 | プログラムから LNK ファイルを分析する Python ライブラリ |
| lnk_parser | 代替の Python LNK 解析ツール |
| Autopsy | LNK ファイル分析モジュールを備えたフォレンジックプラットフォーム |
| KAPE | LNK および Jump List アーティファクトを自動収集 |
| Plaso | スーパータイムラインの作成に使う、LNK ファイルパーサー搭載のタイムラインツール |
| LNK Explorer | LNK ファイルを対話形式で調査する 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