Übertragung privater Ressourcen in eine neue Sphäre ('Ressourcenleck')
Beschreibung
Übertragung privater Ressourcen in eine neue Sphäre tritt auf, wenn ein Produkt Ressourcen für nicht vertrauenswürdige Parteien verfügbar macht, obwohl diese Ressourcen nur vom Produkt selbst zugegriffen werden sollten. Diese Schwäche beinhaltet die unbeabsichtigte Offenlegung interner Ressourcen wie Dateideskriptoren, Speicherreferenzen, Datenbankverbindungen oder anderer System-Handles an externe oder weniger privilegierte Prozesse. Wenn private Ressourcen über Vertrauensgrenzen hinweg lecken, können Angreifer unautorisierten Zugriff auf sensible Daten oder Fähigkeiten erlangen.
Risiko
Ressourcenlecks haben schwerwiegende Auswirkungen. Unautorisierter Zugriff auf sensible Dateien. Privilegieneskalation durch geerbte Deskriptoren. Datenoffenlegung an nicht vertrauenswürdige Prozesse. Umgehung von Zugriffskontrollen. Session-Hijacking. Diebstahl von Datenbankverbindungen. Speicheroffenlegung. Hohe Wahrscheinlichkeit wenn Prozesse Kinder ohne ordnungsgemäße Ressourcenbereinigung erzeugen.
Lösung
Schließen oder invalidieren Sie alle sensiblen Ressourcen bevor Kindprozesse während der Implementierungsphase erzeugt werden. Verwenden Sie Close-on-Exec-Flags für Dateideskriptoren. Implementieren Sie ordnungsgemäßes Ressourcen-Lebenszyklusmanagement. Bereinigen Sie die Umgebung vor der Prozesserstellung während der Architektur- und Design-Phase. Auditieren Sie Ressourcenbehandlung über Vertrauensgrenzen hinweg.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Umfang: Vertraulichkeit Anwendungsdaten lesen - Angreifer können unautorisierten Zugriff auf sensible Informationen durch offengelegte Ressourcen erlangen. |
| Integrität | Umfang: Integrität Modifikation von Daten durch geleckte Schreib-Handles. |
Beispielcode
Anfälliger Code
// Anfällig: C-Code leckt Dateideskriptoren an Kindprozesse
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
// ANFÄLLIG: Dateideskriptor wird von Kind geerbt
void vulnerable_spawn_child(const char* program) {
// Sensible Datei öffnen
int secret_fd = open("/etc/shadow", O_RDONLY);
if (secret_fd < 0) {
perror("open");
return;
}
// ANFÄLLIG: Fork ohne Schließen des sensiblen fd
pid_t pid = fork();
if (pid == 0) {
// Kindprozess - HAT NOCH ZUGRIFF auf secret_fd!
// Auch wenn Kind weniger privilegiert sein kann
// ANFÄLLIG: Nicht vertrauenswürdiges Programm mit gelecktem fd ausführen
execl(program, program, NULL);
exit(1);
}
// Eltern fährt fort...
// Selbst wenn wir hier schließen, hat Kind bereits den fd
close(secret_fd);
waitpid(pid, NULL, 0);
}
// ANFÄLLIG: Datenbankverbindung leckt
void vulnerable_db_handler() {
// Privilegierte Datenbankverbindung herstellen
DB_CONN* conn = db_connect("admin", "password");
// ANFÄLLIG: Verbindungs-Handle für Plugins zugänglich
load_untrusted_plugin(conn); // Plugin kann Admin-Verbindung nutzen
db_disconnect(conn);
}
// ANFÄLLIG: Socket-Deskriptor-Leck
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) {
// Kind behandelt Client
// ANFÄLLIG: listen_fd noch offen im Kind
// Kind könnte neue Verbindungen akzeptieren!
handle_client(client_fd);
exit(0);
}
// Eltern sollte client_fd schließen aber listen_fd leckt zu Kind
close(client_fd);
}
}
# Anfällig: Python-Ressourcenlecks
import os
import subprocess
# ANFÄLLIG: Dateideskriptor-Leck zu Subprocess
def vulnerable_run_command(command):
# Sensible Datei öffnen
secret_file = open('/etc/shadow', 'r')
# ANFÄLLIG: Subprocess erbt alle Dateideskriptoren
result = subprocess.run(command, shell=True, capture_output=True)
# Dateideskriptor war für Subprocess verfügbar
secret_file.close()
return result
# ANFÄLLIG: Datenbankverbindung leckt
class VulnerableApp:
def __init__(self):
# Privilegierte Datenbankverbindung
self.db_conn = connect_database(admin=True)
def run_plugin(self, plugin_code):
# ANFÄLLIG: Plugin hat Zugriff auf self.db_conn
exec(plugin_code) # Kann auf self.db_conn zugreifen
# ANFÄLLIG: Temporäres Datei-Leck
def vulnerable_temp_file():
import tempfile
# Temp-Datei mit sensiblen Daten erstellen
fd, path = tempfile.mkstemp()
os.write(fd, b"geheime daten")
# ANFÄLLIG: fd noch offen beim Aufrufen externen Prozesses
os.system("some_command") # Erbt den fd
os.close(fd)
os.unlink(path)
Korrigierter Code
// Korrigiert: Ordnungsgemäße Ressourcenverwaltung vor dem Erzeugen von Kindern
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/wait.h>
// KORRIGIERT: Close-on-Exec-Flag verwenden
void safe_spawn_child(const char* program) {
// KORRIGIERT: Mit O_CLOEXEC-Flag öffnen
int secret_fd = open("/etc/shadow", O_RDONLY | O_CLOEXEC);
if (secret_fd < 0) {
perror("open");
return;
}
// Dateideskriptor wird automatisch bei exec geschlossen
pid_t pid = fork();
if (pid == 0) {
// Kindprozess
// secret_fd wird geschlossen wenn execl aufgerufen wird
execl(program, program, NULL);
exit(1);
}
close(secret_fd);
waitpid(pid, NULL, 0);
}
// KORRIGIERT: Expliziter Cleanup vor Fork
void safe_spawn_with_cleanup(const char* program) {
int secret_fd = open("/etc/shadow", O_RDONLY);
if (secret_fd < 0) {
return;
}
// ... Datei verwenden ...
// KORRIGIERT: Sensible Ressourcen vor Fork schließen
close(secret_fd);
secret_fd = -1;
pid_t pid = fork();
if (pid == 0) {
// Kind hat keinen Zugriff auf secret_fd
execl(program, program, NULL);
exit(1);
}
waitpid(pid, NULL, 0);
}
// KORRIGIERT: Close-on-Exec nach Open setzen
void safe_set_cloexec(int fd) {
int flags = fcntl(fd, F_GETFD);
if (flags >= 0) {
fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
}
}
// KORRIGIERT: Server mit ordnungsgemäßer fd-Verwaltung
void safe_server() {
int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
// KORRIGIERT: Close-on-Exec auf Listen-Socket setzen
safe_set_cloexec(listen_fd);
bind(listen_fd, ...);
listen(listen_fd, 10);
while (1) {
int client_fd = accept(listen_fd, NULL, NULL);
// KORRIGIERT: Close-on-Exec auch auf Client-fd setzen
safe_set_cloexec(client_fd);
pid_t pid = fork();
if (pid == 0) {
// KORRIGIERT: listen_fd im Kind schließen
close(listen_fd);
handle_client(client_fd);
close(client_fd);
exit(0);
}
// Eltern schließt client_fd
close(client_fd);
}
}
// KORRIGIERT: Alle unnötigen fds vor exec schließen
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);
}
}
}
# Korrigiert: Python mit ordnungsgemäßer Ressourcenisolierung
import os
import subprocess
# KORRIGIERT: Dateideskriptoren vor Subprocess schließen
def safe_run_command(command):
secret_file = open('/etc/shadow', 'r')
secret_fd = secret_file.fileno()
# ... Datei verwenden ...
# KORRIGIERT: Vor Subprocess-Ausführung schließen
secret_file.close()
# KORRIGIERT: close_fds=True verwenden (Standard in Python 3)
result = subprocess.run(
command,
shell=True,
capture_output=True,
close_fds=True # KORRIGIERT: Alle fds außer stdin/stdout/stderr schließen
)
return result
# KORRIGIERT: Isolierte Datenbankverbindung
class SafeApp:
def __init__(self):
self._db_conn = None
def _get_db_conn(self):
"""KORRIGIERT: Lazy Initialization, nicht exponiert."""
if self._db_conn is None:
self._db_conn = connect_database(admin=True)
return self._db_conn
def run_plugin(self, plugin_code):
# KORRIGIERT: Begrenzte Verbindung für Plugin erstellen
plugin_conn = connect_database(admin=False, read_only=True)
# KORRIGIERT: Sandbox-Ausführung ohne Zugriff auf Admin-Verbindung
sandbox = {'db': plugin_conn}
exec(plugin_code, sandbox)
plugin_conn.close()
# KORRIGIERT: Sichere temporäre Dateibehandlung
def safe_temp_file():
import tempfile
# Temp-Datei erstellen
fd, path = tempfile.mkstemp()
try:
os.write(fd, b"geheime daten")
# KORRIGIERT: Vor externem Befehl schließen
os.close(fd)
fd = -1
# Jetzt sicher externen Prozess auszuführen
subprocess.run(["some_command"], close_fds=True)
finally:
if fd >= 0:
os.close(fd)
os.unlink(path)
# KORRIGIERT: Kontextmanager für automatischen 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
# Verwendung
with SecureResource('/etc/shadow') as res:
# Ressource verwenden
pass
# Automatisch geschlossen, sicher Prozesse zu erzeugen
CVE-Beispiele
- CVE-2003-0740: Server leckte privilegierten Dateideskriptor, der es Angreifern ermöglichte, die Verbindung zu kapern.
- CVE-2004-1033: Dateideskriptor-Leck erlaubte unautorisierten Dateizugriff.
- CVE-2003-0937: Dateideskriptor-Leck in Daemon erlaubte Privilegieneskalation.
Verwandte CWEs
- CWE-668: Exposure of Resource to Wrong Sphere (Eltern)
- CWE-403: Exposure of File Descriptor to Unintended Control Sphere (Kind)
- CWE-619: Dangling Database Cursor (Kind)
Referenzen
- 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"