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
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Anwendungsdaten lesen - Ressourcen können von unbefugten Parteien während des Berechtigungslückenfensters gelesen werden. |
| Integrität | Bereich: Integrität Anwendungsdaten ändern - Angreifer können Ressourcen modifizieren, bevor korrekte Berechtigungen angewendet werden. |
| Zugriffskontrolle | Bereich: 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
- MITRE Corporation. "CWE-689: Permission Race Condition During Resource Copy." https://cwe.mitre.org/data/definitions/689.html
- CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization.
- CWE-732: Incorrect Permission Assignment for Critical Resource.