
CVE-2026-19553 को पुनः उत्पन्न करने वाली प्रूफ-ऑफ-कॉन्सेप्ट लैब, जहाँ CPython का ssl.SSLContext.wrap_bio() चुपचाप TLS होस्टनाम सत्यापन को छोड़ देता है जब server_hostname None होता है।
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · cve-2026-19553-wrap-bio
CPython ssl - Python Software Foundation
ssl.SSLContext.wrap_bio() को check_hostname सेट होने पर server_hostname के non-None होने की आवश्यकता नहीं थी। SSLObject चुपचाप hostname सत्यापन छोड़ देता है। प्रोग्राम ऐसा दिखता है जैसे check_hostname=True के साथ सफल हुआ हो। कोई ValueError नहीं। प्रमाणपत्र श्रृंखला सत्यापित होती है। पीयर का नाम नहीं। wrap_socket() पहले से ही त्रुटि उठाता था। asyncio SSLProtocol / start_tls / open_connection "" को None में बदल देते हैं और फिर wrap_bio कॉल करते हैं।
एक MITM, जिसके पास किसी भिन्न hostname के लिए CA-वैध प्रमाणपत्र है, उस wrap_bio / asyncio क्लाइंट के विरुद्ध handshake पूरा कर लेता है जिसने server_hostname पास करना भूल किया हो।
| 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 |
| Product | CPython ssl |
| Affected | < 3.12.15, 3.13.0-3.13.15, 3.14.0-3.14.7, 3.15.0a1 3.15.0 से पहले |
| Auth | MITM / आक्रामक TLS सर्वर; पीड़ित एक Python TLS क्लाइंट है जो hostname के बिना wrap_bio या asyncio का उपयोग करता है |
| License | GNU Affero GPL v3.0 |
| Lab | केवल 127.0.0.1 |
पथ पर बैठें (या वह सर्वर बनें जिस तक क्लाइंट पहुँचना चाहता था)। ऐसा प्रमाणपत्र प्रस्तुत करें जिस पर क्लाइंट किसी ऐसे नाम के लिए भरोसा करता है जो क्लाइंट का इच्छित नाम नहीं है। यदि क्लाइंट ने wrap_bio(..., server_hostname=None) का उपयोग किया या asyncio के साथ server_hostname="" का उपयोग किया, जबकि check_hostname=True और CERT_REQUIRED था, तो handshake सफल हो जाता है। पहचान की कभी जाँच नहीं हुई। उसी context पर wrap_socket कोई भी बाइट स्थानांतरित होने से पहले ValueError उठाता है। wrap_bio को गैर-रिक्त hostname पास करने पर भी बेमेल अस्वीकार होता है।
वही उत्पाद, सहोदर अवशेष: SNI SSLContext UAF। विपरीत TLS पक्ष। वे एक साथ संयोजित नहीं होते।
PSF ने CVE-2026-19553 पोस्ट किया। Issue python/cpython#156793, PR 158503, commit 6dc0069a। _check_sslobject_params पहले से ही wrap_socket के लिए चलता था। SSLObject._create ने इसे छोड़ दिया। NEWS: प्रमाणपत्र श्रृंखला सत्यापित करने वाले handshake को पीयर की पहचान सत्यापित किए बिना पूरा करना, इस बात के किसी संकेत के बिना कि जाँच छोड़ दी गई थी। पैच के बाद, check_hostname=True और server_hostname=None/"" के साथ wrap_bio ValueError("check_hostname requires server_hostname") उठाता है। 3.12 backport इसके बजाय DeprecationWarning उठाता है।
मैंने python:3.14.7-slim-bookworm खड़ा किया। वही lab CA, दो leaves: victim.lab और evil.lab। क्लाइंट check_hostname=True, CERT_REQUIRED।
INJECT: evil.lab के विरुद्ध wrap_bio(server_hostname=None) स्वीकार किया गया। पीयर SAN evil.lab। SNI None। TLS_AES_256_GCM_SHA384। CONTROL A: wrap_socket(server_hostname=None) ने ValueError: check_hostname requires server_hostname उठाया। CONTROL B: evil.lab के विरुद्ध wrap_bio(server_hostname="victim.lab") ने SSLCertVerificationError hostname बेमेल उठाया। NEGATIVE: victim.lab के विरुद्ध वही कॉल स्वीकार किया गया। अतिरिक्त: 127.0.0.1:18510 पर asyncio.open_connection(..., server_hostname="") स्वीकार किया गया ("" None बन जाता है, फिर wrap_bio)।
गलत मोड़ पहले ही दर्ज हो चुके हैं: पहला asyncio start_server ड्राफ़्ट एक क्लोज़िंग पैरन छोड़ गया; होस्ट कंपाइल ने compose से पहले इसे पकड़ लिया। होस्ट 3.14.7 (Clang, OpenSSL 3.6.4) ने वही चार ओरेकल पुनरुत्पादित किए; लैब रिकॉर्ड कंटेनर पिन (GCC, OpenSSL 3.0.22) है। थिएटर: एक रिवर्स शेल। ओरेकल accept-wrong-name के साथ-साथ wrap_socket का अभी भी उठना है।
cd lab
./run.sh
Image python:3.14.7-slim-bookworm. Compose प्रोजेक्ट cve-2026-19553. Loopback 127.0.0.1:18510 asyncio extra है। MemoryBIO को किसी पोर्ट की आवश्यकता नहीं है।
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
wrap_bio(), asyncio.create_connection(), या loop.start_tls() को एक गैर-रिक्त server_hostname पास करें। 3.12.15 / 3.13.16 / 3.14.8 / 3.15.0 में अपग्रेड करें। पैच केवल साइलेंट स्किप को उसी ValueError में बदलता है जो wrap_socket पहले से ही उठाता था।