Symbolischer Name bildet nicht auf korrektes Objekt ab
Beschreibung
Symbolischer Name bildet nicht auf korrektes Objekt ab ist eine Schwachstelle, die auftritt, wenn eine konstante symbolische Referenz auf ein Objekt verwendet wird, obwohl die Referenz im Laufe der Zeit auf ein anderes Objekt auflösen kann. Diese Schwäche entsteht, wenn Programme sich auf symbolische Namen (wie Dateinamen, Symlinks oder Objektreferenzen) verlassen, die zwischen dem Zeitpunkt der Auflösung und dem Zeitpunkt der Verwendung manipuliert oder geändert werden können. Die tatsächlich zugegriffene Ressource kann sich von der beabsichtigten Ressource unterscheiden, was zu unautorisiertem Zugriff oder unbeabsichtigten Operationen führt.
Risiko
Wenn symbolische Namen nicht auf die korrekten Objekte abbilden, können Angreifer die Lücke zwischen Namensauflösung und Objektverwendung ausnutzen. Dies ermöglicht unautorisierten Ressourcenzugriff durch Privilegieneskalationsangriffe. Daten können von unbeabsichtigten Orten gelesen oder dorthin geschrieben werden, was Vertraulichkeits- und Integritätsverletzungen verursacht. Angreifer können Operationen kapern, indem sie symbolische Namen auf bösartige Ressourcen umleiten. Protokolldateien oder Audit-Trails können gelöscht oder modifiziert werden, wenn Dateireferenzen manipuliert werden. Diese Schwachstelle ist eng verwandt mit TOCTOU (Time-of-Check Time-of-Use) Race Conditions und Symlink-Angriffen.
Lösung
Vermeiden Sie es, sich auf symbolische Namen zu verlassen, die sich im Laufe der Zeit ändern können. Verwenden Sie direkte Objektreferenzen (wie Dateideskriptoren) anstatt symbolische Namen wiederholt aufzulösen. Verifizieren Sie, dass aufgelöste Referenzen unmittelbar vor der Verwendung auf erwartete Objekte zeigen. Implementieren Sie ordnungsgemäße Sperrmechanismen beim Arbeiten mit gemeinsam genutzten Ressourcen. Verwenden Sie für Dateioperationen nach dem initialen Öffnen dateideskriptorbasierte Operationen anstelle von pfadbasierten Operationen. Validieren Sie, dass symbolische Links auf erwartete Ziele zeigen. Verwenden Sie wo möglich atomare Operationen, um Race-Fenster zu eliminieren.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Zugriffskontrolle | Umfang: Zugriffskontrolle Privilegien erlangen oder Identität annehmen - Unautorisierter Ressourcenzugriff wird möglich. |
| Integrität | Umfang: Integrität, Vertraulichkeit Datenmodifikation/-zugriff - Race Conditions ermöglichen Lese-/Schreibzugriff auf eingeschränkte Ressourcen. |
| Integrität | Umfang: Integrität Datenkorruption - Ressourcen können von böswilligen Akteuren unerwünscht verändert werden. |
| Nicht-Abstreitbarkeit | Umfang: Nicht-Abstreitbarkeit Versteckte Aktivitäten - Aktivitätsprotokollierung kann fehlschlagen, wenn Ressourcen unsachgemäß zugegriffen werden. |
| Nicht-Abstreitbarkeit | Umfang: Nicht-Abstreitbarkeit, Integrität Dateilöschung - Unautorisierte Löschung von Dateien wie Protokollen kann auftreten. |
Beispielcode
Anfälliger Code
// Anfällig: Symbolischer Name zweimal mit Race-Fenster aufgelöst
void vulnerable_process_config(const char *config_path) {
struct stat st;
// Erste Auflösung: Dateibesitz prüfen
if (stat(config_path, &st) != 0) {
return;
}
if (st.st_uid != 0) {
printf("Konfiguration muss root gehören\n");
return;
}
// Race-Fenster: config_path könnte sich ändern (Symlink-Angriff)
// Zweite Auflösung: Datei öffnen
FILE *f = fopen(config_path, "r"); // Könnte andere Datei öffnen!
if (f) {
process_config(f);
fclose(f);
}
}
// Anfällig: Klassenladen nach Name
// Java-Beispiel
public class VulnerableClassLoader {
public Object loadPlugin(String className) throws Exception {
// Anfällig: Klassenname könnte auf andere Klasse abbilden
// wenn Classpath zwischen Aufrufen manipuliert wird
Class<?> cls = Class.forName(className);
// Angreifer könnte die Klassendatei ersetzen
return cls.newInstance();
}
}
# Anfällig: Dateipfad mehrfach aufgelöst
import os
def vulnerable_read_file(filepath):
# Erste Auflösung: Sicherheitsprüfung
if not os.access(filepath, os.R_OK):
raise PermissionError("Kann Datei nicht lesen")
# Race-Fenster: filepath könnte zu Symlink geändert werden
# Zweite Auflösung: Datei lesen
with open(filepath, 'r') as f: # Könnte andere Datei lesen!
return f.read()
# Anfällig: Symbolischer Link wird gefolgt
def vulnerable_safe_delete(filepath, safe_dir):
# Prüfen ob Datei im sicheren Verzeichnis ist
if not filepath.startswith(safe_dir):
raise SecurityError("Datei nicht im sicheren Verzeichnis")
# Race-Fenster: filepath könnte zu Symlink auf /etc/passwd werden
os.remove(filepath) # Könnte falsche Datei löschen!
Korrigierter Code
// Korrigiert: Dateideskriptor verwenden um Neu-Auflösung zu vermeiden
void secure_process_config(const char *config_path) {
struct stat st;
int fd;
// Datei zuerst öffnen um Handle zu erhalten
fd = open(config_path, O_RDONLY | O_NOFOLLOW);
if (fd < 0) {
return;
}
// Korrigiert: fstat auf dem Dateideskriptor verwenden, nicht auf Pfad
if (fstat(fd, &st) != 0) {
close(fd);
return;
}
if (st.st_uid != 0) {
printf("Konfiguration muss root gehören\n");
close(fd);
return;
}
// Korrigiert: Mit dem bereits geöffneten Dateideskriptor arbeiten
FILE *f = fdopen(fd, "r");
if (f) {
process_config(f);
fclose(f); // Schließt auch fd
} else {
close(fd);
}
}
// Korrigiert: Verifizieren dass aufgelöster Pfad dem erwarteten entspricht
void secure_process_file(const char *user_path, const char *safe_dir) {
char resolved[PATH_MAX];
int fd;
// Pfad zuerst auflösen
if (realpath(user_path, resolved) == NULL) {
return;
}
// Verifizieren dass er im sicheren Verzeichnis ist
if (strncmp(resolved, safe_dir, strlen(safe_dir)) != 0) {
return; // Nicht im sicheren Verzeichnis
}
// Korrigiert: Mit O_NOFOLLOW öffnen um Symlink-Folgen zu verhindern
fd = open(resolved, O_RDONLY | O_NOFOLLOW);
if (fd < 0) {
return;
}
// Mit fd arbeiten
process_fd(fd);
close(fd);
}
# Korrigiert: Dateideskriptor verwenden um Neu-Auflösung zu verhindern
import os
import stat
def secure_read_file(filepath):
# Datei öffnen um Deskriptor zu erhalten
fd = os.open(filepath, os.O_RDONLY | os.O_NOFOLLOW)
try:
# Korrigiert: Berechtigungen auf geöffnetem Deskriptor prüfen
st = os.fstat(fd)
if not (st.st_mode & stat.S_IRUSR):
raise PermissionError("Kann Datei nicht lesen")
# Korrigiert: Vom Deskriptor lesen
with os.fdopen(fd, 'r') as f:
return f.read()
except:
os.close(fd)
raise
# Korrigiert: Sichere Dateilöschung mit Pfadvalidierung
def secure_safe_delete(filepath, safe_dir):
# Zu absolutem Pfad auflösen
real_path = os.path.realpath(filepath)
real_safe = os.path.realpath(safe_dir)
# Verifizieren dass Datei im sicheren Verzeichnis ist
if not real_path.startswith(real_safe + os.sep):
raise SecurityError("Datei nicht im sicheren Verzeichnis")
# Prüfen dass es kein Symlink ist
if os.path.islink(filepath):
raise SecurityError("Kann Symlinks nicht löschen")
# Korrigiert: Mit O_NOFOLLOW öffnen und über fd löschen
fd = os.open(filepath, os.O_RDONLY | os.O_NOFOLLOW)
try:
# Gleiche Datei nach Öffnen verifizieren
st = os.fstat(fd)
lst = os.lstat(filepath)
if st.st_ino != lst.st_ino or st.st_dev != lst.st_dev:
raise SecurityError("Datei hat sich während Operation geändert")
os.remove(filepath)
finally:
os.close(fd)
// Korrigiert: Klassenidentität vor Verwendung verifizieren
public class SecureClassLoader {
private final Map<String, Class<?>> trustedClasses = new HashMap<>();
public void registerTrustedClass(Class<?> cls) {
// Vertrauenswürdige Klassen mit erwarteter Identität vorregistrieren
trustedClasses.put(cls.getName(), cls);
}
public Object loadPlugin(String className) throws Exception {
Class<?> cls = Class.forName(className);
// Korrigiert: Klassenidentität mit registrierter Version verifizieren
Class<?> trusted = trustedClasses.get(className);
if (trusted == null || trusted != cls) {
throw new SecurityException("Nicht vertrauenswürdige oder modifizierte Klasse: " + className);
}
return cls.getDeclaredConstructor().newInstance();
}
}
CVE-Beispiele
Keine spezifischen CVEs sind für diese CWE gelistet. Das Schwachstellenmuster erscheint in:
- Dateioperationen, die Pfade statt Deskriptoren verwenden
- Klassenladesystemen
- Ressourcenauflösung mit symbolischen Referenzen
Referenzen
- MITRE Corporation. "CWE-386: Symbolic Name not Mapping to Correct Object." https://cwe.mitre.org/data/definitions/386.html