Detecting Compiler and REPL Host Abuse (msbuild / dotnet / csi / rcsi / fsi)
Compilers and interactive REPL hosts are, by design, programs that consume source and emit or execute code. Under WDAC that is a problem: if the signed host is allowed to run and the source lives in a data file the policy does not evaluate, the host becomes a general-purpose execution engine. The catalogue is full of these — msbuild.exe, dotnet.exe, csi.exe, rcsi.exe, fsi.exe/fsiAnyCpu.exe, wfc.exe, dnx.exe, dbghost.exe, aspnet_compiler.exe, and Microsoft.Workflow.Compiler.exe.
This post covers how to detect that family being abused. The good news is that, unlike the debugger-as-injector from part 2, the compiler family leaves a loud process-creation and child-process signature. The challenge is the false-positive surface — these tools also run legitimately on every developer and CI host you own.
Table of Contents
- The Mechanism, Briefly
- Why This Family Is Loud
- Detection Signal 1 — Abnormal Parentage and Invocation Path
- Detection Signal 2 — Suspicious Content Argument
- Detection Signal 3 — Child-Process Lineage
- A Sigma Rule for the Family
- Tuning for a Mixed Fleet
- References
1. The Mechanism, Briefly
Each of these binaries accepts attacker-supplied content — a project file, an inline task, a C#/F# script, a workflow XOML — and executes the logic inside it. Classic examples:
msbuild.exe project.xmlwith an inline C#<Task>that runs on build.dotnet.exe/dotnet.exe runagainst a project, ordotnet.exe <assembly.dll>to load and execute.csi.exe script.csx/rcsi.exe script.csxrunning a C# script.fsi.exe script.fsxrunning F#.Microsoft.Workflow.Compiler.exe INPUT.xml OUT.dlldeserialising and executing.
In every case the trusted host does the executing; WDAC sees only the signed image. See the reference catalog for the per-binary citations.
2. Why This Family Is Loud
Unlike a debugger quietly rewriting a thread’s context, a compiler host that executes attacker logic almost always produces observable side effects: it reads a file from a user-writable path, it often spawns a child process, and its command line names the content file. The detection problem is not “is there a signal” — it is “which of these thousands of legitimate msbuild invocations is the malicious one?” That is a filtering and baseline problem, and it is very tractable.
3. Detection Signal 1 — Abnormal Parentage and Invocation Path
Legitimate compiler hosts have a small set of parents and a small set of installation paths. Any deviation is the signal:
- Parentage.
msbuild.exeis normally a child of Visual Studio (devenv.exe),dotnet.exe, MSBuild node hosts (MSBuild.exeitself, for the worker nodes), or a CI agent (msbuild.exeunderpwsh.exe/cmd.exeinside a pipeline workspace). It is not a child ofwinword.exe, a browser, orcmd.exespawned from%TEMP%. - Path. The genuine binaries live under
C:\Program Files\Microsoft Visual Studio\...,C:\Program Files\dotnet\..., or the .NET Framework reference path. Amsbuild.exeinvoked from%USERPROFILE%\Downloads\or%LOCALAPPDATA%is suspicious even if it is the real binary. - Content path. The argument naming the project/script file is the strongest single atom. A project file loaded from a user-writable directory, a UNC path, or an internet-sourced URL is the canonical shape.
4. Detection Signal 2 — Suspicious Content Argument
Per-host fingerprints on the command line:
msbuild.exe: the project argument; inline-task smell is harder to see from the command line alone, so pair with a file-read of the project (Sysmon EID 11 or your EDR’s file events) looking for<Task+using:orCode Language="cs".dotnet.exe:dotnet <path>.dllwhere the DLL is in a user-writable location, ordotnet script/dotnet-script(the scripting tooling).csi.exe/rcsi.exe: any invocation with a.csxargument is suspect on a non-dev host.fsi.exe/fsiAnyCpu.exe: any invocation with a.fsxargument, same caveat.Microsoft.Workflow.Compiler.exe: the two-argumentINPUT.xml OUTshape, rarely seen outside its original purpose.aspnet_compiler.exe: invocation outside a build farm.
The presence of these hosts on a non-developer endpoint is itself the cleanest signal. On a dev endpoint, fall back to path and content provenance.
5. Detection Signal 3 — Child-Process Lineage
When an attacker-controlled inline task or script runs, the host frequently spawns a child — cmd.exe, powershell.exe/pwsh.exe, or an attacker payload — to extend the attack. The lineage to alert on is therefore:
msbuild.exe(ordotnet.exe/csi.exe/fsi.exe) →cmd.exe|powershell.exe|pwsh.exe| an unsigned executable
A trusted compiler host spawning a shell is almost never legitimate. This is the highest-confidence detection in the family and the most resistant to command-line obfuscation, because the child image is what it is regardless of how the parent was invoked.
6. A Sigma Rule for the Family
A rule combining the abnormal path/parent (Signal 1), the suspicious content extension (Signal 2), and the shell-child lineage (Signal 3).
title: WDAC Bypass — Compiler/REPL Host Abuseid: 5b2d7f0a-1c4e-4b9a-8f3d-6e0c1a2b3c4dstatus: experimentaldescription: > Detects trusted developer compilers and REPL hosts (msbuild, dotnet, csi, rcsi, fsi, Microsoft.Workflow.Compiler) invoked from suspicious paths or parents, with script/project arguments from user-writable locations, or spawning shell children — patterns consistent with WDAC/Application Control bypass.references: - https://github.com/bohops/UltimateWDACBypassList - /posts/wdac-bypass-techniques-reference-catalog/author: Anubhav Gaindate: 2026/07/13tags: - attack.defense_evasion - attack.t1127 - attack.t1218 - attack.t1027logsource: product: windows category: process_creationdetection: host_image: Image|endswith: - '\MSBuild.exe' - '\dotnet.exe' - '\csi.exe' - '\rcsi.exe' - '\fsi.exe' - '\fsiAnyCpu.exe' - '\wfc.exe' - '\dnx.exe' - '\Microsoft.Workflow.Compiler.exe' - '\TextTransform.exe' user_writable_arg: CommandLine|contains: - '\Users\Public\' - '\AppData\Local\Temp\' - '\AppData\Roaming\' - '\Downloads\' - '\\\\' # UNC paths script_extension: CommandLine|endswith: - '.csx' - '.fsx' - '.csproj' - '.vbproj' - '.dll' abnormal_parent: ParentImage|endswith: - '\winword.exe' - '\excel.exe' - '\powerpnt.exe' - '\outlook.exe' - '\chrome.exe' - '\msedge.exe' - '\firefox.exe' - '\wscript.exe' - '\cscript.exe' invoked_from_user_writable: Image|contains: - '\Users\Public\' - '\AppData\Local\Temp\' - '\AppData\Roaming\' - '\Downloads\' - '\Windows\Temp\' shell_child: ParentImage|endswith: - '\MSBuild.exe' - '\dotnet.exe' - '\csi.exe' - '\rcsi.exe' - '\fsi.exe' - '\Microsoft.Workflow.Compiler.exe' Image|endswith: - '\cmd.exe' - '\powershell.exe' - '\pwsh.exe' - '\wscript.exe' - '\cscript.exe' condition: >- (host_image and (user_writable_arg or script_extension or abnormal_parent or invoked_from_user_writable)) or shell_childfields: - Image - ParentImage - CommandLine - Userfalsepositives: - Developer workstations and CI agents running legitimate builds (tier separately) - dotnet loading framework DLLs (constrain the .dll extension to user-writable paths)level: mediumNote the level is medium rather than high: this family runs legitimately at scale on dev/CI hosts, so without tiering it will produce volume. The shell_child branch is the sub-condition you can promote to high confidence — a compiler host spawning cmd.exe/powershell.exe is the real deal.
7. Tuning for a Mixed Fleet
- Tier the hosts. Developer and CI hosts route to a lower-severity queue; locked-down workstations and servers use the full-severity rule.
- Anchor legitimate paths. Allow-list
MSBuild.exeanddotnet.exefrom theirProgram Filesinstall roots invoked by the CI service account ordevenv.exe. Everything else stays in scope. - Watch the project files, not just the process. A companion Sysmon rule (FileCreate for
*.csproj/*.vbproj/*.csx/*.fsxunder user-writable paths) gives you lead time before the host executes them. - Promote
shell_child. On any fleet, a compiler host spawning a shell is worth paging on regardless of tier.
8. References
- Bypassing Application Whitelisting using MSBuild.exe — Casey Smith
- DotNet Core: A Vector for AWL Bypass — bohops
- Arbitrary Unsigned Code Execution in Microsoft.Workflow.Compiler.exe — Matt Graeber
- WDAC Bypass Techniques — The Complete Reference Catalog
- MITRE ATT&CK T1127 — Trusted Developer Utilities Proxy Execution
- MITRE ATT&CK T1127.001 — MSBuild