# [CRIT] [GHSA / CRITICAL] CVE-2026-47724: nebula-mesh: API endpoints lack ownership checks, enabling cross-operator privilege escalation

**Source:** GitHub Security Advisories
**Published:** 2026-06-08
**Article:** https://github.com/advisories/GHSA-598g-h2vc-h5vg

## Threat Profile

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

The `/api/v1/*` route surface trusts the bearer token alone for authorisation on most endpoints. The codebase itself admits this at `internal/api/hosts.go:384`: *"API trusts the bearer token for authorisation; per-CA ownership is enforced only in the Web layer."*

The Web UI gates state-changing routes through `loadAccessibleCA` (`internal/web/cas.go`); CA-management endpoints in `internal/api/cas.go`…

## Indicators of Compromise (high-fidelity only)

- **CVE:** `CVE-2026-47724`

## MITRE ATT&CK Techniques

- **T1190** — Exploit Public-Facing Application
- **T1068** — Exploitation for Privilege Escalation
- **T1078.003** — Valid Accounts: Local Accounts
- **T1098.001** — Account Manipulation: Additional Cloud Credentials
- **T1212** — Exploitation for Credential Access
- **T1552.004** — Unsecured Credentials: Private Keys
- **T1550.001** — Use Alternate Authentication Material: Application Access Token
- **T1087** — Account Discovery
- **T1526** — Cloud Service Discovery
- **T1069.003** — Permission Groups Discovery: Cloud Groups
- **T1562.007** — Impair Defenses: Disable or Modify Cloud Firewall
- **T1565.001** — Stored Data Manipulation
- **T1098** — Account Manipulation
- **T1531** — Account Access Removal
- **T1496** — Resource Hijacking

## Kill chain phases observed

_(none detected from narrative keywords)_

## Recommended hunts

### nebula-mesh CVE-2026-47724 — cross-operator admin API key mint via POST /api/v1/operators/{id}/api-keys

`UC_96_1` · phase: **exploit** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count min(_time) as first_seen max(_time) as last_seen values(Web.http_user_agent) as user_agents values(Web.status) as statuses from datamodel=Web where Web.http_method="POST" (Web.uri_path="/api/v1/operators/*/api-keys" OR Web.url="*/api/v1/operators/*/api-keys*") Web.status IN (200,201) by Web.src Web.user Web.dest Web.uri_path
| `drop_dm_object_name(Web)`
| rex field=uri_path "/api/v1/operators/(?<target_operator_id>[^/]+)/api-keys"
| eval cve="CVE-2026-47724"
| table first_seen last_seen src user dest target_operator_id user_agents statuses count cve
| sort - last_seen
```

### nebula-mesh CVE-2026-47724 — cross-tenant host identity hijack via /hosts/{id}/reenroll → /enroll chain

`UC_96_2` · phase: **install** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count from datamodel=Web where Web.http_method="POST" Web.uri_path="/api/v1/hosts/*/reenroll" Web.status IN (200,201) by _time Web.src Web.user Web.dest Web.uri_path span=1s
| `drop_dm_object_name(Web)`
| rex field=uri_path "/api/v1/hosts/(?<victim_host_id>[^/]+)/reenroll"
| eval reenroll_time=_time, reenroll_count=count
| fields - count
| join type=inner src dest [
    | tstats summariesonly=t count as enroll_count from datamodel=Web where Web.http_method="POST" Web.uri_path="/api/v1/enroll" Web.status IN (200,201) by _time Web.src Web.dest span=1s
    | `drop_dm_object_name(Web)`
    | eval enroll_time=_time
    | fields enroll_time enroll_count src dest
  ]
| where enroll_time >= reenroll_time AND enroll_time <= reenroll_time + 60
| eval delay_sec=enroll_time - reenroll_time, cve="CVE-2026-47724"
| stats min(reenroll_time) as first_reenroll values(victim_host_id) as victim_hosts min(delay_sec) as min_delay_sec max(delay_sec) as max_delay_sec count by src user dest cve
| where count >= 1
| sort - first_reenroll
```

### nebula-mesh CVE-2026-47724 — operator roster + API key metadata enumeration burst

`UC_96_3` · phase: **recon** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count dc(Web.uri_path) as distinct_paths values(Web.uri_path) as paths from datamodel=Web where Web.http_method="GET" (Web.uri_path="/api/v1/operators" OR Web.uri_path="/api/v1/operators/*/api-keys") Web.status=200 by Web.src Web.user Web.dest Web.http_user_agent bin(_time, 5m) as bucket
| `drop_dm_object_name(Web)`
| eval cve="CVE-2026-47724"
| where distinct_paths >= 3 OR count >= 8
| sort - bucket
```

### nebula-mesh CVE-2026-47724 — cross-tenant firewall mutation via PUT /api/v1/networks/{id}/firewall

`UC_96_4` · phase: **actions** · confidence: **Medium** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count min(_time) as first_seen max(_time) as last_seen values(Web.http_user_agent) as user_agents from datamodel=Web where (Web.http_method="PUT" OR Web.http_method="POST") Web.uri_path="/api/v1/networks/*/firewall" Web.status IN (200,201,204) by Web.src Web.user Web.dest Web.uri_path Web.http_method
| `drop_dm_object_name(Web)`
| rex field=uri_path "/api/v1/networks/(?<network_id>[^/]+)/firewall"
| eval cve="CVE-2026-47724"
| table first_seen last_seen src user dest network_id http_method user_agents count cve
| sort - last_seen
```

### nebula-mesh CVE-2026-47724 — operator sabotage (disable/enable/key revocation) by non-admin actor

`UC_96_5` · phase: **actions** · confidence: **High** · AI-generated for this article

**Splunk SPL (CIM):**
```spl
| tstats summariesonly=t count min(_time) as first_seen max(_time) as last_seen values(Web.http_user_agent) as user_agents values(Web.uri_path) as paths from datamodel=Web where Web.status IN (200,201,204) AND ((Web.http_method="POST" AND (Web.uri_path="/api/v1/operators/*/disable" OR Web.uri_path="/api/v1/operators/*/enable")) OR (Web.http_method="DELETE" AND Web.uri_path="/api/v1/operators/*/api-keys/*")) by Web.src Web.user Web.dest Web.http_method
| `drop_dm_object_name(Web)`
| rex field=paths "/api/v1/operators/(?<target_operator_id>[^/]+)/(?<action_seg>disable|enable|api-keys)"
| eval action=case(action_seg=="disable","operator_disable", action_seg=="enable","operator_enable", action_seg=="api-keys","api_key_revoke", true(),"other"), cve="CVE-2026-47724"
| table first_seen last_seen src user dest http_method action target_operator_id user_agents count cve
| sort - last_seen
```

### IOC-driven hunts (use shared templates)

These are standard IOC-substitution hunts — the canonical SPL and KQL live once in [`_TEMPLATES.md`](../_TEMPLATES.md), so we don't repeat the same boilerplate on every CVE / hash / network-IOC briefing.

- **Asset exposure — vulnerability matches article CVE(s)** ([template](../_TEMPLATES.md#asset-exposure)) — phase: **recon**, confidence: **High**
  - CVE(s): `CVE-2026-47724`


## Why this matters

Severity classified as **CRIT** based on: CVE present, 6 use case(s) fired, 15 technique(s) inferred. Read the full article for actor attribution, tooling details, and any defanged IOCs in the body that aren't visible in the RSS summary.
