Integer-Überlauf zu Buffer Overflow
Beschreibung
Integer-Überlauf zu Buffer Overflow ist eine zusammengesetzte Schwachstelle, bei der ein Integer-Überlauf während der Berechnung der Speicherallokationsgröße dazu führt, dass ein zu kleiner Puffer allokiert wird, was anschließend zu einem Buffer Overflow führt, wenn das Programm versucht, den unterdimensionierten Puffer zu verwenden. Dies tritt typischerweise bei der Berechnung der Größe für Speicherallokation auf — beispielsweise beim Multiplizieren einer Anzahl mit einer Elementgröße. Wenn die Berechnung überläuft, wickelt das Ergebnis zu einem kleinen Wert um, sodass malloc() oder ähnliche Funktionen einen winzigen Puffer allokieren.
Risiko
Integer-Überlauf-zu-Buffer Overflow-Schwachstellen sind hochgefährlich und werden häufig ausgenutzt. Angreifer können große Eingabewerte bereitstellen, die darauf ausgelegt sind, den Überlauf auszulösen, und so die Kontrolle über Speicherallokationsgrößen erlangen. Der nachfolgende Buffer Overflow ermöglicht Heap-Korruption, die für beliebige Codeausführung genutzt werden kann. Diese Schwachstellen umgehen oft andere Sicherheitsprüfungen, da die berechnete Größe die Validierung passiert, bevor der Überlauf auftritt.
Lösung
Validieren Sie alle Eingaben, die zu Speicherallokationsberechnungen beitragen, bevor Sie Arithmetik durchführen. Prüfen Sie vor der Multiplikation auf potenziellen Überlauf: wenn (a > SIZE_MAX / b), dann würde ein Überlauf auftreten. Verwenden Sie sichere Integer-Arithmetik-Bibliotheken, die Überlauf erkennen. Erwägen Sie die Verwendung von calloc() anstelle von malloc() mit Multiplikation, da einige Implementierungen auf Überlauf prüfen. Nutzen Sie Compiler-Optionen wie -ftrapv (GCC), um bei signiertem Überlauf abzufangen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität Datenmodifikation - Buffer Overflow korrumpiert Heap- oder Stack-Daten und modifiziert möglicherweise sicherheitskritische Informationen. |
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Absturz - Speicherkorruption führt zu Abstürzen und Anwendungsinstabilität. |
| Vertraulichkeit | Bereich: Vertraulichkeit, Integrität, Verfügbarkeit Codeausführung - Heap-basierte Pufferüberläufe können für beliebige Codeausführung ausgenutzt werden. |
Beispielcode und Lösung
Verwundbarer Code
// Verwundbar: Integer-Überlauf in Allokationsgrößenberechnung
#include <stdlib.h>
#include <string.h>
typedef struct {
int width;
int height;
unsigned char* pixels;
} Image;
// Verwundbar: Integer-Überlauf vor malloc
Image* verwundbar_erstelle_bild(int width, int height) {
Image* img = malloc(sizeof(Image));
if (!img) return NULL;
img->width = width;
img->height = height;
// VERWUNDBAR: width * height * 4 kann überlaufen
// Wenn width=65536, height=65536: 65536 * 65536 * 4 überläuft 32-Bit
// Ergebnis könnte eine kleine Zahl wie 0 oder 4 sein
size_t size = width * height * 4; // Integer-Überlauf!
img->pixels = malloc(size); // Allokiert winzigen Puffer
// Späterer Code schreibt vollständige Bilddaten, verursacht Buffer Overflow
return img;
}
// Verwundbar: Array-Allokation mit Überlauf
typedef struct {
char name[32];
int id;
} Record;
Record* verwundbar_lade_records(int num_records) {
// VERWUNDBAR: num_records * sizeof(Record) kann überlaufen
// sizeof(Record) = 36, wenn num_records = 119304648
// 119304648 * 36 = 4.294.967.328 was auf 32-Bit zu kleinem Wert umwickelt
Record* records = malloc(num_records * sizeof(Record));
if (records == NULL) {
return NULL;
}
// Später: memcpy oder Schleife schreibt zu allen num_records Einträgen
// verursacht massiven Buffer Overflow
for (int i = 0; i < num_records; i++) {
read_record(&records[i]); // Schreibt über allozierten Speicher hinaus
}
return records;
}
// Verwundbar: String-Verkettung Längenberechnung
char* verwundbar_concat(const char* str1, const char* str2) {
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
// Verwundbar: len1 + len2 + 1 kann überlaufen wenn Strings riesig
size_t total = len1 + len2 + 1; // Überlauf wickelt zu kleinem Wert um
char* result = malloc(total); // Winzige Allokation
if (result) {
strcpy(result, str1);
strcat(result, str2); // Buffer Overflow!
}
return result;
}
// Verwundbar: Paketheader spezifiziert Größe
void verwundbar_verarbeite_paket(unsigned char* packet) {
// Angreifer kontrolliert Paketheader
unsigned int element_count = *(unsigned int*)packet;
unsigned int element_size = *(unsigned int*)(packet + 4);
// Verwundbar: Multiplikationsüberlauf
unsigned int total_size = element_count * element_size; // Überlauf!
char* buffer = malloc(total_size); // Kleine Allokation
// Paketdaten in Buffer kopieren - Überlauf
memcpy(buffer, packet + 8, element_count * element_size);
process_data(buffer);
free(buffer);
}
Sichere Lösung
// Sicher: Sichere Integer-Arithmetik mit Überlaufprüfung
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>
// Sichere Multiplikation mit Überlaufprüfung
int sichere_multiplikation(size_t a, size_t b, size_t* result) {
if (a > 0 && b > SIZE_MAX / a) {
return -1; // Würde überlaufen
}
*result = a * b;
return 0;
}
// Sichere Addition mit Überlaufprüfung
int sichere_addition(size_t a, size_t b, size_t* result) {
if (a > SIZE_MAX - b) {
return -1; // Würde überlaufen
}
*result = a + b;
return 0;
}
// Sicher: Bilderstellung mit Überlaufschutz
#define MAX_IMAGE_SIZE (1024 * 1024 * 100) // 100MB max
Image* sichere_erstelle_bild(int width, int height) {
// Sicher: Eingaben validieren
if (width <= 0 || height <= 0) {
return NULL;
}
// Sicher: Vor Allokation auf Überlauf prüfen
size_t pixel_count;
if (sichere_multiplikation((size_t)width, (size_t)height, &pixel_count) != 0) {
return NULL; // Überlauf würde auftreten
}
size_t size;
if (sichere_multiplikation(pixel_count, 4, &size) != 0) {
return NULL; // Überlauf würde auftreten
}
// Zusätzliche Plausibilitätsprüfung
if (size > MAX_IMAGE_SIZE) {
return NULL; // Unvernünftig groß
}
Image* img = malloc(sizeof(Image));
if (!img) return NULL;
img->width = width;
img->height = height;
img->pixels = malloc(size);
if (!img->pixels) {
free(img);
return NULL;
}
return img;
}
// Sicher: Verwendung von calloc, das möglicherweise auf Überlauf prüft
#define MAX_RECORDS 1000000
Record* sichere_lade_records(size_t num_records) {
// Sicher: Anzahl validieren
if (num_records == 0 || num_records > MAX_RECORDS) {
return NULL;
}
// Sicher: calloc() mit separater Anzahl und Größe
// Viele Implementierungen prüfen intern auf Überlauf
Record* records = calloc(num_records, sizeof(Record));
if (records == NULL) {
return NULL;
}
for (size_t i = 0; i < num_records; i++) {
if (read_record(&records[i]) != 0) {
free(records);
return NULL;
}
}
return records;
}
// Sicher: String-Verkettung mit Überlaufprüfung
char* sichere_concat(const char* str1, const char* str2) {
if (str1 == NULL || str2 == NULL) {
return NULL;
}
size_t len1 = strlen(str1);
size_t len2 = strlen(str2);
// Sicher: Auf Überlauf prüfen
size_t total;
if (sichere_addition(len1, len2, &total) != 0) {
return NULL;
}
if (sichere_addition(total, 1, &total) != 0) {
return NULL;
}
char* result = malloc(total);
if (result) {
memcpy(result, str1, len1);
memcpy(result + len1, str2, len2 + 1); // Null-Terminator einschließen
}
return result;
}
// Sicher: Paketverarbeitung mit sicherer Arithmetik
#define MAX_PACKET_SIZE (1024 * 1024) // 1MB max
int sichere_verarbeite_paket(unsigned char* packet, size_t packet_len) {
if (packet_len < 8) {
return -1; // Paket zu klein für Header
}
unsigned int element_count = *(unsigned int*)packet;
unsigned int element_size = *(unsigned int*)(packet + 4);
// Sicher: Vor Multiplikation auf Überlauf prüfen
size_t total_size;
if (sichere_multiplikation((size_t)element_count, (size_t)element_size,
&total_size) != 0) {
return -1;
}
// Sicher: Plausibilitätsprüfung der Größe
if (total_size > MAX_PACKET_SIZE) {
return -1;
}
// Sicher: Verifizieren, dass Paket genug Daten enthält
if (packet_len < 8 + total_size) {
return -1;
}
char* buffer = malloc(total_size);
if (buffer == NULL) {
return -1;
}
memcpy(buffer, packet + 8, total_size);
process_data(buffer);
free(buffer);
return 0;
}
// Moderner C11-Ansatz mit geprüfter Arithmetik (falls verfügbar)
#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
#include <stdckdint.h> // C23 geprüfte Integer-Arithmetik
Image* moderne_erstelle_bild(int width, int height) {
if (width <= 0 || height <= 0) return NULL;
size_t pixel_count, size;
// C23 geprüfte Multiplikation
if (ckd_mul(&pixel_count, (size_t)width, (size_t)height)) {
return NULL; // Überlauf
}
if (ckd_mul(&size, pixel_count, 4)) {
return NULL; // Überlauf
}
Image* img = malloc(sizeof(Image));
if (!img) return NULL;
img->pixels = malloc(size);
if (!img->pixels) {
free(img);
return NULL;
}
img->width = width;
img->height = height;
return img;
}
#endif
Ausgenutzt in der Praxis
libpng Integer-Überlauf (2004)
Ein Integer-Überlauf in der weit verbreiteten libpng-Bibliothek bei der Berechnung von Bildpuffergrößen ermöglichte Remote-Code-Ausführung durch speziell gestaltete PNG-Bilder. Dies betraf zahlreiche Browser und Bildverarbeitungsanwendungen.
Microsoft GDI+ Integer-Überlauf (2004)
Ein kritischer Integer-Überlauf in der Windows GDI+-Bibliothek bei der JPEG-Verarbeitung ermöglichte Remote-Code-Ausführung durch das Öffnen manipulierter Bilder. Die Schwachstelle betraf Windows XP, Server 2003 und Office.
Android Stagefright (2015)
Integer-Überläufe in der Android-Medienbibliothek Stagefright ermöglichten Remote-Code-Ausführung durch speziell gestaltete MMS-Nachrichten. Etwa 950 Millionen Android-Geräte waren betroffen.
Tools zum Testen und Ausnutzen
-
AddressSanitizer (ASan) — Compiler-Tool zur Erkennung von Pufferüberläufen zur Laufzeit.
-
UndefinedBehaviorSanitizer — Erkennt Integer-Überlauf und undefiniertes Verhalten.
-
Coverity — Statisches Analysetool zur Erkennung von Integer-Überlauf-Mustern.
CVE-Beispiele
-
CVE-2021-43537 — Typumwandlungsfehler verursacht Integer-Überlauf zu Heap-Buffer Overflow.
-
CVE-2017-1000121 — Ungeprüfte benutzergesteuerte Metadatenwerte führen zu Integer-Überlauf.
-
CVE-2015-3864 — Android Stagefright Integer-Überlauf.
Referenzen
-
MITRE Corporation. "CWE-680: Integer Overflow to Buffer Overflow." https://cwe.mitre.org/data/definitions/680.html
-
CERT C Coding Standard. "INT30-C: Ensure that unsigned integer operations do not wrap." https://wiki.sei.cmu.edu/confluence/display/c/INT30-C.+Ensure+that+unsigned+integer+operations+do+not+wrap
-
CERT C Coding Standard. "INT32-C: Ensure that operations on signed integers do not result in overflow." https://wiki.sei.cmu.edu/confluence/display/c/INT32-C.+Ensure+that+operations+on+signed+integers+do+not+result+in+overflow