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
| Auswirkung | Details |
|---|---|
| Andere | Bereich: Ändere Ausführungslogik ändern - Code wird ausgeführt, wenn er umgangen oder beendet werden sollte. |
| Zugriffskontrolle | Bereich: Zugriffskontrolle Schutzmechanismus umgehen - Authentifizierungs- und Autorisierungskontrollen können umgangen werden. |
| Verfügbarkeit | Bereich: 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
- MITRE Corporation. "CWE-705: Incorrect Control Flow Scoping." https://cwe.mitre.org/data/definitions/705.html
- CWE-691: Insufficient Control Flow Management.
- CWE-698: Execution After Redirect.