Privilege Handling (CWE-271, CWE-426, CWE-377, CWE-61)
What this checks
Privileged programs (SUID/SGID, root daemons, container init) operate with a
credentials gap. Classic mistakes: privileges dropped in the wrong order, environment
variables trusted from an attacker-controlled shell, temp files where an attacker can
symlink-swap before write. Local pattern, cross-trust-boundary consequence. LSMs
provide defense in depth; this catches the source-level mistake.
Vulnerable patterns
- Privileged process drops the effective UID only, leaving the saved set-user-ID intact so the process can regain root later.
- Privileged exec resolves the program name via
PATH, letting the attacker plant a lookalike earlier in the search path.
- Privileged code inherits
LD_PRELOAD, LD_LIBRARY_PATH, or IFS from the calling environment without scrubbing them.
- Process runs with no umask call (or an explicit zero umask), leaving created files world-writable.
- Temp file created via a "generate name then open" pattern (non-atomic), allowing an attacker to symlink the name in between.
- File opened in a world-writable directory without the no-follow flag, letting the attacker pre-create the path as a symlink to a privileged target.
- Multi-call binary that picks behavior based on
argv[0] without canonicalizing — the caller chooses the name via execve's second argument.
Fix immediately
Flag the vulnerable code and explain the risk. Then suggest a fix that establishes
these properties. Translate each property into the audited file's language and
operating-system API — use the platform's documented privilege, environment, and
filesystem calls.
- Privilege drops set all three credential IDs explicitly and check the return. Use the call that sets real, effective, and saved IDs in one operation; verify by re-reading afterwards. Any failure is fatal — abort rather than continue with ambiguous credentials.
- Privileged execs pass an absolute path and a known environment. Do not inherit
PATH, LD_*, or IFS from the caller. Never use exec variants that resolve via PATH in privileged code.
- Temp files are created with an atomic open-and-create primitive that returns a file descriptor in one call — never "make a name, then open". Atomic creation closes the symlink-swap window.
- Files opened in world-writable directories pass a no-follow flag so the open fails if the final path component is a symlink. Combine with exclusive-create when the file should not pre-exist.
- The umask is set explicitly at process start to a restrictive value matching the intended file-mode policy. Do not rely on the inherited umask.
Verification
After rewriting, confirm:
References
1---2name: privilege-handling3description: Detects unsafe privilege handling in SUID/SGID binaries, environment-variable trust in privileged code, insecure umask, temp-file races, and symlink-follow bugs in /tmp. Use when writing or modifying SUID/SGID-installed code, code that drops or elevates privileges via setuid/setgid/seteuid/seteuid_r, anything that reads PATH/LD_*/IFS environment variables, code that creates files in /tmp or other world-writable directories, or anything that opens a file whose path may contain a symlink controlled by a less-privileged user.4---56# Privilege Handling (CWE-271, CWE-426, CWE-377, CWE-61)78## What this checks910Privileged programs (SUID/SGID, root daemons, container init) operate with a11credentials gap. Classic mistakes: privileges dropped in the wrong order, environment12variables trusted from an attacker-controlled shell, temp files where an attacker can13symlink-swap before write. Local pattern, cross-trust-boundary consequence. LSMs14provide defense in depth; this catches the source-level mistake.1516## Vulnerable patterns1718- Privileged process drops the effective UID only, leaving the saved set-user-ID intact so the process can regain root later.19- Privileged exec resolves the program name via `PATH`, letting the attacker plant a lookalike earlier in the search path.20- Privileged code inherits `LD_PRELOAD`, `LD_LIBRARY_PATH`, or `IFS` from the calling environment without scrubbing them.21- Process runs with no umask call (or an explicit zero umask), leaving created files world-writable.22- Temp file created via a "generate name then open" pattern (non-atomic), allowing an attacker to symlink the name in between.23- File opened in a world-writable directory without the no-follow flag, letting the attacker pre-create the path as a symlink to a privileged target.24- Multi-call binary that picks behavior based on `argv[0]` without canonicalizing — the caller chooses the name via `execve`'s second argument.2526## Fix immediately2728Flag the vulnerable code and explain the risk. Then suggest a fix that establishes29these properties. Translate each property into the audited file's language and30operating-system API — use the platform's documented privilege, environment, and31filesystem calls.32331. **Privilege drops set all three credential IDs explicitly and check the return.** Use the call that sets real, effective, and saved IDs in one operation; verify by re-reading afterwards. Any failure is fatal — abort rather than continue with ambiguous credentials.342. **Privileged execs pass an absolute path and a known environment.** Do not inherit `PATH`, `LD_*`, or `IFS` from the caller. Never use exec variants that resolve via `PATH` in privileged code.353. **Temp files are created with an atomic open-and-create primitive** that returns a file descriptor in one call — never "make a name, then open". Atomic creation closes the symlink-swap window.364. **Files opened in world-writable directories pass a no-follow flag** so the open fails if the final path component is a symlink. Combine with exclusive-create when the file should not pre-exist.375. **The umask is set explicitly at process start** to a restrictive value matching the intended file-mode policy. Do not rely on the inherited umask.3839## Verification4041After rewriting, confirm:4243- [ ] Every privilege drop sets real, effective, and saved IDs explicitly and checks the return values44- [ ] Every exec from privileged code passes an absolute path and a sanitized environment, never the inherited environment45- [ ] No path-searching exec variant (the family that resolves via `PATH`) is used in privileged code paths46- [ ] Every temp-file creation uses an atomic open-and-create primitive — never a name-then-open pattern47- [ ] Every file open in a world-writable directory passes a no-follow flag, plus exclusive-create when the file should not pre-exist48- [ ] The umask is set explicitly at process start to a restrictive value4950## References5152- CWE-271 ([Privilege Dropping / Lowering Errors](https://cwe.mitre.org/data/definitions/271.html))53- CWE-426 ([Untrusted Search Path](https://cwe.mitre.org/data/definitions/426.html))54- CWE-377 ([Insecure Temporary File](https://cwe.mitre.org/data/definitions/377.html))55- CWE-61 ([UNIX Symbolic Link Following](https://cwe.mitre.org/data/definitions/61.html))