Spotting timestomping and anti-forensics in the USN journal

Attackers who edit MFT timestamps can't hide from the change journal. How $STANDARD_INFORMATION vs $FILE_NAME mismatches and unexpected BasicInfoChange records expose anti-forensic activity.

By 7 min read

Operators who care about their tradecraft do not just delete evidence. They camouflage what they leave behind. The most common move is timestomping — rewriting an NTFS file's timestamps so the file looks innocent, or so it ages out of the investigation window. It used to work reliably. With the USN journal turned on, which it is by default on every modern Windows host, it leaves a signature so distinctive that a single filter and a parent-path resolution will surface it.

This post walks through what timestomping does to the disk, how the journal exposes it, and what other anti-forensic moves you can catch with the same artefact for free.

A quick refresher on NTFS timestamps

Every MFT entry carries timestamps in two attributes:

  • $STANDARD_INFORMATION (SI). The timestamps user-space tools and most APIs read and write. dir, Explorer, Get-ChildItem, os.stat, GetFileTime — they all return SI. Microsoft documents the layout in the NTFS file times reference.
  • $FILE_NAME (FN). The timestamps NTFS sets internally for each $FILE_NAME attribute (a file may have multiple names if hard-linked). FN values are much harder to change — they update only on specific operations and the kernel does not expose a clean API to rewrite them.

Timestomping tools target SI by default. The legacy Metasploit timestomp.exe, SetMACE, and dozens of custom implants written by operators with even modest tradecraft all rewrite $STANDARD_INFORMATION via NtSetInformationFile with a FileBasicInformation payload. Modern implants (Cobalt Strike's timestomp command among them) can also rewrite FN by manipulating the MFT directly, which is more involved and produces additional $LogFile evidence.

Either way, the act of writing the MFT entry lights up the journal.

What the journal records for a timestamp edit

When SI timestamps are written, NTFS emits a BasicInfoChange | Close record on the file's FileReferenceNumber. That record is the diagnostic:

  • A legitimate touch produces BasicInfoChange | Close together with a DataExtend or DataOverwrite from the actual write that triggered the timestamp update.
  • A timestomp produces a bare BasicInfoChange | Close with no preceding write on the same FileReferenceNumber.

When you see BasicInfoChange | Close with no DataOverwrite, DataExtend, FileCreate or rename in the same handle session, you are looking at metadata manipulation. The vast majority of those are timestomping; the rest are attribute toggles (+H, +R, compression on/off) and EFS state changes.

The SI vs FN comparison

The most authoritative timestomping detection compares each file's SI timestamps against its FN timestamps. The pattern dates to Mandiant's early Windows forensic research and is covered in Brian Carrier's File System Forensic Analysis. The shapes that should make you stop:

  • SI earlier than FN. A file cannot legitimately have been modified before its name existed. This is the canonical smoking gun.
  • SI in the future of FN by an unrealistic margin. Operator pushing the apparent age forward to look like a system file from a clean install.
  • Both SI and FN look impossibly clean — round seconds, identical M/A/C/B values, all matching a known Windows install date. Worth a closer look even when no individual pair flags.

The journal complements the comparison rather than replacing it. The MFT comparison tells you that something tampered with timestamps. The journal tells you exactly when the SI rewrite happened, which is the timestamp you put in the report. Pair the two and the case writes itself:

  1. MFT comparison flags SI < FN on \Windows\System32\drivers\suspicious.sys.
  2. Journal has a BasicInfoChange | Close on the file's FileReferenceNumber at 2026-04-12 03:08:14Z with no surrounding writes.
  3. That timestamp is when the operator ran their timestomp command. Correlate with Sysmon event 1 and you have the process.

$LogFile and the journal should agree

The journal records writes at the per-file level. $LogFile records the underlying MFT transactions at the metadata-write level. A timestomp produces:

  • One MFT write transaction in $LogFile, modifying the $STANDARD_INFORMATION attribute of the target entry.
  • One BasicInfoChange | Close in the journal.

If $LogFile shows an MFT-update transaction on a file with no matching journal BasicInfoChange | Close, the operator may have deleted the journal record by wiping $UsnJrnl and creating a new one. That manoeuvre is possible (fsutil usn deletejournal /D /N C: followed by fsutil usn createjournal /m ... /a ... C:), but is itself noisy.

Other anti-forensic moves the journal catches

Disabling or wiping the journal

fsutil usn deletejournal /D /N C: removes the journal entirely. After it runs:

  • The journal file is rebuilt empty. Pre-call records are gone forever.
  • The MFT entry of $UsnJrnl is freshly written, which $LogFile records.
  • The Security log records a Sensitive Privilege Use event for SeRestorePrivilege or SeManageVolumePrivilege, if SACLs were configured (which they usually were not).

The journal cannot tell you what it used to contain, but the act of disabling it is itself a strong anti-forensics signal. Microsoft documents the management commands in the fsutil usn reference.

Operators who know the journal exists tend to do one of two things: wipe it on the way out (loud), or selectively avoid leaving evidence in it (harder, requires staying off disk). On a host where the journal is suspiciously empty around a known incident window, treat that as a hypothesis worth testing.

Alternate data streams

ADS hiding is older than I am, and the journal records it cleanly. The StreamChange reason bit fires when an alternate data stream is added, renamed or removed on a file. Filter on StreamChange and every ADS create/modify event on the volume surfaces.

Expected false positives: legitimate Zone.Identifier streams from browser downloads (paired with the host file's FileCreate), Mark-of-the-Web streams from email clients, and SmartScreen state. Anything else producing StreamChange records on signed system binaries, on files in \Windows\System32\, or on user-profile binaries outside of normal browser-download flow deserves a look.

HardLinkChange fires when hard links are added or removed. Operators sometimes use hard links to make a malicious binary accessible via two parents, which can defeat AppLocker rules and path-based EDR controls. Pivot on FileReferenceNumber to see all parents for a given inode.

Reparse-point abuse

ReparsePointChange lights up when a reparse point — junction, symbolic link, mount point — is added, modified or removed. Look for reparse points being created in \$Recycle.Bin\, \Users\Default\, \ProgramData\ or other locations where they would not normally exist. The recycle bin parser is the complementary artefact for the $Recycle.Bin side.

Security and object-ID changes

SecurityChange exposes ACL and owner rewrites. ObjectIdChange exposes the rarer object-ID manipulations. Both are quiet on a healthy host. Bursts of either on user-profile binaries or on system files are worth running down.

A practical scan procedure

The fastest screen for timestomping using a parsed journal:

  1. Filter to records whose reason mask is exactly BasicInfoChange | Close. Most parsers default to "contains the bit"; you want exact match for this scan.
  2. For each candidate, look at the prior five minutes of records on the same FileReferenceNumber. If there is no DataOverwrite, DataExtend, FileCreate or rename, flag the record.
  3. For flagged records, compute the SI-vs-FN delta in the MFT. Sort by absolute delta.

The top of the list is your timestomping. The remaining flagged entries are usually attrib +H or attrib +R invocations, or scripts toggling read-only — context-dependent, rarely interesting to a typical IR investigation, but worth keeping in the ranked output for completeness.

The parser on this page exposes exact-mask filtering. For step 3, you need a parsed $MFT open alongside — most analysts use Eric Zimmerman's MFTECmd for the CSV export and pivot in a spreadsheet.

What you cannot catch this way

A short list of anti-forensic moves that bypass the journal entirely:

  • Booting from external media and rewriting the disk. Never touched the live OS, never wrote to the journal.
  • Userland file rewrites that preserve content size at the same offset with no SI update. Produces DataOverwrite records but loses the original content. The journal sees the write, not what was there before.
  • Disabling the journal before doing anything else. The journal can only record what happened while it was running.

For those, you need $LogFile, Volume Shadow Copies, off-host telemetry, or a RAM dump acquired close enough to the incident to still contain the relevant state.

Further reading

  • Microsoft Learn — File Times and the NtSetInformationFile API reference. The latter is the syscall every timestomping tool eventually calls.
  • Eric Zimmerman's MFTECmd — the canonical offline parser for SI and FN timestamps in a flat CSV.
  • Joachim Metz's libfsntfs documentation — the most thorough open reference on $LogFile internals, which is the second witness when the journal alone is not enough.
  • Brian Carrier, File System Forensic Analysis — the chapter on NTFS metadata attributes is still the canonical reference for how SI and FN behave under different operations.