VNX-279: Incorrect Execution-Assigned Permissions
Detects a program or install step that sets the setuid bit on a file at run time: chmod-family calls with 04xxx, S_ISUID or perms::set_uid in C and C++, os.chmod with 0o4xxx or stat.S_ISUID in Python, os.ModeSetuid in Go, chmod-family calls with a setuid mode in PHP, JavaScript/TypeScript, Ruby and Rust, mode: '4755' in Ansible tasks, and chmod u+s/4755, install -m 4755 and COPY --chmod=4755 in shell scripts and Dockerfiles.
| Field | Value |
|---|---|
| Rule ID | VNX-279 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | C, C++, Python, Go, PHP, JavaScript / TypeScript, Ruby, Rust, Shell, Dockerfile, YAML |
| CWE | CWE-279 |
| 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 | exec-assigned-permissions setuid chmod file-permissions cwe-279 |
| Source | internal/sast/rules/vnx-279.rego |
Overview
VNX-279 maps to CWE-279: Incorrect Execution-Assigned Permissions: while it runs, the product sets the permissions of an object in a way nobody intended. CWE-279 is abstract, so this rule reports its most concrete and most dangerous form: assigning the setuid bit. Whoever executes a setuid file does so with the identity of its owner, usually root, so any flaw in that file, or the ability to replace it, becomes a privilege escalation.
Reported:
- C, C++:
chmod,fchmod,fchmodat,lchmodwith a04xxx-07xxxmode orS_ISUID, andstd::filesystem::permissionswithperms::set_uid. - Python:
os.chmod,os.fchmod,Path.chmodwith0o4xxxorstat.S_ISUID. - Go:
os.Chmod/File.Chmodwithos.ModeSetuid, andsyscall/unixChmodwith a setuid mode. - PHP:
chmod($f, 04755). JavaScript/TypeScript:fs.chmod/chmodSyncwith0o4755or"4755". Ruby:File.chmod(04755, f),FileUtils.chmod("u+s", f). Rust:fs::Permissions::from_mode(0o4755)orset_mode(0o4755). - Ansible:
mode: '4755'ormode: u+sinfile/copy/templatetasks. - Shell scripts and Dockerfiles:
chmod u+s,chmod +s,chmod a+s,chmod 4755,install -m 4755,COPY --chmod=4755. - The same
chmodcommand run throughsystem,subprocess,os.system,exec.Command,execSyncorshell_exec, and a mode held in a variable assigned such a literal just above.
What Gets Flagged
chmod(helper, 04755); // FLAGGED
chmod(path, st.st_mode | S_ISUID); // FLAGGED
os.chmod(path, os.stat(path).st_mode | stat.S_ISUID) # FLAGGED
RUN chmod u+s /usr/bin/find # FLAGGED
What Is Not Flagged
- Setgid alone (
chmod g+s dir,02775): on a directory it is the normal way to share a group. - Clearing or testing the bit:
st.st_mode & ~S_ISUID,fi.Mode() &^ os.ModeSetuid,chmod u-s,chmod a-s,if (st.st_mode & S_ISUID),find / -perm -4000. - Ordinary and sticky modes (
0755,01777). - Go
os.Chmod(p, 0o4755): Go’sos.FileModedoes not map the04000bit to setuid, so this call does not set it (os.ModeSetuiddoes). - Test code and the pattern in comments or strings.
Remediation
Avoid setuid files. Give the program the single capability it needs (setcap cap_net_bind_service=+ep), or run the privileged part as a separate service or through a reviewed sudoers or polkit rule. In container images, remove setuid bits rather than adding them:
RUN find / -xdev -perm /6000 -type f -exec chmod a-s {} \; || true
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-279
// nosec vnx-279
Or record an ignore rule: vulnetix ignore add --rule vnx-279 --reason "...". See Suppressing findings.