Privilege separation/dropping are techniques that can be used to limit the capabilities available to an attacker if they are able to compromise an application. By decomposing an application into functionality blocks, each with a set of well-known privilege requirements, an attacker can be “walled off” into a block with little ability to make further lateral movements (or further application compromise).
For example, consider the common case of an internet-facing web server. Root or administrator privilege may be required for the server to open ports 80 and 443 for accepting incoming connections. If the server retains this privilege for the duration of its run, an attacker who is able to successfully exploit a vulnerability in the server would find themselves in control of an application with essentially unlimited privileges on the machine where the server is running.
If, instead, the application was written using privilege separation and/or privilege dropping, root privileges could be removed after opening the listening socket. This means that if an attacker exploits a vulnerability after connecting to the server, the only privileges they would have available would be the
reduced set of privileges needed by the server for basic request servicing functionality.
Privilege separation and privilege dropping are most easily architected during initial application development; by planning ahead of time, a development team can outline, for each block of functionality, which privileges are the minimum set required for the application to function. Applying privilege separation or privilege dropping to an already existing application may be more challenging as a thorough inspection of the application may be required to both discover the functional blocks as well as the minimal set of privileges required for each block.
Privilege separation is typically accomplished by enforcing a process boundary between functional blocks; in the example above, the web server might fork and then re-exec itself after changing its UID to a lower-privileged user after the initial incoming client request is accepted in the parent process. If any communication is needed between parent (more privileged) and child (less privileged) processes, socket pairs or named pipes can be used.
Privilege dropping can be accomplished in the following ways:
- Using capabilities frameworks like SELinux, AppArmor, or seccomp/seccomp-bpf
- Changing to a lower privileged user (such as the “nobody” user)
- Using chroot or jail-like functionality available to the application