
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 को कॉल करते हैं।
किसी भिन्न hostname के लिए CA-वैध प्रमाणपत्र वाला MITM उस 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 को non-empty 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) स्वीकृत। Peer 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 ड्राफ्ट में एक क्लोज़िंग paren छूट गया; host compile ने compose से पहले इसे पकड़ लिया। Host 3.14.7 (Clang, OpenSSL 3.6.4) ने वही चार oracles दोहराए; lab रिकॉर्ड कंटेनर पिन है (GCC, OpenSSL 3.0.22)। नाटक: एक reverse shell। Oracle है 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 अतिरिक्त है। 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() को एक non-empty server_hostname पास करें। 3.12.15 / 3.13.16 / 3.14.8 / 3.15.0 में अपग्रेड करें। पैच केवल मूक छोड़ने को उसी ValueError में बदलता है जो wrap_socket पहले से उठाता था।