Buffer Under-read
Beschreibung
Buffer Under-read tritt auf, wenn ein Programm Daten von Speicherstellen vor dem Anfang eines beabsichtigten Puffers liest. Dies geschieht typischerweise, wenn ein Pointer oder Array-Index unter Null dekrementiert wird, Pointer-Arithmetik einen negativen Offset produziert oder ein negativer Index für Array-Zugriff verwendet wird. Anders als Buffer Overflows verursachen Under-reads primär Informationsoffenlegung, indem sie Daten aus Speicher vor dem Zielpuffer offenlegen. Dies kann sensible Informationen aus anderen Variablen, Heap-Metadaten oder zuvor freigegebenen Speicherinhalten umfassen.
Risiko
Buffer Under-reads stellen Vertraulichkeitsrisiken dar, indem sie potenziell sensible Daten offenlegen, die in Speicherstellen vor dem Zielpuffer gespeichert sind. Obwohl typischerweise weniger schwerwiegend als Buffer Overflows, können Under-reads kryptografische Schlüssel, Passwörter oder andere Geheimnisse leaken, die in angrenzendem Speicher gespeichert sind. In einigen Fällen können Under-reads Abstürze verursachen, wenn sie auf nicht gemappte Speicherbereiche zugreifen, was zu Denial-of-Service führt. Die Schwachstelle kann auch mit anderen Schwächen kombiniert werden, um schwerwiegendere Exploitation-Ergebnisse zu erzielen.
Lösung
Validieren Sie alle Array-Indizes und Pointer-Offsets vor der Verwendung und prüfen Sie sowohl obere als auch untere Grenzen. Verwenden Sie unsigned Integer-Typen für Array-Indizes, wo negative Werte logisch ungültig sind. Implementieren Sie grenzenprüfende Wrapper-Funktionen für Pufferzugriff. Verwenden Sie speichersichere Sprachen oder C++-Container, die Out-of-bounds-Zugriff verhindern. Aktivieren Sie AddressSanitizer während der Entwicklung, um Under-reads zu erkennen. Überprüfen Sie Pointer-Arithmetik-Operationen, besonders solche mit Subtraktions- oder Dekrementierungsoperationen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Informationsoffenlegung Das Lesen von Speicher vor dem Pufferstart kann sensible Daten aus anderen Variablen oder Datenstrukturen offenlegen. |
| Verfügbarkeit | Bereich: Verfügbarkeit Der Zugriff auf nicht gemappte Speicherseiten verursacht Segmentation Faults und Abstürze. |
| Integrität | Bereich: Informationsleck Geleakte Informationen können Angreifern helfen, Sicherheitsmechanismen zu umgehen oder andere Schwachstellen auszunutzen. |
Beispielcode + Lösungscode
Anfälliger Code
#include <string.h>
#include <stdlib.h>
// ANFÄLLIG: Negativer Index-Under-read
int get_previous_value(int *array, int current_index) {
// Keine Prüfung auf negatives Ergebnis
return array[current_index - 1]; // Under-read wenn current_index == 0
}
// ANFÄLLIG: Pointer-Dekrement-Under-read
char *find_start_of_line(char *buffer, char *current_pos) {
// Dekrement ohne Prüfung der Puffergrenze
while (*current_pos != '\n') {
current_pos--; // Kann vor Pufferstart gehen
}
return current_pos + 1;
}
// ANFÄLLIG: Negativer Offset aus Konfiguration
void read_at_offset(char *buffer, int offset) {
// Benutzergesteuerter Offset könnte negativ sein
char value = buffer[offset]; // Under-read wenn offset < 0
process(value);
}
Korrigierter Code
#include <string.h>
#include <stdlib.h>
#include <stdbool.h>
// SICHER: Grenzenprüfung vor Zugriff
bool get_previous_value_safe(int *array, size_t array_size,
size_t current_index, int *out_value) {
// Prüfen, dass es ein vorheriges Element GIBT
if (current_index == 0 || current_index > array_size) {
return false; // Kein gültiges vorheriges Element
}
*out_value = array[current_index - 1];
return true;
}
// SICHER: Puffergrenzen prüfen
char *find_start_of_line_safe(char *buffer_start, char *buffer_end,
char *current_pos) {
// Validieren, dass current_pos innerhalb des Puffers ist
if (current_pos < buffer_start || current_pos >= buffer_end) {
return NULL;
}
// Am Pufferstart stoppen
while (current_pos > buffer_start && *current_pos != '\n') {
current_pos--;
}
// Position nach Newline zurückgeben (oder Pufferstart)
return (current_pos == buffer_start) ? current_pos : current_pos + 1;
}
// SICHER: Validieren, dass Offset nicht negativ ist
bool read_at_offset_safe(const char *buffer, size_t buffer_size,
size_t offset, char *out_value) {
// size_t ist unsigned, aber obere Grenze auch prüfen
if (offset >= buffer_size) {
return false;
}
*out_value = buffer[offset];
return true;
}
Ausgenutzt in der Praxis
c-ares DNS-Bibliothek (Mehrere Systeme, 2024)
CVE-2024-25629 ist eine Buffer-Under-read-Schwachstelle in der c-ares-C-Bibliothek für asynchrone DNS-Anfragen. Ein NULL-Zeichen am Anfang einer neuen Zeile in Konfigurationsdateien (/etc/resolv.conf, /etc/nsswitch.conf) könnte Lesezugriffe vor dem Pufferstart verursachen und zu Abstürzen führen. Die Schwachstelle betraf IBM Storage Ceph, Node.js und viele Linux-Distributionen.
Mehrere Herstellerprodukte (Verschiedene, fortlaufend)
Buffer-Under-read-Schwachstellen werden weiterhin in verschiedenen Produkten entdeckt, einschließlich Medien-Codecs, Netzwerkprotokoll-Parsern und Dateiformat-Handlern, was typischerweise zu Informationsoffenlegung oder Denial-of-Service führt.
Tools zum Testen/Ausnutzen
-
AddressSanitizer — erkennt Buffer Under-reads zur Laufzeit mit präziser Fehlermeldung.
-
Valgrind — Speicherfehlerdetektor, der Lesezugriffe vor allokiertem Speicher identifiziert.
-
AFL++ — Fuzzer, der effektiv bei der Entdeckung von Grenzbedingungsfehlern ist.
CVE-Beispiele
-
CVE-2024-25629 — c-ares Buffer Under-read verursacht Denial-of-Service.
-
CVE-2023-52160 — wpa_supplicant Buffer Under-read in EAP-TLS-Verarbeitung.
-
CVE-2021-3449 — OpenSSL NULL-Pointer-Dereferenzierung aus Under-read-Bedingung.
Referenzen
-
MITRE. "CWE-127: Buffer Under-read." https://cwe.mitre.org/data/definitions/127.html
-
CERT. "ARR30-C. Do not form or use out-of-bounds pointers or array subscripts." https://wiki.sei.cmu.edu/confluence/display/c/ARR30-C