New to Claude Skills? Learn how to install them →

mukul975 on GitHub

Hunting for Defense Evasion

Free

Detect timestamp manipulation in NTFS file systems.

Get this skill

Free · Opens the source repo

What Hunting for Defense Evasion does

The Hunting for Defense Evasion via Timestomping skill is designed to assist cybersecurity professionals in detecting potential timestamp manipulation on NTFS file systems. By analyzing discrepancies between the $STANDARD_INFORMATION and $FILE_NAME timestamps in the Master File Table (MFT), this skill identifies signs of anti-forensic activity that may be employed by adversaries to obscure malicious actions. This is particularly relevant in threat hunting scenarios, where understanding the integrity of file timestamps can reveal attempts to blend malware into legitimate directories.

This skill leverages Python and the analyzeMFT tool to parse MFT entries, enabling users to compare timestamps effectively. It identifies anomalies such as when the $STANDARD_INFORMATION timestamps are older than the $FILE_NAME timestamps, which indicates potential timestomping. The detection process can be integrated into automated pipelines, allowing Security Operations Center (SOC) analysts to flag suspicious files based on temporal discrepancies, thereby enhancing threat detection capabilities.

Users will find this skill particularly useful during forensic examinations, where validating timeline integrity is crucial. It can help in triaging files that exhibit unusual creation dates or timestamps inconsistent with known deployment timelines. However, it is important to note that this skill should not be the sole detection method, as advanced adversaries may manipulate both timestamp types, necessitating additional corroborative analysis using tools like the USN Journal or ShimCache.

In summary, the Hunting for Defense Evasion via Timestomping skill provides a focused approach to identifying timestamp manipulation, making it a valuable tool for cybersecurity professionals engaged in forensic analysis and threat hunting.

When to use it

Use this skill when investigating suspected timestamp manipulation or conducting threat hunting for defense evasion across Windows systems.

When not to use it

This skill should not be relied upon as the only detection method, as advanced adversaries can manipulate timestamps in more sophisticated ways.

What you can build with it

Forensic Investigations

Use this skill to validate timeline integrity during forensic examinations of disk images.

Threat Hunting

Employ this skill to identify potential defense evasion tactics on compromised Windows systems.

Automated Detection Pipelines

Integrate this skill into automated workflows to flag temporal anomalies in MFT data.

How to install Hunting for Defense Evasion

View source

1. Install with the skills CLI

npx skills add mukul975/anthropic-cybersecurity-skills/hunting-for-defense-evasion-via-timestomping --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by mukul975

Hunting for Defense Evasion via Timestomping

Detect timestamp manipulation by analyzing NTFS MFT entries for discrepancies between $STANDARD_INFORMATION and $FILE_NAME attributes.

When to Use

  • Investigating suspected anti-forensic activity where an adversary may have altered file timestamps to blend malware into legitimate directories
  • Threat hunting for defense evasion (MITRE ATT&CK T1070.006) across compromised Windows systems
  • Validating timeline integrity during forensic examinations of disk images or live acquisitions
  • Triaging suspicious files that appear to have creation dates older than the OS installation or inconsistent with known deployment timelines
  • Detecting tools like Timestomp (Metasploit), NTimeStomp, SetMACE, or PowerShell Set-ItemProperty used to alter timestamps
  • Building automated detection pipelines that flag temporal anomalies in MFT data for SOC analysts

Do not use as the sole detection method; advanced adversaries can manipulate both $STANDARD_INFORMATION and $FILE_NAME timestamps (though the latter requires raw disk access and is much harder). Combine with USN Journal, $LogFile, and ShimCache/Amcache analysis for corroboration.

Prerequisites

  • Raw $MFT file extracted from a Windows system (via FTK Imager, KAPE, or live extraction)
  • MFTECmd (Eric Zimmerman tool) or analyzeMFT for MFT parsing
  • Python 3.8+ with pandas for analysis
  • Optional: mft Python library (pip install mft) for programmatic MFT parsing
  • Optional: KAPE (Kroll Artifact Parser and Extractor) for automated artifact collection
  • Timeline Explorer or Excel for visual analysis of parsed MFT output

Workflow

Step 1: Extract the $MFT from a Live System or Disk Image

# 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

Step 2: Parse the MFT with MFTECmd

Use Eric Zimmerman's MFTECmd to produce a CSV with both $STANDARD_INFORMATION and $FILE_NAME timestamps:

# 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

Step 3: Detect Timestomping via SI vs FN Comparison

The core detection: $STANDARD_INFORMATION timestamps are easily modified by user-mode tools, but $FILE_NAME timestamps are updated only by the NTFS driver (kernel-mode). When SI timestamps are OLDER than FN timestamps, timestomping is likely:

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()

Step 4: Corroborate with USN Journal Analysis

The USN Journal records metadata change events that persist even after timestomping:

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

Step 5: Check ShimCache and Amcache for Timeline Validation

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

Step 6: Generate a Timestomping Detection Report

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")

Verification

  • Confirm MFTECmd parses the $MFT without errors and produces both 0x10 (SI) and 0x30 (FN) timestamp columns
  • Create a test file and use a timestomping tool (e.g., NTimeStomp) in a lab to verify the detection logic catches the manipulation
  • Validate that the nanosecond-zeroed check does not produce excessive false positives on files created by installers that legitimately set timestamps
  • Cross-reference flagged files with the USN Journal to confirm BASIC_INFO_CHANGE events exist at the expected times
  • Verify ShimCache and Amcache timestamps provide independent corroboration of timeline inconsistencies
  • Test against known-clean system images to establish a false-positive baseline (some backup/imaging software legitimately resets timestamps)
  • Confirm the detection pipeline correctly handles deleted MFT entries (InUse=false) which may contain evidence of timestomped files that were later removed

Frequently asked questions about Hunting for Defense Evasion

Similar skills