Skip to content

Commit ae231bc

Browse files
1 parent fa0c5d4 commit ae231bc

1 file changed

Lines changed: 60 additions & 0 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-8h84-fhqq-q58v",
4+
"modified": "2026-06-10T18:34:29Z",
5+
"published": "2026-06-10T18:34:29Z",
6+
"aliases": [
7+
"CVE-2026-48025"
8+
],
9+
"summary": "nebula-mesh: Decrypted CA private key persists in heap after signing",
10+
"details": "`internal/pki/resolver.go:36-64` constructs a `CAManager` with the plaintext `ed25519.PrivateKey` after unwrapping via the master key; `internal/pki/ca.go:13-16` stores it. Callers at `internal/api/enroll.go:116`, `internal/api/updates.go:297`, and `internal/api/mobile_bundle.go:40` use the manager for one `Sign()` and drop the reference on function return — but the underlying slice contents are not wiped before release.\n\nThe keystore package's contract (`internal/keystore/keystore.go` doc: *\"Callers MUST zeroise the returned plaintext DEK as soon as it is no longer needed\"*) is not met by the `CAManager` consumer. Decrypted CA private keys persist in process heap until Go's GC scavenges the underlying slice — minutes to hours under load, indefinitely on idle servers.\n\n## Affected\nAll released versions up to v0.3.6.\n\n## Threat model\nMemory-read access: core dump, ptrace, kernel swap to disk, container/VM snapshot, OOM-debug bundle, side-channel via shared cache lines. Not a remote-network vulnerability, but defeats the master-key + envelope-encryption design's promise of \"private key never lingers\".\n\n## Suggested fix\nAdd a `Wipe()` method on `CAManager`:\n\n```go\n// internal/pki/ca.go\nfunc (m *CAManager) Wipe() {\n if m == nil {\n return\n }\n keystore.Zeroize(m.caKey)\n}\n```\n\nAt each call site (`enroll.go:116`, `updates.go:297`, `mobile_bundle.go:40`, and any new caller), `defer caMgr.Wipe()` immediately after the `Resolve()` call. Pattern mirrors the existing `defer keystore.Zeroize(dek)` discipline in the keystore package.\n\nOptional follow-up: wrap `m.Sign()` to zeroize after each call, removing the contract on callers — but the `defer` pattern is sufficient as a minimum.",
11+
"severity": [],
12+
"affected": [
13+
{
14+
"package": {
15+
"ecosystem": "Go",
16+
"name": "github.com/juev/nebula-mesh"
17+
},
18+
"ranges": [
19+
{
20+
"type": "ECOSYSTEM",
21+
"events": [
22+
{
23+
"introduced": "0"
24+
},
25+
{
26+
"fixed": "0.3.7"
27+
}
28+
]
29+
}
30+
]
31+
}
32+
],
33+
"references": [
34+
{
35+
"type": "WEB",
36+
"url": "https://github.com/juev/nebula-mesh/security/advisories/GHSA-8h84-fhqq-q58v"
37+
},
38+
{
39+
"type": "WEB",
40+
"url": "https://github.com/forgekeep/nebula-mesh/commit/bca1d5914fbaf3517d3b86145a802c00de4a8122"
41+
},
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/forgekeep/nebula-mesh/releases/tag/v0.3.7"
45+
},
46+
{
47+
"type": "PACKAGE",
48+
"url": "https://github.com/juev/nebula-mesh"
49+
}
50+
],
51+
"database_specific": {
52+
"cwe_ids": [
53+
"CWE-244"
54+
],
55+
"severity": "MODERATE",
56+
"github_reviewed": true,
57+
"github_reviewed_at": "2026-06-10T18:34:29Z",
58+
"nvd_published_at": null
59+
}
60+
}

0 commit comments

Comments
 (0)