# Authlib Vulnerable to JWE RSA1_5 Bleichenbacher Padding Oracle

Authlib's RSA1_5 JWE unwrap raises a distinct ValueError on invalid PKCS#1 v1.5 padding versus InvalidTag on MAC failure, creating a Bleichenbacher exception oracle.

- Payout: Research
- Severity: high
- Program: Authlib
- Found by: Pr00fOf3xpl0it, Jaynornj
- Disclosed: 2026-03-16
- CVE: CVE-2026-28490
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-7432-952r-cw78
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-7432-952r-cw78
- Tags: area:oss domain:oss impact:data-leak impact:integrity kind:research platform:ghsa sev:high tech:jwt tech:python technique:source-review vuln:crypto vuln:oracle-manipulation

**Target:** Authlib JWE RSA1_5 key unwrap (jwe_algs.py RSAAlgorithm.unwrap)
**Root cause:** After cryptography returns random bytes on invalid PKCS#1 v1.5 padding, Authlib's length check raises ValueError('Invalid "cek" length') before AES-GCM tag validation, producing a different exception class than the InvalidTag path for valid padding.
**Steps:**
1. Install authlib 1.6.8 + cryptography 46.0.5 and generate a 2048-bit RSA key.
2. Build a compact JWE with header `{"alg":"RSA1_5","enc":"A128GCM"}` and a random 256-byte `ek`.
3. Call `jwe.deserialize_compact(token, private_key)`; observe `ValueError: Invalid "cek" length`.
4. Build a second JWE with `ek = public_key.encrypt(os.urandom(16), PKCS1v15())` and random ciphertext/tag; observe `InvalidTag`.
5. Distinguish the two error paths at the HTTP layer to drive Bleichenbacher's algorithm.
**How found:** Code review of RSAAlgorithm.unwrap noticed the length check raising a distinct exception immediately after cryptography's decrypt, defeating the library's random-bytes Bleichenbacher mitigation; confirmed empirically that the two failure paths yield different exception classes.
**Exploitation:** Attacker obtains the server RSA public key (e.g. JWKS endpoint), submits crafted RSA1_5 JWE tokens with manipulated `ek`, and uses the distinguishable error responses (ValueError vs InvalidTag) as a Bleichenbacher oracle to recover the CEK, then decrypts intercepted JWE payloads or forges new tokens.
**Insight:** When a crypto library returns random bytes on padding failure as a mitigation, any downstream length/format check that raises its own exception re-creates the oracle; audit JOSE/JWE unwrap paths for post-decrypt validation that distinguishes padding failure from MAC failure.

## Proof of concept (copied exactly from the original report)

**Exception oracle test cases A and B**

```
# --- caso A: ek con padding invalido (basura aleatoria) ---
    jwe_bad = make_jwe(header_b64, os.urandom(256))

    try:
        jwe.deserialize_compact(jwe_bad, private_key)
    except Exception as e:
        vuln(f"Caso A (padding invalido):   {type(e).__name__}: {e}")

    # --- caso B: ek con padding valido, ciphertext basura ---
    valid_ek  = private_key.public_key().encrypt(os.urandom(16), asym_padding.PKCS1v15())
    jwe_good  = make_jwe(header_b64, valid_ek)

    try:
        jwe.deserialize_compact(jwe_good, private_key)
    except Exception as e:
        ok(f"Caso B (padding valido/MAC malo): {type(e).__name__}: {e}")

    print()
    info("Los dos caminos producen excepciones de clases DIFERENTES.")
    info("Un framework web que mapea excepciones a HTTP codes expone el oracle.")
    info("El atacante no necesita acceso al stack trace — solo al HTTP status code.")
```

