VNX-276: Incorrect Default Permissions
Detects files and directories given a world-writable mode by a call the umask does not filter: chmod-family calls in C, C++, Python, Go, PHP, JavaScript/TypeScript, Ruby, Java and Rust, chmod/install -m/mkdir -m/COPY --chmod in shell scripts, Makefiles and Dockerfiles, mode: in Ansible tasks, and chmod commands run through system/exec/subprocess.
| Field | Value |
|---|---|
| Rule ID | VNX-276 |
| Severity | Medium |
| SARIF level | warning |
| Kind | sast |
| Languages | C, C++, Python, Go, PHP, JavaScript / TypeScript, Ruby, Java, Rust, Shell, Dockerfile, YAML |
| CWE | CWE-276 |
| 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 | default-permissions world-writable chmod file-permissions cwe-276 |
| Source | internal/sast/rules/vnx-276.rego |
Overview
VNX-276 maps to CWE-276: Incorrect Default Permissions. When a program or its installer gives what it installs a mode that lets others write, any local user (or any process in the container) can replace the code, configuration or data the program later trusts.
A mode is world-writable when it grants others write: an octal mode ending in 2, 3, 6 or 7 (0777, 0o666, 777), S_IWOTH/S_IRWXO, os.ModePerm, perms::all, or symbolic o+w/a+w/a+rwx. The rule reports such a mode passed to a call that sets permissions directly, bypassing the umask:
| Language | Calls |
|---|---|
| C, C++ | chmod, fchmod, lchmod, fchmodat, std::filesystem::permissions |
| Python | os.chmod, os.fchmod, Path.chmod |
| Go | os.Chmod, (*os.File).Chmod |
| PHP | chmod(), $fs->chmod() |
| JavaScript, TypeScript | fs.chmod, chmodSync, fchmod, fchmodSync, fs/promises chmod (numeric or string modes) |
| Ruby | File.chmod, FileUtils.chmod, FileUtils.chmod_R, and FileUtils.mkdir/mkdir_p/install with mode: (they chmod after creating, so the umask does not apply) |
| Java | Files.setPosixFilePermissions with rwxrwxrwx or OTHERS_WRITE, File.setWritable(true, false) |
| Rust | fs::Permissions::from_mode, PermissionsExt::set_mode (they feed fs::set_permissions; OpenOptions/DirBuilder .mode() is umask-filtered and not reported) |
| Ansible | mode: '0777', mode: 0666, mode: a+w and mode=0777 in file/copy/template tasks |
| Shell, Makefile, PKGBUILD, Dockerfile | chmod, install -m (also after -o/-g), mkdir -m, COPY --chmod, RUN ["chmod", ...] |
A chmod 777-style command run through system, exec, subprocess, exec.Command, Runtime.exec or backticks is reported too, and so is a mode held in a variable that was assigned a world-writable literal a few lines above, or in a constant declared once for the file (const socketMode = 0o777, #define MODE 0777, SOCKET_MODE = 0o777). A call whose arguments run onto the next lines is read to its closing parenthesis, and a mode spelled as a string (octdec('0777'), int("777", 8)) counts.
What Gets Flagged
os.chmod(upload_dir, 0o777) # FLAGGED
os.chmod(config_file, current | stat.S_IWOTH) # FLAGGED
subprocess.run(["chmod", "-R", "777", build_dir], check=True) # FLAGGED
chmod($target, 0777); // FLAGGED
FileUtils.mkdir_p("tmp/uploads", mode: 0o777) # FLAGGED
fs::set_permissions(sock, fs::Permissions::from_mode(0o777))?; // FLAGGED
- ansible.builtin.file:
path: /var/www/uploads
state: directory
mode: '0777' # FLAGGED
install:
chmod 777 $(DESTDIR)/var/lib/app # FLAGGED
RUN chmod -R 777 /app # FLAGGED
COPY --chmod=777 entrypoint.sh /entrypoint.sh # FLAGGED
What Is Not Flagged
- Modes passed to creation calls that the umask filters:
open(p, O_CREAT, 0666),mkdir(d, 0777),os.makedirs(d, 0o777),os.WriteFile(p, b, 0666),fs.writeFileSync(p, d, { mode: 0o666 }), PHPmkdir($d, 0777, true). - Sticky shared directories:
chmod 1777 /tmp/shared,a+rwxt. - Computed or masked modes:
0777 - umask(),st.st_mode & ~S_IWOTH,fi.Mode() &^ 0o022,0o777 & ~umask, and a mode variable masked after it is assigned (mode &= ~old_umask;). - Group-only or owner-only changes:
chmod +x,g+rwX,g=u,u+w,+w(no who: the umask decides). - A tree made writable only to be removed:
os.chmod(path, 0o777); func(path)in anrmtreeonerror handler, or a walk that chmods beforeos.RemoveAll. - Reading permissions:
(st.permissions() & fs::perms::others_write) != fs::perms::none. - Test code (including
test.shand.batsscripts), comments and log strings.
Not covered: modes held in shell variables, Kotlin and C# APIs, Kubernetes defaultMode, and chmod in YAML CI steps.
Remediation
Give write access to the owner, and to a group if several accounts must write, never to everyone. For a shared drop directory use the sticky bit.
chmod 0755 /var/www/uploads
chmod 0640 /etc/app/app.conf
install -m 0644 app.conf /etc/app/app.conf
chmod 1777 /srv/drop # shared, but users cannot delete each other's files
References
- CWE-276: Incorrect Default Permissions
- CWE-732: Incorrect Permission Assignment for Critical Resource
- chmod(2)
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-276
// nosec vnx-276
Or record an ignore rule: vulnetix ignore add --rule vnx-276 --reason "...". See Suppressing findings.