What Is an Incident Timeline in DFIR?
HuntRule Team · · 10 min read

On this page
MFTECmd writes a column named SI<FN into its $MFT CSV. It holds a boolean. True means the $STANDARD_INFORMATION created time on that file is earlier than the $FILE_NAME created time, which on a file that nobody has tampered with does not happen. One column, one bit, and it is frequently the most useful thing in the whole export.
That column only means something inside a timeline. On its own it is a curiosity. Placed next to a 4624 logon, a 7045 service install and a Prefetch execution, it becomes a claim about who did what and in which order. The timeline is the artifact that turns a directory of evidence into a sequence you can hand to a lawyer, an executive or the next analyst, and it is where most of the analytical work in incident response actually happens.
The sources and their timestamps
Every artifact class stores time differently, and the differences matter more than the count of entries.
NTFS keeps four timestamps in two places. The $STANDARD_INFORMATION attribute (0x10) and the $FILE_NAME attribute (0x30) each carry Modified, Accessed, Metadata-changed and Born times. That is the MACB set. Both are 64-bit FILETIME values in UTC with 100-nanosecond resolution. MFTECmd names the columns after the attributes rather than after the concept:
Created0x10, Created0x30
LastModified0x10, LastModified0x30
LastRecordChange0x10, LastRecordChange0x30
LastAccess0x10, LastAccess0x30
SI<FN, uSecZeros, Copied, SiFlags, NameTypeThe registry gives you far less. A hive stores a LastWrite time per key, not per value. When NTUSER.DAT tells you that a Run key was last written at 14:02:51Z, you know the key changed. You do not know which value changed, and you do not know whether it changed once or forty times. Treat registry last-write as an upper bound on the last modification and nothing more.
Windows event logs record TimeCreated SystemTime in UTC inside the EVTX record. Prefetch files under C:\Windows\Prefetch\ hold up to eight run times on Windows 8 and later, also UTC. Shell and browser history are the awkward ones. Chrome's History SQLite database counts microseconds from 1601-01-01, Firefox's places.sqlite counts microseconds from the Unix epoch, and %APPDATA%\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt carries no timestamps at all. On Linux, ~/.bash_history is the same story unless HISTTIMEFORMAT was set before the shell wrote its history.
Then there is the clock authority problem. NTFS and EVTX times come from the host's own clock. The domain controller's events come from its clock. The firewall, the proxy and the cloud provider each have their own. Kerberos tolerates five minutes of skew by default, which means two hosts in the same domain can be four minutes apart and nothing complains. Normalize everything to UTC before you merge, record the source clock for every entry, and expect to discover a host that was wrong by hours.
FAT and exFAT are the trap here. FAT stores local time, not UTC. exFAT also records local time, but the specification pairs each timestamp with a UtcOffset field, so the offset is recoverable when the writing implementation actually set it. A file copied from a FAT-formatted USB stick carries timestamps that are only meaningful if you know which timezone the writing machine was in, and usually you do not.
When the timestamps disagree

Timestomping (T1070.006) is the deliberate modification of file times to make a dropped binary look like it shipped with the OS. MITRE's own description is precise about the split. $SI is what the user and most file tools see, and it can be set from user mode through documented API calls. $FN is maintained by the kernel and normally only changes on file creation, move or rename, so setting it requires either kernel interaction or forcing a rename.
That asymmetry is what makes the timeline the detector. Set $SI back to 2009 and leave $FN alone, and the disagreement between the two attributes is itself the finding. MFTECmd's SI<FN column is exactly that test, and --at controls whether the 0x30 timestamps appear at all or only when they differ from 0x10:
at When true, include all timestamps from 0x30 attribute vs only when they differ from 0x10. Default is FALSEThe second tell is precision. NTFS stores 100-nanosecond granularity, but the common user-mode approach writes whole seconds, so the sub-second field lands on exactly zero. That is what uSecZeros flags. It is a weak signal on its own. Files restored from archives, files copied from FAT media and plenty of installer output all have zeroed fractions legitimately.
Attackers know about the $SI and $FN comparison. MITRE explicitly documents double timestomping, where both attributes are modified together to defeat exactly this check. On Linux and ESXi the equivalent is touch -a -m -t <timestamp> <file> or touch -r <reference> <file>, which copies another file's times wholesale. When the pair-comparison fails, the timeline is still what catches it, because the forged time now contradicts everything around it. A binary claiming a 2009 birth date that first appears in the USN journal at C:\$Extend\$UsnJrnl:$J last Tuesday is not a 2009 binary.
Super timelines and the volume problem
A super timeline merges every timestamped artifact on an image into one sorted stream. plaso is the reference implementation. log2timeline.py extracts events into a storage file, and psort.py filters, sorts and renders them:
log2timeline.py --storage-file timeline.plaso /evidence/image.E01
psort.py -o l2tcsv -w timeline.csv timeline.plasoNow the honest part. The worked example in plaso's own documentation processes a single image into 499,347 events from 18,675 sources. Real workstation images routinely land in the millions. An unfiltered super timeline is not an investigation, it is a haystack you built yourself, and any workflow that assumes somebody will read it top to bottom is broken.
The counterweight is targeted parsing plus a viewer that can handle the output. MFTECmd for the filesystem artifacts, Timeline Explorer for reading the CSVs with filtering and tagging that Excel cannot manage at that row count:
MFTECmd.exe -f "E:\C\$MFT" --csv "C:\cases\0142" --csvf mft.csv
MFTECmd.exe -f "E:\C\$Extend\$J" -m "E:\C\$MFT" --csv "C:\cases\0142"Pivot outward from a known event
The method that scales is not "read the timeline". It is "start at one entry you trust and expand until the story stops moving".
Pick the anchor from something with a record, not an inference. A 4688 process creation with a command line, a 7045 service install, a 4624 network logon. Then take a slice around it. plaso builds this in:
psort.py -q --slice "2026-03-11T14:02:51" --slice_size 30 timeline.plaso--slice defaults to five minutes either side of the timestamp. --slice_size widens it in minutes. Five minutes around a service install on a normal workstation is a readable number of rows. Read them, find the next anchor, slice again. Add an entry to your working timeline only when it advances the story. An artifact that is merely interesting belongs in your notes, not in the sequence you will defend.
Record confidence on every row, because the rows are not equally strong. Reconstructed for illustration, this is the shape:
2026-03-11 14:02:18Z | high | Security 4624 | logon type 3, svc_backup from 10.4.2.19 | direct record
2026-03-11 14:02:51Z | high | System 7045 | service WinDefendUpd, ImagePath C:\Windows\Temp\wdu.exe | direct record
2026-03-11 14:02:51Z | medium | $MFT 0x30 created | C:\Windows\Temp\wdu.exe, SI<FN true | attribute disagreement
2026-03-11 14:05:12Z | low | Prefetch WDU.EXE-* | last run time from 8-slot array | execution inferredHigh means a log record states the fact. Medium means two artifacts agree. Low means a single artifact that could have another explanation. Prefetch tells you a binary ran, not who ran it or with what arguments. Say so in the row rather than in a caveat nobody reads.
What the rule catches
Sysmon Event ID 2 fires when a process explicitly modifies a file creation time, which is the live-telemetry version of everything above. Sigma's file_change category maps to it.
title: File creation time changed in a user-writable directory
id: 5c3a0f2b-9d41-4f77-9a1e-0b6d5e2c7a83
status: experimental
description: Detects a process explicitly setting the creation time of a file under a directory writable by low-privileged code.
references:
- https://attack.mitre.org/techniques/T1070/006/
author: huntrule.com
date: 2026-07-31
tags:
- attack.defense-evasion
- attack.t1070.006
logsource:
product: windows
category: file_change
detection:
selection:
TargetFilename|contains:
- '\Users\Public\'
- '\AppData\Local\Temp\'
- '\Windows\Temp\'
- '\ProgramData\'
filter_main_installers:
Image|endswith:
- '\msiexec.exe'
- '\TrustedInstaller.exe'
- '\OneDrive.exe'
filter_main_archivers:
Image|endswith:
- '\7zFM.exe'
- '\WinRAR.exe'
condition: selection and not 1 of filter_main_*
falsepositives:
- Archive tools restoring stored creation times on extraction
- Installers, updaters and patch tooling
- File sync agents preserving timestamps across machines
level: lowWhat it misses. Sigma has no operator for comparing two fields against each other, so the interesting test (CreationUtcTime is earlier than PreviousCreationUtcTime) has to live in the SIEM query, not in the rule. Sysmon Event ID 2 is also blind to anything that writes the MFT directly rather than going through the file system API, and it never fires if the file was created on another machine and copied in with its times already forged. The rule is a starting filter, not a verdict, which is why level is low.
The retrospective version runs over MFTECmd's CSV loaded into SQLite:
SELECT ParentPath, FileName, Created0x10, Created0x30, uSecZeros
FROM mft
WHERE InUse = 'True'
AND IsDirectory = 'False'
AND ("SI<FN" = 'True' OR uSecZeros = 'True')
AND ParentPath NOT LIKE '%\Windows\WinSxS%'
ORDER BY Created0x30 DESC;Exclude WinSxS and the servicing stack directories or the result set is mostly Windows Update. We have not measured the false positive rate of this query across a fleet, only on individual images, so treat the exclusion list as a starting point you will have to extend for your own build.
Technique | Name | Where it shows in the timeline |
|---|---|---|
T1070.006 | Indicator Removal: Timestomp | 0x10 and 0x30 disagreement, zeroed sub-second fields, Sysmon EID 2 |
T1070.001 | Indicator Removal: Clear Windows Event Logs | Security 1102, System 104, and a gap where records should be |
T1070.003 | Indicator Removal: Clear Command History | Missing or truncated |
T1543.003 | Create or Modify System Process: Windows Service | System 7045, registry last-write on the service key |
Summary
The timeline is not a byproduct of the investigation, it is the investigation. Its value comes from normalizing many clocks into one ordering and from being explicit about how strongly each entry is supported. Timestamp forgery is the case where the timeline does something no single artifact can do, because a forged time only reveals itself against its neighbours. Build outward from anchors you trust, and keep the thing small enough to read.
The last step is the one that pays for the rest. Take the earliest entry in your finished timeline that you could plausibly have alerted on, and check whether a rule exists for it. That gap between what you can reconstruct afterwards and what you would have caught live is the entire backlog, and it is where the next detection comes from. Start with the Windows filesystem and process-creation coverage in /rules?domain=windows, or search the catalog directly for /rules?q=timestomp.
Collect for timeline work (Windows):
$MFT, $Extend\$UsnJrnl:$J, $LogFile
C:\Windows\Prefetch\*.pf
C:\Windows\System32\config\{SYSTEM,SOFTWARE,SECURITY,SAM}
C:\Users\*\NTUSER.DAT, USRCLASS.DAT
C:\Users\*\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
C:\Windows\System32\winevt\Logs\*.evtx
Event IDs worth anchoring on:
Security 4624, 4625, 4672, 4688, 4697, 1102
System 7045, 104
Sysmon 1 (process create), 2 (file creation time changed), 11 (file create), 13 (registry value set)Related articles

What Is a Lessons Learned Session in IR?
Two lines came out of the same incident. The first: "improve communication with the network team." The second: "on-call had no out-of-hours route to network engineering, so the rota now lists a named…
2025-11-078 min read

What Is Post-Incident Review (PIR)?
NIST SP 800-61 Revision 2 lists nine questions for a lessons learned meeting. The last three are the only ones that point forward, and they are the ones teams skip. Corrective actions, precursors and…
2025-10-2310 min read

What Is Root Cause Analysis in Security Incidents?
At 09:14 a user typed their password into a page that looked like the corporate SSO portal. By 10:47, 4,214 files had been read from \\FS01\Finance. The report that names the phishing email as the…
2025-10-169 min read