Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The meaning of "appropriate privileges" varies from platform to platform. For example, on Solaris, appropriate privileges for setuid() means that the PRIV_PROC_SETID privilege is in the effective privilege set of the process. On BSD, it means that the effective user ID (EUID) is zero (that is, the process is running as root) or that uid=geteuid(). On Linux, it means that the process has CAP_SETUID capability and that setuid(geteuid()) will fail if the effective user ID (EUID) the EUID is not equal to 0, the real user ID (RUID), or the saved set-user - ID (SSUID).

Because of this complex behavior, there may be cases where the desired privilege drops are unsuccessfulsometimes may fail. For example, the range of Linux Kernel versions (2.2.0-20–2.2.15) is vulnerable to an insufficient privilege attack wherein setuid(getuid()) did not drop privileges as expected when the capability bits were set to zero. As a precautionary measure, subtle behavior and error conditions for the targeted implementation must be carefully noted.

...

This noncompliant code example compiles cleanly on most POSIX systems, but no explicit checks have been checks are made to ensure that privilege relinquishment has succeeded. This may be dangerous depending on the sequence of the preceding privilege changes.

Code Block
bgColor#ffcccc
langc

/* Code intended to run with elevated privileges */

/* Temporarily drop privileges */
if (seteuid(getuid()) != 0) {
  /* Handle error */
}

/* Code intended to run with lower privileges */

if (need_more_privileges) {
  /* Restore privileges */
  if (seteuid(0) != 0) {
    /* Handle error */
  }

  /* Code intended to run with elevated privileges */
}

/* ... */

/* Permanently drop privileges */
if (setuid(getuid()) != 0) {
  /* Handle error */
}

/*
 * Code intended to run with lower privileges,
 * but if privilege relinquishment failed,
 * attacker can regain elevated privileges!
 */

If the program is run as a setuid root program, over time, the state of the UIDs UIDs might look like the following:

...

Code Block
bgColor#ccccff
langc

/* Code intended to run with elevated privileges   */

/* Temporarily drop privileges */
if (seteuid(getuid()) != 0) {
  /* Handle error */
}

/* Code intended to run with lower privileges */

if (need_more_privileges) {
  /* Restore Privileges */
  if (seteuid(0) != 0) {
    /* Handle error */
  }

  /* Code intended to run with elevated privileges */
}

/* ... */

/* Permanently drop privileges */
if (setuid(getuid()) != 0) {
  /* Handle error */
}

if (setuid(0) != -1) {
  /* Privileges can be restored, handle error */
}

/*
 * Code intended to run with lower privileges;
 * attacker cannot regain elevated privileges
 */

...

A better solution is to ensure that proper privileges exist before attempting to carry out to perform a permanent drop.

Code Block
bgColor#ccccff
langc

/* Store the privileged ID for later verification */
uid_t privid = geteuid();

/* Code intended to run with elevated privileges   */

/* Temporarily drop privileges */
if (seteuid(getuid()) != 0) {
  /* Handle error */
}

/* Code intended to run with lower privileges  */

if (need_more_privileges) {
  /* Restore Privileges */
  if (seteuid(privid) != 0) {
    /* Handle error */
  }

  /* Code intended to run with elevated privileges   */
}

/* ... */

/* Restore privileges if needed */
if (geteuid() != privid) {
  if (seteuid(privid) != 0) {
    /* Handle error */
  }
}

/* Permanently drop privileges */
if (setuid(getuid()) != 0) {
  /* Handle error */
}

if (setuid(0) != -1) {
  /* Privileges can be restored, handle error */
}

/*
 * Code intended to run with lower privileges;
 * attacker cannot regain elevated privileges
 */

...

A process may have a number of supplementary group IDs, in addition to its effective group ID, and the supplementary groups can allow privileged access to files. The getgroups() function returns an array which that contains the supplementary group IDs and can also contain the effective group ID. The setgroups() function can set the supplementary group IDs and can also set the effective group ID on some systems. Using setgroups() usually requires privileges. While  Although POSIX defines the getgroups() function, it does not define setgroups().

Under normal circumstances, setuid() and related calls do not alter the supplementary group IDs. However, a setuid-root program can alter its supplementary group IDs and then relinquish root privileges, in which case, it maintains the supplementary group IDs but lacks the privilege necessary to relinquish them. Consequently, it is recommended that a program immediately relinquish supplementary group IDs before relinquishing root privileges.

Rule POS36-C. Observe correct revocation order while relinquishing privileges discusses how to drop supplementary group IDs. To ensure that supplementary group IDs are indeed relinquished, you can use the following eql_sups function:

Code Block
bgColor#ccccff
langc

/* Returns nonzero if the two group lists are equivalent (taking into
   account that the lists may differ wrt the egid */
int eql_sups(const int cursups_size, const gid_t* const cursups_list,
	     const int targetsups_size, const gid_t* const targetsups_list) {
  int i;
  int j;
  const int n = targetsups_size;
  const int diff = cursups_size - targetsups_size;
  const gid_t egid = getegid();
  if (diff > 1 || diff < 0 ) {
    return 0;
  }
  for (i=0, j=0; i < n; i++, j++) {
    if (cursups_list[j] != targetsups_list[i]) {
      if (cursups_list[j] == egid) {
	i--; /* skipping j */
      } else {
	return 0;
      }
    }
  }
  /* If reached here, we're sure i==targetsups_size. Now, either
     j==cursups_size (skipped the egid or it wasn't there), or we didn't
     get to the egid yet because it's the last entry in cursups */
  return j == cursups_size ||
    (j+1 == cursups_size && cursups_list[j] == egid);
}

...

Processes on Linux have two additional values called fsuid and fsgid. These values indicate the privileges used when accessing files on the file system. These values  They normally shadow the effective user ID and effective group ID, but the setfsuid() and setfsgid() functions allow them to be changed. Since Because changes to the euid and egid normally also apply to fsuid and fsgid, a program relinquishing root privileges needs need not be concerned with setting fsuid or fsgid to safe values. However, there has been at least one kernel bug that violated this invariant ([Chen 2002] and [Tsafrir 2008]). Consequently, a prudent program will check checks that fsuid and fsgid have harmless values after relinquishing privileges.

...

section

Tool

Version

Checker

Description

Klocwork

Include Page
Klocwork_V
Klocwork_V
section

SV.FIU.PERMISSIONS
SV.USAGERULES.PERMISSIONS

 

Related Vulnerabilities

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

...

ISO/IEC PDTR 24772 "XYO Privilege Sandbox Issuessandbox issues"

MITRE CWE: CWE-250, "Execution with Unnecessary Privilegesunnecessary privileges"

MITRE CWE: CWE-273, "Failure to Check Whether Privileges Were Dropped Successfullycheck whether privileges were dropped successfully"

...

Sources

[Chen 2002] "Setuid Demystified"
[Dowd 2006] Chapter 9, "Unix I: Privileges and Files"
[Open Group 2004] setuid(), getuid(), seteuid()
[Tsafrir 2008] "The Murky Issue of Changing Process Identity: Revising 'Setuid Demystified'"
[Wheeler 2003] Section 7.4, "Minimize Privilegesprivileges"

...