Erreichbare Assertion

Beschreibung

Erreichbare Assertion tritt auf, wenn ein Produkt eine assert()-Anweisung oder ähnliche Anweisung enthält, die von einem Angreifer ausgelöst werden kann, was zu einem Anwendungsabbruch oder anderem Verhalten führt, das schwerwiegender als notwendig ist. Assertions sind Debugging-Tools, die dazu gedacht sind, Logikfehler während der Entwicklung zu erkennen - sie sollten niemals durch externe Eingaben in der Produktion ausgelöst werden. Wenn Assertions durch Angreifer-kontrollierte Eingaben erreichbar sind, können sie ausgenutzt werden, um Denial of Service durch Absturz der Anwendung zu verursachen. In Server-Anwendungen, die mehrere Verbindungen handhaben, kann ein Assertion-Fehler den gesamten Prozess beenden und alle Benutzer betreffen.

Risiko

Erreichbare Assertions bieten Angreifern einen zuverlässigen Denial-of-Service-Mechanismus. CVE-2025-49630 in Apache HTTP Servers mod_proxy_http2 ermöglicht entfernten Angreifern, den Server mit speziell erstellten Anfragen zum Absturz zu bringen, wenn HTTP/2-Backends geproxyt werden. CVE-2025-41068 in Open5GS 5G-Kernnetzwerk ermöglicht Angreifern, die Network Repository Function (NRF) zum Absturz zu bringen und Mobilfunknetzwerk-Service-Discovery zu storen. CVE-2025-59029 in PowerDNS Recursor ermöglicht Remote-DoS durch manipulierte DNS-Abfragen. Diese Schwachstellen sind besonders schwerwiegend in Infrastruktursoftware, wo Abstürze viele Benutzer betreffen oder kritische Dienste storen.

Lösung

Entfernen oder deaktivieren Sie Assertions in Produktions-Builds. Verwenden Sie Compile-Zeit-Flags, um Assertions auszuschließen (NDEBUG in C/C++). Ersetzen Sie Assertions durch ordnungsgemäße Fehlerbehandlung, die das Problem protokolliert und anmutig zurückkehrt. Verwenden Sie niemals Assertions, um externe Eingaben zu validieren - verwenden Sie explizite Eingabevalidierung mit angemessenen Fehlerantworten. Wenn Assertions bleiben müssen, stellen Sie sicher, dass sie nicht durch benutzergesteuerte Eingaben ausgelöst werden können. Implementieren Sie Prozessüberwachung, um abgestürzte Dienste automatisch neu zu starten.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Denial of Service

Assertion-Fehler beenden die Anwendung und verursachen Dienstunterbrechung für alle Benutzer.
ZuverlässigkeitBereich: Dienstunterbrechung

Wiederholte Assertion-Auslöser können Dienste nicht verfügbar halten und die Systemzuverlässigkeit beeinträchtigen.
SicherheitBereich: Informationsoffenlegung

Assertion-Meldungen können internen Zustand, Dateipfade oder Debug-Informationen offenlegen.

Beispielcode + Lösungscode

Verwundbarer Code

// VERWUNDBAR: Assert auf Benutzereingabe
#include <assert.h>
#include <string.h>

void process_request(const char* input, size_t length) {
    // Angreifer kann length > MAX_INPUT senden um Server zum Absturz zu bringen!
    assert(length <= MAX_INPUT);  // Bringt gesamten Prozess zum Absturz

    // Eingabe verarbeiten
    handle_data(input, length);
}

// VERWUNDBAR: Assert in Netzwerkprotokoll-Handler
void handle_packet(struct Packet* pkt) {
    // Fehlerhaftes Paket kann Server zum Absturz bringen
    assert(pkt->version == PROTOCOL_VERSION);
    assert(pkt->checksum == calculate_checksum(pkt));

    process_packet(pkt);
}
// VERWUNDBAR: Assert in Multi-Thread-Server
class ConnectionHandler {
public:
    void handleRequest(Request& req) {
        // Benutzer-kontrolliertes type-Feld kann gesamten Server zum Absturz bringen
        assert(req.getType() >= 0 && req.getType() < REQUEST_TYPE_COUNT);

        handlers[req.getType()]->process(req);
    }
};

// VERWUNDBAR: Assert in XML-Parser
void parseXMLElement(XMLNode* node) {
    // Fehlerhaftes XML kann Assertion auslösen
    assert(node != nullptr);
    assert(node->name != nullptr);
    assert(strlen(node->name) > 0);

    // Element verarbeiten
}
# VERWUNDBAR: Assert in Webanwendung
def process_upload(file_data, file_size):
    # Angreifer kontrolliert file_size-Parameter
    assert file_size > 0, "Dateigröße muss positiv sein"
    assert file_size < MAX_FILE_SIZE, f"Datei zu groß: {file_size}"

    # Python-Assertions können mit -O-Flag deaktiviert werden,
    # aber Server verwendet möglicherweise dieses Flag nicht

# VERWUNDBAR: Assert in API-Handler
def get_user(user_id):
    assert isinstance(user_id, int), "Benutzer-ID muss Integer sein"
    assert user_id > 0, "Benutzer-ID muss positiv sein"

    return database.find_user(user_id)

Lösungscode

// SICHER: Ordnungsgemäße Fehlerbehandlung statt assert
#include <errno.h>
#include <syslog.h>

int process_request_safe(const char* input, size_t length) {
    // Eingabe mit ordnungsgemäßer Fehlerbehandlung validieren
    if (length > MAX_INPUT) {
        syslog(LOG_WARNING, "Anfrage zu groß: %zu Bytes", length);
        return -EINVAL;  // Fehler zurückgeben, nicht abstürzen
    }

    if (input == NULL) {
        syslog(LOG_WARNING, "NULL-Eingabe empfangen");
        return -EINVAL;
    }

    return handle_data(input, length);
}

// SICHER: Anmutige Behandlung ungültiger Pakete
int handle_packet_safe(struct Packet* pkt) {
    if (pkt == NULL) {
        log_error("NULL-Paket empfangen");
        return -EINVAL;
    }

    if (pkt->version != PROTOCOL_VERSION) {
        log_warning("Nicht unterstützte Protokollversion: %d", pkt->version);
        return -EPROTONOSUPPORT;
    }

    if (pkt->checksum != calculate_checksum(pkt)) {
        log_warning("Prüfsummenfehler");
        return -EBADMSG;
    }

    return process_packet(pkt);
}

// Assertions nur für interne Logik behalten (Entwicklung)
#ifndef NDEBUG
    // Dies sollte NIEMALS über externe Eingabe erreichbar sein
    assert(internal_state_is_consistent());
#endif
// SICHER: Exception-basierte Fehlerbehandlung
#include <stdexcept>
#include <optional>

class SecureConnectionHandler {
public:
    std::optional<Response> handleRequest(Request& req) {
        // Mit ordnungsgemäßer Fehlerbehandlung validieren
        int type = req.getType();
        if (type < 0 || type >= REQUEST_TYPE_COUNT) {
            logger.warn("Ungültiger Anfrage-Typ: {}", type);
            return std::nullopt;  // Fehler zurückgeben, nicht abstürzen
        }

        try {
            return handlers[type]->process(req);
        } catch (const std::exception& e) {
            logger.error("Anfrageverarbeitung fehlgeschlagen: {}", e.what());
            return std::nullopt;
        }
    }
};

// SICHER: Null-sicheres XML-Parsing
std::optional<XMLElement> parseXMLElementSafe(XMLNode* node) {
    if (node == nullptr) {
        logger.warn("NULL-XML-Knoten");
        return std::nullopt;
    }

    if (node->name == nullptr || strlen(node->name) == 0) {
        logger.warn("XML-Knoten mit leerem Namen");
        return std::nullopt;
    }

    return XMLElement(*node);
}
# SICHER: Exception-basierte Validierung
class ValidationError(Exception):
    pass

def process_upload_safe(file_data, file_size):
    """Verarbeitet Upload mit ordnungsgemäßer Validierung."""
    if not isinstance(file_size, int) or file_size <= 0:
        raise ValidationError("Ungültige Dateigröße")

    if file_size > MAX_FILE_SIZE:
        raise ValidationError(f"Datei zu groß: {file_size} Bytes (max: {MAX_FILE_SIZE})")

    # Upload verarbeiten
    return save_file(file_data, file_size)

# SICHER: Typprüfung mit ordnungsgemäßen Fehlerantworten
def get_user_safe(user_id):
    """Holt Benutzer mit Eingabevalidierung."""
    # Eingabetypen validieren
    try:
        user_id = int(user_id)
    except (TypeError, ValueError):
        raise ValidationError("Benutzer-ID muss gültiger Integer sein")

    if user_id <= 0:
        raise ValidationError("Benutzer-ID muss positiv sein")

    user = database.find_user(user_id)
    if user is None:
        raise NotFoundError(f"Benutzer {user_id} nicht gefunden")

    return user

# Flask-Route mit ordnungsgemäßer Fehlerbehandlung
@app.route('/user/<user_id>')
def get_user_endpoint(user_id):
    try:
        user = get_user_safe(user_id)
        return jsonify(user.to_dict())
    except ValidationError as e:
        return jsonify({'error': str(e)}), 400
    except NotFoundError as e:
        return jsonify({'error': str(e)}), 404

Ausgenutzt in der Praxis

Apache HTTP Server mod_proxy_http2 (Apache, 2025)

CVE-2025-49630 in Apache HTTP Server 2.4.26-2.4.63 ermöglicht entfernten Angreifern, den Server durch Auslösen eines Assertion-Fehlers in mod_proxy_http2 zum Absturz zu bringen, wenn ProxyPreserveHost aktiviert ist und HTTP/2-Backends geproxyt werden.

Open5GS 5G-Kernnetzwerk (Open5GS, 2025)

CVE-2025-41068 in Open5GS bis 2.7.5 ermöglicht Angreifern, die Network Repository Function (NRF) zum Absturz zu bringen, indem ein NF mit ungültigem Typ erstellt und dann abgefragt wird, was eine Assertion auslöst, die die Service-Discovery-Funktion zum Absturz bringt.

PowerDNS Recursor (PowerDNS, 2025)

CVE-2025-59029 in PowerDNS Recursor 5.3.0 ermöglicht entfernten nicht-authentifizierten Angreifern, Denial of Service zu verursachen, indem DNS-Abfragen mit qtype=ANY nach dem Cachen manipulierter Records gesendet werden, was einen Assertion-Fehler auslöst.


Tools zum Testen/Ausnutzen

  • AFL++ — Fuzzing um Assertion-Auslöser zu entdecken
  • Burp Suite — fehlerhafte Anfragen erstellen um Assertions auszulösen
  • American Fuzzy Lop — Coverage-geführtes Fuzzing

CVE-Beispiele

  • CVE-2025-49630 — Apache HTTP Server Assertion-DoS
  • CVE-2025-41068 — Open5GS NRF Assertion-Absturz
  • CVE-2025-59029 — PowerDNS Recursor Assertion-DoS

Referenzen

  1. MITRE. "CWE-617: Reachable Assertion." https://cwe.mitre.org/data/definitions/617.html
  2. CERT. "ERR06-C. Understand the termination behavior of assert() and abort()." https://wiki.sei.cmu.edu/confluence/display/c/ERR06-C.+Understand+the+termination+behavior+of+assert%28%29+and+abort%28%29