Snyk has a proof-of-concept or detailed explanation of how to exploit this vulnerability.
The probability is the direct output of the EPSS model, and conveys an overall sense of the threat of exploitation in the wild. The percentile measures the EPSS probability relative to all known EPSS scores. Note: This data is updated daily, relying on the latest available EPSS model version. Check out the EPSS documentation for more details.
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 applicationsUpgrade jose to version 1.11.13 or higher.
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.
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.