Zugriff auf Ressource mit inkompatiblem Typ ('Type Confusion')

Beschreibung

Type Confusion tritt auf, wenn ein Produkt eine Ressource mit einem Typ allokiert oder initialisiert, aber später auf diese Ressource mit einem inkompatiblen Typ zugreift. Wenn die Ressource mit dem falschen Typ zugegriffen wird, fehlen möglicherweise erwartete Eigenschaften, was logische Fehler auslöst. In speicherunsicheren Sprachen wie C und C++ kann Type Confusion Out-of-Bounds-Speicherzugriff ermöglichen, da verschiedene Typen unterschiedliche Größen und Speicherlayouts haben. Angreifer können Type Confusion ausnutzen, um Speicher außerhalb der beabsichtigten Grenzen zu lesen oder zu schreiben, was möglicherweise zu Codeausführung, Informationsoffenlegung oder Systemabstürzen führt.

Risiko

Type-Confusion-Schwachstellen sind besonders gefährlich in Low-Level-Sprachen, in denen Typinformationen zur Laufzeit nicht durchgesetzt werden. Zugriff auf Speicher durch einen inkompatiblen Typ kann verursachen: Lesen über Puffergrenzen hinaus (Informationsoffenlegung), Schreiben über Puffergrenzen hinaus (Speicherbeschädigung), falsche Dateninterpretation (Logikfehler) und Beschädigung benachbarter Speicherstrukturen (Codeausführung). In Sprachen mit lockereren Typsystemen (PHP, JavaScript, Perl) kann Type Confusion unerwartetes Verhalten bei Vergleichen, mathematischen Operationen oder Datenverarbeitung verursachen. Browser-Engines und Dokument-Parser sind häufige Ziele für Type-Confusion-Angriffe.

Lösung

Verwenden Sie typsichere Programmierpraktiken und Sprachen, wo möglich. In C/C++ vermeiden Sie Unions für Type-Punning, es sei denn, es ist absolut notwendig, verwenden Sie explizite Typprüfungen vor dem Casten, implementieren Sie Laufzeit-Typverfolgung für polymorphe Objekte und verwenden Sie Tagged Unions oder Discriminated Unions. In dynamischen Sprachen validieren Sie Typen explizit vor Operationen und verwenden Sie strikte Vergleichsoperatoren. Aktivieren Sie Compiler-Warnungen für Typ-Diskrepanzen. Verwenden Sie statische Analysetools zur Erkennung potenzieller Type Confusion. Implementieren Sie defensive Programmierung mit Assertions zur Verifizierung von Typerwartungen. Erwägen Sie die Verwendung speichersicherer Sprachfeatures oder speichersicherer Sprachen.

Häufige Auswirkungen

AuswirkungDetails
Vertraulichkeit, IntegritätBereich: Vertraulichkeit, Integrität

Speicher lesen/modifizieren - Wenn Speicher mit falschem Typ zugegriffen wird, könnte er außerhalb der Puffergrenzen lesen oder schreiben.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz/Beendigung/Neustart - Type Confusion kann Abstürze durch ungültigen Speicherzugriff oder Assertion-Fehler verursachen.
Integrität, Vertraulichkeit, VerfügbarkeitBereich: Integrität, Vertraulichkeit, Verfügbarkeit

Unerlaubten Code ausführen - Speicherbeschädigung durch Type Confusion kann zu beliebiger Codeausführung führen.

Beispielcode

Anfälliger Code

// Anfällig: Union-basierte Type Confusion
#include <stdio.h>
#include <string.h>

typedef union {
    struct {
        int type;
        int value;
    } integer_msg;
    struct {
        int type;
        char *data;  // Pointer - 8 Bytes auf 64-Bit
    } string_msg;
} Message;

void vulnerable_process_message(Message *msg) {
    // Typ-Feld prüfen
    if (msg->integer_msg.type == 1) {
        // Als Integer-Nachricht behandeln
        printf("Integer-Wert: %d\n", msg->integer_msg.value);
    } else if (msg->integer_msg.type == 2) {
        // Als String-Nachricht behandeln
        printf("String-Daten: %s\n", msg->string_msg.data);
    }
}

void exploit_type_confusion() {
    Message msg;

    // Als Integer-Nachricht einrichten
    msg.integer_msg.type = 1;
    msg.integer_msg.value = 0x41414141;

    // Angreifer modifiziert Typ-Feld ohne Daten zu aktualisieren
    msg.integer_msg.type = 2;

    // Jetzt wird value (0x41414141) als Pointer interpretiert!
    // Zugriff hierauf liest von beliebiger Speicheradresse
    vulnerable_process_message(&msg);  // Absturz oder Info-Leak
}
// Anfällig: C++ Polymorphie Type Confusion
class Base {
public:
    virtual void print() { std::cout << "Base\n"; }
};

class Derived : public Base {
public:
    char buffer[100];
    void print() override { std::cout << "Derived: " << buffer << "\n"; }
};

class Malicious {
public:
    char exploit_buffer[200];  // Andere Größe!
    void print() { /* Anderes Layout */ }
};

void vulnerable_process(Base* obj) {
    // Anfällig: Unsicherer Downcast ohne Typprüfung
    Derived* d = (Derived*)obj;  // C-Style-Cast - keine Laufzeitprüfung

    // Wenn obj tatsächlich Malicious* ist, greift dies auf falsches Speicherlayout zu
    d->print();
    strcpy(d->buffer, "data");  // Kann überlaufen wenn Größen unterschiedlich
}
// Anfällig: PHP Type Confusion im Vergleich
<?php
function vulnerable_auth($input_hash, $stored_hash) {
    // Anfällig: == Operator erlaubt Type Juggling
    if ($input_hash == $stored_hash) {
        return true;  // Authentifiziert
    }
    return false;
}

// Angriff: PHP Type Juggling
$stored_hash = "0e462097431906509019562988736854";  // Sieht aus wie wissenschaftliche Notation
$input_hash = "0";  // String "0" == "0e..." weil beide zu 0 evaluieren

// "0" == "0e462097431906509019562988736854" ist WAHR!
// Weil beide als Zahlen behandelt werden: 0 == 0

$result = vulnerable_auth($input_hash, $stored_hash);  // Gibt true zurück!
?>
# Anfällig: Perl Type Confusion mit Array/Skalar-Kontext
sub vulnerable_check_admin {
    my ($username, $groups) = @_;

    # Anfällig: $groups könnte Array-Referenz oder Skalar sein
    # Entwickler erwartet Array-Referenz
    if ($groups eq "admin") {  # Anfälliger Vergleich
        return 1;
    }

    # Wenn $groups als Array-Ref ["admin", "users"] übergeben wird
    # Stringifiziert der eq-Vergleich es zu etwas wie "ARRAY(0x...)"
    # was nicht "admin" entspricht

    # Aber wenn Angreifer String "admin" direkt übergibt...
    return 0;
}

# Erwarteter Aufruf: check_admin("alice", ["admin", "users"])
# Angriffs-Aufruf: check_admin("hacker", "admin")  # Umgeht ordnungsgemäße Prüfung
// Anfällig: JavaScript Type Confusion
function vulnerableCompare(a, b) {
    // Anfällig: Loser Vergleich erlaubt Typkonvertierung
    if (a == b) {
        return true;
    }
    return false;
}

// Type-Confusion-Beispiele:
vulnerableCompare("0", 0);      // true - String zu Zahl konvertiert
vulnerableCompare("", false);    // true - beide zu falsy konvertiert
vulnerableCompare(null, undefined);  // true - Spezialfall
vulnerableCompare([], false);    // true - Array zu 0 konvertiert
vulnerableCompare("1e1", 10);   // true - wissenschaftliche Notation

// Sicherheitsimplikationen
function vulnerableAuthCheck(inputToken, storedToken) {
    // Angreifer sendet Array: {"token": [true]}
    // [true] == "actual_token" könnte unerwartetes Verhalten zeigen
    if (inputToken == storedToken) {
        return true;  // Könnte Zugang falsch gewähren
    }
    return false;
}

Korrigierter Code

// Korrigiert: Tagged Union mit expliziter Typverfolgung
#include <stdio.h>
#include <string.h>
#include <assert.h>

typedef enum {
    MSG_TYPE_INVALID = 0,
    MSG_TYPE_INTEGER = 1,
    MSG_TYPE_STRING = 2
} MessageType;

typedef struct {
    MessageType type;  // Explizites Typ-Tag
    union {
        int value;
        char *data;
    } payload;
} Message;

void fixed_process_message(Message *msg) {
    // Korrigiert: Typ vor Zugriff validieren
    assert(msg != NULL);

    switch (msg->type) {
        case MSG_TYPE_INTEGER:
            printf("Integer-Wert: %d\n", msg->payload.value);
            break;

        case MSG_TYPE_STRING:
            if (msg->payload.data != NULL) {
                printf("String-Daten: %s\n", msg->payload.data);
            }
            break;

        default:
            fprintf(stderr, "Ungültiger Nachrichtentyp: %d\n", msg->type);
            break;
    }
}

// Korrigiert: Typsichere Nachrichten-Erstellungsfunktionen
Message create_integer_message(int value) {
    Message msg;
    msg.type = MSG_TYPE_INTEGER;
    msg.payload.value = value;
    return msg;
}

Message create_string_message(char *data) {
    Message msg;
    msg.type = MSG_TYPE_STRING;
    msg.payload.data = data;
    return msg;
}
// Korrigiert: C++ mit sicherem Downcasting
#include <iostream>
#include <typeinfo>

class Base {
public:
    virtual ~Base() = default;
    virtual void print() const { std::cout << "Base\n"; }

    // Korrigiert: Laufzeit-Typidentifikation
    virtual const char* getType() const { return "Base"; }
};

class Derived : public Base {
public:
    char buffer[100] = {0};

    void print() const override {
        std::cout << "Derived: " << buffer << "\n";
    }

    const char* getType() const override { return "Derived"; }
};

void fixed_process(Base* obj) {
    if (obj == nullptr) {
        return;
    }

    // Korrigiert: dynamic_cast für sicheres Downcasting verwenden
    Derived* d = dynamic_cast<Derived*>(obj);

    if (d != nullptr) {
        // Sicher: d ist tatsächlich ein Derived-Objekt
        d->print();
        strncpy(d->buffer, "data", sizeof(d->buffer) - 1);
    } else {
        // Kein Derived-Objekt - entsprechend behandeln
        std::cerr << "Objekt ist nicht vom Typ Derived\n";
        obj->print();  // Basisklassen-Interface verwenden
    }
}

// Noch besser: std::variant für typsichere Unions verwenden (C++17)
#include <variant>
#include <string>

using SafeMessage = std::variant<int, std::string>;

void process_safe_message(const SafeMessage& msg) {
    std::visit([](auto&& arg) {
        using T = std::decay_t<decltype(arg)>;
        if constexpr (std::is_same_v<T, int>) {
            std::cout << "Integer: " << arg << "\n";
        } else if constexpr (std::is_same_v<T, std::string>) {
            std::cout << "String: " << arg << "\n";
        }
    }, msg);
}
// Korrigiert: PHP mit striktem Vergleich
<?php
function fixed_auth($input_hash, $stored_hash) {
    // Korrigiert: === für strikten Typvergleich verwenden
    if (!is_string($input_hash) || !is_string($stored_hash)) {
        return false;  // Nicht-String-Eingaben ablehnen
    }

    // Korrigiert: hash_equals für zeitkonstanten Vergleich verwenden
    if (hash_equals($stored_hash, $input_hash)) {
        return true;
    }
    return false;
}

// Besser: Immer strikten Vergleich verwenden
$stored_hash = "0e462097431906509019562988736854";
$input_hash = "0";

// "0" === "0e462097431906509019562988736854" ist FALSCH (String-Vergleich)
$result = ($input_hash === $stored_hash);  // Gibt korrekt false zurück

// Noch besser: Explizite Typvalidierung
function validate_and_compare($input, $expected) {
    if (!is_string($input)) {
        throw new TypeError("Input muss String sein");
    }

    if (strlen($input) !== strlen($expected)) {
        return false;
    }

    return hash_equals($expected, $input);
}
?>
# Korrigiert: Perl mit expliziter Typprüfung
use strict;
use warnings;
use Scalar::Util qw(reftype);

sub fixed_check_admin {
    my ($username, $groups) = @_;

    # Korrigiert: Typ explizit validieren
    unless (ref($groups) eq 'ARRAY') {
        die "groups muss eine Array-Referenz sein";
    }

    # Korrigiert: Ordnungsgemäße Array-Mitgliedschaftsprüfung
    foreach my $group (@{$groups}) {
        if ($group eq "admin") {
            return 1;
        }
    }

    return 0;
}

# Verwendung mit Validierung
sub safe_check_admin {
    my ($username, $groups) = @_;

    # Typvalidierung
    my $ref_type = reftype($groups);

    if (!defined $ref_type || $ref_type ne 'ARRAY') {
        warn "Ungültiger groups-Parametertyp";
        return 0;
    }

    return grep { $_ eq 'admin' } @{$groups} ? 1 : 0;
}
// Korrigiert: JavaScript mit strikter Gleichheit und Typvalidierung
function fixedCompare(a, b) {
    // Korrigiert: Strikte Gleichheit verwenden (keine Typkonvertierung)
    if (a === b) {
        return true;
    }
    return false;
}

// Korrigiert: Explizite Typvalidierung
function fixedAuthCheck(inputToken, storedToken) {
    // Typen explizit validieren
    if (typeof inputToken !== 'string') {
        console.warn('inputToken muss ein String sein');
        return false;
    }

    if (typeof storedToken !== 'string') {
        console.warn('storedToken muss ein String sein');
        return false;
    }

    // Korrigiert: Strikter Gleichheitsvergleich
    if (inputToken === storedToken) {
        return true;
    }
    return false;
}

// Noch besser: TypeScript für Kompilierzeit-Typsicherheit verwenden
// function fixedAuthCheck(inputToken: string, storedToken: string): boolean {
//     return inputToken === storedToken;
// }

// Für Objekte, Struktur validieren
function validateMessageType(msg) {
    if (typeof msg !== 'object' || msg === null) {
        throw new TypeError('Message muss ein Objekt sein');
    }

    if (typeof msg.type !== 'string') {
        throw new TypeError('Message-Typ muss ein String sein');
    }

    const validTypes = ['integer', 'string', 'binary'];
    if (!validTypes.includes(msg.type)) {
        throw new TypeError(`Ungültiger Message-Typ: ${msg.type}`);
    }

    return true;
}

Ausgenutzt in der Praxis

Internet Explorer Type Confusion (Microsoft, 2014)

CVE-2014-0322 war eine Type-Confusion-Schwachstelle in Internet Explorer, die bei Watering-Hole-Angriffen gegen die US Veterans of Foreign Wars-Website aktiv ausgenutzt wurde. Angreifer könnten beliebigen Code ausführen und Systeme von Besuchern kompromittieren.

Adobe Flash Player Type Confusion (Adobe, 2015-2016)

Mehrere Type-Confusion-Schwachstellen in Adobe Flash Player wurden von Exploit-Kits wie Angler und Nuclear aktiv ausgenutzt, um Malware zu verbreiten. Diese Schwachstellen ermöglichten Remote-Codeausführung durch bösartige Flash-Inhalte.

Chrome V8 Type Confusion (Google, fortlaufend)

Type-Confusion-Schwachstellen in der V8 JavaScript-Engine von Chrome wurden mehrfach in freier Wildbahn ausgenutzt. CVE-2021-21224 ermöglichte Angreifern die Ausführung von beliebigem Code durch speziell gestaltete Webseiten.


Tools zum Testen/Ausnutzen

  • AddressSanitizer — Laufzeiterkennung von Speicherfehlern einschließlich Type Confusion.

  • TypeSan — Compile-Zeit-Tool zur Erkennung von Type-Confusion-Schwachstellen in C++.

  • Clang Static Analyzer — Statische Analyse zur Erkennung potenzieller Typfehler.


CVE-Beispiele

  • CVE-2025-32352: PHP-Authentifizierungsumgehung via Type Confusion beim Vergleich von MD5-Hashes.
  • CVE-2010-4577: CSS-Parsing Type Confusion verursacht Out-of-Bounds-Lesen im Browser.
  • CVE-2011-0611: Größeninkonsistenz zwischen Typen ermöglichte beliebige Codeausführung.
  • CVE-2010-0258: Datei-Parsing Type Confusion verursachte Objekt-Typ-Fehlinterpretation.

Verwandte CWEs

  • CWE-704: Falsche Typkonvertierung oder Cast (Eltern)
  • CWE-1287: Unzureichende Validierung des spezifizierten Eingabetyps (verwandt)
  • CWE-119: Unzureichende Einschränkung von Operationen innerhalb der Grenzen eines Speicherpuffers (kann folgen)
  • CWE-136: Typfehler (Kategorie)

Referenzen

  1. MITRE Corporation. "CWE-843: Access of Resource Using Incompatible Type ('Type Confusion')." https://cwe.mitre.org/data/definitions/843.html
  2. Microsoft. "Understanding Type Confusion Vulnerabilities."
  3. Google Project Zero. Type Confusion vulnerability research.