VNX-270: Privilege Context Switching Error
Another program is executed after only the effective user ID was lowered (seteuid, setreuid(-1, uid), os.seteuid), so it starts with root as its real user ID and can switch straight back to root.
| Field | Value |
|---|---|
| Rule ID | VNX-270 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | C, C++, Python |
| CWE | CWE-270 |
| CAPEC | CAPEC-66 |
| ATT&CK | T1190 |
| CWSS vector | CWSS:1.0/TI:L/AP:RU/AL:S/IC:N/FC:LT/RP:RU/RL:S/AV:L/AS:N/IN:A/SC:A/BI:L/DI:H/EX:L/EC:N/P:L |
| Tags | privilege-context-switching privilege-drop cwe-270 |
| Source | internal/sast/rules/vnx-270.rego |
Overview
VNX-270 maps to CWE-270: Privilege Context Switching Error. It reports an exec, system, popen, posix_spawn or subprocess call that follows a temporary privilege drop in the same function, with no permanent drop in between.
Why This Matters
seteuid(uid) changes only the effective user ID; the real user ID stays root. That is the right tool for a short unprivileged section inside a root process, but it is not a context switch for a new program. A program started afterwards inherits root as its real user ID, and any process whose real user ID is root may call setuid(0) again. A user’s hook script, an editor or a plugin started this way can take back full root with one call.
What Gets Flagged
if (setegid(gid) != 0 || seteuid(uid) != 0)
return -1;
execl("/bin/sh", "sh", "-c", hook, (char *)NULL); /* flagged */
::setresuid(-1, uid, -1);
::execvp(argv[0], argv); // flagged
os.seteuid(uid)
subprocess.run(cmd, check=True) # flagged
Not flagged: a permanent drop before the exec (setuid(uid), setresuid(uid, uid, uid), setreuid(uid, uid), os.setuid(uid), or a drop_privileges()-style wrapper); root restored with seteuid(0) before the exec, where the program runs as root on purpose (VNX-272 judges whether that is necessary); subprocess.run(..., user=uid); the two calls in different functions; and the names in comments or string literals.
Remediation
- Fork, then in the child drop permanently before exec:
setgroups,setgid(gid),setuid(uid)(orsetresgid/setresuidwith all three IDs), and check each result. - In Python, use
subprocess.run(cmd, user=uid, group=gid, extra_groups=[]), which performs the permanent drop in the child.
References
- CWE-270: Privilege Context Switching Error
- SEI CERT POS37-C: Ensure that privilege relinquishment is successful
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-270
// nosec vnx-270
Or record an ignore rule: vulnetix ignore add --rule vnx-270 --reason "...". See Suppressing findings.