Mehrfache Operationen auf Ressource im Einzeloperationskontext
Beschreibung
Mehrfache Operationen auf Ressource im Einzeloperationskontext ist eine Schwachstelle, bei der ein Programm dieselbe Operation auf einer Ressource zwei- oder mehrmals ausführt, obwohl die Operation nur einmal angewendet werden sollte. Dies manifestiert sich häufig als Double-Free-Schwachstellen, doppeltes Schließen von Dateihandles, doppelte Sperr-Operationen oder wiederholte Initialisierung. Das Problem entsteht typischerweise durch unklare Besitzverhältnisse bei Ressourcen, unsachgemäße Fehlerbehandlung, die doppelte Bereinigung auslöst, oder Verwirrung darüber, welcher Codepfad für die Ressourcenverwaltung verantwortlich ist.
Risiko
Doppelte Operationen auf Ressourcen schaffen ernsthafte Sicherheits- und Stabilitätsrisiken. Double-Free-Schwachstellen in der Speicherverwaltung können Heap-Metadaten korrumpieren und ermöglichen beliebige Codeausführung. Doppeltes Schließen von Dateideskriptoren kann unzusammenhängende Dateien schließen, wenn Deskriptornummern wiederverwendet werden, was zu Datenkorruption oder Sicherheitsverletzungen führt. Doppeltes Entsperren von Mutexen verursacht undefiniertes Verhalten und kann Multi-Thread-Anwendungen destabilisieren.
Lösung
Implementieren Sie klare Besitzsemantiken für alle Ressourcen. Verwenden Sie Einzelbesitzer-Muster, bei denen eine Komponente für den Ressourcenlebenszyklus verantwortlich ist. Setzen Sie Pointer nach dem Freigeben auf NULL, um Double-Free zu verhindern. Schließen Sie Dateideskriptoren, indem Sie sie nach dem Schließen auf -1 setzen. Verwenden Sie Referenzzählung für gemeinsam genutzte Ressourcen. Implementieren Sie Statusverfolgung, um doppelte Operationen zu erkennen und zu verhindern. Verwenden Sie RAII-Muster in C++ oder try-with-resources in Java.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz - Double-Free und Double-Close-Operationen verursachen typischerweise Abstürze. |
| Integrität | Bereich: Integrität Datenmodifikation - Heap-Korruption durch Double-Free kann beliebige Daten modifizieren. |
| Vertraulichkeit | Bereich: Vertraulichkeit, Integrität, Verfügbarkeit Codeausführung - Heap-Korruption durch Double-Free ist oft für beliebige Codeausführung ausnutzbar. |
Beispielcode und Lösung
Verwundbarer Code
// Verwundbar: Double-Free
#include <stdlib.h>
void verwundbar_double_free(char* buffer, int fehler_aufgetreten) {
if (fehler_aufgetreten) {
free(buffer); // Erstes Free
// Fehlerbehandlung geht weiter...
}
// Später in der Funktion
free(buffer); // Zweites Free - Heap-Korruption!
}
// Verwundbar: Double-Free in Fehlerbehandlung
char* verwundbar_erstelle_nachricht(const char* prefix, const char* suffix) {
char* result = malloc(256);
if (result == NULL) return NULL;
char* temp = malloc(128);
if (temp == NULL) {
free(result); // Bereinigung bei Fehler
return NULL;
}
if (formatiere_nachricht(result, prefix, suffix) < 0) {
free(temp);
free(result);
return NULL;
}
free(temp);
return result;
}
void verwundbarer_aufrufer() {
char* msg = verwundbar_erstelle_nachricht("Hallo", "Welt");
if (msg == NULL) {
// Einige Codepfade könnten versuchen, msg hier freizugeben
// Aber es wurde bereits in der Funktion freigegeben!
}
free(msg); // Double-Free wenn Fehler auftrat!
}
// Verwundbar: Double-Close von Dateideskriptor
#include <unistd.h>
#include <fcntl.h>
void verwundbar_double_close() {
int fd = open("daten.txt", O_RDONLY);
if (fd < 0) return;
// Datei verarbeiten...
if (verarbeite_datei(fd) < 0) {
close(fd); // Schließen bei Fehler
// Weiter mit Fehlerbehandlung...
}
// Spätere Bereinigung
close(fd); // Double-Close!
// fd-Nummer könnte für eine andere Datei wiederverwendet worden sein
// Wir haben gerade die falsche Datei geschlossen!
}
// Verwundbar: Double-Unlock von Mutex
#include <pthread.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int shared_data = 0;
void verwundbar_double_unlock(int fehler_aufgetreten) {
pthread_mutex_lock(&lock);
shared_data++;
if (fehler_aufgetreten) {
pthread_mutex_unlock(&lock); // Entsperren bei Fehler
// Fehler behandeln...
}
// Normaler Pfad
pthread_mutex_unlock(&lock); // Double-Unlock wenn Fehler auftrat!
// Undefiniertes Verhalten - kann Mutex-Status korrumpieren
}
Sichere Lösung
// Sicher: Double-Free mit NULL-Zuweisung verhindern
#include <stdlib.h>
void sicheres_free(char** ptr) {
if (ptr != NULL && *ptr != NULL) {
free(*ptr);
*ptr = NULL; // Double-Free verhindern
}
}
void sichere_operation(char* buffer, int fehler_aufgetreten) {
if (fehler_aufgetreten) {
sicheres_free(&buffer); // Free und NULL setzen
return;
}
// Normale Verarbeitung...
sicheres_free(&buffer); // Sicher auch bei doppeltem Aufruf
}
// Sicher: Klare Besitzsemantiken
char* sichere_erstelle_nachricht(const char* prefix, const char* suffix) {
char* result = malloc(256);
if (result == NULL) return NULL;
char* temp = malloc(128);
if (temp == NULL) {
free(result);
return NULL; // Aufrufer weiß: NULL bedeutet nichts freizugeben
}
if (formatiere_nachricht(result, prefix, suffix) < 0) {
free(temp);
free(result);
return NULL; // Klarer Vertrag: NULL = Fehler, nichts freizugeben
}
free(temp);
return result; // Aufrufer besitzt diesen Speicher
}
void sicherer_aufrufer() {
char* msg = sichere_erstelle_nachricht("Hallo", "Welt");
if (msg == NULL) {
// Fehler aufgetreten, aber Funktion hat aufgeräumt
behandle_fehler();
return; // Nicht freigeben - nichts allokiert
}
// Nachricht verwenden...
free(msg); // Einzelnes Free - wir besitzen es
msg = NULL; // Gute Praxis
}
// Sicher: Double-Close mit fd-Tracking verhindern
#include <unistd.h>
#include <fcntl.h>
int sicheres_close(int* fd) {
if (fd != NULL && *fd >= 0) {
int result = close(*fd);
*fd = -1; // Als geschlossen markieren
return result;
}
return 0;
}
void sichere_datei_operation() {
int fd = open("daten.txt", O_RDONLY);
if (fd < 0) return;
if (verarbeite_datei(fd) < 0) {
sicheres_close(&fd); // Schließen und markieren
behandle_fehler();
return;
}
// Normale Bereinigung
sicheres_close(&fd); // Sicher - fd ist jetzt -1 wenn bereits geschlossen
}
// Sicher: Ordnungsgemäße Mutex-Behandlung
#include <pthread.h>
#include <stdbool.h>
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int shared_data = 0;
void sichere_operation(int fehler_aufgetreten) {
bool lock_gehalten = false;
pthread_mutex_lock(&lock);
lock_gehalten = true;
shared_data++;
if (fehler_aufgetreten) {
// Nur entsperren wenn wir die Sperre halten
if (lock_gehalten) {
pthread_mutex_unlock(&lock);
lock_gehalten = false;
}
behandle_fehler();
return; // Bereits entsperrt
}
// Normaler Pfad
if (lock_gehalten) {
pthread_mutex_unlock(&lock);
lock_gehalten = false;
}
}
Ausgenutzt in der Praxis
glibc Double-Free Exploit (Linux, 2019)
Eine Double-Free-Schwachstelle in glibc ermöglichte es Angreifern, Heap-Korruption auszunutzen und beliebigen Code auszuführen. Dies betraf zahlreiche Linux-Distributionen und erforderte sofortige Patches.
- https://www.zdnet.com/article/linux-security-new-glibc-vulnerability-puts-all-linux-systems-at-risk/
Internet Explorer Double-Free (Microsoft, 2014)
Eine Double-Free-Schwachstelle im Internet Explorer wurde aktiv von der APT-Gruppe "Hurricane Panda" ausgenutzt, um gezielte Angriffe durchzuführen.
Android Media Framework Double-Free (Google, 2016)
Eine Double-Free-Schwachstelle im Android Media Framework ermöglichte Remote-Code-Ausführung durch speziell gestaltete Mediendateien.
Tools zum Testen und Ausnutzen
-
AddressSanitizer (ASan) — Compiler-Tool zur Erkennung von Double-Free und anderen Speicherfehlern.
-
Valgrind — Dynamisches Analysetool zur Erkennung von Speicherverwaltungsfehlern.
-
Electric Fence — Debug-Bibliothek zur Erkennung von Speicherfehlern.
CVE-Beispiele
-
CVE-2009-0935 — Mutex zweimal entsperrt führt zu Korruption.
-
CVE-2019-13351 — Double-Close von Dateideskriptor.
-
CVE-2017-9047 — libxml2 Double-Free.
Referenzen
-
MITRE Corporation. "CWE-675: Multiple Operations on Resource in Single-Operation Context." https://cwe.mitre.org/data/definitions/675.html
-
CERT C Coding Standard. "MEM30-C: Do not access freed memory." https://wiki.sei.cmu.edu/confluence/display/c/MEM30-C.+Do+not+access+freed+memory
-
CERT C Coding Standard. "FIO46-C: Do not access a closed file." https://wiki.sei.cmu.edu/confluence/display/c/FIO46-C.+Do+not+access+a+closed+file