pyOpenSSL allows TLS connection bypass via unhandled callback exception in set_tlsext_servername_callback
Description
pyOpenSSL is a Python wrapper around the OpenSSL library. Starting in version 0.14.0 and prior to version 26.0.0, if a user provided callback to set_tlsext_servername_callback raised an unhandled exception, this would result in a connection being accepted. If a user was relying on this callback for any security-sensitive behavior, this could allow bypassing it. Starting in version 26.0.0, unhandled exceptions now result in rejecting the connection.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In pyOpenSSL before 26.0.0, unhandled exceptions in the TLS server name callback silently allowed connections, bypassing security checks.
Root Cause: In pyOpenSSL versions 0.14.0 through 25.3.0, the Context.set_tlsext_servername_callback function did not properly handle exceptions raised by the user-provided callback. If the callback raised an unhandled exception, the exception was silently swallowed and the TLS handshake proceeded as if the callback had succeeded [1][2].
Exploitation: An attacker could exploit this by sending a TLS ClientHello with a Server Name Indication (SNI) that triggers an exception in the callback. No special network position is required beyond the ability to initiate a TLS connection. If the callback performs security-sensitive actions such as access control or certificate selection, the exception could cause those checks to be bypassed [4].
Impact: Successful exploitation could allow an attacker to bypass security controls implemented in the server name callback, potentially gaining access to resources or services that should have been denied. This is particularly critical if the callback is used for authentication or authorization decisions [2][4].
Mitigation: The issue is fixed in pyOpenSSL version 26.0.0. In the fix, unhandled exceptions are caught, sys.excepthook is called, and the connection is rejected with a fatal TLS alert. Users should update to 26.0.0 or later. No workaround is available beyond updating [1][3].
AI Insight generated on May 18, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
pyopensslPyPI | >= 0.14.0, < 26.0.0 | 26.0.0 |
Affected products
2- Range: >=0.14.0, <26.0.0
- pyca/pyopensslv5Range: >= 0.14.0, < 26.0.0
Patches
1d41a814759a9Handle exceptions in set_tlsext_servername_callback callbacks (#1478)
3 files changed · +57 −1
CHANGELOG.rst+1 −0 modified@@ -20,6 +20,7 @@ Changes: ^^^^^^^^ - Added ``OpenSSL.SSL.Connection.get_group_name`` to determine which group name was negotiated. +- ``Context.set_tlsext_servername_callback`` now handles exceptions raised in the callback by calling ``sys.excepthook`` and returning a fatal TLS alert. Previously, exceptions were silently swallowed and the handshake would proceed as if the callback had succeeded. 25.3.0 (2025-09-16) -------------------
src/OpenSSL/SSL.py+6 −1 modified@@ -2,6 +2,7 @@ import os import socket +import sys import typing import warnings from collections.abc import Sequence @@ -1752,7 +1753,11 @@ def set_tlsext_servername_callback( @wraps(callback) def wrapper(ssl, alert, arg): # type: ignore[no-untyped-def] - callback(Connection._reverse_mapping[ssl]) + try: + callback(Connection._reverse_mapping[ssl]) + except Exception: + sys.excepthook(*sys.exc_info()) + return _lib.SSL_TLSEXT_ERR_ALERT_FATAL return 0 self._tlsext_servername_callback = _ffi.callback(
tests/test_ssl.py+50 −0 modified@@ -2034,6 +2034,56 @@ def servername(conn: Connection) -> None: assert args == [(server, b"foo1.example.com")] + def test_servername_callback_exception( + self, monkeypatch: pytest.MonkeyPatch + ) -> None: + """ + When the callback passed to `Context.set_tlsext_servername_callback` + raises an exception, ``sys.excepthook`` is called with the exception + and the handshake fails with an ``Error``. + """ + exc = TypeError("server name callback failed") + + def servername(conn: Connection) -> None: + raise exc + + excepthook_calls: list[ + tuple[type[BaseException], BaseException, object] + ] = [] + + def custom_excepthook( + exc_type: type[BaseException], + exc_value: BaseException, + exc_tb: object, + ) -> None: + excepthook_calls.append((exc_type, exc_value, exc_tb)) + + context = Context(SSLv23_METHOD) + context.set_tlsext_servername_callback(servername) + + # Necessary to actually accept the connection + context.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem)) + context.use_certificate( + load_certificate(FILETYPE_PEM, server_cert_pem) + ) + + # Do a little connection to trigger the logic + server = Connection(context, None) + server.set_accept_state() + + client = Connection(Context(SSLv23_METHOD), None) + client.set_connect_state() + client.set_tlsext_host_name(b"foo1.example.com") + + monkeypatch.setattr(sys, "excepthook", custom_excepthook) + with pytest.raises(Error): + interact_in_memory(server, client) + + assert len(excepthook_calls) == 1 + assert excepthook_calls[0][0] is TypeError + assert excepthook_calls[0][1] is exc + assert excepthook_calls[0][2] is not None + class TestApplicationLayerProtoNegotiation: """
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
5- github.com/advisories/GHSA-vp96-hxj8-p424ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-27448ghsaADVISORY
- github.com/pyca/pyopenssl/blob/358cbf29c4e364c59930e53a270116249581eaa3/CHANGELOG.rstghsax_refsource_MISCWEB
- github.com/pyca/pyopenssl/commit/d41a814759a9fb49584ca8ab3f7295de49a85aa0ghsax_refsource_MISCWEB
- github.com/pyca/pyopenssl/security/advisories/GHSA-vp96-hxj8-p424ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.