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.
| 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):
- A user-id drop followed, within 15 lines of the same function, by a group call (
setgid,setegid,setregid,setresgid,setgroups,initgroups). - A permanent drop to another user (
setuid,setreuid,setresuidwith an argument other thangetuid()) in a file that never callssetgroups,initgroupsorsetusercontext.
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
- Drop in this order:
setgroups()orinitgroups(), thensetgid(), thensetuid(). - Check every return value (see VNX-273) and verify afterwards that
setuid(0)fails. - Prefer
setresgid/setresuidso that the real, effective and saved ids all change together.
References
- CWE-648: Incorrect Use of Privileged APIs
- CERT POS36-C: Observe correct revocation order while relinquishing privileges
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.