CVE-2026-61699
HIGHSeverity by source
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N
Lifecycle Timeline
3DescriptionGitHub Advisory
Summary
nebula-mesh revokes a host by adding its certificate fingerprint to a per-CA blocklist and shipping that list to every other agent on each poll. Slack's Nebula enforces certificate revocation ONLY through the pki.blocklist list in config.yml (no CRL/OCSP). The project's own code states this: internal/pki/durations.go:15 - "Revocation via the blocklist remains the immediate security control."
The server side is fully implemented (computes per-CA blocklist via GetBlocklistForCA, returns it in the agent-updates response, sets has_updates=true when non-empty). The agent side was never implemented:
- The agent decodes the
blocklistJSON field intoUpdatesResponse.Blocklist(internal/agent/poller.go:33) and then DISCARDS it -poll()appliesCertificatePEM,CACertPEM,ConfigYAML, but never referencesupdates.Blocklist(internal/agent/poller.go:300-339). - The config generator has NO field to emit
pki.blocklist-pkiSectionis onlyca/cert/key(internal/configgen/marshal.go:42-46) andGeneratorInputcarries no blocklist (internal/configgen/generator.go:23-52). So even the server-renderedconfig.ymlshipped viaConfigYAMLcannot carry it.
Result: a blocked/offboarded/compromised host's certificate is never rejected by its peers. Its handshakes keep succeeding for the full remaining cert lifetime - up to 30 days for agent hosts (DefaultAgentCertDuration) and 365 days for mobile hosts (DefaultMobileCertDuration). Blocking a host in the UI/API has no effect on the data plane.
Affected components
- Agent drops the blocklist:
internal/agent/poller.go:33(decode target),internal/agent/poller.go:300-339(poll() applies cert/CA/config, never the blocklist). - Generator cannot emit it:
internal/configgen/marshal.go:42-46(pkiSection{CA,Cert,Key}),internal/configgen/generator.go:23-52(GeneratorInputhas no blocklist),internal/api/enroll.go:255-330(renderHostConfig, source of shipped ConfigYAML). - Server correctly produces/ships it (proves intent):
internal/store/sqlite.go:2034(GetBlocklistForCA),internal/api/updates.go:182-191(resp.Blocklist),internal/api/updates.go:277(has_updatesset on blocklist change). - Dead helper:
internal/pki/blocklist.go(Blocklisttype) is never used in non-test code - no server-side enforcement either.
Reachability (hop by hop)
- Operator clicks Block on host B (or B is compromised/offboarded). B's fingerprint enters the per-CA
blocklisttable. - Every other host A under the same CA polls
GET /api/v1/agent/updates; server returnsblocklist: [<B-fp>, ...]andhas_updates=true. - A's agent decodes
Blocklistthen discards it;poll()has no blocklist branch. - Even on a config re-render,
configgen.Generateemitspki: {ca,cert,key}with noblocklistkey (proven by PoC). - A's Nebula daemon has an empty blocklist and accepts handshakes from B's still-valid cert. B keeps full mesh access.
Impact
Revocation is the only in-band mechanism that isolates a compromised/offboarded host from a Nebula mesh. Because the blocklist never reaches any peer's config.yml, a Blocked host retains full overlay reachability to every peer under its CA (and internal services on the mesh) for up to 30d (agent) / 365d (mobile). An attacker who exfiltrates host.key+host.crt can run stock slackhq/nebula directly, ignore the agent's 403/410 poll responses, and stay connected after the operator revokes the host. Operator-visible state (UI shows blocked, audit log records it) is misleading.
Proof of Concept (benign)
internal/configgen/blocklist_poc_test.go renders a fully-populated host config and asserts the output contains the pki section but NO blocklist key:
$ go test ./internal/configgen/ -run TestPoC_NMESH001 -v
=== RUN TestPoC_NMESH001_GeneratedConfigOmitsBlocklist
CONFIRMED: generated config has a pki section but no blocklist key
pki:
ca: /etc/nebula/ca.crt
cert: /etc/nebula/host.crt
key: /etc/nebula/host.key
...
--- PASSThe agent half is verifiable by inspection: poll() has branches for CertificatePEM/CACertPEM/ConfigYAML/RekeyRequired but none for Blocklist.
Distinctness
NOT a duplicate of GHSA-339v / CVE-2026-53602 (revocation durability = a blocked host getting a NEW cert re-issued; its fix CheckIssuanceAllowed is present and orthogonal). This bug is that the EXISTING cert is never rejected at peers - the distribution/enforcement layer. Checked against all 17 known advisories; none cover blocklist application in the agent or pki.blocklist generation.
Remediation
- Add
Blocklist []safeStringtopkiSection(yamlblocklist,omitempty) andGeneratorInput; consider alsopki.disconnect_invalid: true. - Have the agent apply
updates.Blocklistby re-rendering/rewritingconfig.yml+ SIGHUP (same path asConfigYAML). Simplest: fold the blocklist into the server-renderedConfigYAMLso it flows through the existing write path. - Add a regression test asserting a non-empty server blocklist yields a
pki.blocklistentry in the agent's written config.yml.
AnalysisAI
Certificate revocation bypass in forgekeep/nebula-mesh before 0.7.1 lets a blocked, offboarded, or compromised host keep full mesh connectivity because the per-CA blocklist is computed and shipped by the server but never applied by the agent. The agent decodes the updates response's Blocklist field and discards it, and the config generator has no field to emit pki.blocklist, so Nebula's only revocation control never reaches any peer's config.yml. …
Unlock full vulnerability intelligence
- Risk assessment & exploitation conditions
- Attack chain visualization
- Remediation with exact patch versions
- Threat intelligence from 22 sources
- Personal watchlist & email alerts
Free forever · No credit card required
Recommended ActionAI
Within 24 hours, identify all systems running forgekeep/nebula-mesh and document current versions in use. …
Sign in for detailed remediation steps and compensating controls.
Threat intelligence, references, and detailed analysis are available after sign-in.
Same weakness CWE-299 – Improper Check for Certificate Revocation
View allSame technique Information Disclosure
View allShare
External POC / Exploit Code
Leaving vuln.today
GHSA-cm26-5974-52h8