CVE-2025-15346
wolfssl-py fails to enforce client certificates, allowing mTLS bypass.
- CVSS 9.3
- CWE-287
- Authentication, Authorization, and Session Management
- Remote
A vulnerability in the handling of verify_mode = CERT_REQUIRED in the wolfssl Python package (wolfssl-py) causes client certificate requirements to not be fully enforced. Because the WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT flag was not included, the behavior effectively matched CERT_OPTIONAL: a peer certificate was verified if presented, but connections were incorrectly authenticated when no client certificate was provided. This results in improper authentication, allowing attackers to bypass mutual TLS (mTLS) client authentication by omitting a client certificate during the TLS handshake. The issue affects versions up to and including 5.8.2.
- CWE
- CWE-287
- CVSS base score
- 9.3
- Published
- 2026-01-08
- OWASP
- A07 Identification and Authentication Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Authentication, Authorization, and Session Management
- Subcategory
- Improper SSL/TLS Certificate Validation
- Accessibility scope
- Remote
- Impact
- Unauthorized Access
- Affected component
- wolfssl-py
- Fixed by upgrading
- Yes
Solution
Upgrade the wolfssl-py package to version 5.8.3 or later.
Vulnerable code sample
import ssl
# This code is a representative example of the logic that existed in
# the wolfssl-py package prior to the fix for CVE-2025-15346.
# It simulates how the Python-level settings were translated into
# calls to the underlying wolfSSL C library.
# --- Simulated constants from the underlying wolfSSL C library ---
# In a real implementation, these would be defined by the C library binding.
# WOLFSSL_VERIFY_NONE tells wolfSSL not to verify the peer.
WOLFSSL_VERIFY_NONE = 0
# WOLFSSL_VERIFY_PEER tells wolfSSL to verify the peer certificate if presented.
# Connections are allowed if the peer does not present a certificate.
WOLFSSL_VERIFY_PEER = 1
# WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT is the flag that, when combined with
# WOLFSSL_VERIFY_PEER, enforces that a client certificate MUST be presented.
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT = 2
class VulnerableSSLContext:
"""
A simplified, representative model of the wolfssl.SSLContext object
from a vulnerable version (<= 5.8.2). It demonstrates how setting
`verify_mode` to `CERT_REQUIRED` was incorrectly handled.
"""
def __init__(self):
self._verify_mode = ssl.CERT_NONE
# In the actual library, self._wolfssl_ctx would be a C pointer
# to the wolfSSL context structure.
self._wolfssl_ctx = object() # Placeholder for the C context
@property
def verify_mode(self):
return self._verify_mode
@verify_mode.setter
def verify_mode(self, value):
"""
This property setter contains the vulnerable logic. It determines which
flags to pass to the underlying wolfSSL C library function that sets
the verification mode.
"""
self._verify_mode = value
# In the actual code, a C function like wolfSSL_CTX_set_verify()
# would be called here. We simulate the logic that determines the arguments.
if self._verify_mode == ssl.CERT_REQUIRED:
#
# THE VULNERABILITY IS HERE
#
# The code should combine WOLFSSL_VERIFY_PEER with
# WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT to enforce client certs.
# However, it only sets WOLFSSL_VERIFY_PEER, which makes the
# behavior identical to CERT_OPTIONAL: a certificate is verified
# if sent, but the connection is NOT terminated if it is missing.
#
c_flags = WOLFSSL_VERIFY_PEER
# Simulated call to the C library:
# _lib.wolfSSL_CTX_set_verify(self._wolfssl_ctx, c_flags, callback)
print(f"Vulnerable path: Setting C flags to {c_flags} for CERT_REQUIRED")
elif self._verify_mode == ssl.CERT_OPTIONAL:
# This logic is correct for CERT_OPTIONAL. The vulnerability is that
# CERT_REQUIRED incorrectly uses this same logic.
c_flags = WOLFSSL_VERIFY_PEER
# Simulated call to the C library:
# _lib.wolfSSL_CTX_set_verify(self._wolfssl_ctx, c_flags, callback)
print(f"Correct path: Setting C flags to {c_flags} for CERT_OPTIONAL")
else: # ssl.CERT_NONE
c_flags = WOLFSSL_VERIFY_NONE
# Simulated call to the C library:
# _lib.wolfSSL_CTX_set_verify(self._wolfssl_ctx, c_flags, callback)
print(f"Correct path: Setting C flags to {c_flags} for CERT_NONE")Patched code sample
import ssl
# This code provides a representative example of the fix for CVE-2025-15346.
# In the actual wolfssl-py library, this logic would be integrated within the
# SSLContext class methods that configure the underlying C-level wolfSSL context.
# Constants representing the C-level flags in the wolfSSL library.
# These would be defined within the wolfssl-py package.
WOLFSSL_VERIFY_PEER = 0x01
WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT = 0x02
def _configure_context_verify_mode(wolfssl_c_context, verify_mode, verify_callback):
"""
Represents the fixed internal function for setting the certificate verification mode.
The key change is the addition of the WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT flag
when verify_mode is CERT_REQUIRED, which was missing in the vulnerable versions.
Args:
wolfssl_c_context: A pointer or handle to the underlying C-level wolfSSL context.
verify_mode: The SSL verification mode (e.g., ssl.CERT_REQUIRED).
verify_callback: A callback function for custom verification logic.
"""
# VULNERABLE CODE (prior to the fix):
#
# if verify_mode == ssl.CERT_REQUIRED:
# # PROBLEM: This sets the server to verify a client certificate if one is
# # sent, but it does NOT fail the connection if the client sends no
# # certificate at all. The behavior was equivalent to CERT_OPTIONAL.
# verify_flags = WOLFSSL_VERIFY_PEER
# _wolfssl_lib.wolfSSL_CTX_set_verify(wolfssl_c_context, verify_flags, verify_callback)
# FIXED CODE:
if verify_mode == ssl.CERT_REQUIRED:
# SOLUTION: Add the WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT flag.
# This flag explicitly instructs wolfSSL to fail the TLS handshake
# if the peer (client) does not provide a certificate. This correctly
# enforces the mutual TLS (mTLS) requirement.
verify_flags = WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT
# This would be a call to the underlying C library function.
# _wolfssl_lib.wolfSSL_CTX_set_verify(wolfssl_c_context, verify_flags, verify_callback)
elif verify_mode == ssl.CERT_OPTIONAL:
# The behavior for CERT_OPTIONAL remains unchanged, as it was already correct.
verify_flags = WOLFSSL_VERIFY_PEER
# _wolfssl_lib.wolfSSL_CTX_set_verify(wolfssl_c_context, verify_flags, verify_callback)
else: # ssl.CERT_NONE
# The behavior for CERT_NONE remains unchanged.
verify_flags = 0 # WOLFSSL_VERIFY_NONE
# _wolfssl_lib.wolfSSL_CTX_set_verify(wolfssl_c_context, verify_flags, verify_callback)
# For demonstration purposes, we can print the flags that would be set.
# In the actual library, the commented-out C-level calls would be executed.
# print(f"For verify_mode '{verify_mode}', the resolved wolfSSL verify_flags are: {verify_flags}")Cite this entry
@misc{vaitp:cve202515346,
title = {{wolfssl-py fails to enforce client certificates, allowing mTLS bypass.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2025-15346},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-15346/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
