Detecting UAC Bypass (T1548.002): KQL and SPL Queries for Fodhelper, Eventvwr, and Mock Trusted Directory Attacks
Most intrusions on Windows endpoints begin at medium integrity. The user is a local administrator, but User Account Control (UAC) gives the session a filtered token. To dump credentials, turn off security tooling, install services or load drivers, the attacker needs a high-integrity process. Clicking through a consent prompt is noisy and the user might refuse it, so attackers use a UAC bypass instead. MITRE ATT&CK tracks this as T1548.002 – Abuse Elevation Control Mechanism: Bypass User Account Control.
UAC bypasses are worth detecting because commodity loaders, infostealers, RATs and ransomware operators all use them, and the known methods leave a small set of recognisable artifacts. The open-source UACMe project lists dozens of methods, but most of them fall into three groups. This guide gives KQL for Microsoft Sentinel and Defender XDR, and SPL for Splunk with Sysmon, for each group.
How UAC Bypasses Work
Windows marks some signed system binaries as auto-elevate. When an administrator in the default UAC mode runs one, it gets a high-integrity token without showing a prompt. Examples are fodhelper.exe, computerdefaults.exe, eventvwr.exe, sdclt.exe, wsreset.exe and slui.exe. A bypass makes one of these trusted binaries run code the attacker controls. The three common approaches are:
- Registry handler hijack. Many auto-elevating binaries look up a shell handler or file association in
HKCUbeforeHKLM. Any user can write toHKCU, so a value underHKCU\Software\Classes\ms-settings\shell\open\commandmakesfodhelper.exelaunch the attacker's payload at high integrity.eventvwr.exeworks the same way throughmscfile, andwsreset.exethrough its AppX class key. - Environment variable hijack. Some elevated scheduled tasks, such as SilentCleanup, expand
%windir%when they build a path. If the attacker setsHKCU\Environment\windirto a command, that command runs elevated. - Mock trusted directory / DLL hijack. Auto-elevation checks that a binary lives in a trusted location. The attacker creates
C:\Windows \System32\(note the trailing space), copies an auto-elevating binary into it, and plants a malicious DLL next to it. This overlaps with T1574.001 DLL search order hijacking.
Each approach leaves its own telemetry. Build a separate detection for each one.
Detection 1: Registry Handler and Environment Hijacks
This is the highest-value rule. In normal use, almost nothing writes a shell\open\command value for ms-settings or mscfile into the user's hive. That activity also falls under T1112 Modify Registry.
KQL (Microsoft Defender XDR / Sentinel)
let HijackKeys = dynamic([
@"\ms-settings\shell\open\command",
@"\mscfile\shell\open\command",
@"\Folder\shell\open\command",
@"\exefile\shell\open\command",
@"\Launcher.SystemSettings\shell\open\command",
@"\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\open\command",
@"\CurrentVersion\App Paths\control.exe"
]);
DeviceRegistryEvents
| where Timestamp > ago(14d)
| where ActionType in ("RegistryValueSet", "RegistryKeyCreated")
| where RegistryKey has_any (HijackKeys)
or (RegistryKey endswith @"\Environment" and RegistryValueName in~ ("windir", "systemroot"))
| project Timestamp, DeviceName, InitiatingProcessAccountName,
RegistryKey, RegistryValueName, RegistryValueData,
InitiatingProcessFileName, InitiatingProcessCommandLine
| order by Timestamp descThe ms-settings technique also writes an empty DelegateExecute value. If you see RegistryValueName == "DelegateExecute" together with a command value under the same key, treat it as a near-certain bypass. Payloads that point at cmd.exe, powershell.exe, rundll32.exe or a file under %TEMP% or %APPDATA% should go to the top of the queue.
SPL (Splunk + Sysmon)
index=sysmon (EventCode=12 OR EventCode=13 OR EventCode=14)
(TargetObject="*\\ms-settings\\shell\\open\\command*"
OR TargetObject="*\\mscfile\\shell\\open\\command*"
OR TargetObject="*\\Folder\\shell\\open\\command*"
OR TargetObject="*\\exefile\\shell\\open\\command*"
OR TargetObject="*\\Launcher.SystemSettings\\shell\\open\\command*"
OR TargetObject="*\\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\\Shell\\open\\command*"
OR TargetObject="*\\App Paths\\control.exe*"
OR TargetObject="*\\Environment\\windir"
OR TargetObject="*\\Environment\\systemroot")
| stats earliest(_time) as first_seen latest(_time) as last_seen
values(Details) as written_value values(Image) as writer
by host, User, TargetObject
| convert ctime(first_seen) ctime(last_seen)Sysmon logs writes made through HKCU\Software\Classes as HKU\<SID>_Classes\.... For that reason the patterns above match on the handler path and do not include the Software\Classes prefix. Your Sysmon configuration must also log registry events (EventCode 12–14) for these keys. Many community configs only collect them for specific paths, so check yours.
Detection 2: Auto-Elevating Binaries Spawning Unexpected Children
A registry write shows the setup. The process tree shows the bypass actually firing. In a normal session, fodhelper.exe and computerdefaults.exe almost never start a shell or script host as a child process.
KQL (Defender XDR)
let AutoElevate = dynamic(["fodhelper.exe", "computerdefaults.exe", "eventvwr.exe",
"sdclt.exe", "slui.exe", "wsreset.exe", "changepk.exe"]);
// Expected children - tune for your estate
let BenignChildren = dynamic(["mmc.exe", "conhost.exe", "werfault.exe", "control.exe"]);
DeviceProcessEvents
| where Timestamp > ago(14d)
| where InitiatingProcessFileName in~ (AutoElevate)
| where ProcessIntegrityLevel =~ "High"
| where FileName !in~ (BenignChildren)
| project Timestamp, DeviceName, AccountName, InitiatingProcessFileName,
FileName, ProcessCommandLine, ProcessIntegrityLevel,
InitiatingProcessParentFileName
| order by Timestamp descCheck InitiatingProcessParentFileName, which is the process that launched the auto-elevating binary. If fodhelper.exe was started by powershell.exe, cmd.exe, wscript.exe or an unsigned binary in a user-writable folder, you have the whole chain in one row.
KQL (Sentinel with Windows Security Event 4688)
If you collect native process-creation auditing instead of EDR telemetry, use the TokenElevationType field. A value of %%1937 means the new process received a full, elevated token.
let AutoElevate = dynamic(["fodhelper.exe", "computerdefaults.exe", "eventvwr.exe",
"sdclt.exe", "slui.exe", "wsreset.exe", "changepk.exe"]);
SecurityEvent
| where TimeGenerated > ago(14d)
| where EventID == 4688
| extend ParentImage = tolower(extract(@"([^\\]+)$", 1, ParentProcessName))
| where ParentImage in (AutoElevate)
| where TokenElevationType == "%%1937"
| where NewProcessName !endswith @"\mmc.exe" and NewProcessName !endswith @"\conhost.exe"
| project TimeGenerated, Computer, SubjectUserName, ParentProcessName,
NewProcessName, CommandLine, MandatoryLabelSPL (Sysmon EventCode 1)
index=sysmon EventCode=1 IntegrityLevel=High
(ParentImage="*\\fodhelper.exe" OR ParentImage="*\\computerdefaults.exe"
OR ParentImage="*\\eventvwr.exe" OR ParentImage="*\\sdclt.exe"
OR ParentImage="*\\slui.exe" OR ParentImage="*\\wsreset.exe"
OR ParentImage="*\\changepk.exe")
NOT (Image="*\\mmc.exe" OR Image="*\\conhost.exe" OR Image="*\\WerFault.exe" OR Image="*\\control.exe")
| table _time, host, User, ParentImage, ParentCommandLine, Image, CommandLine, IntegrityLevelThis rule needs very little tuning in most environments. eventvwr.exe normally launches mmc.exe, which is why it is excluded. If you see mmc.exe started by eventvwr.exe with a command line other than eventvwr.msc, investigate it.
Detection 3: Mock Trusted Directories
Windows path normalisation drops the trailing space when it compares C:\Windows \System32, so the auto-elevation check treats the fake folder as trusted. Legitimate software does not create a folder with that name. Any activity in it is high fidelity and should be investigated immediately.
KQL
union DeviceProcessEvents, DeviceImageLoadEvents, DeviceFileEvents
| where Timestamp > ago(30d)
| where FolderPath startswith @"C:\Windows \"
| project Timestamp, DeviceName, ActionType, FolderPath, FileName, SHA256,
InitiatingProcessFileName, InitiatingProcessCommandLine,
InitiatingProcessAccountName
| order by Timestamp ascSPL
index=sysmon
((EventCode=1 Image="C:\\Windows \\*")
OR (EventCode=7 ImageLoaded="C:\\Windows \\*")
OR (EventCode=11 TargetFilename="C:\\Windows \\*"))
| eval artifact=coalesce(Image, ImageLoaded, TargetFilename)
| stats count earliest(_time) as first_seen values(artifact) as artifacts by host, EventCode
| convert ctime(first_seen)Sort the results in time order. A typical sequence is a file create (EventCode 11) for a copied binary such as ComputerDefaults.exe, then a DLL drop, then the process start and image load. The DLL hash is your best pivot for searching the rest of the fleet.
Triage Checklist
- Identify the stager. Find out what process wrote the registry value or created the mock folder. Look for PowerShell (T1059.001), an Office child process, or a binary in
%APPDATA%. - Check what ran elevated next. UAC bypass is a means to an end. Over the following minutes, look for LSASS access, Defender exclusions, service creation or shadow copy deletion.
- Check for cleanup. Mature tooling deletes the hijack key straight after use. A
RegistryKeyDeletedevent on the same path within seconds of the write is a strong signal in itself. - Look for related techniques. CMSTP-based elevation (T1218.003) and COM elevation moniker abuse reach the same goal through different telemetry, so make sure your library covers them too.
False Positives and Tuning
Some IT tools, deployment scripts and older software set file associations under HKCU\Software\Classes\exefile or Folder. This is rare for ms-settings and mscfile. Baseline the writing process and command line, and suppress known-good tools by signer and path rather than by file name. For Detection 2, run the query over 30 days in report mode before you enable alerting. Any recurring benign child processes will show up in that window.
Hardening Alongside Detection
Detection works best alongside these controls:
- Set UAC to Always notify. This blocks many auto-elevate bypasses.
- Take local administrator rights away from daily-use accounts. UAC bypass needs an admin token to elevate in the first place.
- Monitor
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Systemfor changes toEnableLUAandConsentPromptBehaviorAdmin. Attackers who already have elevation often weaken UAC there for later use.
UAC bypass is a good candidate for a high-fidelity detection. The number of methods is small, the artifacts are specific, and an alert usually means an attacker is about to act with administrator rights. Deploy the registry and mock-directory rules first, because they need almost no tuning. Add the process-tree rule after a short baseline. For the full technique mapping and more query variants, see the T1548.002 detection page.