Local Privilege escalation concepts — Linux

The Dark Lord
10 min readJan 24, 2023

--

What is a mob to a king? What is a king to a god?

One thing all mortals share, is an incessant desire to go beyond what we are capable of — be it money, status or power. We all want to elevate ourselves, and sometimes will do whatever it takes to see it to fruition. Hackers are no different. Who has ever been happy with an account without sudo, an account bereft of all the riches the SYSTEM account bears?

In this blog, we will look at the adage of privilege escalation in the context of Linux systems.

Overview

Privilege escalation is one of the most lucrative targets to pursue after an account or workstation has been compromised. Privilege escalation can happen at a domain level , like in an AD environment or locally like on a workstation , from a user account to root account. Here we shall go through some of the basic privilege escalation techniques on a Linux machine.

This blog will be mostly going through some of the lowest hanging fruits when it comes to privilege escalation on a Linux system. I will enlist some theoretical concepts, I will if required, cover the application of these concepts in some other blog. I have listed some additional free resources where you can go and practice the application of these concepts. In a later blog, I will use a few of these techniques to escalate privileges on a challenge box.

Concept: File Permission

In Linux, the permissions are assigned on the basis of the ownership of the file or directory. The first 3 bits represent the owner permissions, the next 3 bits represent the group, and the next set of 3 bits represent everyone else . (3 bits are in order of read, write and execute).

-rw-r-x---

There is a 4th special bit, which we call sticky bit. Special permissions make up a fourth access level in addition to user, group, and other. Special permissions allow for additional privileges over the standard permission sets (as the name suggests).

Concept: Process ids

In Linux every process starts 3 ids associated with the user. Real user id (RUID), Effective User Id (EUID) and Set User Id (SUID). Though we can explore the significance of each one of them in detail, for brevity, we would focus on the RUID and EUID here. The real user id of a process is the user id of the user that started the process. If a process started it , then the RUID from that process is inherited.

Most access control decisions in the Linux, is however based on EUID / or effective user id. The effective user id of a process differs from a RUID in certain situations, one of those when running an executable via SUDO . If a user starts a process using a sudo, then that process’s RUID will be the UID of the user, but the EUID will be the UID of root (1000)

Privilege Escalation Techniques

Let’s enumerate over some of the lower hanging fruits. We have got access on a Linux machine, and want to identify weaknesses in different areas which we can exploit to escalate privileges. In some cases, we might need to escalate privileges multiple times to get to the root access.

Sudo -l

Let us analyze what we got in the bag, by analyzing what our current user has access to. This command helps us understand the damage we can do with the current user privileges. This will list the commands which the current user can run as superuser on the machine. The most insecure of these settings is when we get the following output.

ALL (NOPASSWD): ALL

This means our current user can run all commands as root without even supplying the password. Any command which allows us to read or execute files, if allowed to be run as sudo, can be used to escalate/abuse privileges https://gtfobins.github.io/ is a valuable source for finding such executables.

SUID executables

These executables are like our best friends during our privilege escalation exercise. However, like our friends not all of them have the best solutions to our problems. SUID executables allow users to execute them as the user id the executable belongs to. There are certain commands in linux which require root privileges and are SUID to allow normal non-sudo users to execute them as root like ping. This is due to the nature of the privileged calls to the kernel they make, and due to common use, they need to be accessible to all users despite that.

Executing ping as root maybe harmless, but certain systems can have commands which have suid set by mistake for root, and can open up avenues for privilege escalation. If the find for eg has suid set, we can exploit it to read any file in the system or execute any command on it using the “-exec” flag

Enumerating such executables on the system which have suid set can be done by using

find "$DIRECTORY" -perm /u=s,g=s

Any command which allows us to read, write or execute on the filesystem and has the SUID bit set can help us further our initiative.

Env variables and $PATH

  • Improper references to environment variables within SUID executables can lead to privilege escalation, as these are often controlled by the Real User , or the user executing the SUID due to being loaded from the user’s session (.bashrc or .bash_profile) . If the a path to SUID uses the environment variable to execute it like /usr/bin/env {executable} , you can abuse this by overriding the $PATH variable. (For any command that isn't inbuilt to the terminal or that is not defined with a non-relative/absolute path, the OS will start searching in folders defined under PATH in left to right prcedence .) Place an executable called echo in /tmp/ and export PATH=/tmp/:$PATH to make your executable get called instead of the actual executable.
  • This also holds true if the environment variable is being used by an SUID executable within any command as string. We could cause command injection by placing the value of an arbitrary command
  • Look for plausible environment variables in executables by using the strace command.

Cron Jobs

  • Cron jobs run automated jobs in linux and often used to manage upstart of services should they be downed, and run other hourly,daily,monthly or weekly checks. They can often be retired incorrectly by leaving the entry pointing to a no longer functioning executable. They can also be left pointing to a path which can be written by everyone, and should this be on the crontab of a superuser, it can be abused for privilege escalation.
  • One thing to note is that non-root user’s cannot view the user crontab aka use the crontab -u {anyuser} command , or the path at (var/spool/cron/crontabs). However, the systemwide crontabs can be read by any user , which resides at /etc/crontab
  • On examining -> /etc/crontab you should search for directories for root user that you might have write access to, or if any of the executables use the environment variable which you control or relative path which can be tampered with directly or by abusing symbolic links.

LD_PRELOAD

In C, LD_PRELOAD is an environment variable used to change the path to a shared object file. This is used to load any library before any form of shared library is loaded in Linux. There are 2 important things to keep in mind using LD_PRELOAD for privilege escalation.

It does not work with executables in secure execution mode and where RUID and EUID are equal, hence SUID binaries cannot be exploited using this technique. However, an interesting bypass of this is if the SUID binary can be read or copied, we can use it to create a non-suid version of that executable by copying it.

The second requirement is that sudo -l must preserve this command i.e allow us to sudo this command.

env_keep+=LD_PRELOAD

What we are essentially targeting is our ability to substitute a genuine function with our own rogue function and have that execute instead of the original function instead.

Consider an executable which we can run using sudo, which just calls the ping function, which we can also run using sudo.

We will create a rogue C program (fake.c) like follows: (When an object is loaded init function is executed)

void init()
{
unsetenv("LD_PRELOAD");
setgid(0);
setuid(0);
system("/bin/sh")
}

Next we have this compiled as a library, for which we need to pass some flags so that it gets recognized by the calling executable as a library

gcc -fpIC -shared -nostartfiles -o /tmp/preload.so fake.c

Next, we set the LD_PRELOAD path to load the shared libraries for our function/executable of target

sudo LD_PRELOAD=/tmp/preload.so {executable}

Note: In some cases, we can use it to overwrite select functions instead of init(). For eg, if there is a executable which just manually checks for username or id to grant access to a file, we could just overwrite the function checking this , and do the same technique to bypass it.

Shared Libraries and LD_LIBRARY_PATH

Shared libraries are a way for the OS to minimize loading functions multiple times for different processes and optimize memory usage. In windows, we know them by the term DLLs. Like DLL search order, which is often an attack vector for DLL injection, we have in Linux a variable called LD_LIBRARY_PATH.

On running an executable, the paths specified by LD_LIBRARY_PATH are searched in order. Not all of the shared libraries are required for running the executable (similar to DLLs). You can view this by stracing the executable. Other useful commands are ltrace and strings.

The paths where the shared libraries are being searched for will show up on strace with the message ENOENT (No such file or directory) alongside the name of the shared library . Next, we look for paths which we have write access to. Then we need to place a rogue library with the same name as the missing library and place it in the path where we have write access to.

For eg, if the library libso.6.so is being reffered to (and missing) in the strace and looked for in the /var/tmp/ directory, we can escalate privileges by placing one there as usually every user has write access to that directory.

The syntax for doing so is a bit complex, and it would be very verbose to go into detail here. What is important that we override the __libc_start_main function and place arbitrary code there - in this case we place a shell spawn via system call

#include <stdio.h>
#include <sys/syscall.h>
#include <unistd.h>
void __cxa_finalize(void *d) {
return;
}
int __libc_start_main(int (*main) (int, char * *, char * *), int argc, char * * ubp_av, void (*init) (void), void (*fini) (void), void (*rtld_fini) (void), void (* stack_end)) {
system(“/bin/sh”); //s
return 0;
}

We can name the C file anything we want like test.c , but the output object file must match the name of the shared library we are trying to fake. Note that for this exploitation we often need a version file. we just need to find the GLIBC version and put this in that (depending on the version). If you’re unsure, use objdump on the target executable to find out the correct version.

GLIBC_2.0 {};

We then compile our shared library

gcc -o /var/tmp/libc.so.6 -static-libgcc -shared -fPIC -Wl,--version-script=/path/to/versionfile test.c

Capabilities

In linux, capabilities are a way to manage privileges at a more granular level. This used in many systems to selectively grant certain functions or executable access to a group or account.

We can use the getcap tool to list enabled capabilities of an executable. Since an unprivileged user will generate errors, you can direct it to null filestream. Certain executables which have been granted access via the capabilities option may be abused to read or write files and spawn root/privileged shells

NFS Root Squash

In a network file system, privileges work differently. A host linux machine who is serving the mounted filesystem is supposed to configure how remote users of other systems are granted privileges and access on the NFS. An improper configuration can lead to privilege abuse. A

The NFS configuration can be checked by reading the /etc/exports file. One dreadful error in configuring the NFS mounts is to set the "no_root_squash" option. By default a root user is 'squashed' to 'nobody' on an NFS share. However, if this is set on an NFS write share, a remote root (say on an adversary machine) can write to the NFS share as root.

We can create a rogue C program and compile it and set the SUID flag as root on our remote attacker machine after mounting the writeable NFS share. You can check or enumerate shares on the target machine by doing showmount and mount using , well , mount.

Once you have written to the target writeable mounted share from a machine you’re root on, you can execute it from the victim machine as a non-privileged user and since it is a suid binary , you can become root on the victim machine.

Services and Searchsploit

When all else fails, try testing what has been done before. Find vulnerabilities on the services running on the system or even the underlying operating system. netstat can be used to see running services on ports. You may also run nmap service scan on the local system

nmap -sV -Pn localhost will print the services and versions running on the current system. If there is a web service running, a good approach would be to look for command injection or remote file exclusion as the web service could be running as a more privileged user than the one we have access to, or be able to read or write certain files.

Alternatively, you can find vulnerabilities in the underlying kernel or operating system or services if they are running a vulnerable version . Searchsploit is a tool which we can use to search exploit-db via the commandline. Using uname -r command we can see the kernel version. Then we can search for exploits in exploit-db. On older kernels, we can find excellent-good level exploits which can be used to escalate privileges.

A sample searchsploit output shown below, for Kernel 2.6.x brings up the infamous DirtyCOW privilege escalation exploit.

Tools and Resources

Practice Privilege escalation:

Supplement Materials

Linux tools to make your life easier (Source TryHackMe) :

--

--

The Dark Lord

Computer & N/w security enthusiast, cryptography fanatic. Exploiting things in a dimly lit room.