VYPR
High severityNVD Advisory· Published Feb 4, 2026· Updated Feb 5, 2026

Devtron Attributes API Unauthorized Access Leading to API Token Signing Key Leakage

CVE-2026-25538

Description

Devtron is an open source tool integration platform for Kubernetes. In version 2.0.0 and prior, a vulnerability exists in Devtron's Attributes API interface, allowing any authenticated user (including low-privileged CI/CD Developers) to obtain the global API Token signing key by accessing the /orchestrator/attributes?key=apiTokenSecret endpoint. After obtaining the key, attackers can forge JWT tokens for arbitrary user identities offline, thereby gaining complete control over the Devtron platform and laterally moving to the underlying Kubernetes cluster. This issue has been patched via commit d2b0d26.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Devtron's Attributes API exposes the global JWT signing key to any authenticated user, enabling full platform compromise and Kubernetes cluster takeover.

Vulnerability

The vulnerability resides in Devtron's Attributes API, specifically the GetAttributesByKey handler (CVE-2026-25538). The RBAC authorization check was commented out, allowing any authenticated user—including low-privileged CI/CD Developers—to retrieve the global apiTokenSecret by requesting the endpoint /orchestrator/attributes?key=apiTokenSecret [1][3].

Exploitation

An attacker can simply authenticate and issue a GET request to the above endpoint to obtain the HMAC-SHA256 signing key used for all API tokens. With the key in hand, the attacker can forge JWT tokens offline, impersonating any user identity without further authentication [1].

Impact

Successful exploitation grants the attacker full administrative control over the Devtron platform. Because Devtron manages Kubernetes clusters, this access can be leveraged for lateral movement, potentially compromising the entire underlying Kubernetes infrastructure [1][3].

Mitigation

The issue has been patched via commit d2b0d26, which re-adds authorization checks and blocks access to internal-only attributes [4]. Users are strongly advised to update to a version containing this fix [1].

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/devtron-labs/devtronGo
<= 2.0.0

Affected products

2
  • Devtron/Devtronllm-create
    Range: <=2.0.0
  • devtron-labs/devtronv5
    Range: <= 2.0.0

Patches

1
d2b0d260d858

fix: prevent exposure of internal-only attributes in API responses and requests

https://github.com/devtron-labs/devtronAsh-expFeb 3, 2026via ghsa
2 files changed · +67 6
  • api/restHandler/AttributesRestHandlder.go+62 6 modified
    @@ -19,14 +19,16 @@ package restHandler
     import (
     	"encoding/json"
     	"errors"
    -	"github.com/devtron-labs/devtron/pkg/attributes/bean"
    +	"fmt"
     	"net/http"
     	"strconv"
     
     	"github.com/devtron-labs/devtron/api/restHandler/common"
     	"github.com/devtron-labs/devtron/pkg/attributes"
    +	"github.com/devtron-labs/devtron/pkg/attributes/bean"
     	"github.com/devtron-labs/devtron/pkg/auth/authorisation/casbin"
     	"github.com/devtron-labs/devtron/pkg/auth/user"
    +	"github.com/devtron-labs/devtron/util/sliceUtil"
     	"github.com/gorilla/mux"
     	"go.uber.org/zap"
     )
    @@ -57,6 +59,20 @@ func NewAttributesRestHandlerImpl(logger *zap.SugaredLogger, enforcer casbin.Enf
     	}
     	return userAuthHandler
     }
    +
    +// isInternalOnlyKey checks if the given key is internal-only and should not be exposed
    +func (handler AttributesRestHandlerImpl) isInternalOnlyKey(key string) bool {
    +	return bean.InternalOnlyKeys[key]
    +}
    +
    +// filterInternalAttributes removes internal-only attributes from the list
    +func (handler AttributesRestHandlerImpl) filterInternalAttributes(attributes []*bean.AttributesDto) []*bean.AttributesDto {
    +	filtered := make([]*bean.AttributesDto, 0, len(attributes))
    +	return sliceUtil.Filter(filtered, attributes, func(attr *bean.AttributesDto) bool {
    +		return !handler.isInternalOnlyKey(attr.Key)
    +	})
    +}
    +
     func (handler AttributesRestHandlerImpl) AddAttributes(w http.ResponseWriter, r *http.Request) {
     	userId, err := handler.userService.GetLoggedInUser(r)
     	if userId == 0 || err != nil {
    @@ -78,6 +94,13 @@ func (handler AttributesRestHandlerImpl) AddAttributes(w http.ResponseWriter, r
     		return
     	}
     
    +	// Check if the key is internal-only (not allowed to be created via API)
    +	if handler.isInternalOnlyKey(dto.Key) {
    +		handler.logger.Warnw("attempt to create internal-only attribute", "key", dto.Key, "userId", userId)
    +		common.WriteJsonResp(w, fmt.Errorf("forbidden: cannot create attribute with key: %q", dto.Key), nil, http.StatusForbidden)
    +		return
    +	}
    +
     	handler.logger.Infow("request payload, AddAttributes", "payload", dto)
     	resp, err := handler.attributesService.AddAttributes(&dto)
     	if err != nil {
    @@ -105,6 +128,14 @@ func (handler AttributesRestHandlerImpl) UpdateAttributes(w http.ResponseWriter,
     	}
     
     	token := r.Header.Get("token")
    +
    +	// Check if the key is internal-only (not allowed to be created via API)
    +	if handler.isInternalOnlyKey(dto.Key) {
    +		handler.logger.Warnw("attempt to create internal-only attribute", "key", dto.Key, "userId", userId)
    +		common.WriteJsonResp(w, fmt.Errorf("forbidden: cannot edit attribute with key: %q", dto.Key), nil, http.StatusForbidden)
    +		return
    +	}
    +
     	if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionUpdate, "*"); !ok {
     		common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
     		return
    @@ -145,6 +176,14 @@ func (handler AttributesRestHandlerImpl) GetAttributesById(w http.ResponseWriter
     		common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
     		return
     	}
    +
    +	// Filter out internal-only attributes
    +	if res != nil && handler.isInternalOnlyKey(res.Key) {
    +		handler.logger.Warnw("attempt to read internal-only attribute", "key", res.Key, "userId", userId)
    +		common.WriteJsonResp(w, fmt.Errorf("forbidden: cannot read attribute with key: %q", res.Key), nil, http.StatusForbidden)
    +		return
    +	}
    +
     	common.WriteJsonResp(w, nil, res, http.StatusOK)
     }
     
    @@ -167,7 +206,10 @@ func (handler AttributesRestHandlerImpl) GetAttributesActiveList(w http.Response
     		common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
     		return
     	}
    -	common.WriteJsonResp(w, nil, res, http.StatusOK)
    +
    +	// Filter out internal-only attributes from the list
    +	filteredRes := handler.filterInternalAttributes(res)
    +	common.WriteJsonResp(w, nil, filteredRes, http.StatusOK)
     }
     
     func (handler AttributesRestHandlerImpl) GetAttributesByKey(w http.ResponseWriter, r *http.Request) {
    @@ -185,9 +227,17 @@ func (handler AttributesRestHandlerImpl) GetAttributesByKey(w http.ResponseWrite
     
     	vars := mux.Vars(r)
     	key := vars["key"]
    +
    +	// Check if the key is internal-only (not allowed to be read via API)
    +	if handler.isInternalOnlyKey(key) {
    +		handler.logger.Warnw("attempt to read internal-only attribute by key", "key", key, "userId", userId)
    +		common.WriteJsonResp(w, fmt.Errorf("forbidden: cannot read attribute with key: %q", key), nil, http.StatusForbidden)
    +		return
    +	}
    +
     	res, err := handler.attributesService.GetByKey(key)
     	if err != nil {
    -		handler.logger.Errorw("service err, GetAttributesById", "err", err)
    +		handler.logger.Errorw("service err, GetAttributesByKey", "key", key, "err", err)
     		common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
     		return
     	}
    @@ -204,21 +254,27 @@ func (handler AttributesRestHandlerImpl) AddDeploymentEnforcementConfig(w http.R
     	var dto bean.AttributesDto
     	err = decoder.Decode(&dto)
     	if err != nil {
    -		handler.logger.Errorw("request err, AddAttributes", "err", err, "payload", dto)
    +		handler.logger.Errorw("request err, AddDeploymentEnforcementConfig", "err", err, "payload", dto)
     		common.WriteJsonResp(w, err, nil, http.StatusBadRequest)
     		return
     	}
     
    +	// Check if the key is enforce deployment type config
    +	if dto.Key != bean.ENFORCE_DEPLOYMENT_TYPE_CONFIG {
    +		common.WriteJsonResp(w, fmt.Errorf("invalid key: %q", dto.Key), nil, http.StatusBadRequest)
    +		return
    +	}
    +
     	token := r.Header.Get("token")
     	if ok := handler.enforcer.Enforce(token, casbin.ResourceGlobal, casbin.ActionCreate, "*"); !ok {
     		common.WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden)
     		return
     	}
     
    -	handler.logger.Infow("request payload, AddAttributes", "payload", dto)
    +	handler.logger.Infow("request payload, AddDeploymentEnforcementConfig", "payload", dto)
     	resp, err := handler.attributesService.AddDeploymentEnforcementConfig(&dto)
     	if err != nil {
    -		handler.logger.Errorw("service err, AddAttributes", "err", err, "payload", dto)
    +		handler.logger.Errorw("service err, AddDeploymentEnforcementConfig", "err", err, "payload", dto)
     		common.WriteJsonResp(w, err, nil, http.StatusInternalServerError)
     		return
     	}
    
  • pkg/attributes/bean/bean.go+5 0 modified
    @@ -24,6 +24,11 @@ const (
     	UserPreferencesResourcesKey           = "resources"
     )
     
    +// InternalOnlyKeys are the internal attribute keys - cannot be read or written via API
    +var InternalOnlyKeys = map[string]bool{
    +	API_SECRET_KEY: true,
    +}
    +
     type AttributesDto struct {
     	Id     int    `json:"id"`
     	Key    string `json:"key,omitempty"`
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.