
Proof-of-Concept-Lab zur Reproduktion von CVE-2026-19553, bei dem CPython ssl.SSLContext.wrap_bio() die TLS-Hostnamenüberprüfung stillschweigend überspringt, wenn server_hostname None ist.
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · cve-2026-19553-wrap-bio
CPython ssl - Python Software Foundation
ssl.SSLContext.wrap_bio() verlangte nicht, dass server_hostname nicht-None ist, wenn check_hostname gesetzt ist. SSLObject überspringt stillschweigend die Hostnamen-Verifikation. Das Programm sieht aus, als wäre es mit check_hostname=True erfolgreich gewesen. Kein ValueError. Die Zertifikatskette wird verifiziert. Der Name des Peers nicht. wrap_socket() warf bereits einen Fehler. asyncio SSLProtocol / start_tls / open_connection verwandeln "" in None und rufen dann wrap_bio auf.
Ein MITM mit einem CA-gültigen Zertifikat für einen anderen Hostnamen schließt den Handshake gegen einen wrap_bio-/asyncio-Client ab, der vergessen hat, server_hostname zu übergeben.
| ID | CVE-2026-19553 |
| CWE | CWE-297 |
| CVSS | High: 7.6 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N |
| Produkt | CPython ssl |
| Betroffen | < 3.12.15, 3.13.0–3.13.15, 3.14.0–3.14.7, 3.15.0a1 vor 3.15.0 |
| Auth | MITM / angreifender TLS-Server; Opfer ist ein Python-TLS-Client, der wrap_bio oder asyncio ohne Hostnamen verwendet |
| Lizenz | GNU Affero GPL v3.0 |
| Lab | nur 127.0.0.1 |
Sich auf den Pfad setzen (oder der Server sein, den der Client erreichen wollte). Ein Zertifikat präsentieren, dem der Client für einen Namen vertraut, der nicht der vom Client beabsichtigte ist. Wenn der Client wrap_bio(..., server_hostname=None) oder asyncio mit server_hostname="" verwendet hat, während check_hostname=True und CERT_REQUIRED gesetzt sind, gelingt der Handshake. Die Identität wurde nie geprüft. wrap_socket im selben Kontext wirft einen ValueError, bevor sich irgendwelche Bytes bewegen. Die Übergabe eines nicht-leeren Hostnamens an wrap_bio lehnt eine Nichtübereinstimmung weiterhin ab.
Gleiches Produkt, verwandtes Überbleibsel: SNI SSLContext UAF. Entgegengesetzte TLS-Seite. Sie lassen sich nicht kombinieren.
Die PSF veröffentlichte CVE-2026-19553. Issue python/cpython#156793, PR 158503, Commit 6dc0069a. _check_sslobject_params lief bereits für wrap_socket. SSLObject._create übersprang es. NEWS: Abschluss eines Handshakes, der die Zertifikatskette verifizierte, ohne die Identität des Peers zu verifizieren, ohne einen Hinweis darauf, dass die Prüfung übersprungen wurde. Nach dem Patch wirft wrap_bio mit check_hostname=True und server_hostname=None/"" einen ValueError("check_hostname requires server_hostname"). Der 3.12-Backport wirft stattdessen eine DeprecationWarning.
Ich habe python:3.14.7-slim-bookworm aufgesetzt. Gleiche Lab-CA, zwei Leaf-Zertifikate: victim.lab und evil.lab. Client check_hostname=True, CERT_REQUIRED.
INJECT: wrap_bio(server_hostname=None) gegen evil.lab akzeptiert. Peer-SAN evil.lab. SNI None. TLS_AES_256_GCM_SHA384. CONTROL A: wrap_socket(server_hostname=None) warf ValueError: check_hostname requires server_hostname. CONTROL B: wrap_bio(server_hostname="victim.lab") gegen evil.lab warf SSLCertVerificationError Hostname-Mismatch. NEGATIVE: derselbe Aufruf gegen victim.lab akzeptiert. Extra: asyncio.open_connection(..., server_hostname="") auf 127.0.0.1:18510 akzeptiert ("" wird zu None, dann wrap_bio).
Bereits dokumentierte Irrwege: Der erste asyncio-start_server-Entwurf ließ eine schließende Klammer fallen; die Host-Kompilierung fing das vor dem Compose ab. Host 3.14.7 (Clang, OpenSSL 3.6.4) reproduzierte dieselben vier Orakel; der Lab-Datensatz ist die Container-Pin (GCC, OpenSSL 3.0.22). Theater: eine Reverse Shell. Das Orakel ist accept-wrong-name plus wrap_socket, das weiterhin einen Fehler wirft.
cd lab
./run.sh
Image python:3.14.7-slim-bookworm. Compose-Projekt cve-2026-19553. Loopback 127.0.0.1:18510 ist das asyncio-Extra. MemoryBIO benötigt keinen Port.
INJECT wrap_bio_none vs evil.lab: accept peer=evil.lab
CONTROL_A wrap_socket_none: ValueError:check_hostname requires server_hostname
CONTROL_B wrap_bio_name='victim.lab' vs evil.lab: mismatch-reject
SUCCESS CVE-2026-19553 wrap_bio_none=accept wrap_socket_none=ValueError wrap_bio_name=mismatch-reject CVE-2026-19553-WRAPBIO-HOST-WITNESS
Übergeben Sie einen nicht-leeren server_hostname an wrap_bio(), asyncio.create_connection() oder loop.start_tls(). Aktualisieren Sie auf 3.12.15 / 3.13.16 / 3.14.8 / 3.15.0. Der Patch verwandelt das stille Überspringen lediglich in denselben ValueError, den wrap_socket bereits warf.