Berechtigungs-Race-Condition beim Ressourcenkopieren

Beschreibung

Berechtigungs-Race-Condition beim Ressourcenkopieren ist eine zusammengesetzte Schwachstelle, die auftritt, wenn Software eine Ressource kopiert oder klont, aber die Berechtigungen oder Zugriffskontrollen der Ressource nicht setzt, bis der Kopiervorgang abgeschlossen ist. Während des Zeitfensters zwischen Erstellung und Berechtigungszuweisung existiert die Ressource mit unsicheren Standardberechtigungen, was anderen Prozessen oder Benutzern in unterschiedlichen Sicherheitskontexten den Zugriff oder die Modifikation der Ressource ermöglicht. Dies ist besonders gefährlich beim Extrahieren von Dateien aus Archiven oder beim Kopieren tief verschachtelter Verzeichnisstrukturen.

Risiko

Diese Schwachstelle erzeugt eine Time-of-Check-to-Time-of-Use (TOCTOU) Race Condition, bei der Angreifer während der Berechtigungslücke auf sensible Ressourcen zugreifen können. Aus Archiven extrahierte Dateien können weltweit lesbar oder beschreibbar sein, bevor korrekte Berechtigungen angewendet werden, was unbefugten Benutzern das Lesen vertraulicher Daten oder das Einschleusen bösartiger Inhalte ermöglicht. Datenbankobjekte, die vor der Berechtigungszuweisung erstellt werden, können von unbefugten Prozessen zugegriffen oder modifiziert werden. Das Risiko ist besonders schwerwiegend in Mehrbenutzersystemen, in denen andere Prozesse aktiv nach ausnutzbaren Race Conditions suchen. Angreifer können automatisierte Tools verwenden, um wiederholt während des verwundbaren Fensters Zugriff zu versuchen.

Lösung

Setzen Sie korrekte Berechtigungen sofort bei der Ressourcenerstellung, bevor irgendwelche Inhalte geschrieben werden. Verwenden Sie atomare Operationen wo verfügbar - erstellen Sie Ressourcen von Anfang an mit restriktiven Berechtigungen, anstatt sie zu erstellen und dann die Berechtigungen zu ändern. Wenden Sie umask-Einstellungen an, die sichere Standardwerte erzeugen. Für die Dateiextraktion verwenden Sie sichere temporäre Verzeichnisse mit eingeschränktem Zugriff. Beim Kopieren von Verzeichnissen setzen Sie zuerst restriktive Berechtigungen auf übergeordnete Verzeichnisse. Verwenden Sie plattformspezifische Mechanismen wie O_CREAT mit korrekten Modus-Bits. Erwägen Sie die Verwendung von Betriebssystemfunktionen für atomare Berechtigungszuweisung während der Ressourcenerstellung.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Anwendungsdaten lesen - Ressourcen können von unbefugten Parteien während des Berechtigungslückenfensters gelesen werden.
IntegritätBereich: Integrität

Anwendungsdaten ändern - Angreifer können Ressourcen modifizieren, bevor korrekte Berechtigungen angewendet werden.
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Sicherheitskontrollen sind während des verwundbaren Fensters unwirksam.

Beispielcode

Anfälliger Code

// Anfällig: Archivextraktion mit Berechtigungs-Race-Condition
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

void vulnerable_extract_file(const char* archive_path,
                             const char* dest_path,
                             mode_t intended_mode) {
    // Anfällig: Datei mit Standardberechtigungen erstellt (oft 0666 & ~umask)
    int fd = open(dest_path, O_CREAT | O_WRONLY | O_TRUNC, 0666);
    // Datei existiert jetzt mit unsicheren Berechtigungen!

    // ... Dateiinhalte aus Archiv kopieren ...
    copy_from_archive(archive_path, fd);

    close(fd);

    // Anfällig: Berechtigung NACHDEM Datei vollständig geschrieben
    // Race-Fenster existierte während des gesamten Kopiervorgangs
    chmod(dest_path, intended_mode);
}

// Anfällig: Verzeichniskopie mit Berechtigungs-Race
void vulnerable_copy_directory(const char* src, const char* dest) {
    // Zielverzeichnis mit Standardberechtigungen erstellen
    mkdir(dest, 0777);  // Initial weltweit zugänglich!

    // Alle Dateien kopieren (jede hat ihr eigenes Race-Fenster)
    copy_directory_contents(src, dest);

    // Korrekte Berechtigungen NACHDEM alle Dateien kopiert setzen
    // Alle Dateien waren während des Kopierens exponiert
    chmod(dest, 0700);
}
# Anfällig: Python Dateiextraktion mit Race Condition
import os
import tarfile
import shutil

def vulnerable_extract_tar(archive_path, dest_dir):
    with tarfile.open(archive_path, 'r') as tar:
        for member in tar.getmembers():
            # Anfällig: Zuerst mit Standardberechtigungen extrahieren
            tar.extract(member, dest_dir)
            # Datei existiert mit unsicheren Berechtigungen

            # Beabsichtigte Berechtigungen NACH Extraktion anwenden
            extracted_path = os.path.join(dest_dir, member.name)
            os.chmod(extracted_path, member.mode)
            # Race Condition existierte während der Extraktion

def vulnerable_copy_with_permissions(src, dest, mode):
    # Anfällig: Kopie erstellt Datei mit Standardberechtigungen
    shutil.copy(src, dest)  # Datei mit umask erstellt

    # Berechtigungen nach Kopierabschluss setzen
    os.chmod(dest, mode)  # Race-Fenster während des Kopierens
// Anfällig: Java Dateikopie mit Berechtigungsverzögerung
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class VulnerableCopy {

    public void vulnerableCopyFile(Path source, Path dest,
                                   Set<PosixFilePermission> perms)
            throws IOException {

        // Anfällig: Kopie erstellt Datei mit Standardberechtigungen
        Files.copy(source, dest);
        // Datei existiert mit unsicheren Standardberechtigungen!

        // Berechtigungen nach Kopierabschluss setzen
        Files.setPosixFilePermissions(dest, perms);
        // Race Condition während des gesamten Kopiervorgangs
    }

    public void vulnerableCreateAndWrite(Path path, byte[] data,
                                         Set<PosixFilePermission> perms)
            throws IOException {

        // Anfällig: Zuerst Datei erstellen
        Files.createFile(path);
        // Datei existiert mit Standardberechtigungen

        // Inhalt schreiben
        Files.write(path, data);

        // Berechtigungen zuletzt setzen
        Files.setPosixFilePermissions(path, perms);
        // Inhalt während des Schreibens exponiert
    }
}
// Anfällig: Datenbankobjekt-Erstellung (CVE-2005-2174 Muster)
#include <stdio.h>

typedef struct {
    int id;
    char* data;
    int permissions;
} DbObject;

DbObject* vulnerable_create_object(const char* data) {
    // Objekt in Datenbank einfügen
    DbObject* obj = db_insert_object(data);
    // Objekt existiert in Datenbank ohne gesetzte Berechtigungen!

    // ... andere Verarbeitung ...

    // Berechtigungen nach dem Einfügen setzen
    obj->permissions = PERM_OWNER_ONLY;
    db_update_permissions(obj);
    // Race-Fenster während der Verarbeitung

    return obj;
}

Lösung

// Korrigiert: Datei mit korrekten Berechtigungen von Anfang an erstellen
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

void secure_extract_file(const char* archive_path,
                         const char* dest_path,
                         mode_t intended_mode) {
    // Korrigiert: Restriktive umask vor Erstellung setzen
    mode_t old_umask = umask(0077);

    // Korrigiert: Mit beabsichtigten Berechtigungen von Anfang an erstellen
    int fd = open(dest_path, O_CREAT | O_WRONLY | O_TRUNC, intended_mode);

    // umask wiederherstellen
    umask(old_umask);

    if (fd < 0) {
        return;  // Fehler behandeln
    }

    // Inhalte kopieren - Datei hat bereits korrekte Berechtigungen
    copy_from_archive(archive_path, fd);

    close(fd);
    // Kein chmod nötig - Berechtigungen wurden bei Erstellung gesetzt
}

// Korrigiert: Sichere Verzeichniserstellung
void secure_copy_directory(const char* src, const char* dest) {
    // Korrigiert: Verzeichnis mit eingeschränkten Berechtigungen erstellen
    if (mkdir(dest, 0700) != 0) {
        return;  // Fehler behandeln
    }

    // Dateien kopieren (jede mit korrekten Berechtigungen)
    secure_copy_directory_contents(src, dest);

    // Kein chmod nötig - Elternverzeichnis war bereits sicher
}

// Korrigiert: Atomare Dateierstellung mit Berechtigungen verwenden
int secure_create_file_atomic(const char* path, mode_t mode) {
    // Sicherstellen dass umask Berechtigungen nicht schwächt
    mode_t old_umask = umask(0);

    int fd = open(path,
                  O_CREAT | O_WRONLY | O_EXCL,  // O_EXCL für atomare Erstellung
                  mode);

    umask(old_umask);

    return fd;
}
# Korrigiert: Python sichere Dateiextraktion
import os
import tarfile
import tempfile
import shutil

def secure_extract_tar(archive_path, dest_dir):
    # Korrigiert: Restriktive umask setzen
    old_umask = os.umask(0o077)

    try:
        # Zuerst sicheres temporäres Verzeichnis erstellen
        with tempfile.TemporaryDirectory(dir=dest_dir) as temp_dir:
            with tarfile.open(archive_path, 'r') as tar:
                for member in tar.getmembers():
                    # Member-Pfad validieren um Path Traversal zu verhindern
                    if member.name.startswith('/') or '..' in member.name:
                        continue

                    # Mit beabsichtigtem Modus direkt extrahieren
                    # Einige tar-Implementierungen unterstützen dies
                    tar.extract(member, temp_dir, set_attrs=True)

            # Atomar von temp zum endgültigen Ziel verschieben
            for item in os.listdir(temp_dir):
                shutil.move(os.path.join(temp_dir, item),
                           os.path.join(dest_dir, item))
    finally:
        os.umask(old_umask)

def secure_copy_with_permissions(src, dest, mode):
    # Korrigiert: umask setzen und mit korrekten Berechtigungen erstellen
    old_umask = os.umask(0o077)

    try:
        # Ziel mit spezifischem Modus öffnen
        fd = os.open(dest, os.O_CREAT | os.O_WRONLY | os.O_TRUNC, mode)

        # Inhalte kopieren
        with open(src, 'rb') as src_file:
            data = src_file.read()
            os.write(fd, data)

        os.close(fd)
    finally:
        os.umask(old_umask)
// Korrigiert: Java atomare Dateierstellung mit Berechtigungen
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.*;

public class SecureCopy {

    public void secureCopyFile(Path source, Path dest,
                               Set<PosixFilePermission> perms)
            throws IOException {

        // Korrigiert: Datei mit Berechtigungen atomar erstellen
        FileAttribute<Set<PosixFilePermission>> attr =
            PosixFilePermissions.asFileAttribute(perms);

        // Mit korrekten Berechtigungen von Anfang an erstellen
        try (OutputStream out = Files.newOutputStream(dest,
                StandardOpenOption.CREATE_NEW,
                StandardOpenOption.WRITE)) {

            // Berechtigungen sofort nach Erstellung setzen
            Files.setPosixFilePermissions(dest, perms);

            // Inhalt kopieren - Datei ist bereits gesichert
            Files.copy(source, out);
        }
    }

    public void secureCreateAndWrite(Path path, byte[] data,
                                     Set<PosixFilePermission> perms)
            throws IOException {

        // Korrigiert: Mit Berechtigungen atomar erstellen
        FileAttribute<Set<PosixFilePermission>> attr =
            PosixFilePermissions.asFileAttribute(perms);

        Files.createFile(path, attr);

        // Datei mit korrekten Berechtigungen erstellt
        // Jetzt sicher zu schreiben
        Files.write(path, data);
    }
}
// Korrigiert: Datenbankobjekt mit atomarer Berechtigungszuweisung
#include <stdio.h>

typedef struct {
    int id;
    char* data;
    int permissions;
} DbObject;

DbObject* secure_create_object(const char* data, int permissions) {
    // Korrigiert: Transaktion beginnen
    db_begin_transaction();

    // Objekt mit Berechtigungen in einer Operation erstellen
    DbObject* obj = db_insert_object_with_permissions(data, permissions);

    // Atomar committen
    db_commit_transaction();
    // Objekt existiert nie ohne Berechtigungen

    return obj;
}

// Alternative: In eingeschränktem Zustand erstellen, dann aktivieren
DbObject* secure_create_object_staged(const char* data, int final_perms) {
    // Mit KEINEN Berechtigungen initial erstellen (alles verweigern)
    DbObject* obj = db_insert_object(data);
    obj->permissions = PERM_DENY_ALL;
    db_update_permissions(obj);

    // Setup durchführen...

    // Endgültige Berechtigungen aktivieren
    obj->permissions = final_perms;
    db_update_permissions(obj);

    return obj;
}

CVE-Beispiele

  • CVE-2002-0760: Archiv-Extraktor dekomprimiert Dateien mit weltweit lesbaren Berechtigungen, setzt dann später korrekte Berechtigungen.
  • CVE-2005-2174: Datenbankobjekt wird ohne Berechtigungszuweisung eingefügt, dann werden Berechtigungen in separater Operation gesetzt.
  • CVE-2006-5214: Fehlerdatei hatte schwache Berechtigungen bevor chmod ausgeführt wurde.
  • CVE-2005-2475: Archiv-Berechtigungs-Race-Condition mit Hard Links.
  • CVE-2003-0265: Datenbankdateien weltweit beschreibbar erstellt bevor setuid-Bits initialisiert werden.

Referenzen

  1. MITRE Corporation. "CWE-689: Permission Race Condition During Resource Copy." https://cwe.mitre.org/data/definitions/689.html
  2. CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization.
  3. CWE-732: Incorrect Permission Assignment for Critical Resource.