Transmission of Private Resources into a New Sphere ('Resource Leak')
Description
Transmission of Private Resources into a New Sphere occurs when a product makes resources available to untrusted parties when those resources are only intended to be accessed by the product itself. This weakness involves the unintended exposure of internal resources such as file descriptors, memory references, database connections, or other system handles to external or less-privileged processes. When private resources leak across trust boundaries, attackers can gain unauthorized access to sensitive data or capabilities.
Risk
Resource leaks have severe implications. Unauthorized access to sensitive files. Privilege escalation through inherited descriptors. Data exposure to untrusted processes. Bypass of access controls. Session hijacking. Database connection theft. Memory disclosure. High likelihood when processes spawn children without proper resource cleanup.
Solution
Close or invalidate all sensitive resources before spawning child processes during implementation phase. Use close-on-exec flags for file descriptors. Implement proper resource lifecycle management. Sanitize the environment before process creation during architecture and design phase. Audit resource handling across trust boundaries.
Common Consequences
| Impact | Details |
|---|---|
| Confidentiality | Scope: Confidentiality Read Application Data - Attackers may gain unauthorized access to sensitive information through exposed resources. |
| Integrity | Scope: Integrity Modification of data through leaked write handles. |
Example Code
Vulnerable Code
// Vulnerable: C code leaking file descriptors to child processes
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
// VULNERABLE: File descriptor inherited by child
void vulnerable_spawn_child(const char* program) {
// Open sensitive file
int secret_fd = open("/etc/shadow", O_RDONLY);
if (secret_fd < 0) {
perror("open");
return;
}
// VULNERABLE: Fork without closing sensitive fd
pid_t pid = fork();
if (pid == 0) {
// Child process - STILL HAS ACCESS to secret_fd!
// Even though child may be less privileged
// VULNERABLE: Execute untrusted program with leaked fd
execl(program, program, NULL);
exit(1);
}
// Parent continues...
// Even if we close here, child already has the fd
close(secret_fd);
waitpid(pid, NULL, 0);
}
// VULNERABLE: Database connection leaked
void vulnerable_db_handler() {
// Establish privileged database connection
DB_CONN* conn = db_connect("admin", "password");
// VULNERABLE: Connection handle accessible to plugins
load_untrusted_plugin(conn); // Plugin can use admin connection
db_disconnect(conn);
}
// VULNERABLE: Socket descriptor leak
void vulnerable_server() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
bind(listen_fd, ...);
listen(listen_fd, 10);
while (1) {
int client_fd = accept(listen_fd, NULL, NULL);
pid_t pid = fork();
if (pid == 0) {
// Child handles client
// VULNERABLE: listen_fd still open in child
// Child could accept new connections!
handle_client(client_fd);
exit(0);
}
// Parent should close client_fd but listen_fd leaks to child
close(client_fd);
}
}
# Vulnerable: Python resource leaks
import os
import subprocess
# VULNERABLE: File descriptor leak to subprocess
def vulnerable_run_command(command):
# Open sensitive file
secret_file = open('/etc/shadow', 'r')
# VULNERABLE: subprocess inherits all file descriptors
result = subprocess.run(command, shell=True, capture_output=True)
# File descriptor was available to the subprocess
secret_file.close()
return result
# VULNERABLE: Database connection leak
class VulnerableApp:
def __init__(self):
# Privileged database connection
self.db_conn = connect_database(admin=True)
def run_plugin(self, plugin_code):
# VULNERABLE: Plugin has access to self.db_conn
exec(plugin_code) # Can access self.db_conn
# VULNERABLE: Temporary file leak
def vulnerable_temp_file():
import tempfile
# Create temp file with sensitive data
fd, path = tempfile.mkstemp()
os.write(fd, b"secret data")
# VULNERABLE: fd still open when calling external process
os.system("some_command") # Inherits the fd
os.close(fd)
os.unlink(path)
Fixed Code
// Fixed: Proper resource management before spawning children
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
// FIXED: Use close-on-exec flag
void safe_spawn_child(const char* program) {
// FIXED: Open with O_CLOEXEC flag
int secret_fd = open("/etc/shadow", O_RDONLY | O_CLOEXEC);
if (secret_fd < 0) {
perror("open");
return;
}
// File descriptor will be automatically closed on exec
pid_t pid = fork();
if (pid == 0) {
// Child process
// secret_fd will be closed when execl is called
execl(program, program, NULL);
exit(1);
}
close(secret_fd);
waitpid(pid, NULL, 0);
}
// FIXED: Explicit cleanup before fork
void safe_spawn_with_cleanup(const char* program) {
int secret_fd = open("/etc/shadow", O_RDONLY);
if (secret_fd < 0) {
return;
}
// ... use the file ...
// FIXED: Close sensitive resources before fork
close(secret_fd);
secret_fd = -1;
pid_t pid = fork();
if (pid == 0) {
// Child has no access to secret_fd
execl(program, program, NULL);
exit(1);
}
waitpid(pid, NULL, 0);
}
// FIXED: Set close-on-exec after open
void safe_set_cloexec(int fd) {
int flags = fcntl(fd, F_GETFD);
if (flags >= 0) {
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
}
// FIXED: Server with proper fd management
void safe_server() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
// FIXED: Set close-on-exec on listen socket
safe_set_cloexec(listen_fd);
bind(listen_fd, ...);
listen(listen_fd, 10);
while (1) {
int client_fd = accept(listen_fd, NULL, NULL);
// FIXED: Set close-on-exec on client fd too
safe_set_cloexec(client_fd);
pid_t pid = fork();
if (pid == 0) {
// FIXED: Close listen_fd in child
close(listen_fd);
handle_client(client_fd);
close(client_fd);
exit(0);
}
// Parent closes client_fd
close(client_fd);
}
}
// FIXED: Close all unnecessary fds before exec
void close_all_fds_except(int keep_fd) {
int max_fd = sysconf(_SC_OPEN_MAX);
for (int fd = 3; fd < max_fd; fd++) {
if (fd != keep_fd) {
close(fd);
}
}
}
# Fixed: Python with proper resource isolation
import os
import subprocess
# FIXED: Close file descriptors before subprocess
def safe_run_command(command):
secret_file = open('/etc/shadow', 'r')
secret_fd = secret_file.fileno()
# ... use the file ...
# FIXED: Close before running subprocess
secret_file.close()
# FIXED: Use close_fds=True (default in Python 3)
result = subprocess.run(
command,
shell=True,
capture_output=True,
close_fds=True # FIXED: Close all fds except stdin/stdout/stderr
)
return result
# FIXED: Isolated database connection
class SafeApp:
def __init__(self):
self._db_conn = None
def _get_db_conn(self):
"""FIXED: Lazy initialization, not exposed."""
if self._db_conn is None:
self._db_conn = connect_database(admin=True)
return self._db_conn
def run_plugin(self, plugin_code):
# FIXED: Create limited connection for plugin
plugin_conn = connect_database(admin=False, read_only=True)
# FIXED: Sandboxed execution without access to admin conn
sandbox = {'db': plugin_conn}
exec(plugin_code, sandbox)
plugin_conn.close()
# FIXED: Secure temporary file handling
def safe_temp_file():
import tempfile
# Create temp file
fd, path = tempfile.mkstemp()
try:
os.write(fd, b"secret data")
# FIXED: Close before external command
os.close(fd)
fd = -1
# Now safe to run external process
subprocess.run(["some_command"], close_fds=True)
finally:
if fd >= 0:
os.close(fd)
os.unlink(path)
# FIXED: Context manager for automatic cleanup
class SecureResource:
def __init__(self, path):
self.fd = os.open(path, os.O_RDONLY | os.O_CLOEXEC)
def __enter__(self):
return self
def __exit__(self, *args):
if self.fd >= 0:
os.close(self.fd)
self.fd = -1
# Usage
with SecureResource('/etc/shadow') as res:
# Use resource
pass
# Automatically closed, safe to spawn processes
CVE Examples
- CVE-2003-0740: Server leaked a privileged file descriptor enabling attackers to hijack the connection.
- CVE-2004-1033: File descriptor leak permitted unauthorized file access.
- CVE-2003-0937: File descriptor leak in daemon allowed privilege escalation.
Related CWEs
- CWE-668: Exposure of Resource to Wrong Sphere (parent)
- CWE-403: Exposure of File Descriptor to Unintended Control Sphere (child)
- CWE-619: Dangling Database Cursor (child)
References
- MITRE Corporation. "CWE-402: Transmission of Private Resources into a New Sphere." https://cwe.mitre.org/data/definitions/402.html
- CERT C. "FIO42-C. Close files when they are no longer needed"
- Linux man pages. "open(2) - O_CLOEXEC flag"