CVE-2026-55954
ueberauth_apple — ID token accepted without claim validation (account takeover)
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:N/SC:N/SI:N/SA:NSummary
ueberauth_apple is the Sign in with Apple strategy for Ueberauth, the common authentication library for Elixir. It verifies the Apple id_token's signature correctly, then trusts every claim inside it without validation.
Technical analysis
The callback handler checks the JWT signature against Apple's public keys but validates none of the token's claims: not the audience, issuer or expiry, and not the nonce. Any Apple-signed token that carries a victim's subject is then accepted, whether a stale token taken from a log or one minted for a different client in the same developer team; completing the callback also needs a valid authorisation code, which the attacker supplies from their own Apple flow.
The verifier stops at the signature. Ueberauth.Strategy.Apple.Token.payload/2 calls JOSE.JWT.verify/2 with a key pinned to Apple's JWKS by kid, then hands back the token's fields map as-is. Nothing looks at iss, aud, exp, iat, or nonce. The crypto is fine — the hole is the registered-claim validation that simply isn't there. lib/ueberauth/strategy/apple/token.ex
Identity is lifted from the unvalidated token. handle_callback!/1 reads the fields returned above and drops sub and email straight into the Überauth user record. The OAuth.get_access_token/2 code exchange that follows is never tied to the token identity — it only needs some code exchange to succeed, and an attacker happily supplies that from their own Sign-in-with-Apple flow. lib/ueberauth/strategy/apple.ex
# signature IS checked against Apple's JWKS, keyed by `kid`…
{:ok, jwt} = JOSE.JWT.verify(apple_jwks_for(header.kid), token)
# …and then every claim is returned untouched.
# no aud check, no iss check, no exp check, no nonce check.
jwt.fields #=> %{"sub" => ..., "email" => ..., "aud" => ANY, "iss" => ANY, "exp" => PAST}Proof of concept
Renesoft confirmed this against the real package in an isolated environment, with no live systems. The attack path, then the recorded test output:
- Obtain an Apple-signed
id_tokenbearing the victim'ssub— capture a real token (server logs, referrer leaks, shared-device history), or take one the victim obtained for a sibling client in the same developer team, where the signature is valid but theaudis the other client (audience confusion). - Start a Sign-in-with-Apple flow against the target to obtain any valid
code; it can be the attacker's own, because the exchange only has to succeed, never match the token. - POST the callback with that
codeand the victim'sid_token; the library verifies the signature, skips every claim check, and mints an authenticated session for the victim'ssub.
$ mix run poc_siwa_replay.exs
legit accepted, sub=victim-sub
forged accepted, sub=victim-sub <-- wrong iss + wrong aud + EXPIRED, still accepted
neg control OK: tampered signature rejected (signature IS verified)
exit 0 · 3/3 assertions — VERDICT: CONFIRMEDImpact
Account takeover of a user whose Apple id_token an attacker can obtain, for example from a log. A leaked token can be replayed until Apple rotates the signing key out of its JWKS, because the missing expiry check removes the token's own lifetime.
Affected versions
- Affected
- ≤ 0.6.1
- Fixed
- 0.6.2
- CVSS
- 9.1 (4.0)
- Weakness
- CWE-290
- Ecosystem
- Elixir · Hex
- Disclosed
- 2026-07-14
Remediation
Fixed in 0.6.2, which validates the registered claims after the signature check. Applications on 0.6.1 and earlier should upgrade.
Timeline
- Discovered and proven against ueberauth_apple 0.6.1.
- Reported privately through GitHub private vulnerability reporting.
- Fixed in 0.6.2; advisory published and CVE-2026-55954 assigned.