MENASEC

The .NET CLR usage log: a per-tool execution receipt attackers forget about

Every time a managed executable runs, the CLR quietly writes a small log file named after it. It survives deletion, it names the tool, and hardly anyone collects it. Here's what it catches and where it goes blind.

MENASEC Published 12 Jul 2026

Half of the current offensive tradecraft on Windows is written in .NET. Rubeus, the AD collectors, the situational-awareness tools, the certificate-abuse utilities, most of the well-known ones are managed executables. Which means the Common Language Runtime has to load to run them, and the CLR leaves a receipt when it does. It is one of the more useful DFIR artifacts almost nobody forwards, and it is worth a short writeup because the trade-offs are specific.

What the CLR writes, and where

When a managed executable runs, the runtime writes a small usage-log file under the user’s profile, in %LOCALAPPDATA%\Microsoft\CLR_v4.0\UsageLogs\ (and a CLR_v4.0_32 sibling for the 32-bit runtime). The filename is the name of the executable that invoked the runtime. Run Rubeus.exe and you get a Rubeus.exe.log sitting in that directory.

Two properties make this interesting. First, it names the tool, which is a lot more than most execution artifacts hand you directly. Second, it outlives the binary. An operator who drops a .NET tool, runs it, and deletes the executable to clean up has left the usage log behind, because deleting the program does not touch the receipt the runtime wrote in a different directory. On a host where the attacker thought they had cleaned up, this is sometimes the clearest evidence that a specific tool ran.

Turning it into a detection

The forensic version is manual: pull the UsageLogs directory during triage and read the filenames. The live version is a rule. Sysmon Event ID 11, FileCreate, scoped to that UsageLogs path, alerts the moment the runtime writes a new receipt, and the created filename tells you which managed binary ran. It is cheap, because that directory does not see high write volume, and it is specific, because a new entry corresponds to a new managed executable being launched under that profile.

Renaming does not defeat it in the way it defeats an image-name rule, but it does change what you see: the log is named after whatever the executable was called, so a renamed Rubeus.exe masquerading as updater.exe writes an updater.exe.log. You lose the tool identity, but you still learn that a managed binary by an unusual name executed, which on a server that has no business running ad-hoc .NET is itself the finding.

Where it goes blind, because this matters

I would be selling you something if I left out the limitation. The receipt is named for the process image that hosts the runtime, so the technique attackers use specifically to avoid on-disk tooling, loading a .NET assembly reflectively inside an existing process, does not produce a receipt named after the tool. A beacon that runs an assembly in memory generates a usage log named after the beacon’s host process, not the assembly. So this artifact is strong against .NET executables that run as their own process and weak against in-memory assembly loading. It catches the on-disk half of the field, which is still a large half, and it does not catch the fully in-memory half.

The backstop for the in-memory case is not this log at all. It is the runtime loading where it shouldn’t: watch for clr.dll or coreclr.dll appearing as an image load (Sysmon EID 7) inside a process that is not normally managed and has no reason to host the CLR. That is the signal that something turned an unmanaged process into a .NET host, which is exactly what reflective assembly loading does. The two together, the usage-log receipt for on-disk tools and the anomalous CLR image load for in-memory ones, cover most of how .NET tradecraft actually runs.

Because so many of these tools are discovery and credential utilities, a hit here often correlates with the behaviors I described elsewhere, like a SharpHound collection run leaving both a usage-log receipt and the LDAP-and-session pattern at the same time. When two independent artifacts light up for the same host in the same window, you stop debating whether the single one was a false positive.

dfirdetection-engineeringdotnetexecutionthreat-hunting