Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

FeedsContactPrivacy© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
cve-2026-19553-wrap-bio — Proof-of-concept lab reproducing CVE-2026-19553, where CPython ssl.SSLContext.wrap_bio() silently skips TLS hostname verification when server_hostname is None. | Kitploit
Tools/GitHubGitHub/abraxas/cve-2026-19553-wrap-bio
Vulnerability AnalysisExploitationWeb Application ExploitationNetwork SecurityCryptographyLearning & EducationLabs & Practice
GitHubabraxas/cve-2026-19553-wrap-bio

cve-2026-19553-wrap-bio

Proof-of-concept lab reproducing CVE-2026-19553, where CPython ssl.SSLContext.wrap_bio() silently skips TLS hostname verification when server_hostname is None.

View Repository
11 day agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Abraxas Labs - cve-2026-19553-wrap-bio

abraxaslabs.tech  ·  github.com/abraxas  ·  @abraxas_null  ·  [email protected]  ·  cve-2026-19553-wrap-bio

cve-2026-19553-wrap-bio

CPython ssl - Python Software Foundation

ssl.SSLContext.wrap_bio() did not require server_hostname to be non-None when check_hostname is set. SSLObject silently skips hostname verification. The program looks like it succeeded with check_hostname=True. No ValueError. The certificate chain is verified. The peer's name is not. wrap_socket() already raised. asyncio SSLProtocol / start_tls / open_connection turn "" into None and then call wrap_bio.

A MITM with a CA-valid cert for a different hostname completes the handshake against a wrap_bio / asyncio client that forgot to pass server_hostname.

IDCVE-2026-19553
CWECWE-297
CVSSHigh: 7.6 CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:P/VC:H/VI:H/VA:N
ProductCPython ssl
Affected< 3.12.15, 3.13.0–3.13.15, 3.14.0–3.14.7, 3.15.0a1 before 3.15.0
AuthMITM / attacker TLS server; victim is a Python TLS client using wrap_bio or asyncio without a hostname
LicenseGNU Affero GPL v3.0
Lab127.0.0.1 only

What an attacker can do

Sit on the path (or be the server the client meant to reach). Present a cert the client trusts for a name that is not the one the client intended. If the client used wrap_bio(..., server_hostname=None) or asyncio with server_hostname="" while check_hostname=True and CERT_REQUIRED, the handshake succeeds. Identity was never checked. wrap_socket on the same context raises ValueError before any bytes move. Passing a non-empty hostname to wrap_bio still rejects a mismatch.

Same product, sibling leftover: SNI SSLContext UAF. Opposite TLS side. They do not compose.

How I found it

PSF posted CVE-2026-19553. Issue python/cpython#156793, PR 158503, commit 6dc0069a. _check_sslobject_params already ran for wrap_socket. SSLObject._create skipped it. NEWS: completing a handshake that verified the certificate chain without verifying the peer's identity, with no indication that the check had been skipped. After the patch, wrap_bio with check_hostname=True and server_hostname=None/"" raises ValueError("check_hostname requires server_hostname"). The 3.12 backport raises DeprecationWarning instead.

I stood python:3.14.7-slim-bookworm. Same lab CA, two leaves: victim.lab and evil.lab. Client check_hostname=True, CERT_REQUIRED.

INJECT: wrap_bio(server_hostname=None) against evil.lab accepted. Peer SAN evil.lab. SNI None. TLS_AES_256_GCM_SHA384. CONTROL A: wrap_socket(server_hostname=None) raised ValueError: check_hostname requires server_hostname. CONTROL B: wrap_bio(server_hostname="victim.lab") against evil.lab raised SSLCertVerificationError hostname mismatch. NEGATIVE: same call against victim.lab accepted. Extra: asyncio.open_connection(..., server_hostname="") on 127.0.0.1:18510 accepted ("" becomes None, then wrap_bio).

Wrong turns already recorded: first asyncio start_server draft dropped a closing paren; host compile caught it before compose. Host 3.14.7 (Clang, OpenSSL 3.6.4) reproduced the same four oracles; the lab record is the container pin (GCC, OpenSSL 3.0.22). Theatre: a reverse shell. The oracle is accept-wrong-name plus wrap_socket still raising.

Lab

cd lab
./run.sh

Image python:3.14.7-slim-bookworm. Compose project cve-2026-19553. Loopback 127.0.0.1:18510 is the asyncio extra. MemoryBIO needs no 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

The fix

Pass a non-empty server_hostname to wrap_bio(), asyncio.create_connection(), or loop.start_tls(). Upgrade to 3.12.15 / 3.13.16 / 3.14.8 / 3.15.0. The patch only changes the silent skip into the same ValueError wrap_socket already raised.

References

  • CVE-2026-19553
  • python/cpython#156793
  • PR 158503
  • commit 6dc0069a
  • PSF security-announce
Download Tool