Linux Privilege Escalation in Four Ways
The “Principle of Least Privilege” means that applications and processes should only be granted the privileges that they require to complete their tasks. It is a best practice that lowers the risk of system compromise during an attack.
For example, when an application requires only read access to a file, it should not be granted any write or execute permissions. Because if an attacker hijacks an application that runs with high privilege, the attacker can gain its permissions.
In reality, many applications and services run using high or even root privileges. This is because some systems lack the granular permissions control needed to apply the principle of least privilege. Sometimes, developers and admins forget to apply the best practice. Still, sometimes, developers and admins take a shortcut to avoid dealing with detailed permission control.
Additionally, some applications that are not meant to be run using high privileges do not implement the appropriate safety precautions against attackers. Overprivileged processes thus create a dangerous security weakness that could compromise the entire system.
Today, let’s look at three things that attackers can do when they encounter an overprivileged process running as root.
Exploiting a classic command injection
Let’s say that a web application suffers from a classic command injection attack.
The application allows users to read a system file by submitting the filename via a GET request parameter.
This is a pretty bad vulnerability already. The application lacks any input validation on the system call and enables attackers to execute all kinds of system commands via command injection.
But what if the web application has root privileges? Then the attacker can do a lot worse because the command injection will also run under root privileges. For example, “/etc/passwd” is only editable by users with root privileges. The attacker can add themselves as a root user by editing the “/etc/passwd” file.
In this command below, “0” is the UID of the root user, so adding a user with the UID of “0” will give that user root privileges. This command will add a root user with the username “vickie” and an empty password.
echo “vickie::0:0:System Administrator:/root/root:/bin/bash” >> /etc/passwd
Exploiting a database injection
Sometimes, attackers can achieve RCE through a database injection. Many applications and services allow attackers to run system commands through an injection.
Let’s look at a PostgreSQL injection for example! If an attacker can gain access to a PostgreSQL database with root privileges, what can they do?
psql --user postgres -h sqlserver
In PostgreSQL 9.3 and above, the “COPY TO/FROM PROGRAM” function allows database superusers to execute code in the context of the database’s operating system user.
First, the attacker needs to create a table to hold the system command’s output.
> CREATE TABLE cmd_exec(cmd_output text);
Then, they can run the system command via the COPY TO/FROM PROGRAM function.
> COPY cmd_exec FROM PROGRAM 'id';
The damage that such an attack can cause if limited if the database runs as a low-privilege user. The attacker might be able to read some files and gain more information about the machine. But if the database is running as root, then things can become much worse.
> COPY cmd_exec FROM PROGRAM 'echo “vickie::0:0:System Administrator:/root/root:/bin/bash” >> /etc/passwd';
Exploiting a path traversal vulnerability
Let’s say that a path traversal attack exists on this endpoint in a web application.
An attacker can read files outside of the current directory by using the sequence “../” in the filename parameter.
The “/etc/shadow” file is a file in Linux systems that contains the hashed passwords of system users. If the web application has the permissions to view the “/etc/shadow” file, an attacker can utilize the path traversal vulnerability to gain access to this file. Then, the attacker can crack the passwords they found in this file to gain access to privileged users’ accounts on the system.
Let’s look at another example. If an attacker gains access to an overprivileged Redis instance, they can utilize Redis to escalate their privileges on the system.
Let’s dive into how the attack works. Attackers can use Redis to write their RSA public key to the “/root/.ssh/authorized_keys” file and gain root access through SSH.
Let’s say that an attacker was able to gain access to an unprotected Redis server.
First, the attacker needs to locally generate an SSH public and private key pair with the ssh-keygen command. Then, they pad the top and bottom of the file with newlines.
$ ssh-keygen -t rsa -b 2048$ (echo -e “\n\n”; cat ~/.ssh/id_rsa.pub; echo -e “\n\n”) > public.txt
The attacker can then connect to the exposed Redis service to write the key file.
$ cat public.txt | redis-cli -h 18.104.22.168 -x set public
Finally, the attacker configures Redis and writes the public key file into the “authorized_keys” file in “/ root/.ssh”.
$ redis-cli -h 22.214.171.124
> config set dir /root/.ssh/
> config set dbfilename "authorized_keys"
This will add the attacker’s public key into “/root/.ssh/authorized_keys”, granting the attacker root access over SSH. The attacker can then log in to the server with their corresponding private key.
$ ssh –i id_rsa firstname.lastname@example.org
Remember, Redis should run as the Redis user and not as the root user.
How to run processes with lower privileges
The three attacks introduced in this post are all introduced by an overprivileged process.
Overprivileged processes can be prevented by carefully configuring permission settings and never using “run as root” as the default solution to permission issues.
First, you can use “systemctl unit files” to run a service as a particular user or group. In Linux, unit files contain configuration directives that will control a service’s behavior. Custom unit files for system-wide services are located in “/etc/systemd/system/”. And unit files of user packages are located in “/lib/systemd/system/”.
A unit file is comprised of three sections, Unit, Service, and Install. You can add a “User” or “Group” directive in the “Service” section of the service’s unit file. Doing so will direct the service to run as the user or group.
You can read more about unit files here.
systemd.unit — Unit configuration A unit file is a plain text ini-style file that encodes information about a service…
In crontabs, you can specify the user that will execute the command after the entry’s time fields.
30 21 * * * USERNAME cd /Users/vickie/scripts/security; ./scan.sh
It’s also a good idea to carry out everyday tasks as a lower-privileged user, rather than constantly be logged in as root. To switch out the current user, you can use the “su” command.
$ su USERNAME
You can also use security controls such as chroot, seccomp, containers, and VMs to limit the capabilities of a process instead. Chroot limits the application to a modified environment that cannot access files and commands outside that environment. Seccomp limits the system calls that can be made by the application. Containers and VMs isolate the processes running inside the environment from the greater outside system, thus limiting the damage that attackers can cause.
Problems caused by lowering privileges
Sometimes execution will break if an application is not run as root. This might be because the application needs access to a file that it does not have permissions to access, or when applications try to bind to port numbers less than 1024. Ports with numbers less than 1024 are privileged ports that only root users are allowed to access.
To resolve these issues that arise when you lower application privileges, you can consider granting the application or user a single Linux capability rather than running the application as root.
Applications and processes should only be granted the privileges that they require to complete their tasks. Carefully configure process permissions, and you can prevent overprivileged processes from putting your system at risk.