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

AuswirkungDetails
ZugriffskontrolleUmfang: Zugriffskontrolle

Privilegien erlangen oder Identität annehmen - Unautorisierter Ressourcenzugriff wird möglich.
IntegritätUmfang: Integrität, Vertraulichkeit

Datenmodifikation/-zugriff - Race Conditions ermöglichen Lese-/Schreibzugriff auf eingeschränkte Ressourcen.
IntegritätUmfang: Integrität

Datenkorruption - Ressourcen können von böswilligen Akteuren unerwünscht verändert werden.
Nicht-AbstreitbarkeitUmfang: Nicht-Abstreitbarkeit

Versteckte Aktivitäten - Aktivitätsprotokollierung kann fehlschlagen, wenn Ressourcen unsachgemäß zugegriffen werden.
Nicht-AbstreitbarkeitUmfang: 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

  1. MITRE Corporation. "CWE-386: Symbolic Name not Mapping to Correct Object." https://cwe.mitre.org/data/definitions/386.html