Windows on-disk forensics #
NTFS metadata
NTFS stores not just files but a parallel set of metadata files prefixed with $ that live in the volume root (hidden). An image gives you these for free and they survive deletion of individual user files for a surprisingly long time. In practice you'll pull the triage set, parse $MFT first to answer "did this file exist?", then $UsnJrnl / $LogFile for "what happened to it?", then $I30 to recover filenames out of directory listings whose MFT entries have been reused.
| Artifact | What it proves | How it's generated / key fact | Timestamps | Parsers / tools |
|---|---|---|---|---|
$MFT | Every file/dir that existed, incl. deleted entries | The NTFS driver creates a 1 KB FILE record for every file and directory at creation, updates it on every metadata change, and on delete only marks it unused, so deleted entries survive until the record is reused. Each record holds $STANDARD_INFORMATION (SI), $FILE_NAME (FN) and $DATA (small files are stored inside the record itself). | 4 SI + 4 FN timestamps (MACB), 100-ns FILETIME UTC. SI changes with normal activity and all four values (incl. Changed) are settable from user mode (SetFileTime, SetFileInformationByHandle). FN is set by the kernel on create, rename and move - harder, but not impossible, to forge (SetMACE, move-rename trick). | MFTECmd (Zimmerman), AnalyzeMFT (dkovar), mft2csv (jschicht), Autopsy, X-Ways, EnCase, Velociraptor Windows.NTFS.MFT |
$LogFile | Transaction journal (recent file ops) | NTFS's own transaction log: the driver writes redo/undo records for every metadata change before committing it, so the volume can recover after a crash. Circular, about 64 MB by default - minutes to hours of history on a busy volume. | Records are ordered by LSN, not stamped with their own wall-clock time; times come from the SI / FN values captured inside the redo and undo data. Sequence by LSN, date by the embedded attributes. | LogFileParser (jschicht), ntfs-log-tracker (dkovar), Velociraptor |
$UsnJrnl:$J | Change journal - creates/renames/deletes | The Update Sequence Number journal. When enabled (default on system volumes) NTFS appends a record for each change to a file - create, overwrite, extend, rename, delete, security change, basic-info (timestamp) change, close - with the name, MFT and parent references and reason flags. Sparse, default max 32 MB, so days of history on active systems. Great for "what was staged then wiped". | Per-record TimeStamp, FILETIME UTC, written by the kernel when the change is logged, not user-settable. A BASIC_INFO_CHANGE record on a file is the corroboration for timestomping. | MFTECmd (-f $J), UsnJrnl2Csv (jschicht), fsutil usn readjournal |
$I30 (INDX) | Directory index - recovers deleted filenames in a folder | Each directory's B+ tree index ($INDEX_ROOT / $INDEX_ALLOCATION) listing every child's name and a copy of its FN attribute. When entries are removed or nodes rebalance, old entries stay in slack space and can be carved, even after the MFT record is reused. | Each entry holds the child's FN MACB as it was when indexed, a snapshot that can predate or outlive the current $MFT record. | INDXParse (willi.ms), Velociraptor Windows.NTFS.Index |
$Secure:$SDS | Security descriptors (ACLs) | Volume-wide store of unique security descriptors; each file's SI points to one by Security ID, so identical ACLs are stored once and a new ACL appends a new descriptor. Useful for post-breach permission changes. | No timestamp per descriptor. Bound changes with the owning file's SI Changed time, $UsnJrnl SECURITY_CHANGE records and Security EID 4670 (if auditing is on). | SDSParser, icacls (live), fsutil |
| Alternate Data Streams | Hidden payloads bound to a file | Extra named $DATA streams on a file. Browsers, Outlook and other attachment-manager-aware apps write Zone.Identifier (Mark of the Web) when they save a download - ZoneId 3 = Internet, with ReferrerUrl / HostUrl on Win10+. Attackers strip MOTW, or stash scripts and payloads in streams. | No timestamps of its own - streams share the host file's SI / FN. Zone.Identifier records URLs but no time; match it to browser download history. | Streams.exe (Sysinternals), dir /r, Get-Item -Stream *, Autopsy |
BASIC_INFO_CHANGE record in $UsnJrnl, $LogFile, zeroed sub-second values, and how the file arrived before you write "timestomped".Execution evidence (what ran, when, by whom)
"Execution evidence" is the body of Windows features that happen to leave timestamped proof a program ran. None of them was designed as a forensic log - they exist for performance (Prefetch), compatibility (Amcache, ShimCache), battery usage reporting (SRUM/BAM), UI personalization (UserAssist), or activity sync (ActivitiesCache). Each has different fidelity, different enablement defaults, and different retention. Rank them, corroborate across several, and account for where each one is wrong before you call something executed.
| Artifact | Proof of execution? | How it's generated / key fact | Timestamps | Parsers / tools |
|---|---|---|---|---|
PrefetchC:\ | Yes (on client Win10/11) | Written by the Windows prefetcher (SysMain) to speed up launches: the kernel traces the files and folders a process touches in its first ~10 seconds, then writes or updates <EXE>-<path-hash>.pf, so the same exe run from two folders makes two files. Lists loaded files and volumes. MAM-compressed on Win10+, up to 1,024 files on Win8+. Disabled by default on Windows Server; an SSD does not disable it on clients. | Up to 8 last-run times (Win8+) and a run count, FILETIME UTC, inside the file. The .pf's own creation time ≈ first run (if it hasn't aged out), modification ≈ last run. The prefetcher traces the first ~10 s after launch, so the .pf's times (and the embedded run times) land about 10 seconds after the process actually started - subtract it when sequencing at second level. | PECmd (Zimmerman), WinPrefetchView (NirSoft), Prefetch Parser (Volatility plugin), KAPE module, Velociraptor Windows.Forensics.Prefetch |
Amcache.hveC:\ (+ .LOG1 / .LOG2)Key: Root\ | Presence + metadata (strong indicator) | Registry hive maintained by the compatibility appraiser (the Microsoft Compatibility Appraiser task, program installs and launches) that inventories executables, drivers, installed programs and shortcuts. Root\InventoryApplicationFile holds path, size, publisher, version, PE link date and a SHA-1 (FileId) of the first 30 MiB (31,457,280 bytes) only - larger files won't match a full-file hash. Files can be inventoried without ever running. | Each InventoryApplicationFile key's LastWrite ≈ when Windows catalogued the file, often near first run, but scans add files too. LinkDate = PE compile time (attacker-controlled). No run count. | AmcacheParser (Zimmerman), RegRipper amcache.pl, Registry Explorer |
| ShimCache / AppCompatCache Hive: C:\Key: SYSTEM\, value AppCompatCache | No - only that App Compat checked the file | The kernel shim engine keeps the cache in memory and adds an entry whenever a file's compatibility is checked - on execution, but also when Explorer browses or inspects an executable - then writes it to SYSTEM\CurrentControlSet\Control\Session Manager\AppCompatCache at shutdown or reboot. Up to 1,024 entries (path + the file's modified time); no reliable execution flag on Win10+. Often misreported as proof of execution. | The timestamp is the file's $SI Modified time when it was cached, not when it ran. Only flushed at shutdown, so recent entries exist only in memory on a live host. List order roughly approximates insertion order; absolute times don't. | AppCompatCacheParser (Zimmerman), ShimCacheParser (Mandiant), RegRipper appcompatcache.pl |
SRUMC:\Provider list: SOFTWARE\ | Yes - with network bytes per app per user | The System Resource Usage Monitor service samples per-app, per-user resource use - CPU time, bytes sent and received per interface, energy, notifications - and writes it to this ESE database about hourly and at shutdown (the SOFTWARE\…\SRUM\Extensions key lists the provider DLL and GUID behind each table - collect the SOFTWARE hive too, since parsers use it to name tables and map network interfaces). Apps and SIDs are stored as IDs - join SruDbIdMapTable. Typically 30-60 days. | TimeStamp is an OLE Automation date (ESE DateTime) for the hourly write - the activity happened during the preceding interval. Some columns (e.g. ConnectStartTime) are FILETIME. Precision ceiling ≈ 1 hour: good for "did rclone run between 02:00 and 03:00?", useless for sub-hour sequencing. | SrumECmd (Zimmerman), srum-dump (Mark Baggett), ESEDatabaseView (NirSoft) |
| BAM/DAM Hive: C:\Key: SYSTEM\ (Win10 1809+; 1709-1803: …\); DAM under …\ | Yes - background activity monitor | The Background Activity Moderator driver (bam.sys, Win10 1709+) throttles background apps and records, per user SID, the full path of each executable the last time it ran. Not every process type is recorded. | Each value begins with an 8-byte FILETIME UTC = last run under that user. One value per binary, overwritten each run - most recent only, no history. | RegRipper bam.pl, Registry Explorer, KAPE module, Velociraptor Windows.Forensics.Bam, Belkasoft X |
| UserAssist Hive: C:\Key: NTUSER.DAT\ | Yes - GUI launches via Explorer | Explorer writes it per user when a program or shortcut is launched through the shell - Start menu, desktop, Explorer double-click. Value names are ROT13 of the path or a known-folder GUID path. Not updated for command-line or script launches. | Each value's blob: LastExecuted FILETIME UTC, run count, focus count and focus time (ms in the foreground). Last run only. | RegRipper userassist.pl, UserAssistView (NirSoft), Registry Explorer |
| MUICache Hive: C:\Key: UsrClass.dat\ | Weak - binary was shown to shell | When the shell runs an executable it caches the program's display name (the FileDescription from its version resource) under UsrClass.dat\…\Shell\MuiCache, keyed by path. Shows the shell handled that path for this user. Supplementary. | No per-value timestamp - the key's LastWrite bounds the most recent addition. | RegRipper muicache.pl, MUICacheView (NirSoft) |
ActivitiesCache.db (Timeline)C:\ (+ -wal) | Yes - per-user activities | The Connected Devices Platform service (CDPSvc) records user activities - app launches, documents opened, some clipboard activity - in a per-user SQLite DB under %LocalAppData%\ConnectedDevicesPlatform\<id>\. The Timeline UI is gone in Windows 11 and cloud sync ended in 2021, but the DB may still hold local activity - check the image rather than assume. | StartTime, EndTime, LastModifiedTime, ExpirationTime are Unix epoch seconds (not FILETIME, easy to mis-convert). Retention about 30 days. | WxTCmd (Zimmerman), Autopsy, SQLite browsers |
RecentFileCache.bcfC:\ | Legacy - Win7 era | Win7 / Server 2008 R2: the compatibility scanner records recently run executables here. Superseded by Amcache from Windows 8. | No per-entry time; the .bcf file's own timestamps bound the last update. | RecentFileCacheParser (Zimmerman), KAPE module, Velociraptor (KAPE targets) |
| CIT / Syscache.hve CIT (mostly pre-Windows 10): SOFTWARE\ (hive C:\)Syscache: C:\ | Supporting telemetry | CIT: compatibility-telemetry usage aggregates under SOFTWARE\Microsoft\Windows NT\CurrentVersion\AppCompatFlags\CIT. Syscache.hve (System Volume Information, Win7 / 2008 R2) lists files the compatibility engine touched, by MFT reference. Niche, but useful corroboration. | CIT stores aggregation windows (roughly daily buckets) as FILETIME, low-resolution corroboration only. | CITParser (ANSSI), Registry Explorer |
Shell & user activity
| Artifact | What it proves | How it's generated / key fact | Timestamps | Parsers / tools |
|---|---|---|---|---|
ShellBagsUsrClass.dat\ + \ (Win7+; some in NTUSER.DAT\) | A user opened a folder - local, USB, network share, ZIP, Control Panel. Survives deletion of the folder. | Explorer (and the common Open / Save dialog) saves per-folder view settings - size, position, sort, icon mode - the first time a user browses a folder, so the window reopens the same way. The folder's identity is stored as a chain of shell items in BagMRU, which mirrors the folder tree; settings go under Bags\<n>. Only folders opened through the shell get one - cd in a terminal doesn't. | Shell items embed the folder's own MAC times as DOS date/time (2-second resolution) as they stood when the item was created, not the navigation time. Navigation time comes from the BagMRU key's LastWrite (FILETIME UTC) for the most recently opened child, ordered by MRUListEx. | ShellBags Explorer / SBECmd (Zimmerman), RegRipper shellbags.pl, Velociraptor Windows.Forensics.Shellbags |
Jump Lists%AppData%\, …\ | Files and folders a user opened with a specific app, plus pinned items, including from removable and network drives. | Windows keeps an <AppID>.automaticDestinations-ms file per application (AppID = hash of the app's identity, so the filename tells you which app). Each time the app opens an item through the shell, an LNK-format stream for that item is added or updated inside the OLE compound file and the DestList stream is updated. customDestinations-ms files are written by the apps themselves (tasks, pinned items). | Each DestList entry has a last-access FILETIME UTC and (Win10+) an access count. Each embedded LNK stream carries the target's own MAC times. The .automaticDestinations-ms file's own times bound first and last use of that app's list. | JLECmd (Zimmerman), JumpListExplorer, Velociraptor (KAPE targets) |
LNK files%AppData%\, Office …\, Desktop / Start Menu | A file or folder was opened by this user, and where it lived (volume serial and type, network share, machine name and MAC address). | When a user opens a file through the shell, Explorer creates (or overwrites) a shortcut named after the target in Recent; one LNK per target name, updated on each re-open. The LNK records the target's path, size and MAC times, the volume's serial, type and label, and a tracker block with the originating NetBIOS name and MAC address. Deleted LNKs can often be carved from unallocated space, useful for USB tracking. | Two sets: the target's MAC times as of the last update (FILETIME UTC, inside the LNK), and the LNK file's own times - created ≈ first opened, modified ≈ last opened. | LECmd (Zimmerman), lnk-parser, Velociraptor Windows.Forensics.Lnk |
Office TrustRecordsNTUSER.DAT\ | The user trusted a document - and whether they enabled macros / active content or only editing. | Office writes a value named after the document path when the user clicks Enable Editing (leaving Protected View) or Enable Content (macros / active content). Check the final DWORD of the value: FF FF FF 7F = Enable Content (macros allowed); 00 00 00 00 = Enable Editing only. Behaviour depends on Trust Center settings. | The first 8 bytes are a FILETIME UTC = when the trust decision was recorded. | RegRipper trustrecords.pl, Registry Explorer, dissect trusteddocs |
OneDrive%LocalAppData%\, …\ | Files synced, uploaded, downloaded or deleted through OneDrive, including exfiltration to a personal account. | The sync client writes obfuscated binary .odl logs as it works, and keeps a SQLite SyncEngineDatabase.db per account listing synced files and folders. Each signed-in account (personal or business) gets its own settings folder, so a personal account on a corporate laptop shows up as its own folder. | ODL entries carry per-event timestamps (parsed to UTC by the tools); the database stores item metadata with its own time fields. Log files roll over, so collect early. | OneDriveExplorer (Brian Maloney), odl.py (Yogesh Khatri), sqlite3 |
WER / LocalDumps%ProgramData%\ and \; LocalDumps folder from HKLM\ | A process crashed or hung - often malware, an exploit, or a tool injected into the wrong process. Dumps can hold cleartext secrets and payloads. | Windows Error Reporting (WerFault.exe, WerSvc) writes a Report.wer text file - faulting app path, module, exception code, loaded modules - and sometimes a minidump when a process crashes or hangs. If LocalDumps is configured, full dumps go to that folder too. The same crash is logged as Application EID 1000 / 1001. | Report.wer has EventTime and UploadTime (FILETIME); the .dmp's own file times = when it was written; Application EID 1000 / 1001 give the same moment from the event log. | Text editor, WinDbg, Volatility / MemProcFS for dumps |
AI agent artifacts
AI coding agents and assistants run commands, read files and edit code as the user, so their transcripts are often the best execution log on the host - the prompt, every command, and the output the agent saw. The same files collect any secrets the agent read. Paths below are for Windows; ~ = %USERPROFILE%.
powershell -Command / cmd /c), so they usually don't appear in PSReadLine history (ConsoleHost_history.txt). If EDR shows shells spawned by node.exe, claude.exe, codex.exe, Cursor.exe or Code.exe, the agent transcript is where the command lines and their output are recorded.| Agent | Where | What it proves | Format, retention, notes |
|---|---|---|---|
| Terminal agents | |||
| Claude Code (Anthropic) | %USERPROFILE%\.claude\projects\<encoded-cwd>\<session-uuid>.jsonl - full transcripts%USERPROFILE%\.claude\history.jsonl - every prompt typed, with project path%USERPROFILE%\.claude\file-history\ - pre-edit copies of files it changed%USERPROFILE%\.claude\shell-snapshots\%USERPROFILE%\.claude\debug\%USERPROFILE%\.claude.json - per-project MCP servers, trusted folders, account%USERPROFILE%\.claude\settings.json - permission allow-lists, hooks; also <repo>/.claude/settings*.json | Prompts, model replies, every tool call (shell command lines, file reads and writes, web fetches) and the tool output, with working directory and git branch. File-history lets you diff what it changed. | JSONL, one event per line, ISO-8601 UTC timestamp on each. <encoded-cwd> is the project path with separators and dots turned into -. Transcripts older than cleanupPeriodDays (default 30) are deleted at startup - collect early. Optional OpenTelemetry export (CLAUDE_CODE_ENABLE_TELEMETRY) can mean a central copy in the SIEM. Enterprise policy: C:\ProgramData\ClaudeCode\managed-settings.json or C:\Program Files\ClaudeCode\ (varies by version). |
| Codex CLI (OpenAI) | %USERPROFILE%\.codex\sessions\YYYY\MM\DD\rollout-*.jsonl - session transcripts%USERPROFILE%\.codex\history.jsonl - prompt history%USERPROFILE%\.codex\log\ - TUI / debug log%USERPROFILE%\.codex\config.toml - approval mode, sandbox, MCP servers | Prompts, model output, shell commands and results, patches applied, token use. | JSONL with timestamp / type / payload per line; the date folders bound session days. Newer builds add a SQLite index and may compress old rollouts (.jsonl.zst). approval_policy = "never", --yolo or --dangerously-bypass-approvals-and-sandbox = commands ran unreviewed. |
| Gemini CLI (Google) | %USERPROFILE%\.gemini\tmp\<project-hash>\chats\ - auto-saved sessions%USERPROFILE%\.gemini\tmp\<project-hash>\logs\ - plus checkpoints/ and /chat save tags%USERPROFILE%\.gemini\history\<project-hash>\ - shadow git repo (checkpointing, off by default)%USERPROFILE%\.gemini\settings.json - and GEMINI.md context files | Conversation and tool calls per project; the shadow repo holds file states before edits. | JSON. The hash comes from the absolute project path - open every folder rather than computing it. --yolo / approvalMode = tool calls ran unprompted. |
| GitHub Copilot CLI | %USERPROFILE%\.copilot\session-state\<session>\events.jsonl - plus workspace.yaml, plan.md, checkpoints/%USERPROFILE%\.copilot\session-store.db - SQLite index (newer builds)%USERPROFILE%\.copilot\logs\process-<ms-epoch>-<pid>.log%USERPROFILE%\.copilot\config.json | Full session event history, plans and file checkpoints; one debug log per process. | Older builds used history-session-state/. Log names carry a millisecond epoch and PID, a ready-made process start time to line up with EDR. COPILOT_HOME can relocate the folder. |
| Amazon Q Developer / Kiro CLI | %TEMP%\qlog\ - logs%USERPROFILE%\.aws\amazonq\, %USERPROFILE%\.kiro\ - config | chat.log covers startup, SDK calls and networking; AWS warns it can hold file paths, code, command output and account IDs. | Temp-dir logs don't survive reboots or temp cleanup. Full prompt logging is an admin setting that writes to S3 - ask whether it's on. --trust-all-tools = no approval prompts. |
| Aider | In each repo: .aider.chat.history.md, .aider.input.history%USERPROFILE%\.aider.conf.yml | Full chat as Markdown plus input history; aider also commits its own edits to git. | Lives in the project, not the home dir - sweep for it, and check git log for aider: commits. |
| IDE agents | |||
| GitHub Copilot Chat / agent mode (VS Code) | %APPDATA%\Code\User\workspaceStorage\<hash>\chatSessions\ - chat sessions%APPDATA%\Code\User\workspaceStorage\<hash>\chatEditingSessions\ - files it changed%APPDATA%\Code\User\globalStorage\github.copilot-chat\%APPDATA%\Code\logs\<YYYYMMDDTHHMMSS>\window<n>\exthost\GitHub.copilot-chat\ | Per-workspace chats including agent-mode tool calls and terminal commands; editing sessions show what it changed. | JSON (newer builds JSONL). Map <hash> to a project with the workspace.json next to it; renaming or moving a project orphans the old folder, which stays on disk. Log folder names are editor launch times (local time). |
| Cursor | %APPDATA%\Cursor\User\globalStorage\state.vscdb - table cursorDiskKV: composerData:<id> (session), bubbleId:<id>:<msg> (messages, tool calls, diffs)%APPDATA%\Cursor\User\workspaceStorage\<hash>\state.vscdb - per-workspace chat list (ItemTable)%USERPROFILE%\.cursor\mcp.json - and <repo>/.cursor/ rules and project MCP%APPDATA%\Cursor\logs\ | Agent conversations, the terminal commands it ran, the diffs it applied. | SQLite with JSON values. Composer IDs are UUIDs, not time-ordered - sort on the timestamps inside the JSON. The schema changes between versions, so list keys first (SELECT key FROM cursorDiskKV). The DB can be gigabytes. |
| Windsurf (Cascade) | %USERPROFILE%\.codeium\windsurf\ - mcp_config.json, memories/, Cascade conversation data%APPDATA%\Windsurf\User\%APPDATA%\Windsurf\logs\ | Configured MCP servers, auto-generated memories about the user's projects, Cascade activity logs. | Conversation files are a proprietary binary format (reported as encoded protobuf) - expect to work from logs, memories and file timestamps. Verify against the build. |
| Cline / Roo Code (VS Code extensions) | %APPDATA%\Code\User\globalStorage\saoudrizwan.claude-dev\tasks\<task-id>\ - Cline%APPDATA%\Code\User\globalStorage\rooveterinaryinc.roo-cline\tasks\<task-id>\ - Roo; same extensions also appear under the Cursor / Windsurf profiles | api_conversation_history.json = exactly what went to and from the model; ui_messages.json = what the user saw, incl. command approvals; task_metadata.json = files touched. | Task IDs are millisecond epochs - the folder name is the task start time. checkpoints/ is a shadow git repo of the workspace. |
| Continue | %USERPROFILE%\.continue\sessions\ - chat sessions (JSON)%USERPROFILE%\.continue\config.yaml%USERPROFILE%\.continue\dev_data\ | Chat sessions and (if on) local usage data. | Config shows which model endpoints, including self-hosted ones, it talked to. |
| Desktop assistants and local models | |||
| Claude Desktop | %APPDATA%\Claude\logs\ - logs%APPDATA%\Claude\claude_desktop_config.json - MCP servers | main.log, mcp.log (MCP connections), mcp-server-<name>.log (each server's stderr); the config lists every local MCP server's command line and env vars. | Chat content is server side, not on disk. The MCP config often holds API keys in plaintext env vars - treat them as exposed if the host is compromised. |
| ChatGPT desktop | %LOCALAPPDATA%\Packages\OpenAI.ChatGPT-Desktop_*\ (Store app - verify on the build) | Cached conversations and app state. | Content of record is server side (Enterprise Compliance API). |
| Ollama (local models) | %LOCALAPPDATA%\Ollama\server*.log - server log%USERPROFILE%\.ollama\history - ollama run prompts%USERPROFILE%\.ollama\models\ | API requests with client IP, model pulls (where models came from), interactive prompts. | Listens on 11434; if bound to 0.0.0.0, the server log shows who else on the network used it. |
| LM Studio | %USERPROFILE%\.lmstudio\conversations\ - saved chats (JSON)%USERPROFILE%\.lmstudio\ - models, local server settings | Saved chats; local API server configuration. | Like Ollama, an exposed local server is an abuse and data-exfil surface. |
CopilotInteraction audit records, prompt copies via eDiscovery), and S3 prompt logs for Amazon Q / Kiro. Ask for these in the first data request.What to look for in the transcripts:
- Commands it ran - timeline every shell tool call next to EDR process events.
- Secrets it read -
.env, cloud credential files, SSH keys, kube configs, browser profiles. What was read is in the transcript, so the transcript is now a copy of the secret. - Where data went - web fetches,
curl,git pushto unfamiliar remotes, new MCP servers pointing at external URLs. - Safety switches off -
--dangerously-skip-permissions,--yolo,--trust-all-tools, broad allow rules, auto-approve. Check the settings file's mtime against the incident window. - Instructions the user didn't write - prompt injection arrives in tool output (a web page, issue, README or MCP response). Read the tool results just before an odd action.
- Persistence - a planted
CLAUDE.md,AGENTS.md,GEMINI.md,.cursor/rules, hook or MCP server re-runs the attacker's instructions every session. - Agents driven by malware - the August 2025 Nx "s1ngularity" npm compromise ran
claude,geminiandqwith permission checks off to hunt for secrets. The parent is a package script, not a terminal, and there's no matching user prompt. Record agent and extension versions too: a poisoned Amazon Q VS Code release (1.84.0, July 2025) shipped a wipe prompt.
Inventory (then copy; take SQLite files with their -wal / -shm files, or close the app first - recent rows often live only in the WAL. Check that your KAPE / Velociraptor / UAC targets actually cover these folders):
$targets = '.claude','.claude.json','.codex','.gemini','.copilot','.continue','.cursor','.codeium','.kiro','.aws\amazonq','.ollama','.lmstudio',
'AppData\Roaming\Claude','AppData\Roaming\Code\User','AppData\Roaming\Code\logs','AppData\Roaming\Cursor\User','AppData\Roaming\Cursor\logs',
'AppData\Roaming\Windsurf\User','AppData\Roaming\Windsurf\logs','AppData\Local\Ollama','AppData\Local\Temp\qlog'
Get-ChildItem C:\Users -Directory -Force | ForEach-Object {
foreach ($t in $targets) {
$p = Join-Path $_.FullName $t
if (Test-Path -LiteralPath $p) { Get-Item -LiteralPath $p -Force | Select-Object FullName, LastWriteTimeUtc }
}
}
Get-ChildItem C:\ -Recurse -Force -Filter .aider.chat.history.md -ErrorAction SilentlyContinue | Select-Object FullName, LastWriteTimeUtc
YYYYMMDDTHHMMSS. SQLite stores (Cursor, state.vscdb) keep epoch-millisecond values inside JSON blobs - convert them and don't trust row order. Paths in this category change often: verify against the tool version on the host.Registry hives & where they live
Registry timestamps 101: every key has a LastWrite timestamp (FILETIME UTC, 100-ns precision) = last time that key or any of its values was modified. Values do not have their own timestamps - if you need per-value time, you must infer from LastWrite or from an external log. Hive files on disk have their own $SI MACB (first create, last reg write back to disk). Transaction logs (.LOG1, .LOG2) hold recent changes not yet written into the hive - always parse the main hive with its logs or you'll miss recent writes.
| Hive | Path | What it holds |
|---|---|---|
| SYSTEM | C:\ | Services and drivers, ShimCache (AppCompatCache), BAM/DAM, MountedDevices, USB history (Enum\USBSTOR, Enum\USB), time zone, computer name, network interfaces, the boot key that protects SAM and SECURITY |
| SOFTWARE | C:\ | Installed programs (Uninstall), HKLM Run keys, Winlogon, NetworkList (networks and SSIDs joined), Windows version and install date, SRUM pending data |
| SAM | C:\ | Local accounts and groups, NT hashes (encrypted with the SYSTEM boot key), per-user last logon, password-set and failed-logon times |
| SECURITY | C:\ | LSA secrets (Policy\Secrets: service account passwords, machine account key), cached domain logons as NL$1…NL$10 values under SECURITY\Cache (decryptable with the SYSTEM boot key) |
| DEFAULT | C:\ | HKU\.DEFAULT - settings used before anyone logs on and by the LocalSystem account (HKU\S-1-5-18) |
| NTUSER.DAT | C:\Service accounts: C:\, …\ | Per-user HKCU: Run keys, UserAssist, RecentDocs, RunMRU, TypedPaths, Explorer MRUs, TypedURLs |
| UsrClass.dat | C:\ | Per-user HKCU\Software\Classes: ShellBags (Win7+), MUICache, per-user COM registrations and file associations |
| Amcache.hve | C:\ | Program / file inventory written by the compatibility appraiser (see execution evidence). A separate hive, not mounted under HKLM, easy to forget to collect |
Collect the logs with the hive: every hive has .LOG1 / .LOG2 transaction logs beside it (e.g. SYSTEM.LOG1) holding recent writes not yet flushed - parse hive + logs together. C:\Windows\System32\config\RegBack\ held automatic backups until Windows 10 1803; since then the files are still there but 0 KB unless EnablePeriodicBackup=1 was set under HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Configuration Manager. Older copies of hives may also sit in Volume Shadow Copies.
windows.registry.hivelist/userassist/printkey, MemProcFS virtual registry.Event logs - high-value IDs
EVTX timestamps: every record has System/TimeCreated/@SystemTime in ISO-8601 UTC (100-ns FILETIME precision in the binary, serialized as ISO text in XML). The EventRecordID is a monotonically-increasing serial you should pair with the timestamp when sequencing events across a cleared/rotated log. Logs rotate when the file hits the max size set in the channel properties, default 20 MB for Security on Win10+. Record log size + oldest timestamp per channel at collection; a 20 MB Security log with only 2 days of events on a busy DC may mean the real evidence has already rolled.
| ID | Channel | Meaning / use |
|---|---|---|
| 4624 | Security | Successful logon - LogonType is everything (see below) |
| 4625 | Security | Failed logon - spray/brute |
| 4648 | Security | Logon w/ explicit creds (RunAs, net use /u:) - lateral movement bellwether |
| 4672 | Security | Special privs assigned (admin logon) |
| 4688 | Security | Process creation (enable cmdline logging policy) |
| 4697 | Security | Service installed (Sysinternals PsExec = PSEXESVC; Impacket uses random names). Needs Audit Security System Extension - 7045 in System is always written |
| 4698 | Security | Scheduled task created |
| 4720/4722/4724/4728/4732/4738/4756 | Security | User created / enabled / pw reset / group adds / changes |
| 4769 | Security (DC) | TGS requested - RC4 (etype 0x17) + unusual service = Kerberoasting |
| 4662 | Security (DC) | Directory object access. DCSync = replication rights used by a non-DC account: {1131f6ad-9c07-11d1-f79f-00c04fc2dcd2} (DS-Replication-Get-Changes-All) or {89e95b76-444d-4c62-991a-0facbeda640c} (Get-Changes-In-Filtered-Set). {1131f6aa-…} (Get-Changes) alone is routine. Needs Directory Service Access auditing plus a SACL on the domain object. |
| 5140 / 5145 | Security | Network share accessed / detailed file-share access (which file, which share) - lateral SMB. Needs File Share auditing; 5145 is very noisy |
| 7045 | System | Service installed - classic PsExec/Impacket random name |
| 1102 / 104 | Security / System | Security log cleared (1102) / any other log cleared (104) - hallmark of cover-up |
| 4103 / 4104 | PowerShell/Operational | Module logging / Script block logging |
| 400 / 403 / 600 / 800 | Windows PowerShell | Engine start/stop, provider, pipeline |
| 1116 / 1117 | Defender | Malware detected / action taken |
| Sysmon 1-29 | Microsoft-Windows-Sysmon/Operational | Process, network, file, registry, WMI and DNS activity - see Sysmon event IDs below |
wevtutil, Get-WinEvent, Event Log Explorer.Logon types (4624)
| Type | Meaning |
|---|---|
| 2 | Interactive (console/keyboard) |
| 3 | Network (SMB, IIS, etc.) |
| 4 | Batch (scheduled task) |
| 5 | Service |
| 7 | Unlock |
| 8 | NetworkCleartext (basic auth, IIS) |
| 9 | NewCredentials (runas /netonly) - worth checking; also produced by legitimate admin tools |
| 10 | RemoteInteractive (RDP / Terminal) |
| 11 | CachedInteractive (cached domain creds) |
Sysmon event IDs
Sysmon (Sysinternals System Monitor) is a driver and service that writes detailed process, file, registry, network and WMI activity to Microsoft-Windows-Sysmon/Operational (%SystemRoot%\System32\winevt\Logs\Microsoft-Windows-Sysmon%4Operational.evtx). It only exists if the client installed it, and it only logs what its XML config includes - with no config it records little beyond process creation, so check the config (sysmon -c) before you read an absence as "didn't happen". Every event carries UtcTime (when the activity happened, UTC) as well as the record's TimeCreated.
| ID | Event | What it records | Investigative use |
|---|---|---|---|
| 1 | Process creation | Image, full command line, parent image and command line, user, integrity level, hashes, ProcessGuid / ParentProcessGuid | The backbone of a host timeline - process trees, LOLBin use, encoded PowerShell. Join other events on ProcessGuid (unique per process, unlike PIDs, which are reused). |
| 2 | File creation time changed | A process changed a file's creation time - old and new value | Direct timestomping evidence, with the process that did it. Expect noise from installers and sync clients. |
| 3 | Network connection | TCP/UDP connections with process, user, source and destination IP / port, hostname if resolved | C2 and lateral movement from a specific process; off or heavily filtered in most configs because of volume. |
| 4 | Sysmon service state changed | Sysmon started or stopped | Gaps in coverage; a stop during the incident window can mean tampering. |
| 5 | Process terminated | Process end time and ProcessGuid | Gives process lifetime when paired with EID 1 - short-lived tools stand out. |
| 6 | Driver loaded | Driver path, hashes, signature and signature status | Vulnerable or malicious drivers (BYOVD used to kill EDR); unsigned or unusual signers. |
| 7 | Image loaded | DLLs loaded by a process, with hashes and signature | DLL sideloading and unusual modules in trusted processes; very noisy, usually filtered to specific images. |
| 8 | CreateRemoteThread | A process created a thread in another process | Classic code-injection indicator; legitimate sources (e.g. some AV, debuggers) need baselining. |
| 9 | RawAccessRead | A process read a volume directly (\\.\ device access) | Raw reads used to copy locked files such as NTDS.dit, hives or $MFT - legitimate backup and forensic tools do it too. |
| 10 | Process access | A process opened another process, with the GrantedAccess mask and call trace | Credential-dumping detection when the target is lsass.exe; check the source image and access mask against your baseline. |
| 11 | File created | File created or overwritten, with creating process | Dropped payloads, staging archives, files written to Startup or temp folders. |
| 12 | Registry object created / deleted | Registry key or value created or deleted | Persistence keys being added and cleanup afterwards. |
| 13 | Registry value set | Registry value written, with the data (truncated if long) | Run keys, service ImagePath, IFEO and other autostart changes with the writing process. |
| 14 | Registry key / value renamed | Key or value renamed | Rename-based evasion of registry monitoring. |
| 15 | FileCreateStreamHash | Alternate data stream created, with hash (and content for small streams) | Zone.Identifier (Mark of the Web) on downloads - records the URL - and payloads hidden in streams. |
| 16 | Sysmon config changed | Sysmon configuration state changed | Someone changed what Sysmon logs - check who, and compare old and new config. |
| 17 | Pipe created | Named pipe created, with process | Pipe names used by remote-execution and C2 frameworks; baseline normal pipes first. |
| 18 | Pipe connected | Client connected to a named pipe | Pairs with 17 to show which process talked over the pipe. |
| 19 | WMI event filter | WMI __EventFilter registered | WMI event-subscription persistence (with 20 and 21). |
| 20 | WMI event consumer | WMI consumer registered, with its command or script | Shows what the WMI subscription runs. |
| 21 | WMI consumer-to-filter binding | Filter bound to consumer | Completes the subscription - all three together mean active WMI persistence. |
| 22 | DNS query | DNS query and results, with the querying process (Win8.1+) | Which process resolved a C2 or exfil domain - fills the gap when you only have network DNS logs. |
| 23 | File delete (archived) | File deleted, with a copy saved to the archive folder | Recovers deleted payloads and scripts; needs ArchiveDirectory in the config and disk space. |
| 24 | Clipboard change | Clipboard contents changed (text archived) | Rarely enabled; captures sensitive data, so use deliberately. |
| 25 | Process tampering | Process image changed in memory (hollowing, herpaderping) | In-memory image replacement in a running process. |
| 26 | File delete detected | File deleted - logged only, no archive copy | Cleanup and anti-forensics timeline without the storage cost of 23. |
| 27 | File block executable | Sysmon blocked creation of an executable (config-driven) | Shows an attempt to drop an executable in a protected location. |
| 28 | File block shredding | Sysmon blocked a file-shredding attempt | Anti-forensics attempt (e.g. secure-delete tools). |
| 29 | File executable detected | An executable file was created (logged, not blocked) | New executables written to disk, with hashes - good for spotting dropped tools. |
| 255 | Error | Sysmon internal error | Coverage gaps - check before concluding something didn't happen. |
sysmon-config (baseline) and Olaf Hartong sysmon-modular (per-technique modules). Sysmon for Linux uses the same IDs for the events it supports and logs to syslog / the journal.Persistence - the long tail
Windows exposes dozens of "ASEPs" (Autostart Extensibility Points): mechanisms Windows checks at boot, logon, or specific process start that an attacker can hijack to re-run their code. The sophistication curve runs from Run keys (any user, any malware, instantly spotted) up through signed-code validation hijacks (nation-state tier, hard to see without integrity scanning). Your job is to enumerate every ASEP, then compare each entry's binary and signer against what should be there. Sysinternals Autoruns and PersistenceSniper (Federico Lagrasta) are the standard sweeps; KAPE's RegistryHives + ASEP module + SharpUp cover the collected-image case; on live hosts at scale, Velociraptor's Windows.Sys.StartupItems and Windows.Persistence.PermanentWMIEvents do the same sweep.
Classic autoruns (loud, easy, still common)
- Run / RunOnce keys - The simplest autostart. Any value under
HKLM\Software\Microsoft\Windows\CurrentVersion\Run(system-wide, requires admin) orHKCU\…\Run(per-user, no privs needed) is launched by Explorer at logon; the data is the command line.RunOnceis deleted after it executes once;RunOnceExprocesses its sections in order, synchronously. Also check the WOW6432Node variants on 64-bit systems. Trivial for commodity malware to drop - infostealers (Lumma, RedLine) love it. Hunt: every unknown binary; verify Authenticode signatures. - Startup folder - Any
.lnk,.exe, or script dropped in%AppData%\Microsoft\Windows\Start Menu\Programs\Startup(per-user) or%ProgramData%\…\Startup(all-users) is executed at user logon. Often overlooked because it's "not registry." Parse LNK targets with LECmd. - Scheduled tasks - The Task Scheduler stores each task as an XML in
C:\Windows\System32\Tasks\(mirror in registry underSoftware\Microsoft\Windows NT\CurrentVersion\Schedule\TaskCache\Tree). Tasks can trigger on boot, logon, idle, event ID, or interval, and run as SYSTEM / NETWORK SERVICE / any user whose creds the task was created with. Attackers like them for durability (survive reboot) and masquerade (name a taskMicrosoftEdgeUpdate). Watch for tasks with no author, hidden flag, UNC-path actions, or BASE64-encoded PowerShell. Audit via schtasks /query /fo list /v, Get-ScheduledTask, EID 4698 (created), 4702 (updated), 106/140 inMicrosoft-Windows-TaskScheduler/Operational. - Services - Each entry under
HKLM\SYSTEM\CurrentControlSet\Services\<svc>hasImagePath(exe),Start(0=boot driver, 1=system, 2=auto, 3=manual, 4=disabled),Type(kernel vs user), and optionallyServiceDll(for shared-process svchost services, underParameters). Attackers either install a new service (classic Impacket/PsExec: random 4-letter service name, random 8-char binary,Type=0x10,Start=3) or hijack an existing one (overwriteServiceDllof an unused service). Event IDs 7045 (install) and 4697 are your tells. Compare live service list to a known-good baseline.
Logon / UI-shell hijacks (DLL-level, higher stealth)
- Winlogon Shell / Userinit -
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shelldefaults toexplorer.exe; appending another binary (comma-separated) runs that too at logon.Userinitdefaults touserinit.exe,and is called by Winlogon before Shell - same pattern.GinaDLLis legacy (pre-Vista) but still occasionally seen on old images. - AppInit DLLs -
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Windows\AppInit_DLLs. Any DLL listed here is loaded into every process that linksuser32.dll(nearly all GUI processes). Must be enabled viaLoadAppInit_DLLs=1and (post-Vista) signed. Ignored entirely when Secure Boot is on (Windows 8+), so it matters mainly on legacy-BIOS or Secure-Boot-off hosts. - AppCertDLLs -
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\AppCertDlls. Loaded into any process that callsCreateProcess*. Broader reach than AppInit; even sneakier because it's not in the usual "Autoruns" chase list. - LSA Security / Notification Packages / Authentication Packages - Values under
HKLM\SYSTEM\CurrentControlSet\Control\Lsa(Security Packages,Notification Packages,Authentication Packages). The LSA loads these DLLs in the LSASS process at boot. Perfect for credential harvesting (classic Mimikatz "SSP" trick registeringmimilib.dll). An unknown SSP is a very high-signal detection. - Netsh helper DLLs - Under
HKLM\SOFTWARE\Microsoft\Netsh. Loaded whenevernetsh.exeruns, which happens frequently (scripts, GPOs, update processes). Attackers register a malicious helper so their code runs whenever a netsh is spawned. - Explorer shell extensions / ShellIconOverlayIdentifiers / context-menu handlers - DLLs registered under
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\ShellIconOverlayIdentifiersand various COM-handler keys get loaded into Explorer at logon. Rare but used by advanced actors to live inside a trusted process.
Image hijacks (make a legitimate binary load your code)
- Image File Execution Options (IFEO) debugger hijack -
HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<target.exe>\Debugger. Set to a path and Windows will launch that instead of the target, passing the target's command line as an argument. Originally a legit debugging feature. Classic abuse: hijacksethc.exe(sticky keys) withcmd.exefor an unauth SYSTEM shell at the lock screen; hijack a scheduled binary your victim's AV calls daily. Also abused viaGlobalFlag=0x200under this key, which turns on SilentProcessExit monitoring (configured under its own key - see below). - SilentProcessExit -
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SilentProcessExit\<target.exe>withReportingMode(0x1 = launchMonitorProcess, 0x2 = write a dump, 0x4 = notify),MonitorProcess=<attacker.exe>andDumpType=2(full memory), plusGlobalFlag=0x200under the target's IFEO key. When the target is terminated, Windows launches the monitor and / or writes the dump (default folder%TEMP%\Silent Process Exit, orLocalDumpFolder) and logs it in the Application log with source Process Exit Monitor. Classic use: set target=lsass.exe, have the monitor collect the dump, SYSTEM-level credential harvesting with no third-party driver. Often seen in post-exploitation playbooks. - COM hijacking - Every COM class is identified by a GUID; the implementing DLL is registered under
HKLM\Software\Classes\CLSID\{GUID}\InprocServer32(system) orHKCU\Software\Classes\CLSID\{GUID}\InprocServer32(per-user, takes precedence over HKLM for that user). Attackers point the per-user key at a malicious DLL; whenever any process in that user's context instantiates that CLSID, the malicious DLL loads.TreatAsadds another indirection - class A is "treated as" class B - letting the attacker intercept otherwise-protected system CLSIDs. Scheduled tasks that invoke COM, Explorer, Office - all are triggers. Use OleViewDotNet, or Autoruns to enumerate. - DLL search-order hijacking / sideloading / phantom DLL - Windows looks for a DLL in a specific search order: the loading .exe's directory first, then System32, PATH, etc. Drop a malicious DLL named like a dependency next to a signed binary (
sideloading), or in a dir earlier in PATH, and the signed binary loads your code. Phantom DLL is when the binary tries to load a DLL that doesn't normally exist on disk - the attacker provides it. Lots of threat actors rely on this (e.g., signed vendor utilities and outdated Microsoft binaries shipped alongside a malicious DLL). Procmon on theCreateFile Not Foundoperations is the classic hunt. - Print Processors / Port Monitors - DLLs registered under
HKLM\SYSTEM\CurrentControlSet\Control\Print\Environments\…\Print Processorsand…\Monitorsare loaded by the Print Spooler (spoolsv.exe, runs as SYSTEM). This is the PrintNightmare lineage (CVE-2021-34527) and seen in the wild with Winnti's PipeMon and Gelsemium (MITRE T1547.012) as stealthy SYSTEM persistence. - AppPaths -
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\<name.exe>maps a short name to a full path;start nameorShellExecute namedereferences through here. Overwrite an entry to redirect a common invocation.
Fileless / event-driven (hardest to see)
- WMI event subscriptions - Three WMI classes form the trigger:
__EventFilter(a WQL query that fires on an event - a timer, a logon, a process start),__EventConsumer(what to do: CommandLineEventConsumer runs a cmd, ActiveScriptEventConsumer runs a script, LogFileEventConsumer writes to a file), and__FilterToConsumerBinding(wires them together). All stored inC:\Windows\System32\wbem\Repository\OBJECTS.DATA. No file inRun, no scheduled task, no service - the trigger lives inside WMI. Classic APT tradecraft (APT29, Turla, FIN7). Hunt viaGet-WmiObject -Namespace root\subscription -Class __EventFilter/__EventConsumer/__FilterToConsumerBinding, EID 5861 (consumer-to-filter binding activity), Autoruns -WMI, WMI-Monitor, PyWMIPersistenceFinder. - Office test persistence -
HKCU\Software\Microsoft\Office test\Special\Perf- any DLL path under this undocumented key is loaded into every Office process (Word, Excel, Outlook) on launch. No prompt, no macro, no document. Pure registry trigger. If a user opens Outlook once a day, the DLL runs once a day. Patched by Microsoft's attention eventually, but still works on many fleets. - SIP / Trust Provider hijacking - Windows validates signed binaries by looking up a "Subject Interface Package" DLL (one per signature format: PE, Cabinet, MSI, Catalog). Entries live under
HKLM\SOFTWARE\Microsoft\Cryptography\OID\EncodingType 0\CryptSIPDllGetSignedDataMsg\<OID>and…\CryptSIPDllVerifyIndirectData\<OID>, plus Trust Provider entries under…\Providers\Trust\FinalPolicy. Replace a SIP DLL with a wrapper that always returns "signature valid" and every signed-code check on the system (incl.sigcheck, Defender signature verifications) lies to you. Full write-up by Matt Graeber ("Subverting Trust in Windows"). Advanced-actor persistence that doubles as defense evasion. - Boot execution (BootExecute) -
HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecuteis a REG_MULTI_SZ of native-mode programs run during boot before Win32 subsystem starts (default:autocheck autochk *). Prepending a malicious native-mode binary gives earliest-possible-execution persistence. Rare but used. - BITS jobs - Background Intelligent Transfer Service (
bitsadmin.exe/Start-BitsTransfer) can schedule a download that executes a command on completion (SetNotifyCmdLine). The job can be set to retry indefinitely across reboots. Jobs live in%ALLUSERSPROFILE%\Microsoft\Network\Downloader\qmgr*.dator (newer)qmgr.db. Hunt:bitsadmin /list /allusers /verbose, BitsParser (FireEye), Microsoft-Windows-Bits-Client/Operational EID 59/60.
User-level / GPO / less-common but seen
- Group Policy preferences (GPP) - SYSVOL-stored XML containing scheduled tasks, startup scripts, services, logon scripts. If an attacker has write access to
\\<dc>\SYSVOL, they can push persistence to every joined machine. Watch for recent writes toPolicies\{GUID}\Machine\Preferences\ScheduledTasks\ScheduledTasks.xmland friends. - Logon / startup scripts -
HKLM\Software\Microsoft\Windows\CurrentVersion\Group Policy\Scripts(and…\Group Policy\State\…\Scripts), local Group Policy script references under%SystemRoot%\System32\GroupPolicy\{User,Machine}\Scripts\{Startup,Logon,Shutdown,Logoff}. - Explorer DelegateExecute / Protocol handlers / URL handlers - Registering a protocol handler (
HKCR\<scheme>\shell\open\command) runs your binary when a user clicks ascheme://…URL. Also hijacked for drive-by execution. - Screensaver -
HKCU\Control Panel\Desktop\SCRNSAVE.EXE- the .scr is just a renamed PE. Idle-time execution. Niche but real. - Accessibility backdoor - Replace
sethc.exe,utilman.exe,osk.exe,magnify.exe,narrator.exeor hijack them with IFEO. All are reachable from the pre-logon screen; abuse yields SYSTEM shell without authentication. Classic physical-access / stolen-image technique.
Windows.Persistence.* artifacts, Falcon RTR runscript + custom PowerShell.Credential theft - artifacts and detection
Modern Windows stores credentials in a half-dozen locations. On a domain controller, the master store is NTDS.dit (an ESE database holding every object in Active Directory, including password hashes). On a workstation, the LSASS process holds whatever creds have been used since boot - NT hashes, Kerberos TGTs, cleartext passwords (for logon types that need them), and DPAPI master keys. On disk you'll find the SAM hive (local account hashes) and the SECURITY hive (LSA secrets, cached domain creds). The attacker's goal is almost always to get hashes they can crack or pass, or TGTs they can replay. Your job as DFIR is to answer "did they get it, and if so, what did they do with it after?"
- LSASS dumping - categorize techniques, not just tools:
comsvcs.dll MiniDumpvia rundll32,procdump -ma,Dumpert(direct syscall), nanodump, Taskmgr right-click, MiniDumpWriteDump through PPL bypass. Detect via Sysmon EID 10 to lsass with suspicious GrantedAccess masks (0x1010,0x1410,0x1438). - LSA Protection (PPL) and Credential Guard raise the bar; bypasses exist for PPL (e.g. vulnerable or signed drivers). Credential Guard moves secrets into the isolated
LsaIso.exeprocess, so LSASS dumps on those hosts won't contain NT hashes or TGTs for protected accounts. - SAM/SECURITY offline - grab via
reg save, VSS, raw NTFS read. - NTDS.dit - DC extraction via
ntdsutil ifm, VSS, DCSync. - DCSync - detect via 4662 w/ replication GUID above; monitor replication rights (
DS-Replication-Get-Changes-All). - Kerberos abuse - Kerberoasting (TGS-REP RC4), AS-REP Roasting (accounts with pre-auth disabled), Golden/Silver tickets, S4U2Self/S4U2Proxy (constrained/RBCD).
- Shadow Credentials - attacker writes
msDS-KeyCredentialLinkto request PKINIT auth. - Zerologon (CVE-2020-1472), PetitPotam, PrinterBug → NTLM relay to AD CS → AD CS abuse (ESC1-ESC16 as catalogued since Certified Pre-Owned).
- DPAPI -
%AppData%\Microsoft\Protect\<SID>\<MasterKey>; chromium cookie DPAPI v10/v20 wrapped w/ app-bound key; infostealer workflow.
Lateral movement - what each pivot leaves behind
Every lateral-movement vector produces traces on both hosts - a client-side artifact on the source (what the attacker used to pivot) and a server-side artifact on the target (the authentication, the service install, the file created). Always correlate both sides: a 4624 Type 3 on the target with no matching outbound logon on the putative source means the real source is somewhere else and you haven't fully scoped yet.
| Vector | Source host artifacts | Target host artifacts |
|---|---|---|
| RDP | mstsc Jump List, bitmap cache %LocalAppData%\Microsoft\Terminal Server Client\Cache, default.rdp, 1024/1102 TerminalServices-RDPClient | 4624 type 10, 4778/4779 session reconnect/disconnect, RemoteConnectionManager EID 1149 |
| SMB (admin$) | - (depends on tool) | 5140/5145, 4624 type 3 |
| PsExec / Impacket psexec | Exec'd psexec.exe (Sysmon 1) | 7045 service install (random name on Impacket), 4697, PSEXESVC in Services, named pipe |
WMI (wmiexec.py) | If the source is Windows: process creation of wmic / PowerShell WMI cmdlets; outbound 135 + dynamic RPC | WMI-Activity/Operational 5857-5861; child processes under WmiPrvSE.exe; 4624 type 3; __EventFilter / __EventConsumer if persistent |
| WinRM | HTTP 5985/5986 outbound | 4624 type 3, wsmprovhost.exe hosting the remote session, Microsoft-Windows-WinRM/Operational, PowerShell 4103/4104 |
| SSH (OpenSSH Server) | ssh.exe process creation; %USERPROFILE%\.ssh\known_hosts | OpenSSH/Operational and OpenSSH/Admin event logs (or %ProgramData%\ssh\logs\sshd.log if SyslogFacility LOCAL0 is set) |
Anti-forensics & cover-up
wevtutil cl,Clear-EventLog→ Security EID 1102 (Security log) / System EID 104 (any other log)- Volume Shadow Copy deletion (
vssadmin delete shadows,wmic shadowcopy delete,vssadmin resize shadowstorage), ransomware signature; detect on the command line (4688 / Sysmon 1). VSS EID 8224 is only the service's idle shutdown, not a deletion indicator - Timestomping (SI vs FN divergence)
cipher /w,fsutil usn deletejournal,sdelete- Clearing MRU keys, UserAssist deletion
- Disabling Defender / tamper protection (Defender Operational EID 5001 real-time protection disabled, 5007 configuration changed)
secretsdump, wmiexec, psexec, GetUserSPNs), Rubeus (ref), mimikatz (ref), pypykatz (defensive LSASS parse), DSInternals for NTDS/SAM offline, BMC-Tools (ANSSI) for RDP bitmap cache, Volatility 3 windows.registry.lsadump / windows.registry.hashdump / windows.registry.cachedump.