Falsche Konvertierung zwischen numerischen Typen

Beschreibung

Falsche Konvertierung zwischen numerischen Typen ist eine Schwachstelle, bei der ein Programm Werte zwischen verschiedenen numerischen Typen auf eine Weise konvertiert, die unerwartete oder falsche Ergebnisse erzeugt. Dies umfasst Konvertierungen zwischen vorzeichenbehafteten und vorzeichenlosen Ganzzahlen, Konvertierungen, die Werte abschneiden (z.B. long zu int), Fließkomma-zu-Ganzzahl-Konvertierungen, die Präzision verlieren, und implizite Konvertierungen, die die Wertsemantik ändern. Wenn der konvertierte Wert in sensiblen Operationen wie Speicherallokation, Array-Indizierung oder Sicherheitsentscheidungen verwendet wird, kann der falsche Wert zu ernsthaften Sicherheitslücken führen.

Risiko

Falsche numerische Konvertierungen schaffen verschiedene Sicherheitsrisiken je nach Kontext. Die Konvertierung von signed zu unsigned bei negativen Werten erzeugt große positive Werte, was möglicherweise Pufferüberläufe verursacht, wenn sie als Größen verwendet werden. Die Abschneidung größer Werte auf kleinere Typen verliert höherwertige Bits, was zu unerwartet kleinen Werten führt. Fließkomma-zu-Ganzzahl-Konvertierung verliert Nachkommastellen und kann präzisionsabhängige Sicherheitsprüfungen zum Scheitern bringen. Plattformabhängige Typgrößen schaffen Portabilitätsfehler, die sich als Sicherheitslücken auf verschiedenen Architekturen manifestieren.

Lösung

Vermeiden Sie Typkonvertierungen, wenn möglich, indem Sie konsistente Typen in Berechnungen verwenden. Wenn eine Konvertierung notwendig ist, validieren Sie explizit, dass der Wert in den Bereich des Zieltyps passt, bevor Sie konvertieren. Verwenden Sie sichere Konvertierungsfunktionen oder Makros, die auf Überlauf und Unterlauf prüfen. Seien Sie sich impliziter Konvertierungen in Ausdrücken bewusst und verwenden Sie explizite Casts, um die Absicht klar zu machen. Testen Sie auf mehreren Plattformen mit unterschiedlichen Typgrößen.

Häufige Auswirkungen

AuswirkungDetails
IntegritätBereich: Integrität

Datenmodifikation - Falsche Werte aus schlechten Konvertierungen können Daten korrumpieren oder falsche Berechnungen erzeugen.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz - Unerwartete Werte können Abstürze verursachen, besonders bei Speicheroperationen.
SonstigesBereich: Sonstige

Qualitätsverschlechterung - Programme erzeugen falsche Ergebnisse, wenn Konvertierungen Werte unerwartet ändern.

Beispielcode und Lösung

Verwundbarer Code

// Verwundbar: Signed zu Unsigned Konvertierung
#include <stdlib.h>
#include <string.h>

void verwundbar_kopieren(char* dest, char* src, int length) {
    // VERWUNDBAR: Wenn length negativ ist, macht unsigned Konvertierung es riesig
    // memcpy wird massive Speichermengen lesen/schreiben
    memcpy(dest, src, (size_t)length);  // -1 wird SIZE_MAX!
}

unsigned int verwundbar_lese_große() {
    int size = read_from_network();

    if (size == -1) {
        // Fehlerbedingung gibt -1 zurück
        return size;  // Konvertiert -1 zu 4.294.967.295 (UINT_MAX)!
    }

    return size;
}

// Verwundbar: Abschneidung von größerem zu kleinerem Typ
void verwundbar_abschneidung(size_t large_size) {
    // Auf 64-Bit-System: size_t ist 64-Bit, int ist 32-Bit
    int small_size = (int)large_size;  // Schneidet auf 32 Bits ab

    // Wenn large_size 0x100000010 (4GB + 16 Bytes) war
    // wird small_size nur 16!
    char* buffer = malloc(small_size);
    // Allokiert 16 Bytes statt 4GB

    // Späterer Code verwendet large_size zum Schreiben - Buffer Overflow!
    fill_buffer(buffer, large_size);
}
// Verwundbar: Java Fließkomma zu Ganzzahl
public class VerwundbareKonvertierung {

    // Verwundbar: Präzisionsverlust
    public int berechneRabatt(float preis, float rabattProzent) {
        float rabattBetrag = preis * rabattProzent / 100;

        // Verwundbar: Abschneidung Richtung Null
        int cents = (int)(rabattBetrag * 100);

        // 1,999€ werden 199 Cents, nicht 200
        // Wiederholte Berechnungen akkumulieren Fehler
        return cents;
    }

    // Verwundbar: Überlauf bei Konvertierung
    public int verwundbarerArrayIndex(long userIndex) {
        // Verwundbar: Long zu Int Abschneidung
        int index = (int)userIndex;

        // Wenn userIndex = 0x100000000L (4GB), wird index 0
        // Umgeht Grenzenprüfung, wenn basierend auf Original-Long
        return array[index];
    }
}

Sichere Lösung

// Sicher: Sichere Signed zu Unsigned Konvertierung
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <limits.h>

int sichere_kopie(char* dest, size_t dest_size, char* src, int length) {
    // Sicher: Länge vor Konvertierung validieren
    if (length < 0) {
        return -1;  // Fehler: negative Länge
    }

    if ((size_t)length > dest_size) {
        return -1;  // Fehler: würde Ziel überlaufen
    }

    memcpy(dest, src, (size_t)length);
    return 0;
}

// Sicher: Vor Abschneidung prüfen
int sichere_abschneidung(size_t large_size) {
    // Sicher: Verifizieren, dass Wert in Zieltyp passt
    if (large_size > INT_MAX) {
        return -1;  // Fehler: Wert zu groß
    }

    int small_size = (int)large_size;  // Sicher nach Prüfung

    char* buffer = malloc(small_size);
    if (buffer == NULL) return -1;

    fill_buffer(buffer, small_size);  // Gleiche Variable verwenden
    free(buffer);
    return 0;
}

// Sicher: Sichere Konvertierungs-Hilfsfunktionen
// size_t sicher zu int konvertieren
int size_zu_int(size_t value, int* result) {
    if (value > INT_MAX) {
        return -1;  // Würde überlaufen
    }
    *result = (int)value;
    return 0;
}

// int64_t sicher zu int32_t konvertieren
int i64_zu_i32(int64_t value, int32_t* result) {
    if (value < INT32_MIN || value > INT32_MAX) {
        return -1;  // Außerhalb des Bereichs
    }
    *result = (int32_t)value;
    return 0;
}

// Signed sicher zu Unsigned konvertieren
int signed_zu_unsigned(int value, unsigned int* result) {
    if (value < 0) {
        return -1;  // Negative Werte ungültig für unsigned
    }
    *result = (unsigned int)value;
    return 0;
}
// Sicher: Java sichere Konvertierungen
public class SichereKonvertierung {

    // Sicher: Ordnungsgemäße Rundung für Währung
    public int berechneRabattSicher(double preis, double rabattProzent) {
        double rabattBetrag = preis * rabattProzent / 100;

        // Sicher: Ordnungsgemäße Rundung verwenden
        int cents = (int)Math.round(rabattBetrag * 100);

        return cents;
    }

    // Sicher: Grenzenprüfung vor Konvertierung
    public int sichererArrayIndex(long userIndex) {
        // Sicher: Bereich vor Konvertierung validieren
        if (userIndex < 0 || userIndex >= array.length) {
            throw new IndexOutOfBoundsException("Ungültiger Index: " + userIndex);
        }

        // Sicher: wir haben verifiziert, dass es passt
        int index = (int)userIndex;
        return array[index];
    }

    // Sicher: Math.toIntExact verwenden (Java 8+)
    public int sicherZuInt(long value) {
        // Wirft ArithmeticException wenn Wert nicht in int passt
        return Math.toIntExact(value);
    }
}

Ausgenutzt in der Praxis

OpenSSL Signed-to-Unsigned Schwachstelle (2014)

Eine fehlerhafte Typkonvertierung in OpenSSL bei der Verarbeitung von TLS-Heartbeat-Erweiterungen führte zur Heartbleed-Schwachstelle, die es Angreifern ermöglichte, Speicher vom Server zu lesen.

Firefox Integer-Abschneidung (2016)

Eine Integer-Abschneidung in Firefox bei der Verarbeitung von WebGL-Inhalten ermöglichte Remote-Code-Ausführung durch speziell gestaltete Webseiten.

Linux Kernel Signed/Unsigned Verwechslung (diverse)

Mehrere Linux-Kernel-Schwachstellen resultierten aus Verwechslungen zwischen signed und unsigned Typen, die zu Privilegieneskalation führten.


Tools zum Testen und Ausnutzen

  • UndefinedBehaviorSanitizer — Erkennt Integer-Überlauf und Konvertierungsprobleme.

  • PVS-Studio — Statischer Analysator zur Erkennung von Typkonvertierungsfehlern.

  • Cppcheck — Open-Source-Tool zur statischen Analyse für C/C++.


CVE-Beispiele

  • CVE-2022-2639 — Integer-Coercion-Fehler verhindert ordnungsgemäße Fehleranzeige und führt zu Out-of-Bounds-Write.

  • CVE-2021-43537 — 64-Bit zu 32-Bit Integer-Abschneidung verursacht Heap-Buffer Overflow.

  • CVE-2007-4268 — Vorzeichenerweiterungsfehler löst Heap-Überlauf aus.


Referenzen

  1. MITRE Corporation. "CWE-681: Incorrect Conversion between Numeric Types." https://cwe.mitre.org/data/definitions/681.html

  2. CERT C Coding Standard. "INT31-C: Ensure that integer conversions do not result in löst or misinterpreted data." https://wiki.sei.cmu.edu/confluence/display/c/INT31-C.+Ensure+that+integer+conversions+do+not+result+in+löst+or+misinterpreted+data

  3. CERT C Coding Standard. "INT02-C: Understand integer conversion rules." https://wiki.sei.cmu.edu/confluence/display/c/INT02-C.+Understand+integer+conversion+rules