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 Verification of Cryptographic Signature via jose_jwk_kty_ec:verify/4, which splits the JWS signature at byte_size(Signature) div 2 - a length derived from attacker-controlled input rather than the canonical size mandated by RFC 7518 section 3.4. Because crypto:bytes_to_integer/1 ignores leading zero octets, an attacker who holds one valid compact JWS token can prepend or strip zero octets from each half of the R || S signature to produce an unbounded family of distinct byte strings that all pass verification against the same key and payload. This breaks any control keyed on token or signature uniqueness, such as revoke-by-hash, replay caches, or deduplication logic.
The attached script (`scripts/jose/ecdsa_signature_length_is_never_validated_in_verify_4_signat_2314.exs`, embedded below under "Scripts and Logs") demonstrates the malleability through the library's public verification API.
Threat model: an outside attacker holds one valid, signed JWS compact token (`header.payload.signature`) — exactly what a server hands a client. The attacker does not know the private key and cannot forge new payloads. The only public entry point used is `JOSE.JWS.verify/2`, the trust boundary a real application relies on.
Walkthrough:
1. The victim generates a P-256 key and signs a payload with `alg = ES256`, producing a compact token. The script confirms this honest token verifies: `{true, _, _} = JOSE.JWS.verify(jwk, token)`.
2. The attacker splits the token on `.` and base64url-decodes the signature. For ES256 the signature is `R ‖ S`, each 32 bytes, so it splits at the midpoint into `r` and `s`.
3. For `k` from 1 to 5, the attacker builds a re-padded signature `zeros(k) <> r <> zeros(k) <> s` and re-assembles a token with it. Each forged signature is longer than 64 bytes but still splits evenly, and `crypto:bytes_to_integer/1` strips the injected zeros, so each half decodes back to the original `r` and `s`.
4. Every forged token is fed back through `JOSE.JWS.verify(jwk, t)`.
The script counts how many *distinct* signature byte strings verify against the same key. A strict RFC 7518 verifier accepts exactly 1 (the canonical token); `jose` accepts all five forgeries, so the distinct-verifying-token count is greater than 1 and the script prints a `VERIFIED:` line reporting the number of re-padded signatures accepted. No private key, special configuration, or forged payload is involved — only a single legitimately issued token.
Note: The PoC script demonstrates the malleability through the library's public verification API using a P-256 key and ES256 alg, but states no specific runtime version, operating system, or non-default configuration.