Windows LNKファイル解析による証拠抽出 は何をしますか?
使用タイミング
- Windowsショートカットファイルからユーザーのファイルアクセス履歴を復元する場合
- アクセスされたファイル、ネットワーク共有、リムーバブルメディアを追跡する場合
- 特定のドキュメントをユーザーが開いたことを証明する調査時
- ファイルアクセスを他のタイムライン証拠と関連付ける場合
- リモートシステムやUSBデバイス上のアクセスされたパスを特定する場合
前提条件
- フォレンジックイメージからLNKファイルへのアクセス権(Recent、Desktop、Quick Launch)
- LECmd(Eric Zimmerman)、python-lnk、またはLnkParser(解析ツール)
- LNKファイル構造(Shell Link Binary形式)に関する理解
- Windowsシステム上のLNKファイル保存場所に関する知識
- 解析ツールがインストールされたフォレンジックワークステーション
ワークフロー
ステップ1: フォレンジックイメージからLNKファイルを収集
# フォレンジックイメージをマウント
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}
# RecentアイテムのLNKファイルをコピー(一次ソース)
cp /mnt/evidence/Users/*/AppData/Roaming/Microsoft/Windows/Recent/*.lnk \
/cases/case-2024-001/lnk/recent/ 2>/dev/null
# 自動保存先(ジャンプリスト)
cp /mnt/evidence/Users/*/AppData/Roaming/Microsoft/Windows/Recent/AutomaticDestinations/*.automaticDestinations-ms \
/cases/case-2024-001/lnk/recent/ 2>/dev/null
# カスタム保存先(ピン留めされたジャンプリストアイテム)
cp /mnt/evidence/Users/*/AppData/Roaming/Microsoft/Windows/Recent/CustomDestinations/*.customDestinations-ms \
/cases/case-2024-001/lnk/custom/ 2>/dev/null
# デスクトップショートカット
cp /mnt/evidence/Users/*/Desktop/*.lnk /cases/case-2024-001/lnk/desktop/ 2>/dev/null
# スタートアップフォルダのショートカット(永続化)
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
# システム上のすべてのLNKファイルを検索
find /mnt/evidence/ -name "*.lnk" -type f 2>/dev/null > /cases/case-2024-001/lnk/all_lnk_locations.txt
# カウントとハッシュ化
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ファイルを解析
# Eric ZimmermanのLECmdを使用(WindowsまたはMono経由)
# ディレクトリ内のすべてのLNKファイルを処理
LECmd.exe -d "C:\cases\lnk\recent" --csv "C:\cases\analysis" --csvf lnk_analysis.csv
# 単一のLNKファイルを冗長出力で処理
LECmd.exe -f "C:\cases\lnk\recent\document.pdf.lnk"
# ジャンプリストファイルを処理
JLECmd.exe -d "C:\cases\lnk\recent" --csv "C:\cases\analysis" --csvf jumplist_analysis.csv
# 出力に含まれる情報:
# - ソースファイルパス
# - ターゲットパス(アクセスされたファイル)
# - ターゲットの作成、変更、アクセスタイムスタンプ
# - LNKの作成、変更タイムスタンプ
# - 作業ディレクトリ
# - コマンドライン引数
# - ボリュームシリアル番号とラベル
# - ドライブタイプ(固定、リムーバブル、ネットワーク)
# - マシンID(NetBIOS名)
# - MACアドレス(トラッカーデータベースから)
# - ターゲットファイルのサイズ
ステップ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': '',
}
# ヘッダーのタイムスタンプを抽出
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', ''))
# リンク情報を抽出
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', ''))
# 文字列データを抽出
string_data = info.get('string_data', {})
parsed['working_dir'] = str(string_data.get('working_dir', ''))
parsed['arguments'] = str(string_data.get('command_line_arguments', ''))
# トラッカーデータ(マシンIDと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(f"\n{filename}")
print(f" ターゲット: {parsed['target_path']}")
print(f" 更新日時: {parsed['target_modified']}")
print(f" ドライブ: {parsed['drive_type']} (シリアル: {parsed['volume_serial']})")
if parsed['machine_id']:
print(f" マシン: {parsed['machine_id']}")
except Exception as e:
print(f" 解析エラー {filename}: {e}")
# 結果を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\n解析されたLNKファイルの総数: {len(results)}")
PYEOF
ステップ4: 調査価値の分析
# リムーバブルメディアからアクセスされたファイルを特定
python3 << 'PYEOF'
import csv
with open('/cases/case-2024-001/analysis/lnk_analysis.csv') as f:
reader = csv.DictReader(f)
print("=== リムーバブルメディアからアクセスされたファイル ===\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']} | ボリューム: {row['volume_serial']}")
if 'network' in row.get('drive_type', '').lower() or \
row.get('target_path', '').startswith('\\\\'):
network.append(row)
print(f"\n=== ネットワーク共有からアクセスされたファイル ===\n")
for row in network:
print(f" {row['target_modified']} | {row['target_path']}")
print(f"\nリムーバブルメディアのファイル: {len(removable)}")
print(f"ネットワーク共有のファイル: {len(network)}")
# ユニークなマシンを確認(トラッカーデータ)
machines = set()
for row in [*removable, *network]:
if row.get('machine_id'):
machines.add(row['machine_id'])
if machines:
print(f"\n検出されたマシンID: {machines}")
PYEOF
# スタートアップフォルダのLNKファイル(永続化)を確認
echo "=== スタートアップフォルダのショートカット(永続化) ===" > /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 | 分散リンクトラッキングサービスによって埋め込まれたNetBIOS名 |
| MACアドレス | LNKファイルを作成したマシンのネットワークアダプタMAC |
| ジャンプリスト | アプリケーションごとの最近開いたファイルとピン留めされたファイルのリスト(埋め込みLNKデータを含む) |
| 自動保存先 | 最近開いたファイルのシステム管理ジャンプリストエントリ |
| カスタム保存先 | ユーザーがピン留めしたジャンプリストアイテム(手動削除まで保持) |
ツールとシステム
| ツール | 目的 |
|---|---|
| LECmd | Eric ZimmermanのコマンドラインLNKファイルパーサー(CSV/JSON出力) |
| JLECmd | Eric Zimmermanのジャンプリストパーサー |
| LnkParse3 | プログラムによるLNKファイル解析のためのPythonライブラリ |
| lnk_parser | 代替Python LNK解析ツール |
| Autopsy | LNKファイル解析モジュールを備えたフォレンジックプラットフォーム |
| KAPE | LNKおよびジャンプリストアーティファクトの自動収集ツール |
| Plaso | LNKファイルパーサーを搭載したタイムラインツール(スーパータイムライン作成用) |
| LNK Explorer | インタラクティブなLNKファイル調査用GUIツール |
一般的なシナリオ
シナリオ1: USBドライブ経由のデータ持ち出し RecentフォルダのLNKファイルを解析し、リムーバブルドライブ上のターゲットを特定。ボリュームシリアル番号をUSBSTORレジストリエントリと照合。USBデバイスからアクセスされたファイルのリストを構築。リムーバブルドライブから開かれたドキュメントを特定。ファイルコピータイムスタンプと関連付け。
シナリオ2: スタートアップショートカットによるマルウェア永続化 スタートアップフォルダのLNKファイルを調査し、悪意のあるターゲットを確認。ターゲットパスと引数にエンコードされたコマンドや不審な実行ファイルがないか確認。ターゲットファイルが存在するか確認し、調査。作成タイムスタンプを初期侵害時刻と関連付け。
シナリオ3: ネットワーク共有アクセス調査 ネットワークパス(UNCターゲット)を持つLNKファイルをフィルタリング。どのネットワーク共有がいつアクセスされたかを特定。マシンIDを既知の企業システムと照合。通常業務外の機密ファイルサーバーへのアクセスがないか確認。コンプライアンス調査のためのアクセス・タイムラインを構築。
シナリオ4: 法的手続きのためのドキュメントアクセス・タイムライン Recentフォルダの全LNKファイルを抽出。ユーザーがアクセスしたドキュメントの時系列リストを作成。事件に関連する特定のファイルを特定。ファイルが開かれた時刻を示すターゲットタイムスタンプを提示。メールやコミュニケーションのタイムラインと関連付け。
出力形式
LNKファイル解析サマリー:
ユーザープロファイル: suspect_user
総LNKファイル数: 234 (Recent: 198, Desktop: 23, Startup: 5, その他: 8)
ファイルアクセス統計:
ローカルドライブ (C:): 156 ファイル
リムーバブルメディア: 23 ファイル (3 ユニークボリュームシリアル)
ネットワーク共有: 15 ファイル (\server01, \fileserver)
その他ドライブ: 4 ファイル
検出されたマシンID: DESKTOP-ABC123, LAPTOP-XYZ789
MACアドレス: AA:BB:CC:DD:EE:FF, 11:22:33:44:55:66
リムーバブルメディアアクセス:
ボリュームシリアル 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
スタートアップ永続化:
updater.lnk -> C:\ProgramData\svc\updater.exe (不審)
OneDrive.lnk -> C:\Users\...\OneDrive.exe (正当)
タイムライン: /cases/case-2024-001/analysis/lnk_analysis.csv