highCVE-2026-44300Jul 14, 2026

CVE-2026-44300: OpenCost Unauthenticated GCP Service Key Overwrite

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

OpenCost's /serviceKey endpoint lets anyone on the network overwrite the GCP service account credentials file with no authentication required, which can disrupt cost monitoring or redirect billing…

Packagegithub.com/opencost/opencost
Ecosystemgo
Affected< 1.119.1
Fixed in1.119.1

The problem

The AddServiceKey handler in pkg/costmodel/router.go reads a user-supplied key form field and writes it straight to disk via os.WriteFile with no authentication check whatsoever. The target path is <CONFIG_PATH>/key.json, defaulting to /var/configs/key.json.

Any client that can reach port 9003 can overwrite the GCP credentials file. In clusters where the service is exposed via Ingress or NodePort, that means the internet. Even in cluster-internal deployments, any compromised pod becomes a stepping stone.

Proof of concept

A working proof-of-concept for CVE-2026-44300 in github.com/opencost/opencost, with the exact payload below.

bash
# Overwrite key.json with attacker-controlled GCP credentials (no auth required)
curl -X POST http://<opencost-host>:9003/serviceKey \
  -H 'Content-Type: application/x-www-form-urlencoded' \
  -d 'key={"type":"service_account","project_id":"attacker-project","private_key_id":"abc123","private_key":"-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n","client_email":"hijack@attacker-project.iam.gserviceaccount.com","token_uri":"https://oauth2.googleapis.com/token"}'

The root cause is CWE-306: the handler performs no authentication before a destructive write. The vulnerable code calls r.PostForm.Get("key") then directly passes the bytes to os.WriteFile(env.GetGCPAuthSecretFilePath(), k, 0644). No token check, no JSON schema validation, no CORS restriction beyond Access-Control-Allow-Origin: *.

The patch introduced an ADMIN_TOKEN environment variable. The handler now checks the Authorization: Bearer <token> header against that value before allowing the write. The Helm chart wires this up via the new adminToken block (disabled by default, so operators must explicitly opt in to protect the endpoint).

The fix

Upgrade to OpenCost 1.119.1 or later. After upgrading, set the ADMIN_TOKEN environment variable (or use the Helm chart's opencost.exporter.adminToken block) to enable the bearer-token guard on /serviceKey and other write endpoints. If you cannot upgrade immediately, block access to port 9003 with a Kubernetes NetworkPolicy or remove the route at the ingress layer.

Reporter not attributed.

References: [1][2]

Related research