From 3b8a8d3d0902f3a735efbf5def437a59d536d0d0 Mon Sep 17 00:00:00 2001 From: Ramachandran Thamarai Semmenian <107013420+chan770@users.noreply.github.com> Date: Tue, 1 Sep 2026 01:24:58 -0700 Subject: [PATCH] Do not crash on a JWT with a non-base64url signature (fixes #6101) (#6102) JWT_REGEX accepts a signature segment of any length and parseJWT only validates the header/payload, so a value that matches the JWT pattern but carries a malformed/truncated signature (e.g. 41 base64url characters, which is length % 4 == 1 and thus impossible base64) reached crackHMAC. There decodeBase64(signature) let binascii.Error propagate, aborting the whole run with an unhandled exception during checkJWT(). Guard the signature decode: a signature that is not valid base64url cannot be an HMAC we could verify, so crackHMAC now treats it as not crackable (returns None) instead of raising. The other auditJWT findings are unaffected. Added a regression doctest. Co-authored-by: chan770 --- lib/utils/jwt.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/utils/jwt.py b/lib/utils/jwt.py index 88b5490c92c..c000d839b05 100644 --- a/lib/utils/jwt.py +++ b/lib/utils/jwt.py @@ -89,6 +89,8 @@ def crackHMAC(token, secrets, limit=None): 's3cr3t' >>> crackHMAC(token, ["admin", "letmein"]) is None True + >>> crackHMAC("eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiYWRtaW4ifQ.x", ["secret"]) is None + True """ data = parseJWT(token) @@ -97,7 +99,13 @@ def crackHMAC(token, secrets, limit=None): fn = HMAC_ALGORITHMS[data["header"]["alg"].upper()] signingInput = getBytes(data["signingInput"]) - target = decodeBase64(data["signature"], binary=True) + + try: + target = decodeBase64(data["signature"], binary=True) + except Exception: + # a signature segment that is not valid base64url (e.g. a truncated/malformed token that still + # matches the JWT pattern) cannot be an HMAC we could verify, so it is simply not crackable + return None for index, secret in enumerate(secrets): if limit is not None and index >= limit: