Bug 2493640 (CVE-2026-13451) - CVE-2026-13451 kserve: KServe: Server-Side Request Forgery via malicious storageUri
Summary: CVE-2026-13451 kserve: KServe: Server-Side Request Forgery via malicious stor...
Keywords:
Status: NEW
Alias: CVE-2026-13451
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
high
high
Target Milestone: ---
Assignee: Product Security DevOps Team
QA Contact:
URL:
Whiteboard:
Depends On:
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-06-26 18:16 UTC by OSIDB Bzimport
Modified: 2026-10-27 11:15 UTC (History)
3 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-06-26 18:16:21 UTC
Summary: KServe permits a namespace-scoped user with only create/update permission on InferenceService CRs to cause the storage-initializer init container (injected and executed by the KServe controller) to issue arbitrary outbound HTTP/HTTPS requests from inside the cluster. This is a Server-Side Request Forgery (SSRF) vulnerability, CWE-918.

Verification status: Confirmed via independent codebase review of upstream KServe. The vulnerability exists in the current main branch and v0.17.0.

Root cause: The storageUri field accepts http:// and https:// URIs with no IP/hostname validation at any point in the processing pipeline:

Webhook (pkg/apis/serving/v1beta1/inference_service_validation.go, validateStorageURISpec()): validates only that the MountPath is absolute and contains no ... The Uri value is checked only for emptiness.
Controller (pkg/controller/v1beta1/inferenceservice/utils/utils.go, ValidateStorageURI()): validates only that the URI scheme is in SupportedStorageURIPrefixList. Since http:// is in that list, any http:// URL passes — including http://169.254.169.254/..., http://kubernetes.default.svc/..., and any ClusterIP address.
Go storage fetcher (pkg/agent/storage/https.go, HTTPSDownloader.Download()): passes storageUri directly to http.NewRequest / client.Do with no filtering.
Python storage initializer (python/storage/kserve_storage/kserve_storage.py, _download_from_uri()): passes the URI directly to requests.get(). The only pre-request guard is a check for a non-empty filename in the URL path; there is no IP-range or hostname check anywhere in the call chain.
There are no NetworkPolicy resources in the default KServe install and no compensating controls of any kind for this path.

Attack prerequisites: Authenticated access with create/update on InferenceService resources in a namespace — the standard data scientist role in an OpenShift AI / KServe deployment. No cluster-admin or Pod creation rights required.

Attack vector: Low complexity. Attacker submits a single kubectl apply of an InferenceService with a malicious storageUri. The KServe controller injects the storage-initializer init container, which issues the HTTP GET on behalf of the attacker from within the cluster network, bypassing namespace-scoped network controls.

Potential impact: Reaches cloud instance metadata endpoints (169.254.169.254, GCP metadata.google.internal, Azure IMDS) — credential theft for the node's IAM role; reaches kubernetes.default.svc API server; reaches arbitrary ClusterIP services. Because the init container retries on failure, multiple requests are sent to the target before backoff (4 confirmed in the reporter's PoC).

Red Hat severity: Important — authenticated low-privilege user easily compromises confidentiality through SSRF to protected internal/cloud resources; cloud IAM credential theft could enable further escalation.

Affected versions: All KServe versions that include http:// in SupportedStorageURIPrefixList (at minimum v0.10 onward; confirmed on v0.17.0).

Upstream advisory: GHSA-9qqf-96g5-8234 (private, KServe GitHub security advisories).


Note You need to log in before you can comment on or make changes to this bug.