Aug 4, 2026·7 min read·3 visits
A Bleichenbacher side-channel timing and error oracle in pyca/cryptography before version 50.0.0 allows unauthenticated remote attackers to recover Content Encryption Keys (CEK) and decrypt sensitive S/MIME messages by submitting crafted PKCS#7 ciphertexts and observing decryption responses.
A side-channel vulnerability in pyca/cryptography (versions 44.0.0 through 49.9.9) allows unauthenticated remote attackers to expose a Bleichenbacher oracle. This flaw exists within the PKCS#7 decryption module (specifically pkcs7_decrypt_der, pkcs7_decrypt_pem, and pkcs7_decrypt_smime) during Content Encryption Key (CEK) decryption when using RSA PKCS#1 v1.5 padding. Differences in error classification and symmetric execution timing allow an attacker to reconstruct plaintext keys.
The library pyca/cryptography is the primary cryptographic implementation layer for the Python ecosystem. In versions 44.0.0 through 49.9.9, the module responsible for parsing and decrypting PKCS#7 / CMS (Cryptographic Message Syntax) EnvelopedData payloads suffers from a classic side-channel leakage vulnerability. Specifically, when decrypting the encrypted symmetric Content Encryption Key (CEK) packaged inside a RecipientInfo structure, the application behaves differently depending on the decryption outcome of the RSA PKCS#1 v1.5 cipher block.
This behavior exposes a Bleichenbacher oracle (CWE-208 / CWE-209). If a remote application—such as an automated S/MIME email gateway, secure mail filter, or backend document processing system—automatically processes untrusted incoming PKCS#7 envelopes using a resident private key, it risks disclosing the decrypted CEK to an active network attacker. This bypasses the confidentiality guarantees expected of RSA-wrapped symmetric transport layers.
The vulnerability is highly dependent on the underlying cryptographic backend's handling of padding validation. When linked against libraries that do not enforce implicit rejection of invalid PKCS#1 v1.5 padding (such as OpenSSL 3.0, OpenSSL 3.1, LibreSSL, or BoringSSL), the python-level implementation fails to unify error behaviors. This creates distinct logical branches that an attacker can measure remotely.
The fundamental defect in src/rust/src/pkcs7.rs stems from the sequential, non-constant-time manner in which the PKCS#7 decryption pipeline is executed. When processing an EnvelopedData envelope, the library must decrypt the RSA-wrapped encryptedKey to extract the raw symmetric CEK (typically an AES-128 or AES-256 key), verify the algorithm identifier parameters, and then initialize the symmetric block cipher to decrypt the actual ciphertext payload.
Prior to version 50.0.0, the library executed these steps in a linear, unguarded order. This logical sequence produced four distinguishable outcomes depending on the structure of the attacker-supplied encryptedKey:
Invalid RSA Padding: If the private key decryption failed due to malformed PKCS#1 v1.5 padding, a ValueError exception was immediately raised. The process terminated before symmetric cipher initialization.
Incorrect Key Size: If the RSA decryption succeeded, but the extracted plaintext byte sequence did not match the expected key size of the target symmetric algorithm (e.g., 17 bytes instead of the expected 16 bytes for AES-128-CBC), the initialization of the AES algorithm object aborted with an explicit 'Invalid key size' exception. This leaked the exact length of the decrypted plaintext block.
Incorrect Symmetric Key with Correct Size: If the decrypted plaintext key had the correct length but was incorrect, the symmetric cipher initialized successfully. However, the subsequent AES-CBC decryption phase failed during PKCS#7 unpadding of the symmetric payload. This raised a CBC padding error.
Correct Key: Decryption completed successfully without error.
Because the early-abort scenarios (1 and 2) exit prior to the resource-intensive AES-CBC decryption phase, they take significantly less processing time than scenario 3. An attacker can supply a very large encrypted symmetric payload to dramatically amplify this timing delta, establishing a highly reliable timing oracle alongside the distinguishable error messages.
An analysis of the vulnerable implementation in src/rust/src/pkcs7.rs reveals how the early aborts bypassed the uniform error-handling logic. The vulnerable code executed RSA decryption using PyO3 bindings directly before looking up the required symmetric key length:
// VULNERABLE CODE PATH
let key = match recipient_info.key_encryption_algorithm.oid() {
&oid::RSA_OID => {
let padding = types::PKCS1V15.get(py)?.call0()?;
private_key
.call_method1(
pyo3::intern!(py, "decrypt"),
(recipient_info.encrypted_key, &padding),
)?
.extract::<pyo3::pybacked::PyBackedBytes>()?
}
_ => { /* ... error ... */ }
};
let algorithm_identifier = enveloped_data
.encrypted_content_info
.content_encryption_algorithm;
let (algorithm, mode) = match algorithm_identifier.params {
AlgorithmParameters::Aes128Cbc(iv) => (
types::AES128.get(py)?.call1((key,))?, // Key length check occurs here
// ...
),
// ...
};If private_key.call_method1 raised a PyValueError due to bad RSA padding, the execution aborted immediately. Similarly, if the key length was incorrect, the call to AES128 raised an exception.
The patch in version 50.0.0 implements the RFC 3218 mitigation standard. It extracts the expected key length before RSA decryption, generates a cryptographically random fallback key of that exact size, and executes the RSA decryption in a protected match block. If RSA decryption fails or yields an incorrect key length, the random key is silently substituted:
// PATCHED CODE PATH
let padding = types::PKCS1V15.get(py)?.call0()?;
let random_key = crate::backend::rand::get_rand_bytes(py, key_size)?;
let key = match private_key.call_method1(
pyo3::intern!(py, "decrypt"),
(recipient_info.encrypted_key, &padding),
) {
Ok(key) => {
let key = key.extract::<pyo3::Bound<'_, pyo3::types::PyBytes>>()?;
if key.as_bytes().len() == key_size {
key
} else {
random_key
}
}
Err(e) if e.is_instance_of::<pyo3::exceptions::PyValueError>(py) => random_key,
Err(e) => return Err(e.into()),
};By ensuring that a random key of the correct size is utilized when decryption fails or the size is invalid, the code forces the pipeline to continue execution through the symmetric AES-CBC decryption phase. This equalizes the execution path and eliminates the timing discrepancy.
To exploit this vulnerability, an attacker must have network access to an interface or service that processes user-supplied PKCS#7 envelopes and returns either direct error feedback or measurable timing differences. S/MIME mail gateways and secure email filters are primary targets, as they automatically decrypt incoming encrypted messages using local server certificates.
The attack begins with the generation of modified PKCS#7 ciphertexts. The attacker targets the encrypted_key field (the wrapped CEK) of a captured or intercepted EnvelopedData payload. Following the classical Bleichenbacher algorithm, the attacker applies mathematical modifications to the ciphertext, multiplying the encrypted integer representation by chosen values ($s$).
The modified PKCS#7 structures are then transmitted to the target service. The attacker monitors the response:
By systematically evaluating the oracle's responses across several thousand to millions of adaptive queries, the attacker narrows down the mathematical range of the plaintext CEK until it is fully reconstructed. Once the CEK is recovered, the attacker can decrypt the associated symmetric payload, compromising the confidentiality of the entire message archive.
A successful Bleichenbacher oracle attack results in a complete loss of confidentiality for the encrypted symmetric payload. If an attacker can query the decryption oracle successfully, they can decrypt any historic or newly intercepted message encrypted for the targeted recipient's certificate without possessing the private key.
In CVSS v4.0, this vulnerability receives a rating of 8.2 (High). The vector string is CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N. The High Attack Complexity (AC:H) and Present Attack Requirements (AT:P) reflect the necessity of a timing-stable, high-volume query channel and an automated processing endpoint.
There is currently no evidence of active exploitation in the wild, and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog. However, because pyca/cryptography is deeply integrated into many popular Python libraries, web frameworks, and corporate mail gateways, the actual attack surface is extensive. Services that process S/MIME mail or encrypted medical data are at the highest risk.
The primary remediation step is upgrading the python-cryptography library to version 50.0.0 or later. This introduces the RFC 3218-compliant key substitution mitigation, making RSA decryption failures indistinguishable from symmetric decryption failures.
Security engineers must evaluate whether their downstream runtime environments utilize custom or hardware-based cryptographic engines (HSMs). Because the patched code explicitly filters on pyo3::exceptions::PyValueError, any provider that throws a custom exception class on decryption failure will bypass the error-catching logic. This would propagate the exception immediately to the caller and re-expose the Bleichenbacher oracle. Ensure that any external backends conform to standard Python exception structures.
Additionally, applications must treat PKCS#7 EnvelopedData as inherently unauthenticated. As noted in the updated library documentation, even with the Bleichenbacher oracle patched, PKCS#7 does not natively authenticate its contents. Any service that reveals whether symmetric decryption succeeded or failed—through status codes, database side effects, or distinct error messages—remains vulnerable to standard symmetric padding oracle attacks. Design systems to avoid decrypting untrusted, unauthenticated EnvelopedData in synchronous, user-exposed flows.
CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
cryptography pyca | >= 44.0.0, < 50.0.0 | 50.0.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-208: Observable Timing Discrepancy |
| Attack Vector | Network (AV:N) |
| CVSS v4.0 Score | 8.2 (High) |
| Exploit Status | Proof of concept code exists in library test suites; no public weaponized exploits. |
| CISA KEV Status | Not listed |
| Mitigation Standard | RFC 3218 (Key substitution on failure) |
| Affected Functionality | pkcs7_decrypt_der, pkcs7_decrypt_pem, pkcs7_decrypt_smime |
The system performs a cryptographic operation that takes a variable amount of time depending on the input values, leading to an observable timing discrepancy.
CVE-2026-69246 is a host validation bypass vulnerability in the Guzzle PHP HTTP client. The flaw resides in Guzzle's core HTTP transport handlers (cURL and PHP stream wrappers). Under specific conditions, a parser differential occurs between the host validation layer and the underlying network transport library (e.g., libcurl), allowing remote attackers to bypass SSRF filters, proxy routing rules, and redirect protections via crafted noncanonical URI representations.
An uncontrolled resource consumption vulnerability (CWE-400) exists in the python-cryptography library's Rust-based X.509 verification engine. The flaw allows unauthenticated remote attackers to trigger severe CPU exhaustion and Denial of Service (DoS) by supplying specially crafted certificate chains containing duplicate self-signed certificates, forcing the recursive path builder into an exponential state-search loop.
An improper certificate validation vulnerability (CWE-295) in the Rust-based X.509 verification engine of python-cryptography allows wildcard Subject Alternative Names (SANs) to bypass permitted Name Constraints. This enables an attacker to construct certificates that escape the restricted scope of a subordinate Certificate Authority (CA) and successfully authenticate against vulnerable client installations. The vulnerability is tracked as CVE-2026-69248 and GHSA-m2h6-j472-rp4c, with a CVSS v4.0 base score of 6.9.
A high-severity heap-based out-of-bounds (OOB) read vulnerability exists in the Cython-based HTTP response and request parser extension of aiohttp. When processing malformed HTTP traffic, the parser fails to properly handle raw C pointers returned by the underlying llhttp library during error-message construction. This triggers an uncontrolled strlen() call on non-null-terminated network buffers, which can result in a Denial of Service (DoS) via worker process crash or the exposure of adjacent heap memory inside exception messages.
CVE-2026-69192 is a critical parser differential vulnerability in the 'ip-address' JavaScript library (versions <= 10.3.0). The library parses IPv4 octets containing leading zeros as base-10 (decimal), whereas standard system resolvers and web environments parse them as base-8 (octal). This discrepancy allows remote attackers to bypass SSRF guards and route malicious requests to internal RFC 1918 networks.
A high-severity security vulnerability has been identified within the Angular compiler's internationalization (i18n) metadata collection and translation pipeline. Angular implements strict defenses against client-side execution injection by validating standard attribute and property bindings. However, when parsing elements containing both i18n translation attributes and inline event-handler elements (such as `i18n-onerror`), the compiler failed to assert the safety of the target attribute. Consequently, compromised or untrusted localization translation source files can supply arbitrary JavaScript payloads that replace static event-handler bindings. This arbitrary code is compiled directly into the localized build bundle and executed dynamically by the web browser, bypassing runtime sanitization, security checks, and standard binding constraints.