Unsachgemäße Link-Auflösung vor Dateizugriff ('Link Following')

Beschreibung

Unsachgemäße Link-Auflösung vor Dateizugriff, allgemein bekannt als "Link Following" oder "Symlink-Angriff", tritt auf, wenn Software über einen Pfad auf eine Datei zugreift, der von einem Angreifer modifiziert werden kann, der symbolische Links (Symlinks), Hardlinks oder Junctions erstellt oder manipuliert. Die Schwachstelle entsteht, wenn ein Programm einem symbolischen Link folgt, um auf eine Datei zuzugreifen, ohne zu überprüfen, ob der Link auf einen beabsichtigten und autorisierten Speicherort zeigt. Angreifer nutzen dies aus, indem sie einen Symlink erstellen, der auf eine sensible Datei zeigt (wie /etc/passwd oder Systemkonfigurationsdateien), wodurch die anfällige Anwendung versehentlich die Zieldatei liest, schreibt oder löscht. Dies ist besonders gefährlich, wenn die Anwendung mit erhöhten Privilegien läuft, da der Angreifer diese Privilegien nutzen kann, um auf Dateien zuzugreifen, die er normalerweise nicht modifizieren dürfte.

Risiko

Link-Following-Schwachstellen stellen schwerwiegende Risiken dar, insbesondere in Szenarien, in denen privilegierte Anwendungen Dateien in Verzeichnissen verarbeiten, die für nicht privilegierte Benutzer beschreibbar sind. Angreifer können Dateioperationen auf sensible Systemdateien umleiten, was unbefugtes Lesen von Anmeldedaten und Konfigurationsdaten, Modifikation sicherheitskritischer Dateien oder Löschung wichtiger Systemkomponenten ermöglicht. In Kombination mit Race Conditions (TOCTOU - Time-of-Check-Time-of-Use) werden diese Angriffe zuverlässiger und gefährlicher. Erfolgreiche Ausnutzung führt oft zu lokaler Privilegieneskalation, die Angreifern ermöglicht, Root- oder SYSTEM-Level-Zugriff zu erlangen. Container-Escape-Schwachstellen nutzen häufig Symlink-Angriffe, um aus der Container-Isolation auszubrechen und auf Host-Dateisysteme zuzugreifen.

Lösung

Überprüfen Sie immer, dass Dateipfade keine symbolischen Links traversieren, wenn Sie in Verzeichnissen operieren, die für nicht vertrauenswürdige Benutzer beschreibbar sind. Verwenden Sie Systemaufrufe, die auf Dateideskriptoren statt auf Pfaden operieren, nachdem Dateien geöffnet wurden, und verwenden Sie Flags wie O_NOFOLLOW (auf Unix) beim Öffnen von Dateien, um das Folgen von Symlinks zu verhindern. Überprüfen Sie den Dateityp vor Operationen mit lstat() statt stat(), um symbolische Links zu erkennen. Erstellen Sie temporäre Dateien mit unvorhersagbaren Namen in Verzeichnissen mit eingeschränkten Berechtigungen (z.B. mit mkstemp()). Beim Löschen von Dateien verwenden Sie unlinkat() mit Bewusstsein für das AT_REMOVEDIR-Flag. Unter Windows beschränken Sie die Berechtigungen zur Erstellung symbolischer Links und validieren Sie Junction-Ziele. Implementieren Sie ordnungsgemäßes Privilege-Dropping vor Dateioperationen in Verzeichnissen, die für weniger privilegierte Benutzer zugänglich sind.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Angreifer können privilegierte Anwendungen dazu bringen, sensible Dateien durch symbolische Links zu lesen und so Anmeldedaten, Verschlüsselungsschlüssel und Konfigurationsdaten offenzulegen.
IntegritätBereich: Integrität

Dateischreibvorgänge, die für einen Speicherort bestimmt waren, können durch bösartige Symlinks umgeleitet werden, um kritische Systemdateien, Konfigurationsdaten oder Sicherheitseinstellungen zu überschreiben.
VerfügbarkeitBereich: Verfügbarkeit

Kritische Systemdateien oder Anwendungsdaten können gelöscht oder beschädigt werden, wenn Löschoperationen angreifergesteuerten symbolischen Links folgen.
ZugriffskontrolleBereich: Zugriffskontrolle, Privilegieneskalation

Durch Manipulation, wohin privilegierte Prozesse Daten schreiben, können Angreifer lokale Privilegieneskalation zu Root- oder SYSTEM-Level-Zugriff erreichen.

Beispielcode + Lösungscode

Das folgende Beispiel zeigt ein anfälliges C-Programm, das eine Log-Datei in einem weltweit beschreibbaren temporären Verzeichnis erstellt:

Anfälliger Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>

#define LOG_FILE "/tmp/myapp.log"

void write_log(const char *message) {
    FILE *fp;

    // ANFÄLLIG: Keine Prüfung auf Symlink vor dem Öffnen
    // Angreifer kann: ln -s /etc/passwd /tmp/myapp.log
    fp = fopen(LOG_FILE, "a");
    if (fp == NULL) {
        perror("Fehler beim Öffnen der Log-Datei");
        return;
    }

    fprintf(fp, "%s\n", message);
    fclose(fp);
}

void cleanup_old_log() {
    // ANFÄLLIG: Folgt Symlinks beim Löschen
    // Könnte beliebige Dateien löschen, wenn Symlink existiert
    if (access(LOG_FILE, F_OK) == 0) {
        unlink(LOG_FILE);
    }
}

int main() {
    cleanup_old_log();
    write_log("Anwendung gestartet");
    write_log("Führe Operation durch...");
    return 0;
}

Ein Angreifer kann einen symbolischen Link erstellen, bevor das Programm läuft: ln -s /etc/passwd /tmp/myapp.log. Wenn die privilegierte Anwendung in die Log-Datei schreibt oder sie löscht, modifiziert sie tatsächlich /etc/passwd.

Korrigierter Code

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <errno.h>

#define LOG_DIR "/var/log/myapp"
#define LOG_FILE "/var/log/myapp/app.log"

int safe_open_log(const char *filepath) {
    struct stat st;
    int fd;

    // O_NOFOLLOW verwenden, um das Folgen von Symlinks abzulehnen
    fd = open(filepath, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0640);
    if (fd < 0) {
        if (errno == ELOOP) {
            // Datei ist ein symbolischer Link - ablehnen
            fprintf(stderr, "Fehler: Log-Datei ist ein symbolischer Link\n");
        }
        return -1;
    }

    // Überprüfen, dass geöffnete Datei eine reguläre Datei ist, mit fstat auf dem fd
    if (fstat(fd, &st) < 0) {
        close(fd);
        return -1;
    }

    if (!S_ISREG(st.st_mode)) {
        fprintf(stderr, "Fehler: Log-Datei ist keine reguläre Datei\n");
        close(fd);
        return -1;
    }

    // Überprüfen, dass Datei root gehört und sichere Berechtigungen hat
    if (st.st_uid != 0 || (st.st_mode & S_IWOTH)) {
        fprintf(stderr, "Fehler: Log-Datei hat unsichere Eigentümerschaft/Berechtigungen\n");
        close(fd);
        return -1;
    }

    return fd;
}

void write_log(const char *message) {
    int fd = safe_open_log(LOG_FILE);
    if (fd < 0) {
        return;
    }

    dprintf(fd, "%s\n", message);
    close(fd);
}

void safe_cleanup_old_log() {
    struct stat lst;

    // lstat verwenden, um zu prüfen, ob Pfad ein Symlink ist OHNE ihm zu folgen
    if (lstat(LOG_FILE, &lst) < 0) {
        return; // Datei existiert nicht, nichts zu bereinigen
    }

    // Löschen ablehnen, wenn es ein symbolischer Link ist
    if (S_ISLNK(lst.st_mode)) {
        fprintf(stderr, "Fehler: Symbolischen Link nicht löschen\n");
        return;
    }

    // Überprüfen, dass es eine reguläre Datei ist, vor dem Löschen
    if (!S_ISREG(lst.st_mode)) {
        fprintf(stderr, "Fehler: Log-Pfad ist keine reguläre Datei\n");
        return;
    }

    // Sicher zu löschen - aber dediziertes Log-Verzeichnis mit richtigen Berechtigungen verwenden
    unlink(LOG_FILE);
}

int main() {
    // Sicherstellen, dass Log-Verzeichnis mit richtigen Berechtigungen existiert
    mkdir(LOG_DIR, 0750);

    safe_cleanup_old_log();
    write_log("Anwendung gestartet");
    write_log("Führe Operation durch...");
    return 0;
}

Der korrigierte Code verwendet das O_NOFOLLOW-Flag, um das Öffnen symbolischer Links abzulehnen, lstat(), um den Dateityp zu prüfen, ohne Links zu folgen, fstat() auf dem Dateideskriptor, um die tatsächlich geöffnete Datei zu verifizieren, und speichert Logs in einem geschützten Verzeichnis statt im weltweit beschreibbaren /tmp. Zusätzliche Prüfungen verifizieren Dateieigentümerschaft und Berechtigungen.


Ausgenutzt in der Praxis

Docker/Kubernetes Container-Escape (Cloud-Infrastruktur, 2024)

CVE-2024-21626 in der runC-Container-Runtime ermöglichte Container-Escape durch ein Dateideskriptor-Leck, das Angreifer mit Symlinks ausnutzten, um auf das Host-Dateisystem zuzugreifen. Zusätzlich demonstrierte CVE-2024-23651 eine Symlink-Race-Condition während Dockers Build-Prozess, bei der Angreifer sensible Host-Verzeichnisse während der Cache-Invalidierung in Container mounten könnten. Diese Schwachstellen betrafen Millionen von containerisierten Deployments und ermöglichten Angreifern, aus der Container-Isolation auszubrechen und mit erhöhten Privilegien auf Host-Systeme zuzugreifen.

Nimbuspwn Linux-Privilegieneskalation (Linux-Systeme, 2022)

CVE-2022-29799 und CVE-2022-29800 (zusammen als "Nimbuspwn" bekannt) nutzten Symlink-Race-Conditions in der systemd networkd-dispatcher-Komponente aus, um Root-Privilegieneskalation auf Linux-Systemen zu erreichen. Angreifer könnten Directory Traversal mit Symlink-Manipulation verketten, um beliebigen Code als Root auszuführen. Die Schwachstelle betraf zahlreiche Linux-Distributionen, die systemd verwenden, und demonstrierte, wie Symlink-Angriffe eine bedeutende Bedrohung für moderne Betriebssysteme bleiben.

Windows Defender Arbitrary File Deletion (Windows-Systeme, 2019)

CVE-2019-1161 ermöglichte Angreifern, Windows Defender durch symbolische Links auszunutzen, um beliebige Dateien mit SYSTEM-Privilegien zu löschen. Durch Erstellen eines Junction-Points kombiniert mit einem NTFS-symbolischen Link könnte ein nicht privilegierter Benutzer Windows Defender dazu bringen, während Scan-Operationen jede Datei auf dem System zu löschen. Hunderte Millionen Windows-Maschinen mit Windows 7 und höher waren anfällig für diesen Privilegieneskalationsangriff.


Tools zum Testen/Ausnutzen

  • Symlinkd3 — Google Project Zeros symbolische Link-Test-Tools für Windows, einschließlich Utilities zum Erstellen von Mount-Points, Objektverzeichnissen und zum Testen auf Symlink-Schwachstellen.

  • CreateSymlink — Utility zum Erstellen von NTFS-symbolischen Links und Junctions unter Windows zum Testen von Privilegieneskalations-Schwachstellen.

  • LinPEAS — Linux-Privilegieneskalationsskript, das auf Symlink-bezogene Fehlkonfigurationen in temporären Verzeichnissen und beschreibbaren Pfaden prüft, die von privilegierten Prozessen verwendet werden.


CVE-Beispiele

  • CVE-2024-21626 — runC-Container-Runtime-Dateideskriptor-Leck ermöglicht Container-Escape durch Symlink-Ausnutzung.

  • CVE-2022-29799 — Nimbuspwn Directory Traversal in networkd-dispatcher ermöglicht Symlink-basierte Privilegieneskalation.

  • CVE-2019-1161 — Windows Defender Arbitrary File Deletion durch Manipulation symbolischer Links mit SYSTEM-Privilegien.

  • CVE-2015-3629 — Docker libcontainer Symlink-Angriff ermöglicht Container-Escape und beliebige Dateischreibvorgänge auf dem Host.


Referenzen

  1. MITRE. "CWE-59: Improper Link Resolution Before File Access ('Link Following')." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/59.html

  2. CyberArk. "Follow the Link: Exploiting Symbolic Links with Ease." Threat Research Blog. https://www.cyberark.com/resources/threat-research-blog/follow-the-link-exploiting-symbolic-links-with-ease

  3. NixHacker. "Understanding and Exploiting Symbolic links in Windows." https://nixhacker.com/understanding-and-exploiting-symbolic-link-in-windows/

  4. Wikipedia. "Symlink race." https://en.wikipedia.org/wiki/Symlink_race