Certain programs in Linux environments are required to run with a higher level of privilege than allowed to the normal user. This is supported through the “setuid(0)” function, where the required process would run with root privilege even if it was executed by a normal user. While this enables the basic functions such as those performed by init, passwd scripts, it has been abused in execution of many system daemons due to ease of use. This was a critical concern when it came to system hardening.
POSIX (Portable Operating System Interface for Computing Environments) is a standard in Linux operating systems which allows applications to interface with the operating system. POSIX Capabilities facilitate providing more granular privileges to programs, allowing only the required root privileges to be granted to the specific process. A distinction between capabilities as a security concept and POSIX capabilities would be that capabilities provide a token-based privilege transference between entities while POSIX capabilities cannot be transferred between processes. (They can be inherited, replicated, but not transferred).
Threads can be provided with multiple capabilities sets which affect their their current and future states. Permitted set is a superset for the effective capabilities that a thread can use at a point in its execution. Inheritable set is a subset of the permitted set (unless CAP_SETPCAP capability is in the effective set, which allows passing capabilities to another process. But it is disabled by default, and should be edited in source code to be enabled), and defines what the child processes of the thread can inherit. Effective set is what is currently in effect. Additionally, the Bounding set (cap_bset) can be set beyond which capabilities cannot grow. It was introduced as a system wide property but currently modification through dropping is supported via “prctl()”.
Capabilities lets you mediate the root vs regular user’s all or nothing privilege disparity in an acceptable manner.
It allows several modes of running a binary with minimum privilege while not restricting the functionality of the program. For example, files can be stripped of root ownership limiting their possible abuse in exploits. Processes could be run with uid as non-root and with minimum privileges, or uid as root and with minimum privileges. The latter is achieved through SECURE_NO_SETUID_FIXUP which allows a process to drop root privileges after execution, and prevents the process from regaining them.
The exploitation root privileged daemons is a common step in many exploits. Given that many such daemons are present in a system, the risk of privilege escalation becomes quite high. But granular capability granting to each of these processes forces attackers to compromise more than one privileged process to conduct successful attacks, making their job much harder.
Recall that a process’s capabilities cannot grow beyond the permitted set, or the bounding set if it is configured, allowing a worst-case control.
POSIX Capabilities are made even more useful through integration with File Capabilities. Attackers can only abuse the capabilities present in the effective set of the binary, or capabilities in the permitted set if “cap_set_proc” is called. If attacker tricks the program into executing another file, all capabilities are dropped and file is executed as an unprivileged user.
Arbitrary execution of files is supported with only a specified list of capabilities, suited for limiting abuse of various non-persistent daemons and testing purposes. Another example of use would be when unpatched vulnerabilities exist in setuid programs, they can be executed this way until official patches have been released.
Another advantage of File Capabilities is that they cannot be transferred through a copy operation. Capabilities are associated with inode attributes of files and therefore are dropped upon copying. If a privilege file was copied and edited, it would still not be executable with same set of capabilities. However, this again leaves room for abuse which is discussed later.
Capabilities can be requested to be retained over the next setuid(nonroot) call through prctl, a useful functionality to run with reduced privileges.
As literally anything in security, POSIX capabilities are subjected to abuse, and naive configuration of them could lead to disastrous outcomes.
Given the level of capability provided, it may not be worth it in cases such as CAP_DAC_OVERRIDE, CAP_SYS_MODULE and CAP_SYS_ADMIN. If providing these capabilities, the binary is likely to be just as vulnerable as a setuid(0) script. To add to this, monitoring and forensic systems are yet to support the detection of abuses at file capability level, and therefore the possibility of zero-day attacks and unpatched vulnerabilities would remain unnoticed until exploitation.
In a scenario where a process drops root privileges after execution (SECURE_NO_SETUID_FIXUP), an abuse may be possible if SECURE_NOROOT capability is unset while the execution was in root mode (setuid root or effective uid is root). Executing another program allows the previous process to regain its root capabilities. Furthermore, if CAP_SETUID is set, changing the user back to root will give the process all capabilities.
By setting all of the capabilities to zero in each of the three bitfields (permitted, effective, inherited) and then executing a setuid program that attempts to drop privileges can be exploited. Sendmail and procmail are examples of such a programs. Due to having no privileges, the dropping privileges is unsuccessful. Therefore the process continues executing with superuser privileges.
An example of this would be CVE-2000-0506 vulnerability for Sendmail. In this code example, it runs an externally created .forward file as root leading to adding of a root user with an empty password, leading to complete compromise of the system.
Attackers can gain access to processes running as root even though commands may not be executable due to effective capability set being dropped upon execution. This allows attackers to read or write files as root. Once this is done, certain privileged processes can be tricked into execution of such arbitrarily written exploits. (Eg. cron file can be tricked into executing a script which gives a netcat session on localhost as superuser)
File capabilities can be exploited even without root privileges. File inheritable set is only useful for non-root processes with non-empty capability sets. Capability set in File Permitted Set (fP) is forced in the new process’s permitted set. This can be mediated with setting an appropriate capability bounding set.
System files and filesystem information previously protected by ownership of root may now be open to alteration. (Eg. /etc/mount used in mounting CD ROMS). In such situations, the advantage of old setuid programs hardened over years of exploit attempts are lost through the use of capabilities.
CAP_CHOWN capability allows changing of the ownership. The ownership of daemons can be altered and they could be tricked into executing arbitrary code. The ownership of critical files can be set to non-root and their contents can be read or altered.
Copied binaries does not contain privileges of their original files. While this is good for security, package managers may overwrite a file on an update thus, removing its capabilities and essentially causing the program to abruptly exit.
File capabilities are a relatively new inclusion of the standard, and therefore lacks maturity required to be used as a security feature. Patches applied by different distributions may require more capabilities than can be observed as required through inspection of source code.
As an end-user dependent hardening approach, capabilities can be seen as as too complex for appropriate use. Setting capabilities for all setuid root programs may not be realistic. Assigned capabilities may not be sufficient. There could be an outlier scenario accounted for root execution, but now has insufficient privileges for proper execution.
Just because they can be granted to processes, the capabilities should not be undermined as anything less than root privileges. This may lead to over privileged processes due to careless assignment or unnecessary use by trivial daemons since they are now available to be granted at wish. If you think about the Android application privilege requirements, you know that this is not far fetched.
POSIX Standard is a set of optional features. Different distributions can choose to support a subset of features and advertise that the standard is implemented. Therefore the universal support for POSIX capabilities is not provided.
POSIX capabilities have their particular uses. For example, in allowing an in house script that require a custom set of privileges, capabilities could be invaluable. The bounding set’s worst case scenario privilege restrictions could be used in a system which has open services to the public. But as far as system hardening goes, capabilities might be more trouble than it’s worth.