Unzureichende Prüfung auf ungewöhnliche oder außergewöhnliche Bedingungen

Beschreibung

Eine unzureichende Prüfung auf ungewöhnliche oder außergewöhnliche Bedingungen tritt auf, wenn ein Produkt nicht oder falsch auf ungewöhnliche oder außergewöhnliche Bedingungen prüft, die im normalen Tagesbetrieb nicht häufig auftreten. Diese Bedingungen umfassen Fehler-Rückgabewerte von Funktionen, Ressourcenerschöpfung, unerwartete Eingabewerte, Hardware-Ausfälle, Netzwerk-Timeouts und Grenzfälle bei der Datenverarbeitung. Wenn außergewöhnliche Bedingungen nicht ordnungsgemäß behandelt werden, kann das Programm in einem ungültigen Zustand weiterarbeiten, unerwartet abstürzen, Daten beschädigen oder Sicherheitslücken schaffen, die Angreifer ausnutzen können.

Risiko

Das Versäumnis, außergewöhnliche Bedingungen zu prüfen, führt zu unvorhersehbarem Verhalten und Sicherheitslücken. CVE-2024-52895 in IBM i ermöglicht entfernten Angreifern einen Denial-of-Service durch Umgehung von Datenbank-Berechtigungsprüfungen. CVE-2024-27457 in HPE ProLiant/Synergy-Servern erlaubt lokalen Administratoren die Offenlegung sensibler Informationen. CVE-2024-54175 in IBM MQ ermöglicht lokalen Denial-of-Service. Schneider Electric Modicon PLCs waren anfällig für DoS durch unsachgemäße Bedingungsprüfungen. Diese Schwachstellen ermöglichen typischerweise Denial-of-Service, können aber manchmal zu Informationsoffenlegung oder Codeausführung führen, wenn das Programm in einen unerwarteten Zustand gerät.

Lösung

Prüfen Sie Rückgabewerte aller Funktionen, die Fehlerbedingungen anzeigen können. Implementieren Sie umfassende Fehlerbehandlung für alle externen Interaktionen (Datei-I/O, Netzwerk, Datenbank). Verwenden Sie Ausnahmebehandlungsmechanismen angemessen. Validieren Sie alle Eingabedaten einschließlich Grenzfälle. Implementieren Sie Ressourcenlimit-Prüfungen. Verwenden Sie defensive Programmiertechniken mit mehreren Validierungsebenen. Protokollieren Sie außergewöhnliche Bedingungen zur Überwachung. Implementieren Sie graceful Degradation anstelle von stillem Versagen. Verwenden Sie statische Analysetools zur Identifizierung ungeprüfter Rückgabewerte.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Denial-of-Service

Unbehandelte außergewöhnliche Bedingungen führen oft zu Abstürzen oder Hängern.
IntegritätBereich: Datenbeschädigung

Weiterbetrieb nach Fehlern kann Daten beschädigen oder falsche Ergebnisse produzieren.
VertraulichkeitBereich: Informationsoffenlegung

Fehlerbedingungen können internen Zustand oder sensible Informationen preisgeben.

Beispielcode + Lösungscode

Anfälliger Code

// ANFÄLLIG: Ignorieren des malloc-Rückgabewerts
#include <stdlib.h>
#include <string.h>

void process_data(size_t size) {
    char* buffer = malloc(size);
    // malloc kann bei Fehler NULL zurückgeben!
    strcpy(buffer, "data");  // Absturz wenn malloc fehlschlug
}

// ANFÄLLIG: Ignorieren von Dateioperationsfehlern
void write_config(const char* config) {
    FILE* f = fopen("/etc/myapp/config", "w");
    // fopen kann fehlschlagen - Datei existiert nicht, keine Berechtigung, etc.
    fputs(config, f);  // Undefiniertes Verhalten wenn f NULL ist
    fclose(f);
}

// ANFÄLLIG: Keine Prüfung von Netzwerkoperationsergebnissen
int send_data(int socket, const char* data, size_t len) {
    ssize_t sent = send(socket, data, len, 0);
    // Ignoriert sent < len (teilweises Senden) und sent == -1 (Fehler)
    return 0;  // Gibt Erfolg zurück unabhängig vom tatsächlichen Ergebnis
}
# ANFÄLLIG: Keine Behandlung von Datei-nicht-gefunden
def read_config(path):
    f = open(path)  # Löst FileNotFoundError aus wenn fehlend
    return f.read()  # Schließt Datei nie bei Ausnahme

# ANFÄLLIG: Ignorieren von Datenbankfehlern
def get_user(user_id):
    conn = database.connect()
    result = conn.execute(f"SELECT * FROM users WHERE id = {user_id}")
    # Was wenn Verbindung fehlschlägt? Abfrage fehlschlägt? Keine Ergebnisse?
    return result[0]  # IndexError wenn keine Ergebnisse

# ANFÄLLIG: Annahme dass JSON-Parsing erfolgreich ist
import json

def process_request(body):
    data = json.loads(body)  # JSONDecodeError möglich
    return data['name']  # KeyError wenn 'name' fehlt
// ANFÄLLIG: Verschlucken von Ausnahmen
public class DataProcessor {
    public void processFile(String path) {
        try {
            FileInputStream fis = new FileInputStream(path);
            // Datei verarbeiten...
        } catch (Exception e) {
            // Still verschluckt - Aufrufer denkt Operation war erfolgreich!
        }
    }

    // ANFÄLLIG: Keine Prüfung auf null von externen Methoden
    public void processUser(String userId) {
        User user = userRepository.findById(userId);
        // findById gibt null zurück wenn nicht gefunden
        String email = user.getEmail();  // NullPointerException!
        sendNotification(email);
    }
}

Behobener Code

// SICHER: Alle Rückgabewerte prüfen
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <syslog.h>

int process_data_safe(size_t size) {
    if (size == 0 || size > MAX_BUFFER_SIZE) {
        syslog(LOG_ERR, "Ungültige Puffergröße: %zu", size);
        return -EINVAL;
    }

    char* buffer = malloc(size);
    if (buffer == NULL) {
        syslog(LOG_ERR, "Konnte %zu Bytes nicht allokieren: %s",
               size, strerror(errno));
        return -ENOMEM;
    }

    strncpy(buffer, "data", size - 1);
    buffer[size - 1] = '\0';

    // ... buffer verwenden ...

    free(buffer);
    return 0;
}

// SICHER: Alle Dateioperationsergebnisse prüfen
int write_config_safe(const char* config) {
    if (config == NULL) {
        return -EINVAL;
    }

    FILE* f = fopen("/etc/myapp/config", "w");
    if (f == NULL) {
        syslog(LOG_ERR, "Konnte Config-Datei nicht öffnen: %s", strerror(errno));
        return -errno;
    }

    int result = fputs(config, f);
    if (result == EOF) {
        syslog(LOG_ERR, "Konnte Config nicht schreiben: %s", strerror(errno));
        fclose(f);
        return -EIO;
    }

    if (fclose(f) != 0) {
        syslog(LOG_ERR, "Konnte Config-Datei nicht schließen: %s", strerror(errno));
        return -EIO;
    }

    return 0;
}

// SICHER: Teilweises Senden und Fehler behandeln
int send_data_safe(int socket, const char* data, size_t len) {
    size_t total_sent = 0;

    while (total_sent < len) {
        ssize_t sent = send(socket, data + total_sent, len - total_sent, 0);

        if (sent < 0) {
            if (errno == EINTR) continue;  // Unterbrochen, erneut versuchen
            syslog(LOG_ERR, "Senden fehlgeschlagen: %s", strerror(errno));
            return -errno;
        }

        if (sent == 0) {
            syslog(LOG_WARNING, "Verbindung während Senden geschlossen");
            return -ECONNRESET;
        }

        total_sent += sent;
    }

    return 0;
}
# SICHER: Ordentliche Ausnahmebehandlung
from pathlib import Path
import json
import logging

logger = logging.getLogger(__name__)

def read_config_safe(path):
    """Config-Datei mit ordentlicher Fehlerbehandlung lesen."""
    try:
        with open(path, 'r') as f:  # Context Manager stellt Schließen sicher
            return f.read()
    except FileNotFoundError:
        logger.error(f"Config-Datei nicht gefunden: {path}")
        raise ConfigurationError(f"Fehlende Konfigurationsdatei: {path}")
    except PermissionError:
        logger.error(f"Zugriff verweigert beim Lesen: {path}")
        raise ConfigurationError(f"Kann Konfiguration nicht lesen: {path}")
    except IOError as e:
        logger.error(f"IO-Fehler beim Lesen der Config: {e}")
        raise ConfigurationError(f"Fehler beim Lesen der Konfiguration: {e}")

def get_user_safe(user_id):
    """Benutzer mit ordentlicher Fehlerbehandlung abrufen."""
    try:
        conn = database.connect()
    except DatabaseError as e:
        logger.error(f"Datenbankverbindung fehlgeschlagen: {e}")
        raise ServiceUnavailableError("Datenbank nicht verfügbar")

    try:
        result = conn.execute(
            "SELECT * FROM users WHERE id = %s",
            (user_id,)  # Parametrisierte Abfrage
        )
        rows = result.fetchall()

        if not rows:
            logger.info(f"Benutzer nicht gefunden: {user_id}")
            raise UserNotFoundError(f"Benutzer {user_id} nicht gefunden")

        return rows[0]
    except DatabaseError as e:
        logger.error(f"Abfrage fehlgeschlagen: {e}")
        raise ServiceUnavailableError("Datenbankabfrage fehlgeschlagen")
    finally:
        conn.close()

def process_request_safe(body):
    """JSON-Request mit Validierung verarbeiten."""
    try:
        data = json.loads(body)
    except json.JSONDecodeError as e:
        logger.warning(f"Ungültiges JSON: {e}")
        raise ValidationError("Ungültiges JSON-Format")

    if not isinstance(data, dict):
        raise ValidationError("JSON-Objekt erwartet")

    name = data.get('name')
    if name is None:
        raise ValidationError("Pflichtfeld fehlt: name")

    if not isinstance(name, str) or len(name) == 0:
        raise ValidationError("Ungültiges name-Feld")

    return name
// SICHER: Ordentliche Ausnahmebehandlung und Null-Prüfungen
public class SecureDataProcessor {
    private static final Logger logger = LoggerFactory.getLogger(SecureDataProcessor.class);

    public Result processFile(String path) throws ProcessingException {
        Objects.requireNonNull(path, "Pfad darf nicht null sein");

        try (FileInputStream fis = new FileInputStream(path)) {
            // Datei mit try-with-resources verarbeiten
            return processStream(fis);
        } catch (FileNotFoundException e) {
            logger.error("Datei nicht gefunden: {}", path);
            throw new ProcessingException("Konfigurationsdatei nicht gefunden", e);
        } catch (IOException e) {
            logger.error("IO-Fehler bei Dateiverarbeitung: {}", e.getMessage());
            throw new ProcessingException("Fehler beim Lesen der Datei", e);
        }
    }

    public void processUser(String userId) throws UserNotFoundException, ServiceException {
        Objects.requireNonNull(userId, "Benutzer-ID darf nicht null sein");

        User user = userRepository.findById(userId)
            .orElseThrow(() -> {
                logger.info("Benutzer nicht gefunden: {}", userId);
                return new UserNotFoundException("Benutzer nicht gefunden: " + userId);
            });

        String email = user.getEmail();
        if (email == null || email.isBlank()) {
            logger.warn("Benutzer {} hat keine E-Mail", userId);
            throw new ValidationException("Benutzer hat keine E-Mail-Adresse");
        }

        try {
            sendNotification(email);
        } catch (NotificationException e) {
            logger.error("Benachrichtigung an {} fehlgeschlagen: {}", email, e.getMessage());
            // Entscheiden: erneut werfen, wiederholen, oder graceful degradieren
            throw new ServiceException("Benachrichtigung fehlgeschlagen", e);
        }
    }
}

Ausgenutzt in der Praxis

IBM i Datenbank-Beschränkungsumgehung (IBM, 2025)

CVE-2024-52895 in IBM i 7.4-7.5 ermöglicht entfernten Benutzern einen Denial-of-Service durch Umgehung von Datenbank-Berechtigungsprüfungen, was Datenbankinfrastrukturdateien beeinträchtigt und zu fehlerhaftem Softwareverhalten führt.

HPE ProLiant Informationsoffenlegung (HPE, 2025)

CVE-2024-27457 in HPE ProLiant und Synergy-Servern mit bestimmten Intel-Prozessoren ermöglicht lokalen Administratoren die Offenlegung sensibler Informationen durch unzureichende Prüfung auf ungewöhnliche Bedingungen.

IBM MQ Denial-of-Service (IBM, 2024)

CVE-2024-54175 in IBM MQ ermöglicht lokalen Benutzern einen Denial-of-Service aufgrund unzureichender Prüfung auf ungewöhnliche oder außergewöhnliche Bedingungen (CVSS 5.5).


Tools zum Testen/Ausnutzen

  • Coverity - Statische Analyse für ungeprüfte Rückgabewerte.

  • Cppcheck - C/C++ statische Analyse.

  • SpotBugs - Java statische Analyse für Null-Behandlung.


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-754: Improper Check for Unusual or Exceptional Conditions." https://cwe.mitre.org/data/definitions/754.html

  2. CERT. "ERR33-C. Detect and handle standard library errors." https://wiki.sei.cmu.edu/confluence/display/c/ERR33-C.+Detect+and+handle+standard+library+errors