☑ File Capabilities In Linux

11 Mar 2013 at 12:07PM in Software
 |  | 

File capabilities offer a secure alternative to SUID executables, but can be a little confusing at first.

mixing desk

We all know that SUID binaries are bad news from a security perspective. Fortunately, if your application requires some limited privileges then there is a better way known as capabilities.

To save you reading the article above in detail, in essence this allows processes which start as root, and hence have permission to do anything, to retain certain limited abilities chosen from this set when they drop privileges and run as a standard user. This means that if an attacker manages to compromise the process via a buffer overrun or similar exploit, they can’t take advantage of anything except the specific minimal privileges that the process actually needs.

This is great for services, which are typically always run as root, but what about command-line utilities? Fortunately this is catered for as well, provided you have the correct utilities installed. If you’re on Ubuntu you’ll need the libcap2-bin package, for example. You’ll also need to be running a non-archaic kernel (anything since 2.6.24).

These features allow you to associate capabilities with executables, in a similar way to setting the SUID bit but only for the specific capabilities set. The setcap utility is used to add and remove capabilities from a file.

The first step is to select the capabilities you need. For the purposes of this blog, I’ll assume there’s a network diagnostic tool called tracewalk which needs to be able to use raw sockets. This would typically require the application to be run as root, but consulting the list it turns out that only the CAP_NET_RAW capability is required.

Assuming you’re in the directory where the tracewalk binary is located, you can add this capability as follows:

sudo setcap cap_net_raw=eip tracewalk

For now ignore that =eip suffix on the capability, I’ll explain that in a second. Note that the capability name is lowercase. You can now verify that you’ve set the capabilities properly with:

setcap -v cap_new_raw=eip tracewalk

Or you can recover a list of all capabilities set on a given executable:

getcap tracewalk

For reference, you can also remove all capabilities from an executable with:

setcap -r tracewalk

At this point you should be able to run the executable as an unprivileged user and it should have the ability to deal with raw sockets, but none of the other privileges that the root user has.

So, what’s the meaning of the strange =eip suffix? This requires a brief digression into the nature of capabilities. Each process has three sets of capabilities — inheritable, permitted and effective:

  • Effective capabilities are those which define what a process can actually do. For example, it can’t deal with raw sockets unless CAP_NET_RAW is in the effective set.
  • Permitted capabilities are those which a process is allowed to have should it ask for them with the appropriate call. These don’t allow a process to actually do anything unless it’s been specially written to ask for the capability specified. This allows processes to be written to add particularly sensitive capabilities to the effective set only for the duration when they’re actually required.
  • Inheritable capabilities are those which can be inherited into the permitted set of a spawned child process. During a fork() or clone() operation the child process is always given a duplicate of the capabilities of the parent process, since at this point it’s still running the same executable. The inheritable set is used when an exec() (or similar) is called to replace the running executable with another. At this point the permitted set of the process is masked with the inheritable set to obtain the permitted set that will be used for the new process.

So, the setcap utility allows us to add capabilities to these three sets independently for a given executable. Note that the meaning of the groups is interpreted slightly different for file permissions, however:

  • Permitted file capabilities are those which are always available to the executable, even if the parent process which invoked it did not have them. These used to be called “forced” capabilities.
  • Inheritable file capabilities specifies an additional mask which can also be used to remove capabilities from the calling process’s set. It applies in addition to the calling process’s inheritable set, so a capability is only inherited if exists in both sets.
  • Effective file capability is actually just a single bit rather than a set, and if set then it indicates that the entire permitted set is also copied to the effective set of the new process. This can be used to add capabilities to processes which weren’t specifically written to request them. Since it is a single bit, if you set it for any capability then it must be set for all capabilities. You can think of this as the “legacy” bit because it’s used to allow capabilities to be used for applications which don’t support them.

When specifying capabilities via setcap the three letters e, i and p refer to the effective, inhertable and pemitted sets respectively. So the earlier specification:

sudo setcap cap_net_raw=eip tracewalk

… specifies that the CAP_NET_RAW capability should be added to the permitted and inheritable sets and that the effective bit should also be set. This will replace any previously set capabilities on the file. To set multiple capabilities, use a comma-separated list:

sudo setcap cap_net_admin,cap_net_raw=eip tracewalk

The capabilities man page discusses this all in more detail, but hopefully this post has demystified things slightly. The only remaining things to mention are a few caveats and gotchas.

Firstly, file capabilities don’t work with symlinks — you have to apply them to the binary itself (i.e. the target of the symlink).

Secondly, they don’t work with interpreted scripts. For example, if you have a Python script that you’d like to assign a capability to, you have to assign it to the Python interpreter itself. Obviously this is a potential security issue because then all scripts run with that interpreter will have the specified capability, although it’s still significantly better than making it SUID. The most common workaround appears to be to write a separate executable in C or similar which can perform the required operations and invoke that from within the script. This is similar to the approach used by Wireshark which uses the binary /usr/bin/dumpcap to perform privileged operations:

$ getcap /usr/bin/dumpcap 
/usr/bin/dumpcap = cap_net_admin,cap_net_raw+eip

Thirdly, file capabilities are disabled if you use the LD_LIBRARY_PATH environment variable for hopefully obvious security reasons1. The same also applies to LD_PRELOAD as far as I’m aware.

  1. Because an attacker could obviously subvert one of the standard libraries and use LD_LIBRARY_PATH to cause their subverted library to be invoked in preference to the system one, and hence have their own arbitrary code executed with the same privileges as the calling application. 

11 Mar 2013 at 12:07PM in Software
 |  |