Authentifizierungs-Bypass: OpenSSL CTX-Objekt nach Erstellung von SSL-Objekten modifiziert

Beschreibung

Authentifizierungs-Bypass: OpenSSL CTX-Objekt nach Erstellung von SSL-Objekten modifiziert tritt auf, wenn eine Anwendung SSL_CTX-Sicherheitseinstellungen (wie den Zertifikatsverifizierungsmodus) ändert, nachdem SSL-Objekte bereits aus diesem Kontext erstellt wurden. In OpenSSL erben SSL-Objekte Einstellungen von ihrem CTX zum Erstellungszeitpunkt. Spätere Modifikationen am CTX beeinflussen existierende SSL-Objekte nicht, was bedeutet, dass Sicherheitseinstellungen möglicherweise nicht wie beabsichtigt angewendet werden.

Risiko

Anwendungen können glauben, sie hätten Zertifikatsverifizierung oder andere Sicherheitsfeatures aktiviert, wenn tatsächlich die SSL-Verbindungen die ursprünglichen, potenziell unsicheren Einstellungen verwenden. Dies kann zu Man-in-the-Middle-Angriffen, Verbindung zu bösartigen Servern ohne Zertifikatsvalidierung oder Akzeptanz ungültiger Zertifikate führen. Der Code erscheint bei der Überprüfung sicher, arbeitet aber tatsächlich unsicher.

Lösung

Konfigurieren Sie SSL_CTX immer vollständig, bevor Sie SSL-Objekte daraus erstellen. Setzen Sie Verifizierungsmodi, Zertifikatsketten, Cipher-Suites und alle anderen Sicherheitsparameter zuerst am CTX. Wenn Einstellungen dynamisch geändert werden müssen, erstellen Sie einen neuen SSL_CTX mit den aktualisierten Einstellungen oder wenden Sie Änderungen direkt auf einzelne SSL-Objekte mit SSL_set_verify() und ähnlichen Funktionen an.

Häufige Auswirkungen

AuswirkungDetails
AuthentifizierungBereich: Bypass

Zertifikatsverifizierung wird möglicherweise nicht durchgesetzt, obwohl Code sie setzt.
VertraulichkeitBereich: MITM-Exposure

Verbindungen können anfällig für Abfangen sein.
IntegritätBereich: Datenmanipulation

Unverifizierte Verbindungen erlauben Datenmodifikation.

Beispielcode + Lösungscode

Verwundbarer Code

// VERWUNDBAR: CTX nach SSL-Objekt-Erstellung modifiziert
SSL_CTX* ctx;
SSL* ssl;

void init_ssl_vulnerable() {
    ctx = SSL_CTX_new(TLS_client_method());

    // SSL-Objekt mit aktuellen CTX-Einstellungen erstellen
    ssl = SSL_new(ctx);  // Erbt Einstellungen JETZT

    // Später: Versuchen Verifizierung zu aktivieren
    // FEHLER: Dies beeinflusst bereits erstelltes ssl-Objekt nicht!
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_load_verify_locations(ctx, "ca-bundle.crt", NULL);

    // ssl hat immer noch keine Verifizierung aktiviert!
}

void connect_vulnerable(int fd) {
    SSL_set_fd(ssl, fd);

    // Verbindet ohne Zertifikatsverifizierung!
    if (SSL_connect(ssl) <= 0) {
        handle_error();
    }

    // Verbindung ist unsicher trotz CTX-Einstellungen
}

// VERWUNDBAR: Verzögerte Sicherheitskonfiguration
SSL_CTX* create_context_vulnerable() {
    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
    return ctx;  // Gibt ohne Sicherheitseinstellungen zurück
}

void use_context_vulnerable(SSL_CTX* ctx) {
    // Verbindungen erstellen
    SSL* ssl1 = SSL_new(ctx);  // Keine Verifizierung
    SSL* ssl2 = SSL_new(ctx);  // Keine Verifizierung

    // Jetzt versuchen den Kontext zu sichern
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);

    // ssl1 und ssl2 sind immer noch unsicher!

    // Nur neue SSL-Objekte bekommen die Einstellungen
    SSL* ssl3 = SSL_new(ctx);  // Dieses hat Verifizierung
}

// VERWUNDBAR: Verbindungspool mit später Konfiguration
typedef struct {
    SSL_CTX* ctx;
    SSL* connections[10];
    int count;
} ConnectionPool;

void init_pool_vulnerable(ConnectionPool* pool) {
    pool->ctx = SSL_CTX_new(TLS_client_method());
    pool->count = 0;

    // SSL-Objekte für Pooling vorab erstellen
    for (int i = 0; i < 10; i++) {
        pool->connections[i] = SSL_new(pool->ctx);
    }
}

void secure_pool_vulnerable(ConnectionPool* pool) {
    // Versuchen Sicherheit nach Pool-Erstellung hinzuzufügen
    // FEHLER: Beeinflusst vorab erstellte Verbindungen nicht!
    SSL_CTX_set_verify(pool->ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_load_verify_locations(pool->ctx, "certs/ca.pem", NULL);
}

// VERWUNDBAR: Bedingte Sicherheit die zu spät kommt
void conditional_security_vulnerable(int require_certs) {
    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
    SSL* ssl = SSL_new(ctx);  // Erstellt vor Bedingungsprüfung

    if (require_certs) {
        // Zu spät! ssl existiert bereits
        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    }

    // Selbst mit require_certs=1 hat ssl keine Verifizierung
}
// VERWUNDBAR: C++ Klasse mit Initialisierungsreihenfolge-Problem
class VulnerableSSLClient {
    SSL_CTX* ctx;
    SSL* ssl;

public:
    VulnerableSSLClient() {
        ctx = SSL_CTX_new(TLS_client_method());
        ssl = SSL_new(ctx);  // Sofort erstellt
    }

    void enableVerification() {
        // Nach Konstruktor aufgerufen - zu spät!
        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr);
    }

    void connect(const char* host, int port) {
        // ssl hat keine Verifizierung unabhängig von enableVerification()-Aufrufen
        int fd = create_socket(host, port);
        SSL_set_fd(ssl, fd);
        SSL_connect(ssl);
    }
};

// Verwendung - erscheint sicher, ist es aber nicht
void use_vulnerable() {
    VulnerableSSLClient client;
    client.enableVerification();  // Zu spät!
    client.connect("server.com", 443);  // Unsichere Verbindung
}

Lösungscode

// SICHER: CTX vor SSL-Objekt-Erstellung konfigurieren
SSL_CTX* ctx;
SSL* ssl;

void init_ssl_safe() {
    ctx = SSL_CTX_new(TLS_client_method());

    // ALLE Sicherheitseinstellungen ZUERST konfigurieren
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_load_verify_locations(ctx, "ca-bundle.crt", NULL);
    SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
    SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);

    // JETZT SSL-Objekt erstellen - erbt alle Einstellungen
    ssl = SSL_new(ctx);
}

void connect_safe(int fd) {
    SSL_set_fd(ssl, fd);

    if (SSL_connect(ssl) <= 0) {
        handle_error();
        return;
    }

    // Verifizieren dass Zertifikat tatsächlich geprüft wurde
    if (SSL_get_verify_result(ssl) != X509_V_OK) {
        handle_verification_failure();
        SSL_shutdown(ssl);
        return;
    }

    // Verbindung ist sicher
}

// SICHER: Vollständige Kontextkonfiguration vor Verwendung
SSL_CTX* create_secure_context() {
    SSL_CTX* ctx = SSL_CTX_new(TLS_client_method());
    if (ctx == NULL) return NULL;

    // Alles vor Rückgabe konfigurieren
    SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);

    if (!SSL_CTX_load_verify_locations(ctx, "ca-bundle.crt", NULL)) {
        SSL_CTX_free(ctx);
        return NULL;
    }

    SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
    SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);

    return ctx;
}

// SICHER: Einstellungen auf einzelne SSL-Objekte anwenden wenn nötig
void apply_settings_to_ssl(SSL* ssl) {
    // Kann einzelne SSL-Objekte direkt modifizieren
    SSL_set_verify(ssl, SSL_VERIFY_PEER, NULL);
}

// SICHER: Verbindungspool mit ordnungsgemäßer Initialisierung
typedef struct {
    SSL_CTX* ctx;
    SSL* connections[10];
    int count;
} SecureConnectionPool;

void init_pool_safe(SecureConnectionPool* pool) {
    pool->ctx = SSL_CTX_new(TLS_client_method());

    // Sicherheit VOR Verbindungserstellung konfigurieren
    SSL_CTX_set_verify(pool->ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_load_verify_locations(pool->ctx, "certs/ca.pem", NULL);
    SSL_CTX_set_min_proto_version(pool->ctx, TLS1_2_VERSION);

    // JETZT Pool-Verbindungen erstellen - sie erben Einstellungen
    pool->count = 0;
    for (int i = 0; i < 10; i++) {
        pool->connections[i] = SSL_new(pool->ctx);
    }
}

// SICHER: Neuen CTX erstellen wenn Einstellungen sich ändern
SSL_CTX* update_security_settings(SSL_CTX* old_ctx, int new_settings) {
    // Neuen Kontext mit neuen Einstellungen erstellen
    SSL_CTX* new_ctx = SSL_CTX_new(TLS_client_method());

    // Neue Einstellungen anwenden
    if (new_settings & REQUIRE_CLIENT_CERT) {
        SSL_CTX_set_verify(new_ctx,
            SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
    }

    // Alte SSL-Objekte verwenden noch old_ctx
    // Neue SSL-Objekte verwenden new_ctx mit neuen Einstellungen

    return new_ctx;
}

// SICHER: Factory-Pattern für ordnungsgemäße Initialisierung
typedef struct {
    SSL_CTX* ctx;
    int initialized;
} SSLFactory;

int init_factory(SSLFactory* factory) {
    factory->ctx = SSL_CTX_new(TLS_client_method());
    if (!factory->ctx) return -1;

    // Alle Sicherheitseinstellungen hier konfiguriert
    SSL_CTX_set_verify(factory->ctx, SSL_VERIFY_PEER, NULL);
    SSL_CTX_load_verify_locations(factory->ctx, "ca-bundle.crt", NULL);
    SSL_CTX_set_min_proto_version(factory->ctx, TLS1_2_VERSION);

    factory->initialized = 1;
    return 0;
}

SSL* create_ssl_from_factory(SSLFactory* factory) {
    if (!factory->initialized) {
        return NULL;  // Factory nicht bereit
    }

    // SSL-Objekte immer aus vollständig konfiguriertem CTX erstellt
    return SSL_new(factory->ctx);
}
// SICHER: C++ Klasse mit ordnungsgemäßer Initialisierungsreihenfolge
class SafeSSLClient {
    SSL_CTX* ctx;
    SSL* ssl;

    void configureContext() {
        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr);
        SSL_CTX_load_verify_locations(ctx, "ca-bundle.crt", nullptr);
        SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
    }

public:
    SafeSSLClient() {
        ctx = SSL_CTX_new(TLS_client_method());

        // VOR SSL-Objekt-Erstellung konfigurieren
        configureContext();

        // JETZT SSL erstellen - erbt alle Einstellungen
        ssl = SSL_new(ctx);
    }

    bool connect(const char* host, int port) {
        int fd = create_socket(host, port);
        SSL_set_fd(ssl, fd);

        if (SSL_connect(ssl) <= 0) {
            return false;
        }

        // Zertifikat verifizieren
        return SSL_get_verify_result(ssl) == X509_V_OK;
    }

    ~SafeSSLClient() {
        if (ssl) SSL_free(ssl);
        if (ctx) SSL_CTX_free(ctx);
    }
};

// SICHER: Builder-Pattern für SSL-Konfiguration
class SSLContextBuilder {
    SSL_CTX* ctx;

public:
    SSLContextBuilder() {
        ctx = SSL_CTX_new(TLS_client_method());
    }

    SSLContextBuilder& withPeerVerification() {
        SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, nullptr);
        return *this;
    }

    SSLContextBuilder& withCAFile(const char* path) {
        SSL_CTX_load_verify_locations(ctx, path, nullptr);
        return *this;
    }

    SSLContextBuilder& withMinVersion(int version) {
        SSL_CTX_set_min_proto_version(ctx, version);
        return *this;
    }

    SSL* createSSL() {
        // SSL erst nach aller Konfiguration erstellt
        return SSL_new(ctx);
    }

    SSL_CTX* getContext() { return ctx; }
};

// Verwendung
void use_safe() {
    SSLContextBuilder builder;
    builder.withPeerVerification()
           .withCAFile("ca-bundle.crt")
           .withMinVersion(TLS1_2_VERSION);

    SSL* ssl = builder.createSSL();  // Ordnungsgemäß konfiguriert
}

Ausgenutzt in der Praxis

Zertifikatsverifizierungs-Bypass

Anwendungen, die Zertifikatsverifizierung zu aktivieren schienen, aber tatsächlich ohne Validierung verbanden, wurden für MITM-Angriffe ausgenutzt.

Mobile App SSL-Schwachstellen

Mehrere mobile Anwendungen hatten dieses Schwachstellenmuster, das Traffic-Abfangen ermöglichte.

IoT-Gerätesicherheit

Eingebettete Geräte mit verzögerter SSL-Konfiguration wurden durch Netzwerkangriffe kompromittiert.


Tools zum Testen/Ausnutzen

  • SSLyze — SSL/TLS-Konfigurationsanalyse
  • testssl.sh — SSL/TLS-Testtool
  • mitmproxy — HTTPS-Traffic abfangen und inspizieren
  • Burp Suite — Web-Sicherheitstests

CVE-Beispiele

  • CVEs in Anwendungen mit unsachgemäßem SSL_CTX-Konfigurations-Timing
  • Zertifikatsvalidierungs-Bypass-Schwachstellen
  • MITM-Schwachstellen durch Initialisierungsreihenfolge-Probleme

Referenzen

  1. MITRE. "CWE-593: Authentication Bypass: OpenSSL CTX Object Modified after SSL Objects are Created." https://cwe.mitre.org/data/definitions/593.html
  2. OpenSSL Documentation. SSL_CTX_set_verify man page.