Blind SSRF in Kubeflow Pipelines API Server
Researcher: n00bpr0gramer
Target: Kubeflow Pipelines 2.2.0 (kubeflow/pipelines)
Program: huntr.com (Kubeflow Bounty)
Severity: HIGH (CVSS 3.1: 7.5 β AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N)
Status: Submitted, Pending Triage
Date: 2026-06-09
Summary
The Kubeflow Pipelines API Server (ml-pipeline) uses Go's http.DefaultClient when fetching pipeline version specs from user-supplied URLs. The CreatePipelineAndVersion (POST /apis/v2beta1/pipelines/create) and CreatePipelineVersion (POST /apis/v2beta1/pipelines/{id}/versions) endpoints accept a packageUrl.pipelineUrl field and make an HTTP GET to that URL with zero validation.
http.DefaultClient has no timeout, no IP restrictions, and no redirect policy β allowing an authenticated attacker to force the server to fetch arbitrary URLs including cloud provider metadata endpoints (169.254.x.x), internal Kubernetes services, and other cluster-internal resources.
Root Cause
File: backend/src/apiserver/server/pipeline_server.go (tag 2.2.0)
func NewPipelineServer(resourceManager *resource.ResourceManager, options *PipelineServerOptions) *PipelineServer {
return &PipelineServer{
resourceManager: resourceManager,
httpClient: http.DefaultClient, // Line 201-202 β VULNERABLE
options: options,
}
}
http.DefaultClient has:
- No timeout β requests hang indefinitely
- No IP validation β private IPs (10.x, 172.16-31.x, 192.168.x, 169.254.x) reachable
- No redirect policy β redirects followed automatically (up to 10)
- No transport customization β default
http.Transportwith no restrictions
Two vulnerable code paths:
pipeline_server.go:155βs.httpClient.Get(pipelineUrl.String())(CreatePipelineAndVersion)pipeline_server.go:636βs.httpClient.Get(pipelineUrl.String())(CreatePipelineVersion)
Impact
This is a blind SSRF β the response body is not returned. However, detection and exploitation are possible through status code leakage, DNS resolution leakage, connection timing differences, and TLS behavior analysis.
Cloud Metadata Access
The 169.254.169.254 IP is reachable from any Kubernetes pod in a cloud environment, exposing instance metadata and potentially IAM credentials.
Internal Service Discovery
Cluster-internal networks can be systematically mapped including the Kubernetes API server, internal DNS, and other Kubeflow services.
PoC Files
| File | Description |
|---|---|
poc_kubeflow_ssrf.py |
Standalone Python PoC script |
poc_kubeflow_ssrf_output.txt |
Full PoC output with curl commands and evidence |
PoC Results (7/10 Tests Confirmed)
| Test | Result |
|---|---|
| OCI IMDS v1 | β VULNERABLE β HTTP 200 from metadata |
| OCI IMDS v2 | β VULNERABLE β status code 403 leaked |
| AWS IMDS | β VULNERABLE β status code leakage |
| GCP Metadata | β VULNERABLE β status code leakage |
| K8s API Server | β VULNERABLE β TLS handshake completed |
| DNS OOB exfiltration | β VULNERABLE β DNS resolution leak |
| Port scanning | β VULNERABLE β connection refused vs timeout |
Affected Endpoints
| Method | Path | Vulnerable Line |
|---|---|---|
POST |
/apis/v2beta1/pipelines/create |
pipeline_server.go:155 |
POST |
/apis/v2beta1/pipelines/{id}/versions |
pipeline_server.go:636 |