CVE-2026-69247
A Bleichenbacher-style oracle in PKCS7 decryption can lead to key recovery.
- CVSS 8.2
- 208
- Cryptographic
- Remote
cryptography is a package designed to expose cryptographic primitives and recipes to Python developers. From 44.0.0 until 50.0.0, pkcs7_decrypt_der, pkcs7_decrypt_pem, and pkcs7_decrypt_smime reported the outcome of decrypting a RecipientInfo's encryptedKey in several distinguishable ways, one of which disclosed the exact length recovered from the RSA operation. The same distinction was also observable by timing. An application that decrypts attacker-supplied EnvelopedData and reflects the outcome gives the attacker a Bleichenbacher oracle against the content-encryption key. Decryption ran as RSA PKCS#1 v1.5 decrypt of encryptedKey, build an AES cipher from the result, then AES-CBC decrypt and PKCS#7 unpad. Invalid RSA padding, a valid padding with a bad key length, a correct length with a wrong key, and the real key each failed or succeeded differently. Case 1 is reachable only where the linked library lacks implicit rejection: OpenSSL 3.0 and 3.1, LibreSSL, and BoringSSL. Exploitation requires a service that auto-decrypts untrusted EnvelopedData matching the victim certificate and answers adaptively at high volume, such as an S/MIME gateway or mail filter. This issue is fixed in 50.0.0.
- CWE
- 208
- CVSS base score
- 8.2
- Published
- 2026-08-03
- OWASP
- A02 Cryptographic Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Incorrect Algorithm
- Category
- Cryptographic
- Subcategory
- Cryptographic Implementation Error
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- cryptography
- Fixed by upgrading
- Yes
Solution
Upgrade cryptography to version 50.0.0 or later.
Vulnerable code sample
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding as sym_padding
from cryptography.exceptions import InvalidTag
_SYMMETRIC_KEY_SIZE = 32
def _decrypt_recipient_key(private_key, encrypted_key, data, iv):
try:
# VULNERABLE: Raises a specific error if RSA padding is invalid, creating a distinguishable failure case for a Bleichenbacher oracle.
decrypted_key = private_key.decrypt(
encrypted_key,
padding.PKCS1v15()
)
except ValueError:
# This early exit creates an observable difference in behavior and timing.
raise ValueError("RSA decryption failed")
if len(decrypted_key) != _SYMMETRIC_KEY_SIZE:
# A second distinguishable failure mode based on key length.
raise ValueError("Invalid symmetric key length")
cipher = Cipher(algorithms.AES(decrypted_key), modes.CBC(iv))
decryptor = cipher.decryptor()
try:
padded_plaintext = decryptor.update(data) + decryptor.finalize()
unpadder = sym_padding.PKCS7(algorithms.AES.block_size).unpadder()
plaintext = unpadder.update(padded_plaintext) + unpadder.finalize()
except (ValueError, InvalidTag):
# A third distinguishable failure mode from the symmetric decryption.
raise ValueError("Symmetric decryption failed")
return plaintextPatched code sample
import os
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding as sym_padding
from cryptography.exceptions import InvalidTag
_SYMMETRIC_KEY_SIZE = 32
def _decrypt_recipient_key(private_key, encrypted_key, data, iv):
decrypted_key = None
try:
decrypted_key = private_key.decrypt(
encrypted_key,
padding.PKCS1v15()
)
except ValueError:
# Don't raise, to prevent distinguishing padding errors.
pass
# FIX: A randomly generated key is used if RSA decryption or length validation fails, making all error paths computationally indistinguishable.
is_key_valid = (
decrypted_key is not None and len(decrypted_key) == _SYMMETRIC_KEY_SIZE
)
key_to_use = decrypted_key if is_key_valid else os.urandom(_SYMMETRIC_KEY_SIZE)
cipher = Cipher(algorithms.AES(key_to_use), modes.CBC(iv))
decryptor = cipher.decryptor()
try:
padded_plaintext = decryptor.update(data) + decryptor.finalize()
unpadder = sym_padding.PKCS7(algorithms.AES.block_size).unpadder()
plaintext = unpadder.update(padded_plaintext) + unpadder.finalize()
except (ValueError, InvalidTag):
raise ValueError("Decryption failed")
if not is_key_valid:
# Ensure failure even if the random key miraculously worked.
raise ValueError("Decryption failed")
return plaintextPayload
import base64
# This represents a single probe payload for a Bleichenbacher-style attack.
# It is a syntactically valid S/MIME message containing a PKCS7 EnvelopedData structure.
# The 'encryptedKey' within the RecipientInfo has been manipulated by an attacker.
# An exploit involves sending thousands of variations of this payload and observing
# the server's distinct error responses or timing differences to incrementally
# decrypt a target message's content-encryption key.
b64_payload = "MIAGCSqGSIb3DQEHA6CAMIACAQAxggJAMIIC/AIBADCBnDCBlDELMAkGA1UEBhMCVVMxETAPBgNVBAgMCE1pc3NvdXJpMRIwEAYDVQQHDAlTYW5kYm94Q0ExGTAXBgNVBAoMEFByb2JlIE9yZ2FuaXphdGlvbjEgMB4GA1UEAwwXd3d3LnByb2JlLmV4YW1wbGUuY29tMScwJQYJKoZIhvcNAQkBFhh2aWN0aW1AcHJvYmUuZXhhbXBsZS5jb20CAQEwDQYJKoZIhvcNAQEBBQAEgYA5M9+5pkCP2p33f5yYoTce8j92I6KbtAZqjGvjv4uWq5kGsVAxtup525agIaxpSkvxm2pPMr3uJot3bgyyok9G5Ayv2bVIMs4dLw8k2X8L7pZ0+DmjUvAcpA6j8oA2VfYce2d7C3SCtJGgnc9L+xKj+wKFy1GZGUXnI0DMYwOwggEIBgkqhkiG9w0BBwEwggEFAgEEMC3g8uC3j2X+GRy2AlA/i107vUPZ9x7J2G2I3q3qVtB9JpZ3A7AFAgECgIQwGAYJKoZIhvcNAQEIMAsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjYwMTE1MDIwMzI0WjAjBgkqhkiG9w0BCQQxFgQU+gT4rO/NnUf7JjHqCg+tC73+xjgwga0GCSqGSIb3DQEHA0EARL5Uv8G9FzYpM3p4yv2PG8z6y7Q+4nZG/K4Jp2V8eJ8t3d5e2e8yJ9r7e+e4q3D5t8B/e9t9D4g9b7x6z5A4c5f3e/d6B+g2h/c8a+f8g7b6d5e4g3h2f1t8v7c6a+e5g4i3f2s7w5c+d6g4h3j2s7v5c+d6g4h3j2s7v5c+d6g4h3j2s7v5c+d6g4h3j2s7v5c+d6g4h3j2s7v5c+d6g4h3j2s7v5c+d6g4h3j2s7v5c+d6g4h3j2g=="
smime_message = f"""Content-Type: application/pkcs7-mime; smime-type=enveloped-data; name="smime.p7m"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="smime.p7m"
{b64_payload}
"""
print(smime_message)
Cite this entry
@misc{vaitp:cve202669247,
title = {{A Bleichenbacher-style oracle in PKCS7 decryption can lead to key recovery.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-69247},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-69247/}}
}
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 ::
