Skip to content

Triage: spotting suspicious entries in RecentFileCache

A practical guide to the path heuristics, basename oddities, and drive patterns that turn a list of cached paths into investigative leads.

Published on 4 min read

Once you parse a RecentFileCache.bcf you have a list of paths and nothing else. No timestamps, no hashes, no signers. The triage question is which of those paths deserve a second look. This post collects the heuristics that have earned their keep on real cases.

Four high-signal path neighborhoods

Opportunistic and targeted attackers both gravitate to a small set of writable directories. Filter for these first.

Pattern (case-insensitive)Why it matters
\AppData\Local\Temp\Default unzip destination and a favourite drop site for loaders
\Windows\Temp\Writable as SYSTEM; common for post-escalation malware
\Users\Public\World-writable; convenient for staging across accounts
\ProgramData\World-writable subtree some installers (and malware) abuse
\PerfLogs\Empty by default, surprisingly often used by red teams
\$Recycle.Bin\Almost never legitimate to run something from here
\Recovery\, \System Volume Information\Should be system-only; entries here merit a look

In the parser's table view, sort by Drive then Path and the suspicious neighbourhoods cluster visually.

Remember that the recorded path includes the \??\ NT-prefix in the raw bytes. The parser strips it before display. If you are grepping JSON output from another tool, normalize first or your regexes will silently miss.

Basename oddities

The filename, the last segment, carries its own signal:

  • Single-letter or double-letter names. a.exe, b.exe, xx.exe. Almost always malware, build artifacts, or some developer's helloworld.exe. Pivot to the directory.
  • High-entropy strings. j2k9f.exe, cfa31b.exe. Frequently dropper output or unpacked stages.
  • Double extensions. invoice.pdf.exe, image.jpg.scr. Classic phishing payload pattern. Worth investigating on its own.
  • GUID-like names. {f3b3c8d4-...}.exe. Either a legitimate installer's per-session copy or malware mimicking one.
  • Legitimate name in a wrong place. svchost.exe under C:\Users\Public\ is a textbook signed-binary-relocation move. Compare against C:\Windows\System32\.
  • Trailing whitespace or Unicode tricks. notepad .exe, names with the right-to-left override U+202E. The parser preserves these verbatim. A hex view of the path or a length(path) check exposes them.

Drive-letter analysis

The parser surfaces a drive column. Most cached entries are C:. Anything else deserves attention.

  • D:, E:, F: and beyond. USB sticks, mounted shares, mounted ISOs. Common staging vectors. Cross-reference with shellbags and USBSTOR keys in the Registry to identify the device.
  • UNC paths (\\server\share\...). Execution from a network share. Correlate with SMB session logs and account-logon events in EVTX.
  • No drive at all. Paths that start with a single name (no C:, no \\) are unusual for the BCF and usually mean a relative path was recorded. That itself is interesting because the service almost always normalizes to absolute.

Filename to process pivot

A basename in the BCF is not proof it ran, only that AppCompat saw it. Once you have a short list of candidates, pivot.

  1. Prefetch. C:\Windows\Prefetch\<NAME>-<HASH>.pf. If a corresponding .pf exists, you have an execution event with timestamps and a run counter.
  2. Amcache.hve. Same era as the BCF but with hash, publisher, install date.
  3. The file on disk. If the path still resolves, hash it and pivot to VirusTotal or your internal IOC store. If the path no longer resolves, the deletion itself is an event. Check the MFT and USN journal for the record.
  4. Surrounding execution context. LNK files, jump lists, and the recycle bin for the host. Often you find the dropper's own footprints there.

What is not in there

Equally important. Do not expect the BCF to carry any of the following.

  • Scripts. .ps1, .vbs, .js, .bat, .hta. Not tracked by the Application Experience scanner. A clean BCF is not a clean machine.
  • DLLs loaded by signed hosts. The BCF cares about EXE-style images crossing the loader's AppCompat code path. DLLs loaded by rundll32.exe, regsvr32.exe, or mshta.exe do not normally show up.
  • Anything that bypassed the loader. Reflective injection, process hollowing, in-memory PE. Invisible to RecentFileCache by design.

If you suspect any of those, BCF silence is meaningless. Pivot to Sysmon and EVTX, RAM acquisition, and pagefile carving.

A practical filter

A pragmatic first pass, expressed as jq over the parser's JSON output:

jq '.entries[] | select(
  (.path | ascii_downcase | test(
    "\\\\(appdata\\\\local\\\\temp|windows\\\\temp|users\\\\public|programdata|perflogs|\\$recycle\\.bin)\\\\"
  )) or
  ((.filename // "") | test("^[a-z0-9]{1,3}\\.exe$"; "i")) or
  ((.filename // "") | test("\\.(pdf|doc|docx|xls|xlsx|jpg|png)\\.exe$"; "i"))
) | "\(.drive // "?")\t\(.filename // "?")\t\(.path)"' rfc.json

You get a tab-separated triage list you can paste into a notebook or feed into a hash-and-VT loop.

A piece of advice from too many late nights: do not stop at the first match. Walk the entire BCF first, build the candidate list, then start pivoting. The interesting story is often two or three paths together (a dropper that ran an unpacker that ran a stage), and you miss the chain if you tunnel-vision on the first suspicious basename.

Further reading

Related articles

The BCF's lifecycle is one scheduled task. Understand when ProgramDataUpdater runs, when it clears the file, and how to spot a disabled appraiser.
The BCF format is a header, a list of length-prefixed UTF-16LE paths, and that's it. Here is what every byte means and why nothing has changed since 2009.
A SOC-flagged Win7 endpoint, 14 paths in the BCF, three worth a second look. A realistic corroboration chain and how much the BCF actually contributed.