Run Your Applications with Necessary Privileges: Linux Capabilities

adil
5 min readFeb 12, 2021

--

I’ve realized that setuid is still a common approach to allow normal users to run a process with the root privileges. However, setuid comes with a lot of security issues.

There are a couple of different security modules in the Linux Kernel: SELinux, AppArmor, Seccomp, Tomoyo, Smack, Capabilities, etc.

In this post, I’d like to talk about the Capabilities module of the Linux Kernel.

The root user (the effective user ID is zero) has no restrictions in the Linux Kernel. That user can do anything in Linux. However, we may need to allow a normal user to do some things that only the root user can.

You probably know, normal users can not listen to port numbers below 1024. They are privileged ports. You should have root privileges to listen to a port that is below 1024.

Let’s assume you have an application on port 80. You run your application with the root user to allow your application to run on port 80.

If your application is compromised you may face unintended consequences, because your application is running with the root user.

So, we need something that allows us to use some privileges of the root user not all of the privileges. That’s what the Capabilites module does.

Photo by Dimitri Houtteman on Unsplash

The POSIX Capabilities defined in POSIX 1003.1e Draft 17 (October 1997). Although it is withdrawn, Linux implements some parts of POSIX 1003.1e in the Capabilities module.

You can see implemented capabilities here.

The Linux Kernel checks your privilege with if (capable(…))

The Linux Kernel checks the user’s privileges before executing a syscall. The Linux Kernel does something like this:

If the effective user id is zero or the user space has enough privilege to do this.

The Linux Kernel doesn’t check the user’s privileges before executing some syscalls, which are part of vDSO. Everyone can execute gettimeofday :)
However, settimeofday is not a part of vDSO. Therefore, you must have CAP_SYS_TIME privilege.

How to Run Netcat (on the Port 80) and iftop Without Switching to Root User?

My console output is:

root@adil:~# su - example
example@adil:~$ nc -l -p 80
nc: Permission denied
example@adil:~$ iftop
pcap_open_live(ens3): ens3: You don’t have permission to capture on that device (socket: Operation not permitted)

The nc command must have CAP_NET_BIND_SERVICE privilege. The iftop command must have CAP_NET_RAW. The details are here.

Let’s do it:

root@adil:~# setcap cap_net_bind_service+ep /bin/nc.openbsd
root@adil:~# setcap cap_net_raw+ep /usr/sbin/iftop
root@adil:~# su — example
example@adil:~$ nc -l -p 80
example@adil:~$ iftop

The example user can run the nc command on port 80. The example user can also run iftop.

Let’s assume the nc command has a security flaw. An attacker can’t access the privileged parts of the server since the nc command doesn’t run with the root user.

What does +ep stand for?

Things get complicated. Please fasten your seatbelts.

Each thread, process, task (whatever you say) can have a few different capability sets: Permitted (P), Inheritable (I), Effective (E), Bounding, Ambient.

Each file can have a few different capability sets: Permitted (P), Inheritable (I), Effective (E).

Permitted: The capabilities that the task (thread, process) can have.
Inheritable: The capabilities that are going to be preserved in the child processes.
Effective: The capabilities that are checked by the Linux Kernel whether they are ‘effective’ or not.
Bounding: The capabilities that are available to be using.

Let’s make some analogies

Analogy #1

You have dual citizenship: US/UK. You are living in the US now.
So, you are permitted to enter the US and the UK. You are effectively living in the US now.

Analogy #2

You are a citizen of Turkey. Your dad has a dual citizenship: Turkey/Australia. So, you can be a citizen of Australia (inheritable) since your dad is a citizen of Australia. You can effectively live in Australia since your citizenship was inherited from your dad.

Analogy #3

Let’s say there are 3 countries in the world: A, B, and C. Your boundaries are A, B, and C. If another country is discovered, then you can expand your boundary. (bounding). It is like adding a new privilege to the Linux Kernel.

Let’s go back to the beginning

sudo setcap cap_net_raw+ep /usr/sbin/iftop

We set the capability. So, the normal users are permitted to run the iftop command effectively with the cap_net_raw privilege.

Hey, all users can run iftop now. I want to allow only the test1 user run the iftop command.

There is a PAM module, which is called pam_cap. You can set the capabilities per user.

The pam_cap module is loaded by default in the latest Linux distros. You can check if it is loaded: grep pam_cap /etc/pam.d/*

If it is not loaded in your current operating system:

You should install libcap

Then, load the pam_cap module manually. Add auth required pam_cap.so to /etc/pam.d/login

Your pam_cap module is loaded.

Then, you should edit your /etc/security/capability.conf

Add cap_net_raw test1 to /etc/security/capability.conf. That line must be above none *

Then run this command:

sudo setcap cap_net_raw+ie /usr/sbin/iftop

Please notice that we used +ie not +ep.
So, the test1 user will inherit the cap_net_raw privilege to run the iftop command effectively.

Let’s test it:

root@adil:~# grep test1 /etc/security/capability.conf
cap_net_raw test1
root@adil:~# setcap cap_net_raw=+ie /usr/sbin/iftop
test1@adil:~$ iftop # it is working fine
ubuntu@adil:~$ iftop # it is not working
You don’t have permission to capture on that device (socket: Operation not permitted)

Some additional commands that are related to the Capabilities module:

capsh —-print : You can check the current privileges of the user. You should see something like Current: = cap_net_raw+i when you run this command while logged in to the test1 user.

getpcaps $PID : Get the privileges of the process.
E.g.: getpcaps $(pgrep iftop)
getpcaps $$
-> $$ returns the PID of current shell.

pscap: List all processes with their privileges.

grep Cap /proc/PID/status : You can see the process’ capabilities in hexadecimal format.

Sample output:

grep Cap /proc/$(pgrep iftop)/status
CapInh: 0000000000002000
CapPrm: 0000000000002000
CapEff: 0000000000002000
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000

Let’s decode it:

root@adil:~# capsh --decode=0000000000002000
0x0000000000002000=cap_net_raw

getcap -r / 2>/dev/null : You can find the executable files that have special privileges in your operating system. It is good for the security checks.

--

--