Skip to content

VNX-273: Improper Check for Dropped Privileges

Detects privilege drops whose result is thrown away: setuid/setgid/setresuid/setgroups/initgroups and the Windows impersonation calls as bare or (void)-cast statements in C and C++, syscall/unix Setuid-family calls in Go (also deferred), libc:: and nix calls in Rust, and posix_setuid-family calls in PHP. A failed drop leaves the program running with the identity it meant to give up.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-273
Severity Medium
SARIF level warning
Kind sast
Languages C, C++, Go, Rust, PHP
CWE CWE-273
CAPEC CAPEC-66
ATT&CK T1190
CWSS vector CWSS:1.0/TI:M/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:M/DI:H/EX:M/EC:N/P:C
Tags dropped-privileges setuid unchecked-return impersonation cwe-273
Source internal/sast/rules/vnx-273.rego

Overview

VNX-273 maps to CWE-273: Improper Check for Dropped Privileges. Dropping privileges can fail: setuid() fails with EAGAIN when the target user is at RLIMIT_NPROC, a call made without CAP_SETUID fails with EPERM, and ImpersonateNamedPipeClient fails when the client has not written to the pipe yet. A program that does not check carries on as root, or as the server account, believing it is unprivileged.

The rule reports a drop that is used as a bare statement, so its result is discarded:

  • C and C++: setuid, setgid, seteuid, setegid, setreuid, setregid, setresuid, setresgid, setgroups, initgroups (also ::setuid), including behind a (void) or (void)! cast, static_cast<void>(...) or std::ignore =, which only silence warn_unused_result, or as the body of a braceless one-line if (getuid() == 0) setuid(uid); or } else setgid(gid);; and ImpersonateNamedPipeClient, ImpersonateLoggedOnUser, ImpersonateSelf, ImpersonateAnonymousToken, RpcImpersonateClient, CoImpersonateClient, SetThreadToken.
  • Go: syscall/unix Setuid, Setgid, Seteuid, Setegid, Setreuid, Setresuid, Setresgid, Setgroups as a statement, assigned to _, or deferred (defer syscall.Seteuid(uid) drops the error of the restore).
  • Rust: the same libc:: calls ended with ; or bound to let _ =, and the nix crate’s unistd::setuid and friends as a bare statement, with .ok(); or let _ =.
  • PHP: posix_setuid, posix_setgid, posix_seteuid, posix_setegid, posix_initgroups as a statement. They return false on failure and raise nothing.

Python, Ruby and Node are not covered because their calls raise on failure.

What Gets Flagged

// FLAGGED: if either call fails the job runs as root
setgid(pw->pw_gid);
setuid(pw->pw_uid);
return execv(argv[0], argv);
// FLAGGED: on failure DeleteFileA runs with the server's privileges
ImpersonateNamedPipeClient(hPipe);
DeleteFileA(path);
// FLAGGED
_ = unix.Setgid(gid)
syscall.Setuid(uid)
defer syscall.Seteuid(uid)
// FLAGGED
let _ = setgid(Gid::from_raw(gid));
setuid(Uid::from_raw(uid)).ok();
// FLAGGED
posix_setgid($info['gid']);
\posix_setuid($info['uid']);

What Is Not Flagged

  • A result that is compared, assigned, returned, asserted or passed on: if (setuid(uid) != 0), rc = seteuid(uid);, return setuid(uid);, if err := unix.Setgid(gid); err != nil, setgid(gid)?;, .expect("setuid"), posix_setuid($uid) or die(...).
  • A drop followed within a few lines by a verification on getuid()/geteuid()/getegid(), errno or GetLastError(), or by a checked attempt to regain root (if (setuid(0) != -1) abort();, CERT POS37-C).
  • A raise back to root such as seteuid(0): if it fails, the program is left less privileged, not more.
  • Prototypes and definitions (also BSD style, with int on the line above setresuid(uid_t r, uid_t e, uid_t s) in a compatibility shim), macros, member calls (ops->setuid(uid), my_setuid(uid)) and the names in comments or strings.

Remediation

if (setgroups(0, NULL) != 0 || setgid(gid) != 0 || setuid(uid) != 0)
    abort();
/* paranoid: make sure root cannot be regained */
if (setuid(0) != -1 || getuid() != uid || geteuid() != uid)
    abort();
if (!ImpersonateNamedPipeClient(hPipe))
    return FALSE;

References

Suppressing this rule

Put a nosec comment on the line, or on line 1 to cover the whole file. Use the comment marker of the file’s language (#, //, -- or ;):

# nosec vnx-273
// nosec vnx-273

Or record an ignore rule: vulnetix ignore add --rule vnx-273 --reason "...". See Suppressing findings.

Edit this pageSources: cli/internal/sast/rules/vnx-273.rego, cli/internal/sast/docs/vnx-273.md
esc
↑ ↓ move↵ openSearches in your browser. A key like autonomy.max or a code like threshold_required jumps straight to it.