When a Unix process forks or executes a new program, the child inherits every open file descriptor. If the parent had a privileged file or socket open and the child has fewer privileges, the child can use a handle it could never have opened itself. CWE-403 is that weakness: a process does not close sensitive file descriptors before invoking a child process, which lets the child perform unauthorized I/O.
MITRE lists C, Unix and not language specific platforms. Privileged daemons that drop privileges and then launch helpers are the classic setting.
How it happens
int secret = open("/etc/app/private.key", O_RDONLY); // privileged open
setuid(unprivileged_uid); // drop privileges
execl("/usr/bin/helper", "helper", NULL); // helper inherits fd 'secret'
The helper never opened the key file, but descriptor secret is still open in its process. Any code in the helper can read from it.
The record lists four real cases:
- CVE-2003-0740: a server leaks a privileged file descriptor, allowing the server to be hijacked.
- CVE-2004-1033: a file descriptor leak allows reading of restricted files.
- CVE-2000-0094: access to a restricted resource using a modified file descriptor for stderr.
- CVE-2002-0638: an open file descriptor used as an alternate channel in a complex race condition.
What an attacker gains
MITRE lists confidentiality and integrity: reading and modifying application data through the handle. With a leaked descriptor an attacker can read a file they were never allowed to open, or write to it, and in the server hijack case take over a service.
How to find it
MITRE says automated static analysis (SAST) can find some instances. Practical checks:
- On a running Unix system, list a child process's open files (for example with lsof or by reading its fd directory under /proc on Linux) and compare against what it should have.
- Review every fork or exec in privileged code and check which descriptors are open at that point.
- Look for calls to open, socket and accept that have no close-on-exec flag.
How to fix it
The record lists no mitigations. The standard steps for this weakness:
- Open descriptors with the close-on-exec flag so they do not survive an exec.
- Close every sensitive descriptor before you call exec or hand control to less privileged code.
- Do not drop privileges while holding handles you do not want the lower privilege code to have.
int secret = open("/etc/app/private.key", O_RDONLY | O_CLOEXEC);
In Python, file descriptors are non-inheritable by default for new files, and subprocess closes other descriptors by default (close_fds=True). Keep those defaults.
Quick check
The child process never calls open on the key file. Can it still read it?
Yes, if the descriptor was inherited. Access was checked when the parent opened the file, not when the child uses the handle.