Information Exposure Affecting jose package, versions >=1.3.0 <1.11.13


Severity

Recommended
0.0
medium
0
10

CVSS assessment by Snyk's Security Team. Learn more

Threat Intelligence

Exploit Maturity
Proof of Concept
EPSS
0.09% (1st percentile)

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications
  • Snyk IDSNYK-HEX-JOSE-21755408
  • published27 Oct 2026
  • disclosed26 Oct 2026
  • creditPeter Ullrich

Introduced: 26 Oct 2026

NewCVE-2026-65626  (opens in a new tab)
CWE-203  (opens in a new tab)

How to fix?

Upgrade jose to version 1.11.13 or higher.

Overview

Affected versions of this package are vulnerable to Information Exposure via the pure-Erlang fallback implementation of rsaes_pkcs1_decrypt/2 in jose_jwa_pkcs1.erl, which returns a distinguishable :error response when RSA1_5 ciphertext fails the 00 02 || PS || 00 || M padding check versus a valid decryption. An attacker who can submit attacker-controlled RSA1_5-wrapped keys to an endpoint calling JOSE.JWE.key_decrypt/3 can exploit this one-bit padding oracle to mount an adaptive Bleichenbacher attack, recovering plaintext or private key material using only the server's public RSA key and repeated decryption queries.

Note: This is only exploitable when JOSE.crypto_fallback(true) is enabled, routing RSA1_5 operations through the vulnerable pure-Erlang backend rather than the native OTP crypto implementation.

PoC

The attached script encodes the threat model of an outside attacker who can submit attacker-controlled RSA1_5-wrapped keys to an application that calls the public JOSE.JWE.key_decrypt/3. The attacker knows only the server's public RSA key and never touches the private key.

Setup: it generates a 2048-bit RSA keypair, derives the public key and its modulus byte size, and forces JOSE onto the audited pure-Erlang backend. JOSE.crypto_fallback(true) enables the fallback, and an ETS override pins RSA1_5 to the vulnerable implementation so the OTP runtime does not silently route through its native RSA code:

:ets.insert(:jose_jwa,
  {{:rsa_crypt, :rsa1_5}, {:jose_jwa_pkcs1, [{:rsa_padding, :rsa_pkcs1_padding}]}})

The script then builds two ciphertexts from public information only. The conforming one is produced by encrypting a plaintext to the public key via JOSE.JWE.block_encrypt, compacting the token, and extracting its encrypted_key segment — a well-formed RSA1_5 wrapping. The non-conforming one is the RSA ciphertext integer zero, materialized as an all-zero byte string of modulus length; decrypting it yields an all-zero encoded block that cannot contain the required 00 02 || PS || 00 || M structure.

Both are passed through JOSE.JWE.key_decrypt/3, mimicking the internal chain down to rsaes_pkcs1_decrypt/2 → eme_pkcs1_decode/2. The mechanism is that the conforming block satisfies the padding check and the recovered key is returned, while the zero block fails the check and yields :error. The script captures this difference:

conforming_valid? = is_binary(conforming_result)
nonconforming_rejected? = nonconforming_result == :error
distinguishable? = conforming_valid? and nonconforming_rejected?

It prints the result class of each decryption and the distinguishable? flag. The proof succeeds when the conforming ciphertext returns a binary key and the non-conforming ciphertext returns :error; otherwise the script raises "NOT VERIFIED". A printed padding validity distinguishable? : true demonstrates the one-bit oracle — the exact signal an adaptive Bleichenbacher attack consumes. The full script is attached below under "Scripts and Logs" so the maintainer can run it directly.

Note: The PoC states it requires JOSE.crypto_fallback(true) to be enabled and uses an ETS override to pin RSA1_5 to the vulnerable pure-Erlang backend, exercising JOSE.JWE.key_decrypt/3 with a 2048-bit RSA keypair.

CVSS Base Scores

version 4.0
version 3.1