Threats tagged 'brew-weaviate-cli-cve-2026-114620'
1 threat
Most recently published first
| Severity | CVSS | Signals | Threat | Source | Published | Score and star |
|---|---|---|---|---|---|---|
| Medium | 5.4 | EPSS 0.3% | CVE-2026-114620Authlib · Unvalidated request_uri fetch in JWT-secured authorization request (JAR) leading to SSRF Medium5.4EPSS 0.3%GCVE DatabaseCVE-2026-114620 ### Summary Authlib's RFC 9101 (JAR) extension passes the attacker-controlled `request_uri` authorization parameter straight to the `get_request_object()` fetch hook with no validation, at a pre-authentication endpoint. Authlib already defines the pre-registration allowlist for this (`request_uris` client metadata and `require_request_uri_registration` server metadata) but the extension never consults it, and both docstrings instruct integrators to implement the hook as an unrestricted `requests.get(request_uri)`. Deployments following the documented pattern get an unauthenticated SSRF primitive. ### Details Verified against authlib 1.7.2 and current `main`. All line numbers below are from `authlib/oauth2/rfc9101/authorization_server.py`. The extension registers itself on the `before_get_authorization_grant` hook (lines 62-65), so it runs on every authorization request before any user or client authentication. `support_request_uri` defaults to `True` (line 58). The only gate before the sink is that `client_id` names an existing client (lines 91-103). A `client_id` is a public, non-secret identifier. The sink at line 159 forwards the raw parameter with no scheme, host, or allowlist check: ```python def _get_raw_request_object(self, request: OAuth2Request) -> str: if "request_uri" in request.payload.data: raw_request_object = self.get_request_object( request.payload.data["request_uri"] ) if not raw_request_object: raise InvalidRequestUriError(state=request.payload.state) ``` `get_request_object` is abstract (line 213, `raise NotImplementedError()` at line 225), so authlib itself performs no outbound request. The defect is that authlib supplies unvalidated attacker input to that extension point and then documents the insecure implementation twice — in the class docstring (lines 34-38) and in the hook's own docstring (lines 218-223): ```python def get_request_object(self, request_uri: str): try: return requests.get(request_uri).text except requests.Exception: return None ``` Two points sharpen this beyond a generic documentation issue: - **The allowlist already exists and is ignored.** Authlib validates `request_uris` as client metadata (`authlib/oauth2/rfc7591/claims.py:319`) and `require_request_uri_registration` as server metadata (`authlib/oauth2/rfc8414/models.py:263`). The `rfc9101` package contains zero references to either name, so the pre-registration model is never enforced at the point of use. - **RFC 9101 §10.4.1 asks for controls that are absent.** It states the server should "a) check that the value of the `request_uri` parameter does not point to an unexpected location, b) check that the media type of the response is `application/oauth-authz-req+jwt`, c) implement a timeout for obtaining the content of `request_uri`, and d) not perform recursive GET on the `request_uri`." The extension implements none of these and the documented example implements none of them either. The two failure paths are distinguishable, which supplies the inference channel for a blind SSRF: a falsy fetch result raises `InvalidRequestUriError` (`invalid_request_uri`, lines 162-163), while a successful fetch of non-JWT content raises `InvalidRequestObjectError` (`invalid_request_object`, line 186). ### PoC This demonstrates the library-level defect with no network egress required: it shows the unvalidated URI reaching the fetch hook pre-authentication. ```bash python -m venv .venv && .venv/bin/pip install authlib ``` ```python from authlib.oauth2.rfc6749.requests import BasicOAuth2Payload, OAuth2Request from authlib.oauth2.rfc9101 import JWTAuthenticationRequest reached = [] class Extension(JWTAuthenticationRequest): def get_request_object(self, request_uri): reached.append(request_uri) # the docstring puts requests.get(request_uri) here return None def resolve_client_public_key(self, client): return {"keys": []} def get_server_metadata(self): return {} def get_client_require_signed_request_object(self, client): return False class AuthorizationServer: def query_client(self, client_id): return object() uri = "http://169.254.169.254/latest/meta-data/" request = OAuth2Request("GET", "https://as.example/authorize") request.payload = BasicOAuth2Payload({"client_id": "demo", "request_uri": uri}) try: Extension().parse_authorization_request(AuthorizationServer(), request) except Exception as exc: print("error:", type(exc).__name__) print("fetch hook received:", reached) ``` Output on authlib 1.7.2: ```text error: InvalidRequestUriError fetch hook received: ['http://169.254.169.254/latest/meta-data/'] ``` Against a deployment that used the docstring implementation, the equivalent unauthenticated request is: ```text GET /authorize?response_type=code&client_id=demo&request_uri=http%3A%2F%2F169.254.169.254%2Flatest%2Fmeta-data%2F ``` Returning a fetched | GCVE Database |
Page 1 of 1