关于 hunting-for-defense-evasion-via-timestomping
用于通过比较 MFT 数据中的 NTFS $STANDARD_INFORMATION 与 $FILE_NAME 时间戳,识别时间戳篡改的 Windows 取证技能。它为取证分析人员和 SOC 团队提供与其他证据交叉验证及生成结构化报告的流程。
主要功能
- 01比较 MFT 中的 SI 与 FN 时间戳
- 02使用 MFTECmd 或 analyzeMFT 解析 MFT
- 03将发现与 USN Journal 关联
- 04用 ShimCache 和 Amcache 验证时间线
- 05生成 JSON 检测报告
适用场景
- 01调查疑似时间戳篡改
- 02验证磁盘证据中的取证时间线
- 03构建基于 MFT 的时间异常检测
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mukul975/ anthropic-cybersecurity-skills --skill hunting-for-defense-evasion-via-timestomping安装后在 Claude Code 中输入/hunting-for-defense-evasion-via-timestomping确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSEreferences/api-reference.mdscripts/agent.py详细介绍
通过时间戳篡改排查防御规避行为
通过分析 NTFS MFT 条目中 $STANDARD_INFORMATION 与 $FILE_NAME 属性之间的差异,检测时间戳篡改。
适用场景
- 调查疑似反取证活动:攻击者可能篡改了文件时间戳,以将恶意软件混入合法目录
- 在受感染的 Windows 系统上进行防御规避威胁狩猎(MITRE ATT&CK T1070.006)
- 在磁盘镜像或实时采集的取证检查中验证时间线完整性
- 对创建日期早于操作系统安装时间、或与已知部署时间线不一致的可疑文件进行初步排查
- 检测用于篡改时间戳的工具,如 Timestomp(Metasploit)、NTimeStomp、SetMACE 或 PowerShell Set-ItemProperty
- 构建自动化检测流水线,为 SOC 分析师标记 MFT 数据中的时间异常
请勿将其作为唯一的检测方法;高级攻击者可以同时篡改 $STANDARD_INFORMATION 和 $FILE_NAME 时间戳(不过后者需要原始磁盘访问,难度大得多)。请结合 USN Journal、$LogFile 以及 ShimCache/Amcache 分析进行交叉验证。
前置条件
- 从 Windows 系统提取的原始 $MFT 文件(可通过 FTK Imager、KAPE 或实时提取)
- 用于解析 MFT 的
MFTECmd(Eric Zimmerman 工具)或analyzeMFT - Python 3.8+ 及用于分析的
pandas - 可选:用于以编程方式解析 MFT 的
mftPython 库(pip install mft) - 可选:用于自动化产物收集的 KAPE(Kroll Artifact Parser and Extractor)
- 用于对解析后的 MFT 输出进行可视化分析的 Timeline Explorer 或 Excel
工作流
第 1 步:从实时系统或磁盘镜像中提取 $MFT
# Method 1: Using KAPE to collect MFT and related artifacts
.\kape.exe --tsource C: --tdest D:\Evidence\MFT_Collection --target !SANS_Triage
# Method 2: Using FTK Imager CLI to extract $MFT
ftkimager.exe \\.\C: D:\Evidence\mft_raw.bin --e01 --include $MFT
# Method 3: Raw copy using RawCopy (handles locked NTFS system files)
RawCopy.exe /FileNamePath:C:0 /OutputPath:D:\Evidence\ /OutputName:$MFT
# Method 4: On a mounted forensic image in Linux
sudo mount -o ro,norecovery /dev/sdb1 /mnt/evidence
sudo icat -o 2048 /dev/sdb 0 > /mnt/output/$MFT
# Method 5: Using sleuthkit to extract MFT from disk image
icat -o 2048 evidence.E01 0 > extracted_MFT
第 2 步:使用 MFTECmd 解析 MFT
使用 Eric Zimmerman 的 MFTECmd 生成包含 $STANDARD_INFORMATION 和 $FILE_NAME 两种时间戳的 CSV:
# Parse MFT to CSV with all timestamp columns
MFTECmd.exe -f "D:\Evidence\$MFT" --csv D:\Evidence\Parsed\ --csvf mft_parsed.csv
# The output CSV contains these critical columns:
# Created0x10 - $STANDARD_INFORMATION Created timestamp
# LastModified0x10 - $STANDARD_INFORMATION Modified timestamp
# LastAccess0x10 - $STANDARD_INFORMATION Accessed timestamp
# LastRecordChange0x10 - $STANDARD_INFORMATION Entry Modified timestamp
# Created0x30 - $FILE_NAME Created timestamp
# LastModified0x30 - $FILE_NAME Modified timestamp
# LastAccess0x30 - $FILE_NAME Accessed timestamp
# LastRecordChange0x30 - $FILE_NAME Entry Modified timestamp
第 3 步:通过 SI 与 FN 对比检测时间戳篡改
核心检测原理:$STANDARD_INFORMATION 时间戳很容易被用户态工具修改,但 $FILE_NAME 时间戳仅由 NTFS 驱动(内核态)更新。当 SI 时间戳早于 FN 时间戳时,很可能存在时间戳篡改:
import pandas as pd
from datetime import datetime, timedelta
def load_mft_data(csv_path):
"""Load MFTECmd parsed CSV output."""
df = pd.read_csv(csv_path, low_memory=False)
# Parse timestamp columns
timestamp_cols = [
"Created0x10", "LastModified0x10", "LastAccess0x10", "LastRecordChange0x10",
"Created0x30", "LastModified0x30", "LastAccess0x30", "LastRecordChange0x30"
]
for col in timestamp_cols:
if col in df.columns:
df[col] = pd.to_datetime(df[col], errors="coerce")
return df
def detect_timestomping(df):
"""Detect timestamp manipulation by comparing SI and FN attributes.
Key indicators:
1. SI Created < FN Created (SI timestamp pushed back in time)
2. SI timestamps have nanoseconds = 0000000 (tool artifact)
3. SI Created < FN Entry Modified (impossible under normal NTFS behavior)
4. Large gap between SI and FN timestamps
"""
results = []
for idx, row in df.iterrows():
si_created = row.get("Created0x10")
fn_created = row.get("Created0x30")
si_modified = row.get("LastModified0x10")
fn_modified = row.get("LastModified0x30")
si_entry = row.get("LastRecordChange0x10")
fn_entry = row.get("LastRecordChange0x30")
if pd.isna(si_created) or pd.isna(fn_created):
continue
filepath = row.get("FileName", "unknown")
parent_path = row.get("ParentPath", "")
full_path = f"{parent_path}\\{filepath}" if parent_path else filepath
indicators = []
# Detection 1: SI Created is BEFORE FN Created
# Under normal NTFS operations, SI Created >= FN Created
if si_created < fn_created:
delta = fn_created - si_created
indicators.append({
"check": "SI_Created < FN_Created",
"si_value": str(si_created),
"fn_value": str(fn_created),
"delta": str(delta),
"confidence": "high"
})
# Detection 2: SI Modified is BEFORE FN Created
# A file cannot be modified before it was created
if pd.notna(si_modified) and si_modified < fn_created:
indicators.append({
"check": "SI_Modified < FN_Created",
"si_value": str(si_modified),
"fn_value": str(fn_created),
"confidence": "high"
})
# Detection 3: Nanosecond precision check
# Many timestomping tools set timestamps with zero nanoseconds
if pd.notna(si_created):
si_created_str = str(si_created)
if ".000000" in si_created_str or si_created_str.endswith("00:00:00"):
# Check if FN has normal nanosecond precision
fn_str = str(fn_created)
if ".000000" not in fn_str:
indicators.append({
"check": "SI_nanoseconds_zeroed",
"si_value": si_created_str,
"fn_value": fn_str,
"confidence": "medium"
})
# Detection 4: Large time gap between SI and FN
# Normal gap is seconds to minutes, not years
if abs((si_created - fn_created).days) > 365:
indicators.append({
"check": "SI_FN_gap_exceeds_1_year",
"si_value": str(si_created),
"fn_value": str(fn_created),
"delta_days": abs((si_created - fn_created).days),
"confidence": "high"
})
# Detection 5: SI Entry Modified much later than SI Created
# Indicates the SI attribute was rewritten
if pd.notna(si_entry) and pd.notna(si_created):
entry_delta = si_entry - si_created
if entry_delta.days > 365 * 5: # Entry modified years after creation
indicators.append({
"check": "SI_entry_modified_years_after_creation",
"si_created": str(si_created),
"si_entry_modified": str(si_entry),
"confidence": "medium"
})
if indicators:
results.append({
"file_path": full_path,
"entry_number": row.get("EntryNumber", ""),
"in_use": row.get("InUse", True),
"si_created": str(si_created),
"fn_created": str(fn_created),
"indicators": indicators,
"highest_confidence": max(i["confidence"] for i in indicators),
})
return results
# Run detection
df = load_mft_data("D:\\Evidence\\Parsed\\mft_parsed.csv")
stomped_files = detect_timestomping(df)
print(f"\nTimestomping Detection Results")
print(f"{'='*60}")
print(f"Total MFT entries analyzed: {len(df)}")
print(f"Suspicious entries found: {len(stomped_files)}")
print()
for entry in sorted(stomped_files, key=lambda x: x["highest_confidence"], reverse=True):
print(f"[{entry['highest_confidence'].upper()}] {entry['file_path']}")
print(f" SI Created: {entry['si_created']}")
print(f" FN Created: {entry['fn_created']}")
for ind in entry["indicators"]:
print(f" Check: {ind['check']} (confidence: {ind['confidence']})")
print()
第 4 步:结合 USN Journal 分析进行印证
USN Journal 记录了元数据变更事件,这些记录即使在时间戳被篡改后依然存在:
def correlate_with_usn_journal(stomped_files, usn_csv_path):
"""Cross-reference timestomped files with USN Journal entries.
The USN Journal records a BASIC_INFO_CHANGE reason when timestamps
are modified, providing corroborating evidence of timestomping.
"""
usn_df = pd.read_csv(usn_csv_path, low_memory=False)
usn_df["UpdateTimestamp"] = pd.to_datetime(usn_df["UpdateTimestamp"], errors="coerce")
corroborated = []
for entry in stomped_files:
filename = entry["file_path"].split("\\")[-1]
# Find USN entries for this file with BASIC_INFO_CHANGE
usn_matches = usn_df[
(usn_df["Name"] == filename) &
(usn_df["UpdateReasons"].str.contains("BASIC_INFO_CHANGE", na=False))
]
if not usn_matches.empty:
entry["usn_corroboration"] = True
entry["usn_change_times"] = usn_matches["UpdateTimestamp"].tolist()
entry["highest_confidence"] = "critical"
corroborated.append(entry)
print(f"[CORROBORATED] {filename} - USN Journal confirms "
f"BASIC_INFO_CHANGE at {usn_matches['UpdateTimestamp'].iloc[0]}")
return corroborated
# Parse USN Journal (use MFTECmd or ANJP)
# MFTECmd.exe -f "$J" --csv D:\Evidence\Parsed\ --csvf usn_parsed.csv
第 5 步:检查 ShimCache 和 Amcache 以验证时间线
def check_shimcache_timeline(stomped_files, shimcache_csv):
"""Validate timestamps against ShimCache (AppCompatCache) entries.
ShimCache records the last modification time of executables
independently of NTFS timestamps, providing another corroboration point.
"""
shim_df = pd.read_csv(shimcache_csv, low_memory=False)
shim_df["LastModifiedTimeUTC"] = pd.to_datetime(
shim_df["LastModifiedTimeUTC"], errors="coerce"
)
for entry in stomped_files:
filepath = entry["file_path"]
shim_match = shim_df[
shim_df["Path"].str.lower() == filepath.lower()
]
if not shim_match.empty:
shim_time = shim_match["LastModifiedTimeUTC"].iloc[0]
si_modified = pd.to_datetime(entry.get("si_created"))
if pd.notna(shim_time) and pd.notna(si_modified):
delta = abs((shim_time - si_modified).days)
if delta > 30:
entry["shimcache_mismatch"] = True
entry["shimcache_time"] = str(shim_time)
print(f"[SHIMCACHE MISMATCH] {filepath}")
print(f" SI timestamp: {si_modified}")
print(f" ShimCache timestamp: {shim_time}")
print(f" Delta: {delta} days")
return stomped_files
第 6 步:生成时间戳篡改检测报告
import json
def generate_report(stomped_files, output_path):
"""Generate a structured JSON report of all timestomping detections."""
report = {
"report_title": "Timestomping Detection Analysis",
"generated_at": datetime.utcnow().isoformat() + "Z",
"mitre_technique": "T1070.006 - Indicator Removal: Timestomp",
"total_suspicious_files": len(stomped_files),
"critical_findings": len([f for f in stomped_files if f["highest_confidence"] == "critical"]),
"high_findings": len([f for f in stomped_files if f["highest_confidence"] == "high"]),
"medium_findings": len([f for f in stomped_files if f["highest_confidence"] == "medium"]),
"findings": stomped_files,
}
with open(output_path, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"Report written to {output_path}")
print(f" Critical: {report['critical_findings']}")
print(f" High: {report['high_findings']}")
print(f" Medium: {report['medium_findings']}")
generate_report(stomped_files, "D:\\Evidence\\timestomping_report.json")
验证
- 确认 MFTECmd 能无错误地解析 $MFT,并生成 0x10(SI)和 0x30(FN)两种时间戳列
- 在实验环境中创建一个测试文件,并使用时间戳篡改工具(例如 NTimeStomp)验证检测逻辑能够捕获该篡改行为
- 验证纳秒归零检查不会对安装程序合法设置时间戳创建的文件产生过多误报
- 将标记出的文件与 USN Journal 交叉比对,确认在预期时间存在 BASIC_INFO_CHANGE 事件
- 验证 ShimCache 和 Amcache 时间戳能为时间线不一致提供独立印证
- 针对已知干净的系统镜像进行测试,以建立误报基线(某些备份/镜像软件会合法地重置时间戳)
- 确认检测流水线能正确处理已删除的 MFT 条目(InUse=false),其中可能包含被时间戳篡改后又删除的文件的证据
评论
支持 Markdown