Offenlegung sensibler Systeminformationen an unbefugte Akteure

Beschreibung

Offenlegung sensibler Systeminformationen an unbefugte Akteure ist eine Schwachstelle, bei der ein Produkt sensible Informationen auf Systemebene an Akteure preisgibt, die nicht dasselbe Zugangsniveau wie das Produkt selbst haben. Netzwerkbasierte Produkte wie Webanwendungen operieren auf Betriebssystemen und Anwendungsservern, sollten aber diese zugrunde liegenden Details vor Endbenutzern verbergen. Informationen wie Dateipfade, Betriebssystembenutzer, installierte Softwarepakete und Details zur Anwendungsumgebung können durch Produktkommunikation, Diagnosemeldungen, Fehlerantworten oder Debug-Ausgaben durchsickern. Angreifer nutzen diese Details, um die Systemarchitektur zu verstehen und ihre Angriffsstrategien zu verfeinern.

Risiko

Die Offenlegung von Systeminformationen liefert Angreifern wertvolle Aufklärungsdaten. Fehlerantworten, die Technologien, Betriebssystemversionen und Produktinformationen preisgeben, ermöglichen das Angreifen bekannter Schwachstellen, die spezifisch für diese Versionen sind. Stack-Traces legen Implementierungsdetails, interne Klassennamen und Codestruktur offen. Dateipfade enthüllen Verzeichnislayouts, die Path-Traversal-Angriffe unterstützen. Datenbankverbindungsstrings können Anmeldedaten enthalten. Umgebungsvariablen könnten API-Schlüssel oder Token enthalten. Prozesslisten zeigen laufende Dienste und ihre Konfigurationen. Diese Informationsasymmetrie gibt Angreifern erhebliche Vorteile bei der Planung und Durchführung von Angriffen.

Lösung

Konfigurieren Sie Produktionssysteme so, dass sie niemals interne Details wie Stack-Traces, Debug-Meldungen oder ausführliche Fehlerinformationen an Endbenutzer weitergeben. Implementieren Sie generische, benutzerfreundliche Fehlerseiten, die detaillierte Informationen nur serverseitig protokollieren. Bereinigen Sie alle Fehlermeldungen vor der Anzeige und entfernen Sie Pfade, Versionsnummern und technische Details. Verwenden Sie Security-Header, um Informationslecks zu verhindern. Stellen Sie sicher, dass Logging-Mechanismen keine sensiblen Daten über Log-Viewer preisgeben. HTML-kodieren Sie Fehlermeldungen, die in Logs geschrieben werden, um XSS-Angriffe gegen Log-Viewing-Schnittstellen zu verhindern. Trennen Sie Debug- und Produktionskonfigurationen.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitBereich: Vertraulichkeit

Anwendungsdaten lesen - Die Offenlegung sensibler Systeminformationen ermöglicht Angreifern, den Technologie-Stack zu verstehen, potenzielle Schwachstellen zu identifizieren und gezielte Angriffe zu entwickeln.
SonstigesBereich: Sonstiges

Technische Auswirkung: Aufklärung - Offengelegte Informationen helfen Angreifern bei der Kartierung der Systemarchitektur und der Identifizierung von Angriffsvektoren.

Beispielcode

Verwundbarer Code

// Verwundbar: Stack-Traces an Benutzer weitergeben
import javax.servlet.http.*;
import java.io.*;

public class VulnerableErrorServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws IOException {
        try {
            processRequest(request);
        } catch (Exception e) {
            // Verwundbar: Vollständiger Stack-Trace an Client gesendet
            PrintWriter out = response.getWriter();
            out.println("<html><body>");
            out.println("<h1>Ein Fehler ist aufgetreten:</h1>");
            out.println("<pre>");
            e.printStackTrace(out);  // Legt interne Details offen!
            out.println("</pre>");
            out.println("</body></html>");
        }
    }

    // Verwundbar: Systeminformationen offenlegen
    protected void showSystemInfo(HttpServletResponse response)
            throws IOException {
        PrintWriter out = response.getWriter();

        // Verwundbar: PATH offenlegen
        out.println("PATH: " + System.getenv("PATH"));

        // Verwundbar: Benutzer offenlegen
        out.println("Benutzer: " + System.getProperty("user.name"));

        // Verwundbar: OS-Details offenlegen
        out.println("OS: " + System.getProperty("os.name") + " " +
                    System.getProperty("os.version"));

        // Verwundbar: Java-Version offenlegen
        out.println("Java: " + System.getProperty("java.version"));

        // Verwundbar: Arbeitsverzeichnis offenlegen
        out.println("Verzeichnis: " + System.getProperty("user.dir"));
    }
}

// Verwundbar: Datenbankverbindungsstring in Fehlermeldungen
public class VulnerableDatabaseService {

    private static final String CONNECTION_STRING =
        "jdbc:mysql://db-server:3306/mydb?user=admin&password=secret123";

    public void connect() {
        try {
            Connection conn = DriverManager.getConnection(CONNECTION_STRING);
        } catch (SQLException e) {
            // Verwundbar: Verbindungsstring mit Anmeldedaten protokolliert/angezeigt
            System.out.println("Verbindung fehlgeschlagen: " + CONNECTION_STRING);
            throw new RuntimeException("Datenbankfehler: " + e.getMessage() +
                " Verbindung: " + CONNECTION_STRING);
        }
    }
}
# Verwundbar: Systeminformationen in Python-Webanwendung offenlegen
from flask import Flask, request
import traceback
import os
import subprocess

app = Flask(__name__)

@app.route('/status')
def vulnerable_status():
    # Verwundbar: Umgebungsvariablen offenlegen
    env_info = "<h2>Umgebungsvariablen:</h2><pre>"
    for key, value in os.environ.items():
        env_info += f"{key}={value}\n"  # Kann Geheimnisse enthalten!
    env_info += "</pre>"

    # Verwundbar: Prozessliste offenlegen
    try:
        ps_output = subprocess.check_output(['ps', 'aux']).decode()
        process_info = f"<h2>Laufende Prozesse:</h2><pre>{ps_output}</pre>"
    except:
        process_info = ""

    return env_info + process_info

@app.errorhandler(500)
def vulnerable_error_handler(error):
    # Verwundbar: Vollständiger Traceback an Client
    return f"""
    <html>
    <body>
        <h1>Interner Serverfehler</h1>
        <h2>Traceback:</h2>
        <pre>{traceback.format_exc()}</pre>
        <h2>Anfrage-Details:</h2>
        <pre>
        Pfad: {request.path}
        Methode: {request.method}
        Server: {request.host}
        </pre>
    </body>
    </html>
    """, 500

# Verwundbar: Debug-Modus in Produktion
if __name__ == '__main__':
    app.run(debug=True)  # Niemals in Produktion!
<?php
// Verwundbar: PHP-Info offenlegen
function vulnerableDebugPage() {
    // Verwundbar: Vollständige PHP-Konfiguration offengelegt
    phpinfo();  // Zeigt alle PHP-Einstellungen, Pfade, Module
}

// Verwundbar: Detaillierte Fehlermeldungen
function vulnerableQuery($userId) {
    $conn = new mysqli("localhost", "root", "password123", "mydb");

    $query = "SELECT * FROM users WHERE id = " . $userId;

    if (!$result = $conn->query($query)) {
        // Verwundbar: Query- und Verbindungsdetails offengelegt
        die("Query fehlgeschlagen: " . $query .
            "\nFehler: " . $conn->error .
            "\nServer: " . $conn->server_info);
    }

    return $result;
}

// Verwundbar: Exception mit Systemdetails
try {
    include("/var/www/config/secret.php");
} catch (Exception $e) {
    // Verwundbar: Vollständiger Pfad offengelegt
    echo "Fehler beim Laden der Konfiguration: " . $e->getMessage();
    echo "\nDatei: " . $e->getFile();
    echo "\nZeile: " . $e->getLine();
    echo "\nTrace: " . $e->getTraceAsString();
}
?>

Lösungscode

// Behoben: Sichere Fehlerbehandlung
import javax.servlet.http.*;
import java.io.*;
import java.util.logging.*;

public class SecureErrorServlet extends HttpServlet {
    private static final Logger logger = Logger.getLogger(
        SecureErrorServlet.class.getName()
    );

    @Override
    protected void doGet(HttpServletRequest request,
                         HttpServletResponse response)
            throws IOException {
        try {
            processRequest(request);
        } catch (Exception e) {
            // Behoben: Detaillierten Fehler nur serverseitig loggen
            String errorId = generateErrorId();
            logger.log(Level.SEVERE,
                "Fehler-ID " + errorId + ": " + e.getMessage(), e);

            // Behoben: Generische Nachricht an Client
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            PrintWriter out = response.getWriter();
            out.println("<html><body>");
            out.println("<h1>Ein Fehler ist aufgetreten</h1>");
            out.println("<p>Bitte kontaktieren Sie den Support mit Fehler-ID: " +
                        escapeHtml(errorId) + "</p>");
            out.println("</body></html>");
        }
    }

    // Behoben: Niemals Systeminformationen offenlegen
    protected void showStatus(HttpServletResponse response)
            throws IOException {
        PrintWriter out = response.getWriter();

        // Behoben: Nur notwendige, nicht-sensible Infos offenlegen
        out.println("Anwendungsstatus: Läuft");
        out.println("Version: 1.0");
        out.println("Gesundheit: OK");

        // Behoben: Keine Pfade, Versionen, Benutzer oder Umgebungsdetails
    }

    private String generateErrorId() {
        return "ERR-" + System.currentTimeMillis() + "-" +
               (int)(Math.random() * 10000);
    }

    private String escapeHtml(String input) {
        return input.replace("&", "&amp;")
                    .replace("<", "&lt;")
                    .replace(">", "&gt;")
                    .replace("\"", "&quot;");
    }
}

// Behoben: Sicherer Datenbankdienst
public class SecureDatabaseService {
    private static final Logger logger = Logger.getLogger(
        SecureDatabaseService.class.getName()
    );

    // Behoben: Anmeldedaten aus sicherer Konfiguration
    private String getConnectionString() {
        return SecureConfig.getDatabaseUrl();  // Keine Inline-Anmeldedaten
    }

    public void connect() {
        try {
            Connection conn = DriverManager.getConnection(getConnectionString());
        } catch (SQLException e) {
            // Behoben: Vollständige Details nur serverseitig loggen
            logger.log(Level.SEVERE, "Datenbankverbindung fehlgeschlagen", e);

            // Behoben: Generische Nachricht, keine Verbindungsdetails
            throw new ServiceException("Datenbank vorübergehend nicht verfügbar");
        }
    }
}
# Behoben: Sichere Fehlerbehandlung in Flask
from flask import Flask, request, render_template
import logging
import uuid
import os

app = Flask(__name__)

# Behoben: Sicheres Logging konfigurieren
logging.basicConfig(
    filename='/var/log/app/error.log',
    level=logging.ERROR,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)

@app.route('/status')
def secure_status():
    # Behoben: Nur sichere, nicht-sensible Infos offenlegen
    return {
        "status": "healthy",
        "version": "1.0.0",
        "uptime": get_uptime_safe()
    }

@app.errorhandler(500)
def secure_error_handler(error):
    # Behoben: Fehler-ID zur Nachverfolgung generieren
    error_id = str(uuid.uuid4())[:8]

    # Behoben: Vollständige Details serverseitig loggen
    logger.error(f"Fehler {error_id}: {error}", exc_info=True)

    # Behoben: Generische Nachricht an Client
    return render_template('error.html', error_id=error_id), 500

@app.errorhandler(Exception)
def handle_exception(e):
    error_id = str(uuid.uuid4())[:8]
    logger.exception(f"Unbehandelte Ausnahme {error_id}")

    # Behoben: Niemals Exception-Details offenlegen
    return {
        "error": "Ein unerwarteter Fehler ist aufgetreten",
        "error_id": error_id,
        "message": "Bitte kontaktieren Sie den Support mit dieser Fehler-ID"
    }, 500

def get_uptime_safe():
    # Behoben: Bereinigte Uptime ohne Systemdetails zurückgeben
    try:
        with open('/proc/uptime', 'r') as f:
            uptime_seconds = float(f.readline().split()[0])
            return f"{int(uptime_seconds // 3600)} Stunden"
    except:
        return "unbekannt"

# Behoben: Niemals mit debug=True in Produktion ausführen
if __name__ == '__main__':
    debug_mode = os.environ.get('FLASK_DEBUG', 'false').lower() == 'true'
    if os.environ.get('ENVIRONMENT') == 'production':
        debug_mode = False  # In Produktion erzwingen
    app.run(debug=debug_mode)
<?php
// Behoben: Sichere PHP-Konfiguration
ini_set('display_errors', 0);  // Niemals Fehler an Benutzer anzeigen
ini_set('log_errors', 1);      // Fehler stattdessen loggen
ini_set('error_log', '/var/log/php/error.log');

// Behoben: Benutzerdefinierter Error-Handler
function secureErrorHandler($errno, $errstr, $errfile, $errline) {
    $errorId = uniqid('ERR-');

    // Behoben: Vollständige Details serverseitig loggen
    error_log("[$errorId] Fehler $errno: $errstr in $errfile:$errline");

    // Behoben: Generische Nachricht an Benutzer
    if (!headers_sent()) {
        header('HTTP/1.1 500 Internal Server Error');
    }

    echo json_encode([
        'error' => 'Ein Fehler ist aufgetreten',
        'error_id' => $errorId,
        'message' => 'Bitte kontaktieren Sie den Support'
    ]);

    return true;  // PHPs internen Error-Handler nicht ausführen
}
set_error_handler('secureErrorHandler');

// Behoben: Sichere Datenbankabfragen
function secureQuery($userId) {
    $conn = getSecureConnection();  // Verbindung aus sicherer Konfiguration

    // Behoben: Prepared Statement
    $stmt = $conn->prepare("SELECT * FROM users WHERE id = ?");
    $stmt->bind_param("i", $userId);

    if (!$stmt->execute()) {
        $errorId = uniqid('DB-');
        error_log("[$errorId] Datenbankfehler: " . $stmt->error);

        // Behoben: Generische Nachricht
        throw new Exception("Datenbankfehler. Referenz: $errorId");
    }

    return $stmt->get_result();
}

// Behoben: Sichere Exception-Behandlung
try {
    $config = loadSecureConfig();
} catch (Exception $e) {
    $errorId = uniqid('CFG-');

    // Behoben: Details loggen, generische Nachricht anzeigen
    error_log("[$errorId] Konfigurationsfehler: " . $e->getMessage() .
              " in " . $e->getFile() . ":" . $e->getLine());

    http_response_code(500);
    echo "Konfigurationsfehler. Referenz: $errorId";
}
?>

CVE-Beispiele

  • CVE-2021-32638: Ein Code-Analyse-Produkt legte Zugriffstoken über Kommandozeilenparameter oder Umgebungsvariablen offen, wodurch sie über den ps-Befehl für andere Benutzer auf demselben System sichtbar wurden.

Referenzen

  1. MITRE Corporation. "CWE-497: Exposure of Sensitive System Information to an Unauthorized Control Sphere." https://cwe.mitre.org/data/definitions/497.html
  2. OWASP. "Information Exposure Through Error Messages."
  3. OWASP. "Error Handling Cheat Sheet."