nebula-mesh CVE-2026-47722
HIGHLifecycle Timeline
2DescriptionCVE.org
internal/configgen/generator.go:86,108,119 interpolates the operator-supplied ListenHost and TunDevice fields raw into a text/template that produces the agent's config.yml. internal/web/advanced.go:20-35 accepts both with only strings.TrimSpace - no character or shape validation.
Exploit
An operator (or attacker with any operator key, given the cross-tenant CRUD advisory) sets adv_tun_device to:
nebula0
lighthouse:
am_lighthouse: true
hosts: ["10.0.0.1"]
#The agent fetches the rendered config on its next signed poll. On config reload, it loads the injected YAML keys: the host self-promotes to lighthouse, attracts mesh traffic, or sets am_relay: true to be selected as a relay. The ListenHost field has the same shape.
Affected
All released versions prior to v0.3.2.
Threat model
- Today: operator can compromise their own host's config (trivially allowed if they own the host, but they can also set lighthouse/relay flags that the operator-create form does NOT expose - privilege uplift within their own tenant).
- Combined with the critical /api/v1 authz advisory: any operator key can mutate ANOTHER tenant's host overrides and inject YAML there.
- Post-fix of the authz advisory: still relevant - the agent unconditionally trusts whatever config the server hands it, so any future operator-impersonation bug re-amplifies this.
Suggested fix
Two options, either acceptable:
- Input validation in
parseAdvancedFromForm(internal/web/advanced.go):
ListenHost: regex^[A-Za-z0-9.:\[\]_-]+$(IPv4/IPv6/hostname)TunDevice: regex^[A-Za-z0-9_-]{1,15}$(Linux IFNAMSIZ caps at 15)
Reject invalid input with a form-level error; do not write to the host row.
- Safer marshalling: switch
configgen/generator.goto marshal a typed Go struct viagopkg.in/yaml.v3(which escapes correctly) instead oftext/templatestring-concat. Larger change, but eliminates this entire injection class.
Option 2 is preferable long-term. Option 1 is the quick fix.
The unsafe_routes advanced field is already netip.Parse{Prefix,Addr}-validated at enroll.go:226-233 - apply the same validation discipline to the other advanced fields.
AnalysisAI
YAML injection in nebula-mesh (juev/nebula-mesh, forked as forgekeep/nebula-mesh) prior to v0.3.2 allows an authenticated operator to inject arbitrary YAML keys into a managed agent's config.yml via the unvalidated adv_tun_device and ListenHost host-override fields. A successful injection lets the attacker self-promote a target host to a Nebula lighthouse or relay, redirecting and intercepting mesh traffic across the overlay network. No public exploit identified at time of analysis, but a detailed PoC payload is published in the GHSA advisory itself.
Technical ContextAI
nebula-mesh is a Go web UI that manages Slack's Nebula overlay-network agents. The vulnerable code path is in internal/configgen/generator.go (lines 86, 108, 119), where operator-supplied strings are interpolated raw into a text/template that renders the agent's YAML config.yml. The form handler in internal/web/advanced.go (lines 20-35) only calls strings.TrimSpace on the inputs, performing no character-class or shape validation. This is a textbook CWE-94 (Improper Control of Generation of Code) realized as YAML injection: because text/template performs string concatenation rather than YAML-aware escaping, newlines and structural characters in the input become live YAML syntax. Notably, the adjacent unsafe_routes field is already netip.Parse{Prefix,Addr}-validated at enroll.go:226-233, so the defect is an inconsistency in validation discipline across advanced fields.
RemediationAI
Vendor-released patch: 0.3.2 - upgrade github.com/juev/nebula-mesh (or the forgekeep/nebula-mesh fork) to v0.3.2 or later; the fix commit is https://github.com/forgekeep/nebula-mesh/commit/c1506f7344ab375a145a7449b193af3f19bb41ef, which replaces the text/template renderer with gopkg.in/yaml.v3 typed-struct marshaling so structural-break characters are quoted as scalars rather than concatenated into YAML structure. If immediate upgrade is not possible, the advisory recommends enforcing input validation in parseAdvancedFromForm (internal/web/advanced.go) - regex ^[A-Za-z0-9.:\[\]_-]+$ for ListenHost and ^[A-Za-z0-9_-]{1,15}$ for TunDevice (the latter matching Linux IFNAMSIZ) - and rejecting form submissions that fail; the trade-off is that this only closes the known fields and leaves the underlying string-concat renderer vulnerable to any future advanced field added without similar validation. As a defensive control, restrict operator-UI access to a trusted management network and consider patching the related /api/v1 authorization advisory in tandem, since unfixed cross-tenant CRUD turns this single-tenant flaw into a cross-tenant compromise. Full advisory: https://github.com/juev/nebula-mesh/security/advisories/GHSA-7hp6-g3pq-3pc3.
Same weakness CWE-94 – Code Injection
View allSame technique Code Injection
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-7hp6-g3pq-3pc3