Konkurrierende Ausführung mit gemeinsamer Ressource bei unsachgemäßer Synchronisation ('Race Condition')

Beschreibung

Race-Condition-Schwachstellen treten auf, wenn mehrere Threads, Prozesse oder Systeme gleichzeitig auf gemeinsame Ressourcen zugreifen und diese modifizieren, ohne ordnungsgemäße Synchronisation, wodurch das Ergebnis vom Timing der Operationen abhängt. Eine besonders gefährliche Variante ist Time-of-Check Time-of-Use (TOCTOU), bei der eine Sicherheitsprüfung auf einer Ressource durchgeführt wird, aber die Ressource von einem Angreifer zwischen der Prüfung und der anschließenden Verwendung modifiziert werden kann. Race Conditions können Speicher, Dateien, Netzwerk-Sockets oder jede gemeinsame Ressource betreffen und führen zu Sicherheits-Bypasses, Privilegieneskalation oder Datenkorruption.

Risiko

Race Conditions sind subtile Schwachstellen, die schwer zu erkennen und konsistent auszunutzen sind, aber sie können schwerwiegende Folgen haben. Bei Dateioperationen ermöglichen TOCTOU-Schwachstellen Angreifern, Dateien (oft über Symlinks) zwischen Sicherheitsprüfungen und tatsächlicher Verwendung auszutauschen, was Privilegieneskalation oder beliebigen Dateizugriff ermöglicht. Bei Speicheroperationen können Race Conditions Datenstrukturen korrumpieren, Authentifizierung umgehen oder Double-Free-Schwachstellen ermöglichen. Das Ausnutzungsfenster mag schmal sein, aber Angreifer können es durch Systembelastung oder präzises Timing erweitern. Dirty COW (CVE-2016-5195) demonstrierte, dass Kernel-Race-Conditions Root-Privilegieneskalation ermöglichen können.

Lösung

Verwenden Sie wenn möglich atomare Operationen, um Race-Fenster zu eliminieren. Implementieren Sie ordnungsgemäße Synchronisationsprimitive (Mutexe, Semaphoren) für gemeinsame Ressourcen. Für Dateioperationen verwenden Sie Dateideskriptoren statt Pfade nach dem Öffnen, vermeiden Sie Check-Then-Act-Muster und verwenden Sie sichere temporäre Dateierstellung (mkstemp). Minimieren Sie die Nutzung gemeinsamer Ressourcen und die Zeit, in der Locks gehalten werden. Verwenden Sie thread-sichere Datenstrukturen und Bibliotheken. Wenden Sie das Prinzip der minimalen Privilegien an, um die Auswirkungen von Race Conditions zu begrenzen. Testen Sie mit Stresstests und absichtlicher Verzögerungsinjektion, um Race-Fenster zu erkennen.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleUmfang: Privilegieneskalation

TOCTOU-Datei-Races ermöglichen Angreifern, privilegierte Dateien auszutauschen, Zugriffspr zu umgehen oder erhöhten Zugang zu erlangen.
IntegritätUmfang: Datenkorruption

Nicht synchronisierte gleichzeitige Modifikationen korrumpieren gemeinsame Datenstrukturen und Zustand.
VerfügbarkeitUmfang: Systeminstabilität

Race Conditions verursachen Deadlocks, Abstürze und unvorhersehbares Verhalten.

Beispielcode + Korrigierter Code

Anfälliger Code

#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>

// ANFÄLLIG: TOCTOU Race Condition
int process_file_unsafe(const char *filename) {
    struct stat st;

    // TIME OF CHECK: Verifizieren dass Datei sicher ist
    if (stat(filename, &st) != 0) {
        return -1;
    }

    // Angreifer kann Datei hier durch Symlink ersetzen!
    // Race-Fenster zwischen Prüfung und Verwendung

    if (st.st_uid != getuid()) {
        return -1;  // Nicht unsere Datei
    }

    // TIME OF USE: Öffnen und lesen
    // Könnte jetzt eine andere Datei sein (Symlink des Angreifers)!
    FILE *f = fopen(filename, "r");
    process_contents(f);
    fclose(f);

    return 0;
}

// ANFÄLLIG: Check-Then-Act Race
int withdraw_money(account_t *acc, int amount) {
    // Ein anderer Thread könnte zwischen Prüfung und Update abheben
    if (acc->balance >= amount) {
        // Race-Fenster hier!
        acc->balance -= amount;  // Doppelte Abhebung möglich
        return amount;
    }
    return 0;
}
# ANFÄLLIG: TOCTOU Dateiprüfung
import os

def safe_write_file(filepath, data):
    # Prüfen ob Datei existiert und beschreibbar ist
    if os.path.exists(filepath):
        if not os.access(filepath, os.W_OK):
            return False

    # RACE-FENSTER: Angreifer ersetzt durch Symlink zu /etc/passwd

    # In Datei schreiben - könnte jetzt eine andere Datei sein!
    with open(filepath, 'w') as f:
        f.write(data)
    return True

Korrigierter Code

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

// SICHER: Dateideskriptor verwenden, nicht Pfad
int process_file_safe(const char *filename) {
    // Zuerst öffnen, dann prüfen
    int fd = open(filename, O_RDONLY | O_NOFOLLOW);  // Symlinks nicht folgen
    if (fd < 0) {
        return -1;
    }

    // Mit Dateideskriptor prüfen - kein TOCTOU
    struct stat st;
    if (fstat(fd, &st) != 0) {
        close(fd);
        return -1;
    }

    if (st.st_uid != getuid()) {
        close(fd);
        return -1;
    }

    // Den bereits geöffneten Dateideskriptor verwenden
    FILE *f = fdopen(fd, "r");
    process_contents(f);
    fclose(f);  // Schließt auch fd

    return 0;
}

// SICHER: Atomare Operation mit Mutex
#include <pthread.h>

pthread_mutex_t account_mutex = PTHREAD_MUTEX_INITIALIZER;

int withdraw_money_safe(account_t *acc, int amount) {
    pthread_mutex_lock(&account_mutex);

    int result = 0;
    if (acc->balance >= amount) {
        acc->balance -= amount;
        result = amount;
    }

    pthread_mutex_unlock(&account_mutex);
    return result;
}

// SICHER: Compare-and-Swap verwenden (lock-frei)
#include <stdatomic.h>

int withdraw_money_atomic(atomic_int *balance, int amount) {
    int expected = atomic_load(balance);

    do {
        if (expected < amount) {
            return 0;  // Unzureichendes Guthaben
        }
    } while (!atomic_compare_exchange_weak(balance, &expected, expected - amount));

    return amount;
}
import os
import tempfile
import fcntl

# SICHER: Temporäre Datei sicher erstellen
def safe_write_file(directory, data):
    # mkstemp erstellt Datei atomar mit sicheren Berechtigungen
    fd, filepath = tempfile.mkstemp(dir=directory)
    try:
        os.write(fd, data.encode())
    finally:
        os.close(fd)
    return filepath

# SICHER: Dateisperre verwenden
def atomic_update_file(filepath, data):
    with open(filepath, 'r+') as f:
        # Exklusive Sperre verhindert gleichzeitigen Zugriff
        fcntl.flock(f.fileno(), fcntl.LOCK_EX)
        try:
            f.truncate(0)
            f.write(data)
        finally:
            fcntl.flock(f.fileno(), fcntl.LOCK_UN)

# SICHER: Mit O_NOFOLLOW öffnen und danach prüfen
import stat

def safe_open_file(filepath):
    fd = os.open(filepath, os.O_RDONLY | os.O_NOFOLLOW)
    try:
        # Dateieigenschaften mit fd prüfen, nicht Pfad
        st = os.fstat(fd)
        if stat.S_ISLNK(st.st_mode):
            raise SecurityError("Symlink erkannt")
        return os.fdopen(fd, 'r')
    except:
        os.close(fd)
        raise

Ausgenutzt in der Praxis

Dirty COW (Linux Kernel, 2016)

CVE-2016-5195 war eine Race Condition im Copy-on-Write-(COW)-Mechanismus des Linux-Kernels, die unprivilegierten lokalen Benutzern ermöglichte, Schreibzugriff auf schreibgeschützte Speicherzuordnungen zu erlangen, was Root-Privilegieneskalation ermöglichte. Sie betraf Linux-Systeme 9 Jahre lang vor der Entdeckung.

CVE-2025-68146 in Pythons filelock-Bibliothek ermöglicht Angreifern, beliebige Dateien über Symlink-Angriffe im Race-Fenster zwischen Existenzprüfung und Dateiöffnung mit O_TRUNC zu korrumpieren oder zu kürzen.

Docker runc Container-Escape (Docker, 2019)

CVE-2019-5736 war eine Race Condition in runc, die Container-Escape durch Überschreiben der Host-runc-Binary ermöglichte und die schwerwiegenden Auswirkungen von Race Conditions in Containerisierung demonstrierte.


Tools zum Testen/Ausnutzen

  • ThreadSanitizer — erkennt Data Races zur Laufzeit.

  • Helgrind — Valgrind-Tool zur Erkennung von Synchronisationsfehlern.

  • race-the-web — testet Web-Endpunkte auf Race Conditions.


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization." https://cwe.mitre.org/data/definitions/362.html

  2. CERT. "FIO01-C. Be careful using functions that use file names for identification." https://wiki.sei.cmu.edu/confluence/display/c/FIO01-C