Skip to content

VNX-648: Privilege drop in the wrong order or without clearing supplementary groups

Reports a setuid/seteuid/setreuid/setresuid drop followed by a setgid/setgroups call (wrong order), and a drop to another user in a file that never calls setgroups or initgroups, in C, C++ and Python. Raising to root and drops back to getuid() are not reported.

Updated 02 Nov 2026Checked against cli@de9b52a
Field Value
Rule ID VNX-648
Severity Medium
SARIF level warning
Kind sast
Languages C, C++, Python
CWE CWE-648
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 incorrect-privilege-use privilege-drop setuid cwe-648
Source internal/sast/rules/vnx-648.rego

Overview

VNX-648 maps to CWE-648: Incorrect Use of Privileged APIs. It reports two misuses of the POSIX identity calls (CERT POS36-C):

  1. A user-id drop followed, within 15 lines of the same function, by a group call (setgid, setegid, setregid, setresgid, setgroups, initgroups).
  2. A permanent drop to another user (setuid, setreuid, setresuid with an argument other than getuid()) in a file that never calls setgroups, initgroups or setusercontext.

Why This Matters

Only a privileged process may change its group id. Once setuid() has given up root, the setgid() that follows fails, or succeeds only partly, and the process keeps root’s group. A process that never calls setgroups() keeps the supplementary groups of the identity it started as, which for a daemon started by root often includes privileged groups.

What Gets Flagged

Both findings are reported on the line of the user-id call (setuid, setreuid, …), not on the group call that follows it.

if (setuid(pw->pw_uid) != 0) exit(1);
if (setgid(pw->pw_gid) != 0) exit(1);          /* too late: already unprivileged */

if (setgid(pw->pw_gid) == -1) err(1, "setgid");
if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)   /* no setgroups in the file */
os.setuid(pw.pw_uid)
os.setgid(pw.pw_gid)

Not flagged: setgroups(0, NULL); setgid(gid); setuid(uid);, os.initgroups(user, gid) before the drop, setuid(getuid()) in a setuid-root helper, seteuid(0) (see VNX-272) and temporary seteuid switches.

Remediation

  1. Drop in this order: setgroups() or initgroups(), then setgid(), then setuid().
  2. Check every return value (see VNX-273) and verify afterwards that setuid(0) fails.
  3. Prefer setresgid/setresuid so that the real, effective and saved ids all change together.

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-648
// nosec vnx-648

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

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