CVE-2026-55776 Overview
CVE-2026-55776 is a denial of service vulnerability in OpenBao, an open source identity-based secrets management system. Authenticated callers with write access to transit/keys/* can crash the OpenBao server process by submitting an invalid asymmetric derived-key combination. Setting derived to true while specifying an rsa-, ecdsa-, or ed25519 key type triggers a mutex double-unlock, producing a runtime panic and process exit. The flaw is tracked under [CWE-617] (Reachable Assertion) and affects versions prior to 2.5.5.
Critical Impact
A single crafted JSON or HCL key-creation request from an authenticated caller terminates the OpenBao process, causing service-wide secrets management outage.
Affected Products
- OpenBao versions prior to 2.5.5
- Affected component: builtin/logical/transit/backend.go
- Affected component: sdk/helper/keysutil/policy.go
Discovery Timeline
- 2026-09-15 - CVE-2026-55776 published to NVD
- 2026-09-17 - Last updated in NVD database
Technical Details for CVE-2026-55776
Vulnerability Analysis
The vulnerability resides in the Transit secrets engine's policy creation path. OpenBao's Transit backend accepts key-creation requests that specify a key type and a boolean derived flag. Derived keys are only valid for symmetric algorithms, so combining derived: true with an asymmetric algorithm such as rsa-2048, ecdsa-p256, or ed25519 is invalid.
When the backend processes this invalid combination, it reaches an error path that unlocks a mutex that was never held or was already released. The Go runtime treats unlocking an unlocked sync.Mutex as a fatal error, terminating the process with a panic. No HTTP response is returned to the caller, and all other tenants of the OpenBao instance lose access to secrets.
Root Cause
The root cause is inconsistent lock ownership tracking in GetPolicy and related paths in policy.go. The error-handling branch for invalid derived asymmetric keys calls Unlock() on a policy mutex without a matching Lock(), or after the lock has already been released earlier in the call chain. Concurrent caching logic further complicated the lock lifetime, since dstP.Lock(false) was called conditionally based on CachingDisabled().
Attack Vector
An authenticated caller with write permission on transit/keys/* submits either a JSON or HCL request that sets derived=true and type=rsa-2048 (or another asymmetric type). The invalid combination reaches the vulnerable code path, unlocks an unheld mutex, and crashes the server. The requester needs only low-privileged Transit policy write access, and no user interaction is required.
// Patched code from builtin/logical/transit/backend.go
// Introduces explicit exclusive/non-exclusive policy acquisition
func (b *backend) GetPolicy(ctx context.Context, polReq keysutil.PolicyRequest, rand io.Reader) (retP *keysutil.Policy, retUpserted bool, retErr error) {
return b.getPolicy(ctx, polReq, rand, false /* not exclusive */)
}
func (b *backend) GetPolicyExclusive(ctx context.Context, polReq keysutil.PolicyRequest, rand io.Reader) (retP *keysutil.Policy, retUpserted bool, retErr error) {
return b.getPolicy(ctx, polReq, rand, true /* exclusive */)
}
func (b *backend) getPolicy(ctx context.Context, polReq keysutil.PolicyRequest, rand io.Reader, exclusive bool) (retP *keysutil.Policy, retUpserted bool, retErr error) {
// Acquire read lock to read cacheSizeChanged
b.configMutex.RLock()
if b.lm.GetUseCache() && b.cacheSizeChanged {
// ...
}
}
Source: OpenBao commit bb17827
The patch removes the conditional dstP.Lock(false) in path_byok.go so that lock acquisition and the deferred dstP.Unlock() are consistently paired, eliminating the double-unlock path.
Detection Methods for CVE-2026-55776
Indicators of Compromise
- OpenBao process exit with a Go runtime panic stack trace referencing sync.(*Mutex).Unlock originating from keysutil/policy.go.
- Missing HTTP response for a POST /v1/transit/keys/<name> request where the payload includes derived=true alongside an asymmetric type value.
- Audit log entries showing a Transit key-creation request immediately preceding an unplanned service restart.
Detection Strategies
- Inspect OpenBao audit and systemd/journald logs for panic messages such as fatal error: sync: unlock of unlocked mutex correlated with Transit API calls.
- Alert on unexpected OpenBao process restarts on nodes serving the Transit engine.
- Parse audit request bodies for the invalid parameter combination (derived: true with type in rsa-2048, rsa-3072, rsa-4096, ecdsa-p256, ecdsa-p384, ecdsa-p521, ed25519).
Monitoring Recommendations
- Track OpenBao service uptime and generate alerts when the process restarts outside of maintenance windows.
- Monitor Transit endpoint 5xx rates and dropped connections, which indicate a panic before response emission.
- Review token accessor usage on transit/keys/* to identify which authenticated identities issue key-creation requests.
How to Mitigate CVE-2026-55776
Immediate Actions Required
- Upgrade OpenBao to version 2.5.5 or later, which contains the fix from pull requests #3309 and #3312.
- Audit and tighten policies granting write access to transit/keys/* and revoke tokens that do not require it.
- Enable request auditing on the Transit mount to record all key-creation attempts prior to patching.
Patch Information
The fix is available in OpenBao release v2.5.5 and later, including v2.6.0. Full remediation guidance is published in GitHub Security Advisory GHSA-8w8f-r2xv-4q4j. The commits landing the fix are bb17827 and db57c62.
Workarounds
- Restrict the transit/keys/* path in ACL policies to trusted operators only until the upgrade is applied.
- Deploy an API gateway or reverse proxy rule that rejects Transit key-creation payloads combining derived=true with asymmetric key types.
- Run OpenBao under a supervisor (systemd, Kubernetes) configured for automatic restart to reduce outage duration if the panic is triggered.
# Example restrictive ACL policy limiting Transit key creation
path "transit/keys/*" {
capabilities = ["read", "list"]
}
path "transit/keys/approved-*" {
capabilities = ["create", "update"]
denied_parameters = {
"derived" = [true]
}
}
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

