181 lines
6.3 KiB
Plaintext
181 lines
6.3 KiB
Plaintext
Unchecked return value of a function from the `setuid`-family might cause unexpected behavior and poses a security risk.
|
|
|
|
== Why is this an issue?
|
|
|
|
Functions from the ``++setuid++``-family including ``++setuid++`` and ``++setgid++`` are used to change the identity of the caller process.
|
|
They are used to change privileges for the subsequent actions to be executed.
|
|
If a call to these functions returns an error that is not checked and handled appropriately, the subsequent parts of the program will execute with unexpected privileges.
|
|
This, in turn, leads to unexpected program behavior and poses a _serious_ security risk.
|
|
|
|
|
|
== What is the potential impact?
|
|
|
|
Functions for managing a program's privileges are fairly complex and one needs to take great care to use them correctly.
|
|
|
|
Failing to correctly handle potential errors indicated by those functions' respective return values can lead to unexpected behavior.
|
|
If the program fails to acquire more privileges to execute a privileged operation, for instance, the OS will disallow the operation and the program is likely terminated by the OS.
|
|
However, if the program silently fails to drop its privileges, it will continue to run the subsequent operations with more privileges than expected, which poses a _serious_ security risk.
|
|
|
|
|
|
== Example program that changes between privileged and unprivileged mode
|
|
|
|
A brief example on how ``++setuid++`` can be used and how to correctly check its return value is given in what follows.
|
|
|
|
Consider the following example:
|
|
|
|
Assume that the program shown below has been compiled to its binary named `my-program`.
|
|
One can change its owner to root, for instance.
|
|
|
|
[source,txt]
|
|
----
|
|
$ sudo chown root:root my-program
|
|
----
|
|
|
|
After setting the program's setuid bit using
|
|
|
|
[source,txt]
|
|
----
|
|
$ sudo chmod u+s my-program
|
|
----
|
|
|
|
the program's permissions may look like
|
|
|
|
[source,txt]
|
|
----
|
|
-rwsrwxr-x 1 root root 16416 Aug 21 15:45 my-program
|
|
----
|
|
|
|
The 's' indicates that the executable has the setuid bit set.
|
|
Notice that the program's owner (here root) may be different from its caller.
|
|
Functions from the `setuid`-family can be used to manipulate privileges and acquire (or drop) the program owner's privileges.
|
|
|
|
[source,cpp]
|
|
----
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
static uid_t real_uid;
|
|
static uid_t effective_uid;
|
|
|
|
void do_setuid(void) {
|
|
printf("Try to acquire privileges\n");
|
|
int status;
|
|
status = seteuid(effective_uid);
|
|
if (status < 0) { // Compliant: return code is checked and potential errors are handled.
|
|
fprintf(stderr, "Couldn't set uid.\n");
|
|
exit(status);
|
|
}
|
|
printf("Set uid to %lu\n", (unsigned long int)effective_uid);
|
|
}
|
|
|
|
void undo_setuid(void) {
|
|
printf("Try to drop privileges\n");
|
|
int status;
|
|
status = seteuid(real_uid);
|
|
if (status < 0) { // Compliant: return code is checked and potential errors are handled.
|
|
fprintf(stderr, "Couldn't set uid.\n");
|
|
exit(status);
|
|
}
|
|
printf("Set uid to %lu\n", (unsigned long int)real_uid);
|
|
}
|
|
|
|
int main(void) {
|
|
real_uid = getuid(); // Real ID of the program's caller.
|
|
effective_uid = geteuid(); // Effective ID of the program's owner (here root).
|
|
// Immediately drop privileges and set effective user ID back to real user ID
|
|
// such that operations are performed using the real user ID for determining
|
|
// permissions.
|
|
undo_setuid();
|
|
// Switch back to file user ID (the owner's privileges) only if the effective
|
|
// ID (the privileges) is required to determine permission for privileged
|
|
// operations.
|
|
do_setuid();
|
|
//
|
|
// PERFORM PRIVILEGED OPERATION (using root privileges in this example).
|
|
//
|
|
// Drop privileges as soon as they are no longer needed.
|
|
undo_setuid();
|
|
|
|
// There are a multitude of operations that can be extremely dangerous if
|
|
// executed as root. Assume that this program would not correctly handle
|
|
// potential errors. In that case, the following system call poses a security
|
|
// risk, if dropping privileges fails. The following command recursively removes
|
|
// all files and directories inside the /tmp directory. If executed with root
|
|
// permissions, it can potentially delete important files and directories, causing
|
|
// system instability or data loss.
|
|
system("rm -rf /tmp/*"); // Caution: poses a security risk if accidentally executed as root.
|
|
}
|
|
----
|
|
|
|
|
|
== How to fix it
|
|
|
|
Always check the return values of the ``++setuid++``-family functions and handle any potential error appropriately.
|
|
|
|
|
|
=== Code examples
|
|
|
|
==== Noncompliant code example
|
|
|
|
[source,cpp,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
static uid_t real_uid;
|
|
static uid_t effective_uid;
|
|
|
|
void undo_setuid(void) {
|
|
printf("Try to drop privileges\n");
|
|
// Potential failures of `seteuid` are neither checked nor handled appropriately.
|
|
// This function may hence silently fail to drop privileges and continues execution
|
|
// in a privileged mode. This poses a serious security risk!
|
|
seteuid(real_uid); // Noncompliant: return value is not checked.
|
|
printf("Set uid to %lu\n", (unsigned long int)real_uid);
|
|
}
|
|
----
|
|
|
|
|
|
==== Compliant solution
|
|
|
|
[source,cpp,diff-id=1,diff-type=compliant]
|
|
----
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
static uid_t real_uid;
|
|
static uid_t effective_uid;
|
|
|
|
void undo_setuid(void) {
|
|
printf("Try to drop privileges\n");
|
|
int status;
|
|
status = seteuid(real_uid);
|
|
if (status < 0) { // Compliant: return code is checked and potential errors are handled.
|
|
fprintf(stderr, "Couldn't set uid.\n");
|
|
exit(status);
|
|
}
|
|
printf("Set uid to %lu\n", (unsigned long int)real_uid);
|
|
}
|
|
----
|
|
|
|
|
|
== Resources
|
|
|
|
=== Articles & blog posts
|
|
|
|
* https://www.usenix.org/legacy/events/sec02/full_papers/chen/chen.pdf[Setuid demystified] Chen, Hao, David Wagner, and Drew Dean. 11th USENIX Security Symposium (USENIX Security 02). 2002.
|
|
|
|
|
|
=== Standards
|
|
|
|
* CERT - https://wiki.sei.cmu.edu/confluence/display/c/POS36-C.+Observe+correct+revocation+order+while+relinquishing+privileges[POS36-C. Ensure that privilege relinquishment is successful]
|
|
* CERT - https://wiki.sei.cmu.edu/confluence/display/c/POS37-C.+Ensure+that+privilege+relinquishment+is+successful[POS37-C. Observe correct revocation order while relinquishing privileges]
|
|
* CWE - https://cwe.mitre.org/data/definitions/252[CWE-252 Unchecked Return Value]
|
|
* CWE - https://cwe.mitre.org/data/definitions/272[CWE-272 Least Privilege Violation]
|