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 Improper Validation of Integrity Check Value via gcm_block_decrypt/6 in the AES-GCM fallback implementation, which verifies only bit_size(CipherTag) bits of the authentication tag rather than enforcing the full 128-bit tag length. An attacker who can supply JWE compact tokens to a victim service can truncate the GCM authentication tag to a single byte, reducing the forgery search space to 256 possibilities and allowing unauthenticated ciphertext to be accepted as valid without knowledge of the key.
Note: This is only exploitable when the AES-GCM fallback module (jose_jwa_aes) is active, which occurs automatically on FIPS or stripped-OTP hosts where native AES-GCM is unavailable.
The script encodes an outside attacker against a victim service that decrypts attacker-supplied JWE compact tokens with its own secret key via the documented public entry point `JOSE.JWE.block_decrypt/2`. The attacker never sees the key.
- **Environment setup (not the attack):** The script points the cipher-dispatch ETS entry at the fallback module to reproduce a deployment without native AES-GCM: `:ets.insert(:jose_jwa, {{:cipher, {:aes_gcm, 256}}, {:jose_jwa_aes, {:aes_gcm, 256}}})`. On a FIPS/stripped-OTP host this happens automatically; the script just forces that condition. It prints the active dispatch module to confirm the fallback is in use.
- **Victim:** a random 256-bit key wrapped as an `oct` JWK, decrypting `dir` / `A256GCM` compact tokens.
- **Attacker:** builds forged compact tokens `#{protected}..#{iv}.#{forged_ct}.#{b64(tag)}` with a chosen ciphertext and no knowledge of the key. The `protected` header (`{"alg":"dir","enc":"A256GCM"}`) is the AAD.
- **Mechanism:** because `gcm_block_decrypt/6` verifies only `bit_size(CipherTag)` bits, truncating the tag to a single byte means only 8 bits are checked. The script first runs a **control**: 256 forged tokens with full 16-byte (128-bit) random tags, all of which are correctly `:rejected`. It then runs the **attack**: iterating the 256 possible single-byte tags `<<byte>>` and feeding each to `JOSE.JWE.block_decrypt/2`.
- **What proves the bug:** exactly one 8-bit tag is accepted — the script prints `Forged token ACCEPTED with 8-bit tag`, the number of plaintext bytes the victim returned for the attacker-chosen ciphertext, and the wall time (`:timer.tc`, ≤256 tries) to forge. It prints `VERIFIED` only when the 128-bit control forgeries were all rejected while the 8-bit forgery succeeded, confirming the acceptance is due to tag truncation and not a broken environment.
The full PoC script is attached below under "Scripts and Logs" so the finding can be reproduced directly.
Note: The PoC targets the JOSE.JWE.block_decrypt/2 entry point with the AES-GCM fallback module forced active via ETS manipulation; no specific runtime version or OS is stated.