Improper Validation of Integrity Check Value Affecting jose package, versions >=1.2.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.12% (2nd 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-21755410
  • published27 Oct 2026
  • disclosed26 Oct 2026
  • creditPeter Ullrich

Introduced: 26 Oct 2026

NewCVE-2026-65625  (opens in a new tab)
CWE-354  (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 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.

PoC

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.

CVSS Base Scores

version 4.0
version 3.1