Fehlerhafter Kontrollfluss-Bereich

Beschreibung

Fehlerhafter Kontrollfluss-Bereich ist eine Schwachstelle, bei der Software den Kontrollfluss nach Abschluss einer Aufgabe oder Erkennung einer ungewöhnlichen Bedingung nicht ordnungsgemäß an den entsprechenden Ort zurückgibt. Dies tritt auf, wenn die Codeausführung nach kritischen Operationen oder Exceptions ungeeignet fortgesetzt wird, anstatt zum korrekten Kontrollfluss-Ziel zurückzukehren. Häufige Erscheinungsformen umfassen das Fortsetzen der Ausführung nach dem Senden von Redirects, das Versäumnis, nach der Exception-Behandlung zu beenden, oder die unsachgemäße Verwendung von System.exit() in Umgebungen, wo es mehr als beabsichtigt beendet.

Risiko

Fehlerhafter Kontrollfluss-Bereich schafft schwerwiegende Sicherheitslücken. Execution After Redirect (EAR) Schwachstellen ermöglichen Angreifern den Zugriff auf Inhalte, die durch Authentifizierungs- oder Autorisierungs-Redirects geschützt sein sollten. Nicht gefangene Exceptions können Anwendungen zum Absturz bringen oder sie in inkonsistenten Zuständen belassen. Die Verwendung von System.exit() in J2EE-Umgebungen beendet den gesamten Container statt nur die aktuelle Anfrage. Die Rückkehr aus finally-Blöcken kann Exceptions unterdrücken und Fehlerbedingungen verbergen. Diese Probleme können zur Umgehung der Authentifizierung, Informationspreisgabe, Denial of Service und unvorhersehbarem Anwendungsverhalten führen.

Lösung

Beenden Sie die Ausführung nach Redirects immer ordnungsgemäß durch Aufruf von exit() oder return. Behandeln Sie alle Exceptions auf geeigneten Ebenen - lassen Sie sie nicht unerwartet propagieren. Verwenden Sie niemals System.exit() in Container-verwalteten Umgebungen; werfen Sie stattdessen entsprechende Exceptions. Vermeiden Sie Rückgaben aus finally-Blöcken. Verwenden Sie statische Analysetools, um Kontrollfluss-Anomalien zu erkennen. Strukturieren Sie Code so, dass geschützte Operationen nur in expliziten Erfolgspfaden ausgeführt werden. Implementieren Sie umfassende Exception-Behandlung, die alle Codepfade abdeckt.

Häufige Auswirkungen

AuswirkungDetails
AndereBereich: Ändere

Ausführungslogik ändern - Code wird ausgeführt, wenn er umgangen oder beendet werden sollte.
ZugriffskontrolleBereich: Zugriffskontrolle

Schutzmechanismus umgehen - Authentifizierungs- und Autorisierungskontrollen können umgangen werden.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Absturz, Beenden oder Neustart - Unsachgemäße Beendigung kann Anwendungen oder ganze Container zum Absturz bringen.

Beispielcode

Verwundbarer Code

<?php
// Verwundbar: Ausführung wird nach Redirect fortgesetzt
function checkAuthorization() {
    if (!isLoggedIn()) {
        http_redirect("/login.php");
        // Verwundbar: Kein exit - Ausführung wird fortgesetzt
    }
}

checkAuthorization();

// Sensitiver Code wird auch für nicht autorisierte Benutzer ausgeführt
$secrets = getSecrets();
echo $secrets;  // Nach Redirect-Header offengelegt
?>
// Verwundbar: Nicht gefangene Exception im Servlet
import javax.servlet.http.*;

public class VulnerableServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request,
                        HttpServletResponse response) {

        // Verwundbar: DNS-Lookup kann nicht gefangene Exception werfen
        String hostname = request.getParameter("host");
        InetAddress addr = InetAddress.getByName(hostname);
        // UnknownHostException nicht gefangen - Servlet stürzt ab

        // Rest der Verarbeitung wird nie abgeschlossen
        processRequest(addr);
    }
}

// Verwundbar: System.exit() in J2EE-Container
@Stateless
public class VulnerableEJB {

    public void processData(String data) {
        try {
            validateData(data);
        } catch (ValidationException e) {
            // Verwundbar: Beendet gesamte JVM, nicht nur diese Anfrage!
            System.exit(1);
        }

        processValidData(data);
    }
}
// Verwundbar: Return in finally-Block
public class VulnerableFinally {

    public int vulnerableMethod() {
        try {
            riskyOperation();
            return 1;
        } catch (Exception e) {
            return -1;  // Fehlerindikator
        } finally {
            // Verwundbar: Return in finally unterdrückt die try/catch-Returns!
            return 0;  // Dies wird IMMER zurückgegeben, verbirgt Fehler
        }
    }

    public void vulnerableFinallyException() {
        try {
            throw new SecurityException("Zugriff verweigert");
        } finally {
            // Verwundbar: Return in finally verschluckt die Exception
            return;  // SecurityException ist verloren!
        }
    }
}
# Verwundbar: Exception nicht richtig behandelt
def vulnerable_process(user_input):
    try:
        result = parse_data(user_input)
    except ValueError:
        # Verwundbar: Fangt Exception aber fahrt mit undefiniertem result fort
        pass

    # result kann hier undefiniert sein
    return process_result(result)  # NameError oder verwendet veralteten Wert

# Verwundbar: Zu breites Exception-Fangen
def vulnerable_auth():
    try:
        user = authenticate(username, password)
        if user is None:
            raise AuthenticationError("Ungültige Anmeldedaten")
        return user
    except Exception:
        # Verwundbar: Fangt ALLE Exceptions einschließlich AuthenticationError
        # und fahrt fort als ware nichts passiert
        pass

    # Fahrt ohne Authentifizierung fort!
    return default_user()  # Gefahrlicher Fallback
// Verwundbar: Ungeprüfte Exception-Propagierung
public class VulnerableController {

    public ActionResult Process(string data) {
        // Verwundbar: NullReferenceException kann Anfrage zum Absturz bringen
        var result = data.ToUpper();  // Absturz wenn data null ist

        // Wird nie ausgeführt wenn data null ist
        return View(result);
    }
}

Lösungscode

<?php
// Behoben: Sofort nach Redirect beenden
function checkAuthorization() {
    if (!isLoggedIn()) {
        http_redirect("/login.php");
        exit();  // Behoben: Ausführung beenden
    }
}

checkAuthorization();

// Wird nur ausgeführt wenn autorisiert
$secrets = getSecrets();
echo $secrets;

// Alternative: Code richtig strukturieren
if (isLoggedIn()) {
    $secrets = getSecrets();
    echo $secrets;
} else {
    http_redirect("/login.php");
    exit();
}
?>
// Behoben: Ordnungsgemäße Exception-Behandlung
import javax.servlet.http.*;

public class SecureServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request,
                        HttpServletResponse response)
            throws ServletException, IOException {

        String hostname = request.getParameter("host");

        try {
            // Behoben: Potentielle Exception behandeln
            InetAddress addr = InetAddress.getByName(hostname);
            processRequest(addr);

        } catch (UnknownHostException e) {
            // Behoben: Ordnungsgemäße Fehlerantwort statt Absturz
            response.sendError(HttpServletResponse.SC_BAD_REQUEST,
                             "Ungültiger Hostname");
            return;
        }
    }
}

// Behoben: System.exit() in Containern nicht verwenden
@Stateless
public class SecureEJB {

    public void processData(String data) throws ValidationException {
        try {
            validateData(data);
        } catch (ValidationException e) {
            // Behoben: Exception werfen statt System.exit()
            // Container den Fehler ordnungsgemäß behandeln lassen
            throw e;
        }

        processValidData(data);
    }
}
// Behoben: Niemals aus finally zurückkehren
public class SecureFinally {

    public int secureMethod() {
        int result = 0;

        try {
            riskyOperation();
            result = 1;
        } catch (Exception e) {
            result = -1;
        } finally {
            // Behoben: Nur Bereinigung, kein Return
            cleanup();
        }

        return result;  // Return nach finally-Block
    }

    public void secureMethodWithResource() {
        // Behoben: try-with-resources stattdessen verwenden
        try (Resource res = new Resource()) {
            res.process();
        }  // Automatische Bereinigung, kein finally nötig
    }
}
# Behoben: Ordnungsgemäße Exception-Behandlung
def secure_process(user_input):
    try:
        result = parse_data(user_input)
    except ValueError as e:
        # Behoben: Exception richtig behandeln
        logging.error(f"Parse-Fehler: {e}")
        return None  # Oder benutzerdefinierte Exception werfen

    return process_result(result)

# Behoben: Spezifische Exception-Behandlung
def secure_auth():
    try:
        user = authenticate(username, password)
        if user is None:
            raise AuthenticationError("Ungültige Anmeldedaten")
        return user
    except AuthenticationError:
        # Behoben: Authentifizierungsfehler erneut werfen
        raise
    except ConnectionError as e:
        # Behoben: Spezifische Infrastrukturfehler behandeln
        logging.error(f"Verbindung fehlgeschlagen: {e}")
        raise AuthenticationError("Dienst nicht verfügbar")

# Behoben: Kontextmanager verwenden
def secure_file_process(filename):
    try:
        with open(filename, 'r') as f:
            data = f.read()
            return process(data)
    except FileNotFoundError:
        return None
    except PermissionError:
        raise SecurityError("Zugriff verweigert")
// Behoben: Null-Prüfung und Exception-Behandlung
public class SecureController {

    public ActionResult Process(string data) {
        // Behoben: Auf null prüfen
        if (string.IsNullOrEmpty(data)) {
            return BadRequest("Daten erforderlich");
        }

        try {
            var result = data.ToUpper();
            return View(result);
        } catch (Exception ex) {
            _logger.LogError(ex, "Verarbeitung fehlgeschlagen");
            return StatusCode(500, "Interner Fehler");
        }
    }
}

CVE-Beispiele

  • CVE-2014-1266: Apple SSL "goto fail" Bug - fehlerhafte goto-Anweisung führte zur Umgehung der Zertifikatsvalidierung.
  • CVE-2023-21087: Nicht gefangene Exception im Smartphone-OS verursachte persistente Bootschleife (DoS).
  • CVE-2007-2713: Ausführung nach Redirect ermöglichte unbefugten Administratorzugriff.

Referenzen

  1. MITRE Corporation. "CWE-705: Incorrect Control Flow Scoping." https://cwe.mitre.org/data/definitions/705.html
  2. CWE-691: Insufficient Control Flow Management.
  3. CWE-698: Execution After Redirect.