Back to all posts

What follows is a fictional case. The host name, the file hashes, and the operator are made up. The shape of the intrusion is not. I have built timelines very much like this one out of Prefetch directories on actual engagements, and the patterns repeat.

The point of the walkthrough is to show how Prefetch reads when you have a real intrusion on your hands and the eight execution timestamps are the most reliable thing you have. Pair each step with the artifacts that corroborate or extend it.

The setup

A small professional-services firm. Sixty workstations and four servers. Friday evening, the file shares on FS01 become inaccessible. A ransom note appears at \\FS01\Shared\!!READ-ME!!.txt. By Monday morning the IT manager has acquired triage data from FS01, the jump host JH01, and three workstations including the patient zero, WS-ACCT-04.

I get the Prefetch directories of all five hosts on Tuesday. The EVTX channels for Security and Sysmon are partial. Some have been cleared, some never had Sysmon running. The Prefetch is mostly intact. This is typical.

What WS-ACCT-04 has to say

Start where the attacker started.

Parsing C:\Windows\Prefetch\ on WS-ACCT-04 with PECmd yields 412 .pf files. Most are noise: Office binaries, browsers, antivirus components, Windows utilities. The interesting ones surface when I filter for executable paths outside \Windows\ and \Program Files\, sort by NTFS Created time descending, and look at the first thirty rows.

The top hits, in order of creation time (rounded for the walkthrough):

CreatedFilenameExec pathRuns
Wed 14:02WINWORD.EXE-00B41B71.pfC:\Program Files\Microsoft Office\Office16\WINWORD.EXE47
Wed 14:03EQNEDT32.EXE-1F2E0AB2.pfC:\Program Files\Common Files\Microsoft Shared\Equation\EQNEDT32.EXE1
Wed 14:03MSHTA.EXE-2A1D9904.pfC:\Windows\System32\mshta.exe1
Wed 14:04POWERSHELL.EXE-3DD11E22.pfC:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe8
Wed 14:05WMIPRVSE.EXE-XXXXXXXX.pfC:\Windows\System32\wbem\WmiPrvSE.exe(many)
Wed 14:06CMD.EXE-XXXXXXXX.pfC:\Windows\System32\cmd.exe(many)
Wed 14:11RUNDLL32.EXE-XXXXXXXX.pfC:\Windows\System32\rundll32.exe(many)
Wed 14:12UPDATE.EXE-77AAFF12.pfC:\Users\Public\update.exe1
Wed 14:14NLTEST.EXE-XXXXXXXX.pfC:\Windows\System32\nltest.exe1
Wed 14:14WHOAMI.EXE-XXXXXXXX.pfC:\Windows\System32\whoami.exe1
Wed 14:15PSEXEC.EXE-A1A1A1A1.pfC:\Users\Public\psexec.exe3
Wed 14:237Z.EXE-B2B2B2B2.pfC:\Users\Public\7z.exe1

That is a full intrusion chain in about twenty minutes, reconstructed from the Prefetch directory of one host.

Reading it in plain language:

  • 14:02: Word opens. (Run 47 of 47 — normal use of Word on this host.)
  • 14:03: EQNEDT32.EXE runs for the first time ever on this host. Equation Editor. A 2017-era exploit vector for malicious DOCX. The .pf for WINWORD.EXE updated at 14:02, just before. Pair with LNK files and email-client artifacts. The user opened a malicious document.
  • 14:03: MSHTA.EXE runs for the first time. The load list (more on this below) shows a script in a temp directory. Equation Editor exploit dropped an HTA payload.
  • 14:04: PowerShell starts. The first of eight executions.
  • 14:05: WmiPrvSE.exe activity. Possibly remote WMI calls; check the load list and corresponding EVTX.
  • 14:11: rundll32.exe runs. Often a DLL execution vector.
  • 14:12: A binary named update.exe runs from C:\Users\Public\. First and only execution. Not a legitimate update.exe; the path is wrong. The hash bytes 77AAFF12 will collide with nothing benign.
  • 14:14: nltest.exe and whoami.exe run, both for the first time. Domain reconnaissance.
  • 14:15: psexec.exe runs from C:\Users\Public\. Three executions, suggesting three lateral-movement targets.
  • 14:23: 7z.exe runs from C:\Users\Public\. Archiving for staging or exfiltration.

That ordering came directly from the Prefetch Created timestamps plus the per-.pf Last run field. It is not a guess.

What the load lists confirm

The Prefetch entries tell me what ran. The load lists tell me what each binary did.

MSHTA.EXE-2A1D9904.pf load list, filtered to non-system paths:

\DEVICE\HARDDISKVOLUME2\USERS\ALICE\APPDATA\LOCAL\TEMP\BACK.HTA
\DEVICE\HARDDISKVOLUME2\USERS\ALICE\APPDATA\LOCAL\TEMP\STG1.PS1

That places the HTA payload (delivered by the Equation Editor exploit) at C:\Users\Alice\AppData\Local\Temp\back.hta, and a follow-on PowerShell script at stg1.ps1 in the same directory.

POWERSHELL.EXE-3DD11E22.pf load list, filtered:

\DEVICE\HARDDISKVOLUME2\USERS\ALICE\APPDATA\LOCAL\TEMP\STG1.PS1
\DEVICE\HARDDISKVOLUME2\USERS\ALICE\APPDATA\LOCAL\TEMP\STG2.DLL
\DEVICE\HARDDISKVOLUME2\USERS\PUBLIC\UPDATE.EXE
\DEVICE\HARDDISKVOLUME2\USERS\PUBLIC\PSEXEC.EXE
\DEVICE\HARDDISKVOLUME2\USERS\PUBLIC\7Z.EXE

PowerShell loaded the stage-1 script, loaded a stage-2 DLL, and then either staged or invoked the lateral-movement tooling under \Users\Public\. The Prefetch does not tell me what PowerShell did with those files (read, executed, copied) but the presence of those paths in the load list says PowerShell touched them.

UPDATE.EXE-77AAFF12.pf load list:

\DEVICE\HARDDISKVOLUME2\USERS\PUBLIC\UPDATE.EXE
\DEVICE\HARDDISKVOLUME2\PROGRAMDATA\STG\KEY.BIN
\DEVICE\HARDDISKVOLUME2\WINDOWS\SYSTEM32\BCRYPT.DLL
\DEVICE\HARDDISKVOLUME2\WINDOWS\SYSTEM32\BCRYPTPRIMITIVES.DLL
\DEVICE\HARDDISKVOLUME2\USERS\ALICE\DOCUMENTS\<many .docx, .xlsx, .pdf paths>

update.exe is the encryptor. It loaded a key file from \ProgramData\stg\key.bin. It loaded the Windows cryptography primitives. And it opened every document in Alice's Documents directory.

The load list cap is around 1000 entries. On a documents-heavy host you will hit that cap. The set of paths you see is a partial enumeration of what was touched.

PSEXEC.EXE-A1A1A1A1.pf load list:

\DEVICE\HARDDISKVOLUME2\USERS\PUBLIC\PSEXEC.EXE
\DEVICE\HARDDISKVOLUME2\USERS\PUBLIC\UPDATE.EXE
\DEVICE\HARDDISKVOLUME2\USERS\PUBLIC\PSEXEC.SYS

PsExec loaded itself, the encryptor (suggesting psexec was used to push update.exe to other hosts), and its own kernel driver. The run counter of three combined with the load list strongly suggests three lateral pushes of the encryptor.

The eight timestamps tell me the loop

For PSEXEC.EXE-A1A1A1A1.pf, the eight execution timestamps array contains three valid times:

14:15:22
14:16:48
14:19:01

That gives me the rhythm of the attack: roughly 90 seconds between lateral-movement pushes. On FS01, the file server, I expect to find a UPDATE.EXE-XXXXXXXX.pf whose first execution time is approximately 14:15:30 plus a couple of seconds, since psexec push to start of encryptor is fast.

On FS01's Prefetch directory, I find UPDATE.EXE-77AAFF12.pf (same hash bytes — same path, C:\Users\Public\update.exe, on FS01 too) with a first execution time of 14:15:34. That confirms the lateral movement.

The same update.exe shows up on two other hosts, with first execution times of 14:16:55 and 14:19:08. Each is within seconds of the corresponding psexec push.

I now have, from Prefetch alone, the full lateral-movement timeline for the encryptor across four hosts.

What Prefetch did not give me

The Prefetch did not give me the command line for any of these executions. To get that I need Sysmon EID 1, which existed on two of the five hosts. From those I recover the arguments to PowerShell, the parent process for update.exe, and the user context.

The Prefetch did not give me the network destination of the C2 channel. The back.hta and stg1.ps1 files probably contain that information, but they are not in the Prefetch — only their paths are. If the files survive on disk, I parse them. If they were cleaned, I look at RAM (if I acquired a memory image), the pagefile, or browser history for traces of the C2 domain.

The Prefetch did not give me the actual encrypted content or the recovery prospects. For that I need the MFT and the USN journal to understand which files were touched, in what order, and whether VSCs survived. The encryptor's load list told me which directories were targeted; the MFT and USN told me what happened to the files in those directories.

Why Prefetch was the spine of this investigation

EVTX was incomplete. The attacker had cleared the Security log on FS01 (the 1102 audit-log-cleared event was present, but the relevant 4688 entries were gone). Sysmon was not deployed everywhere. AmCache had hashes for some of the binaries but not all of them (some were renamed before execution, and AmCache associates by binary metadata in ways that occasionally miss).

Prefetch sat across all of that. It is per-machine, it writes continuously, and it was complete on four of the five hosts (one host had been rebooted enough times that the older .pf files had aged out, but the recent ones were there). The eight-timestamp array on the encryptor's .pf files, cross-referenced across hosts, was the single artifact that let me build the lateral-movement timeline with sub-minute confidence.

For the report I cited the timestamps directly. The defending IR firm we worked with had cited an EVTX-only timeline that missed two of the four affected hosts (the ones where the encryptor ran but the security log had been cleared and Sysmon was not present). Their timeline was incomplete in ways the Prefetch corrected.

The pattern in one paragraph

A real intrusion produces a characteristic Prefetch signature: a cluster of first-time executions of system utilities (Equation Editor, MSHTA, rundll32, nltest, whoami) in a tight time window, followed by first-time executions of attacker binaries from user-writable paths (\Users\Public\, \AppData\Local\Temp\, \ProgramData\), followed by a small number of executions of lateral-movement tools (PsExec, WMIC, occasionally legitimate tools like RDP clients), followed by the encryptor itself. The whole sequence usually fits in twenty to ninety minutes. The Prefetch eight-timestamp array per host pins each step to within seconds.

When you parse the Prefetch directory of a host that has been hit and you do not see that signature, either you are looking at a host that was not actually compromised, or the attacker was sophisticated enough to clean Prefetch, in which case the deletion residue in the MFT and the USN journal is the next place to look.

If you want to try the workflow against your own Prefetch directory in the browser, the parser on this site loads .pf files locally and gives you the sortable table the workflow above depends on.

Further reading

  • Eric Zimmerman, PECmd — bulk-parse a directory to CSV and the workflow above falls out naturally.
  • The DFIR Report, annual case writeups — many include Prefetch evidence presented in something like the form above.
  • Microsoft, Mitigating Ransomware — defender-side context for why this matters.