# nebula-mesh: API endpoints lack ownership checks, enabling cross-operator privilege escalation

nebula-mesh API endpoints trusted bearer tokens without per-CA ownership checks, letting any operator mint admin keys and hijack other operators' hosts.

- Payout: Research
- Severity: critical
- Program: nebula-mesh
- Found by: ak2k
- Disclosed: 2026-06-08
- CVE: CVE-2026-47724
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-598g-h2vc-h5vg
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-598g-h2vc-h5vg
- Tags: area:web domain:api impact:ato impact:privesc kind:research platform:ghsa sev:critical tech:go tech:rest technique:source-review vuln:access-control vuln:auth-bypass vuln:idor vuln:privilege-escalation

**Target:** Go REST API server (/api/v1/*) with bearer-token auth
**Root cause:** API layer authorises on bearer token alone; per-CA ownership enforced only in the Web layer, so host/network/firewall/operator handlers lack canAccessCA or admin gates.
**Steps:**
1. Authenticate with any low-privilege operator bearer token.
2. POST `/api/v1/operators/<admin-id>/api-keys` with `{"name":"oops"}` to receive a fresh admin bearer key.
3. Reuse returned key for full admin access.
4. Alternatively POST `/api/v1/hosts/<victim-host-id>/reenroll` to get a single-use enrollment token.
5. POST `/api/v1/enroll` with attacker X25519+Ed25519 keypairs; `enroll.go:175` overwrites `signing_pub_pem` and cert, breaking victim agent's signed polls.
**How found:** Code reading: comment at internal/api/hosts.go:384 admits API trusts bearer token; compared Web layer's loadAccessibleCA gating against API handlers and found host/network/firewall/operator endpoints ungated while cas.go was properly protected.
**Exploitation:** Any operator key mints an admin API key in one request, or reenrolls a victim host to overwrite its signing key and certificate, transferring the victim's Nebula identity; also mass CRUD across hosts, networks and firewall rules.
**Escalation:** Low-priv operator → admin via key minting; → host identity takeover via reenroll; → cross-tenant firewall mutation and operator disable/revoke.
**Insight:** When a codebase comment admits authz lives only in one layer, diff every sibling handler in the other layer against the gated ones; CA-scoped gates don't cover operator-management endpoints, which need separate admin checks.

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

**curl minting admin API key from low-priv key**

```
curl -X POST -H "Authorization: Bearer <low-priv-key>" \
  https://server/api/v1/operators/<admin-id>/api-keys \
  -H 'Content-Type: application/json' -d '{"name":"oops"}'
# Returns: {"key":"<32-byte admin bearer>","entry":{...}}
```

