Broken Object Property Level Authorization

Broken Object Property Level Authorization in Go (Gin) [CVE-2026-25199]

[Updated month year] Updated CVE-2026-25199

Overview

Broken Object Property Level Authorization (a form of access control failure) can allow a low-privilege attacker to access or control resources that should be restricted. In CloudStack deployments that use the Proxmox extension, vulnerable patterns were seen where an instance field proxmox_vmid is user-editable and used to bind a CloudStack instance to a specific Proxmox VM. This is a CWE-200-style exposure and was reported under CVE-2026-25199. Versions 4.21.0.0 through 4.22.0.0 of the CloudStack Proxmox extension were affected, enabling cross-tenant access and full VM control for non-privileged users. Exploitation typically involves changing the proxmox_vmid value on an instance to the ID of a Proxmox VM owned by another tenant. Because the VM IDs are predictable and the server trusts this value, the attacker can issue actions (start/stop/destroy) against that VM without verifying tenant ownership. In Go (Gin) services that manage such third-party integrations, a vulnerable pattern looks like reading proxmox_vmid from a request payload and passing it directly to the Proxmox API without validating ownership against the authenticated user. The fix is to enforce object-level authorization: ensure the proxmox_vmid is mapped to the current user\'s VM and reject requests for others. To remediate, either upgrade to CloudStack 4.22.0.1 or apply a server-side mapping validation approach. The guidance section provides a concrete code example showing the vulnerable and fixed patterns in Go (Gin).

Affected Versions

CloudStack Proxmox extension 4.21.0.0 through 4.22.0.0; fixed in 4.22.0.1

Code Fix Example

Go (Gin) API Security Remediation
package main

import (
  "net/http"
  "github.com/gin-gonic/gin"
)

type ProxmoxRequest struct {
  ProxmoxVMID string `json:"proxmox_vmid"`
}

type VM struct {
  ProxmoxVMID string
  OwnerID     int64
  Name        string
}

var vmStore = map[string]VM{
  "101": {ProxmoxVMID: "101", OwnerID: 1, Name: "TenantA-VM"},
  "202": {ProxmoxVMID: "202", OwnerID: 2, Name: "TenantB-VM"},
}

func main() {
  r := gin.Default()

  // Mock middleware to set current user
  r.Use(func(c *gin.Context) {
     c.Set("user_id", int64(1)) // In real code, extract from auth token/session
     c.Next()
  })

  r.POST("/vuln/assign", vulnerableAssign)
  r.POST("/fix/assign", fixedAssign)

  _ = r.Run(":8080")
}

func currentUserID(c *gin.Context) int64 {
  v, ok := c.Get("user_id")
  if !ok {
     return 0
  }
  return v.(int64)
}

// Vulnerable: uses proxmox_vmid directly without ownership check
func vulnerableAssign(c *gin.Context) {
  var req ProxmoxRequest
  if err := c.ShouldBindJSON(&req); err != nil {
     c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
     return
  }
  // No ownership validation: vulnerability
  c.JSON(http.StatusOK, gin.H{"status": "action performed on proxmox_vmid", "vmid": req.ProxmoxVMID})
}

// Fixed: validates ownership by mapping proxmox_vmid to VM and checking OwnerID
func fixedAssign(c *gin.Context) {
  var req ProxmoxRequest
  if err := c.ShouldBindJSON(&req); err != nil {
     c.JSON(http.StatusBadRequest, gin.H{"error": "invalid payload"})
     return
  }
  vm, ok := vmStore[req.ProxmoxVMID]
  if !ok {
     c.JSON(http.StatusNotFound, gin.H{"error": "vm not found"})
     return
  }
  if vm.OwnerID != currentUserID(c) {
     c.JSON(http.StatusForbidden, gin.H{"error": "not authorized to access this VM"})
     return
  }
  c.JSON(http.StatusOK, gin.H{"status": "action performed on proxmox_vmid", "vmid": req.ProxmoxVMID, "owner": vm.OwnerID})
}

CVE References

Choose which optional cookies to allow. You can change this any time.