VNX-281: Improper Preservation of Permissions
Detects a C/C++ file replaced through a temporary (rename over the original) whose creation mode is the default or group/other-accessible and never copied from the original's st_mode, so a restricted file comes back readable by others.
| Field | Value |
|---|---|
| Rule ID | VNX-281 |
| Severity | Low |
| SARIF level | note |
| Kind | sast |
| Languages | C, C++ |
| CWE | CWE-281 |
| 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 | improper-preservation-permissions file-permissions cwe-281 |
| Source | internal/sast/rules/vnx-281.rego |
Overview
VNX-281 maps to CWE-281: Improper Preservation of Permissions. Rewriting a file safely means writing a temporary and renaming it over the original. The rename replaces the inode, so the new file has whatever mode the temporary was created with, not the mode the administrator gave the original. A 0600 credentials file, ACL or key store rewritten this way comes back 0644 (or 0666 & ~umask).
The rule reports rename(), std::rename() or std::filesystem::rename() of a temporary created in the 60 lines above with:
fopen(tmp, "w")/"a"or astd::ofstream/fstream(default mode),open(tmp, ...O_CREAT..., 0644)orcreat(tmp, 0666)(group/other bits),- or an owner-only temporary (
mkstemp,open(..., 0600)) widened by a literalchmod/fchmodsuch asfchmod(fd, 0644),
when nothing nearby reads the original’s st_mode or applies a mode held in a variable.
What Gets Flagged
FILE *out = fopen(tmp, "w");
fputs(body, out);
fclose(out);
if (rename(tmp, path) != 0) /* FLAGGED: path loses its mode */
return -1;
int fd = mkstemp(tmpname);
write(fd, buf, n);
fchmod(fd, 0644); /* widened to a fixed mode */
rename(tmpname, aclfile); /* FLAGGED */
What Is Not Flagged
- A temporary given the original’s mode:
fchmod(fileno(out), st.st_mode & 07777),chmod(tmp, original_mode). - An owner-only temporary (
mkstemp,open(..., 0600)) that is never widened. rename()that moves an existing file, and writing a new file without replacing one.- Comments and string literals.
Whether the replaced file was ever more restrictive than the new mode is decided at run time and is not judged.
Remediation
stat() the original before writing, create the temporary owner-only with mkstemp(), then apply the original’s mode (and owner, when running as root) before the rename:
struct stat st;
if (stat(path, &st) != 0) return -1;
int fd = mkstemp(tmpname);
/* ... write ... */
if (fchmod(fd, st.st_mode & 07777) != 0) return -1;
fsync(fd);
close(fd);
return rename(tmpname, path);
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-281
// nosec vnx-281
Or record an ignore rule: vulnetix ignore add --rule vnx-281 --reason "...". See Suppressing findings.