diff --git a/lib/core/settings.py b/lib/core/settings.py index ee2ac49de..1330f7c22 100644 --- a/lib/core/settings.py +++ b/lib/core/settings.py @@ -20,7 +20,7 @@ from lib.core.enums import OS from thirdparty import six # sqlmap version (...) -VERSION = "1.10.8.1" +VERSION = "1.10.8.2" TYPE = "dev" if VERSION.count('.') > 2 and VERSION.split('.')[-1] != '0' else "stable" TYPE_COLORS = {"dev": 33, "stable": 90, "pip": 34} VERSION_STRING = "sqlmap/%s#%s" % ('.'.join(VERSION.split('.')[:-1]) if VERSION.count('.') > 2 and VERSION.split('.')[-1] == '0' else VERSION, TYPE) diff --git a/lib/utils/jwt.py b/lib/utils/jwt.py index 358e62b6c..88b5490c9 100644 --- a/lib/utils/jwt.py +++ b/lib/utils/jwt.py @@ -14,6 +14,7 @@ from lib.core.convert import decodeBase64 from lib.core.convert import encodeBase64 from lib.core.convert import getBytes from lib.core.convert import getText +from thirdparty import six # a compact JSON Web Token: base64url(header).base64url(payload).base64url(signature); a header always starts # with '{"' which base64url-encodes to the literal prefix 'eyJ', so this matches JWTs embedded in a larger value @@ -46,7 +47,7 @@ def parseJWT(token): except Exception: return None - if not isinstance(header, dict) or "alg" not in header: + if not isinstance(header, dict) or not isinstance(header.get("alg"), six.string_types): return None return {"header": header, "payload": payload, "signature": signature, "signingInput": token.rsplit('.', 1)[0], "raw": token} diff --git a/tests/test_jwt.py b/tests/test_jwt.py index 31d0cf668..b1ff857d0 100644 --- a/tests/test_jwt.py +++ b/tests/test_jwt.py @@ -24,6 +24,7 @@ from lib.core.data import conf from lib.core.enums import PLACE from lib.utils.jwt import auditJWT from lib.utils.jwt import crackHMAC +from lib.utils.jwt import encodeSegment from lib.utils.jwt import findJWTs from lib.utils.jwt import forgeJWT from lib.utils.jwt import parseJWT @@ -41,6 +42,12 @@ class JWTUtilsTest(unittest.TestCase): for value in ("", "a.b", "a.b.c.d", "not.a.jwt", "eyJx.eyJx"): self.assertIsNone(parseJWT(value)) + def test_parse_rejects_non_string_alg(self): + # RFC 7515: "alg" MUST be a string; a crafted token with e.g. an integer "alg" must not parse + # (a permissive gate here would let a non-string "alg" reach auditJWT's alg.strip() and crash) + token = "%s.%s." % (encodeSegment({"alg": 123}), encodeSegment({})) + self.assertIsNone(parseJWT(token)) + def test_forge_none_is_unsigned(self): token = forgeJWT({"alg": "none"}, {"user": "admin"}) self.assertTrue(token.endswith("."))