
مختبر إثبات مفهوم يعيد إنتاج 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() يشترط أن تكون قيمة server_hostname غير فارغة (non-None) عند تفعيل check_hostname. يتخطى SSLObject التحقق من اسم المضيف بصمت. يبدو البرنامج وكأنه نجح مع check_hostname=True. لا يوجد ValueError. يتم التحقق من سلسلة الشهادات. لا يتم التحقق من اسم الطرف الآخر. كان wrap_socket() يرفع الخطأ بالفعل. يقوم asyncio SSLProtocol / start_tls / open_connection بتحويل "" إلى None ثم استدعاء wrap_bio.
هجوم رجل في المنتصف (MITM) بشهادة صالحة من CA لاسم مضيف مختلف يُكمل المصافحة ضد عميل wrap_bio / asyncio نسي تمرير 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 before 3.15.0 |
| Auth | MITM / خادم TLS مهاجم؛ الضحية عميل TLS بلغة Python يستخدم 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، تنجح المصافحة. لم يتم التحقق من الهوية أبدًا. يرفع wrap_socket على نفس السياق ValueError قبل تحرك أي بايت. تمرير اسم مضيف غير فارغ إلى wrap_bio لا يزال يرفض عدم التطابق.
نفس المنتج، بقايا شقيقة: SNI SSLContext UAF. الجانب المعاكس من TLS. لا يتكاملان.
نشرت PSF CVE-2026-19553. المشكلة python/cpython#156793، PR 158503، الالتزام 6dc0069a. كان _check_sslobject_params يعمل بالفعل مع wrap_socket. تخطاه SSLObject._create. NEWS: إكمال مصافحة تحققت من سلسلة الشهادات دون التحقق من هوية الطرف الآخر، دون أي إشارة إلى أن التحقق قد تم تخطيه. بعد التصحيح، يرفع wrap_bio مع check_hostname=True و server_hostname=None/"" الخطأ ValueError("check_hostname requires server_hostname"). الـ backport الخاص بـ 3.12 يرفع DeprecationWarning بدلاً من ذلك.
قمت بتشغيل python:3.14.7-slim-bookworm. نفس CA المختبري، ورقتان: victim.lab و evil.lab. العميل check_hostname=True، CERT_REQUIRED.
INJECT: wrap_bio(server_hostname=None) ضد evil.lab تم القبول. 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: wrap_bio(server_hostname="victim.lab") ضد evil.lab رفع SSLCertVerificationError عدم تطابق اسم المضيف. NEGATIVE: نفس الاستدعاء ضد victim.lab تم القبول. إضافي: asyncio.open_connection(..., server_hostname="") على 127.0.0.1:18510 تم القبول ("" يصبح None، ثم wrap_bio).
المنعطفات الخاطئة المسجلة بالفعل: أول مسودة asyncio start_server أسقطت قوس إغلاق؛ اكتشفها تجميع المضيف قبل compose. أعاد المضيف 3.14.7 (Clang، OpenSSL 3.6.4) إنتاج نفس المحكات الأربعة؛ سجل المختبر هو تثبيت الحاوية (GCC، OpenSSL 3.0.22). مسرحية: reverse shell. المحك هو قبول الاسم الخاطئ بالإضافة إلى wrap_socket الذي لا يزال يرفع الخطأ.
cd lab
./run.sh
الصورة 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
مرر server_hostname غير فارغ إلى wrap_bio() أو asyncio.create_connection() أو loop.start_tls(). قم بالترقية إلى 3.12.15 / 3.13.16 / 3.14.8 / 3.15.0. التصحيح يغير فقط التخطي الصامت إلى نفس ValueError الذي كان wrap_socket يرفعه بالفعل.