Main Content

Bad order of dropping privileges

Dropped higher elevated privileges before dropping lower elevated privileges

Description

This defect occurs when you use functions such as setuid and setgid in the incorrect order, dropping higher elevated privileges before dropping lower elevated privileges. For example, you drop elevated primary group privileges before dropping elevated ancillary group privileges.

Risk

If you drop privileges in the wrong order, you can potentially drop higher privileges that you need to drop lower privileges. The incorrect order can mean that privileges are not dropped compromising the security of your program.

Fix

Respect this order of dropping elevated privileges:

  • Drop (elevated) ancillary group privileges, then drop (elevated) primary group privileges.

  • Drop (elevated) primary group privileges, then drop (elevated) user privileges.

Examples

expand all

#define _BSD_SOURCE
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <stdlib.h>
#define fatal_error() abort()

static void sanitize_privilege_drop_check(uid_t olduid, gid_t oldgid)
{
    if (seteuid(olduid) != -1)
    {
        /* Privileges can be restored, handle error */
        fatal_error();
    }
    if (setegid(oldgid) != -1)
    {
        /* Privileges can be restored, handle error */
        fatal_error();
    }
}
void badprivilegedroporder(void) {
    uid_t
        newuid = getuid(),
        olduid = geteuid();
    gid_t
        newgid = getgid(),
        oldgid = getegid();

    if (setuid(newuid) == -1) {
        /* handle error condition */
        fatal_error();
    }
    if (setgid(newgid) == -1)  { 
        /* handle error condition */
        fatal_error();
    }
    if (olduid == 0) {
        /* drop ancillary groups IDs only possible for root */
        if (setgroups(1, &newgid) == -1) {
            /* handle error condition */
            fatal_error();
        }
    }

    sanitize_privilege_drop_check(olduid, oldgid);
}

In this example, there are two privilege drops made in the incorrect order. setgid attempts to drop group privileges. However, setgid requires the user privileges, which were dropped previously using setuid, to perform this function. After dropping group privileges, this function attempts to drop ancillary groups privileges by using setgroups. This task requires the higher primary group privileges that were dropped with setgid. At the end of this function, it is possible to regain group privileges because the order of dropping privileges was incorrect.

Correction — Reverse Privilege Drop Order

One possible correction is to drop the lowest level privileges first. In this correction, ancillary group privileges are dropped, then primary group privileges are dropped, and finally user privileges are dropped.

#define _BSD_SOURCE
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
#include <stdlib.h>
#define fatal_error() abort()

static void sanitize_privilege_drop_check(uid_t olduid, gid_t oldgid)
{
    if (seteuid(olduid) != -1)
    {
        /* Privileges can be restored, handle error */
        fatal_error();
    }
    if (setegid(oldgid) != -1)
    {
        /* Privileges can be restored, handle error */
        fatal_error();
    }
}
void badprivilegedroporder(void) {
    uid_t
        newuid = getuid(),
        olduid = geteuid();
    gid_t
        newgid = getgid(),
        oldgid = getegid();

    if (olduid == 0) {
        /* drop ancillary groups IDs only possible for root */
        if (setgroups(1, &newgid) == -1) {
            /* handle error condition */
            fatal_error();
        }
    }
    if (setgid(getgid()) == -1)  {
        /* handle error condition */
        fatal_error();
    }
    if (setuid(getuid()) == -1) {
        /* handle error condition */
        fatal_error();
    }

    sanitize_privilege_drop_check(olduid, oldgid);
}

Result Information

Group: Security
Language: C | C++
Default: Off
Command-Line Syntax: BAD_PRIVILEGE_DROP_ORDER
Impact: High

Version History

Introduced in R2016b