OpenBao Root Namespace Operator May Elevate Token Privileges
Description
OpenBao exists to provide a software solution to manage, store, and distribute sensitive data including secrets, certificates, and keys. In versions 2.3.1 and below, accounts with access to highly-privileged identity entity systems in root namespaces were able to increase their scope directly to the root policy. While the identity system allowed adding arbitrary policies, which in turn could contain capability grants on arbitrary paths, the root policy was restricted to manual generation using unseal or recovery key shares. The global root policy was not accessible from child namespaces. This issue is fixed in version 2.3.2. To workaround this vulnerability, use of denied_parameters in any policy which has access to the affected identity endpoints (on identity entities) may be sufficient to prohibit this type of attack.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/openbao/openbaoGo | >= 0.1.0, < 2.3.2 | 2.3.2 |
github.com/openbao/openbaoGo | < 0.0.0-20250806193240-9b0b5d4f345f | 0.0.0-20250806193240-9b0b5d4f345f |
Affected products
1Patches
19b0b5d4f345fEnsure lowercase policy in entities to forbid root policy (#1627)
5 files changed · +27 −6
changelog/1627.txt+3 −0 added@@ -0,0 +1,3 @@ +```release-note:security +core/identity: Correctly lowercase policy names to prevent root policy assignment. HCSEC-2025-13 / CVE-2025-5999. +```
vault/identity_store_entities.go+3 −3 modified@@ -355,7 +355,7 @@ func (i *IdentityStore) handleEntityUpdateCommon() framework.OperationFunc { // Update the policies if supplied entityPoliciesRaw, ok := d.GetOk("policies") if ok { - entity.Policies = strutil.RemoveDuplicates(entityPoliciesRaw.([]string), false) + entity.Policies = strutil.RemoveDuplicates(entityPoliciesRaw.([]string), true /* lowercase */) } if strutil.StrListContains(entity.Policies, "root") { @@ -470,7 +470,7 @@ func (i *IdentityStore) handleEntityReadCommon(ctx context.Context, entity *iden respData["name"] = entity.Name respData["metadata"] = entity.Metadata respData["merged_entity_ids"] = entity.MergedEntityIDs - respData["policies"] = strutil.RemoveDuplicates(entity.Policies, false) + respData["policies"] = strutil.RemoveDuplicates(entity.Policies, true /* lowercase */) respData["disabled"] = entity.Disabled respData["namespace_id"] = entity.NamespaceID @@ -1061,7 +1061,7 @@ func (i *IdentityStore) mergeEntity(ctx context.Context, txn *memdb.Txn, toEntit // If told to, merge policies if mergePolicies { - toEntity.Policies = strutil.RemoveDuplicates(strutil.MergeSlices(toEntity.Policies, fromEntity.Policies), false) + toEntity.Policies = strutil.RemoveDuplicates(strutil.MergeSlices(toEntity.Policies, fromEntity.Policies), true /* lowercase */) } // If the entity from which we are merging from was already a merged
vault/identity_store_entities_test.go+19 −1 modified@@ -938,7 +938,7 @@ func TestIdentityStore_EntityCRUD(t *testing.T) { if resp.Data["id"] != id || resp.Data["name"] != registerData["name"] || - !reflect.DeepEqual(resp.Data["policies"], strutil.RemoveDuplicates(registerData["policies"].([]string), false)) { + !reflect.DeepEqual(resp.Data["policies"], strutil.RemoveDuplicates(registerData["policies"].([]string), true /* lowercase */)) { t.Fatal("bad: entity response") } @@ -970,6 +970,24 @@ func TestIdentityStore_EntityCRUD(t *testing.T) { t.Fatalf("bad: entity response after update; resp: %#v\n updateData: %#v\n", resp.Data, updateData) } + // For HCSEC-2025-13 / CVE-2025-5999, validate that we cannot set root + // policies with other casing. + for _, name := range []string{"rooT", "Root", "rOoT", "root", "root ", " root"} { + updateReq.Data = map[string]interface{}{ + "policies": []string{name}, + } + resp, err = is.HandleRequest(ctx, updateReq) + if err == nil && (resp == nil || !resp.IsError()) { + t.Fatalf("[policy: %v] err:%v resp:%#v", name, err, resp) + } + + resp, err = is.HandleRequest(ctx, readReq) + if err != nil || (resp != nil && resp.IsError()) { + t.Fatalf("err:%v resp:%#v", err, resp) + } + require.NotContains(t, resp.Data["policies"].([]string), "root") + } + deleteReq := &logical.Request{ Path: "entity/id/" + id, Operation: logical.DeleteOperation,
vault/identity_store_util.go+1 −1 modified@@ -1392,7 +1392,7 @@ func (i *IdentityStore) sanitizeAndUpsertGroup(ctx context.Context, group *ident // Remove duplicate policies if group.Policies != nil { - group.Policies = strutil.RemoveDuplicates(group.Policies, false) + group.Policies = strutil.RemoveDuplicates(group.Policies, true /* lowercase */) } txn := i.db(ctx).Txn(true)
vault/request_handling.go+1 −1 modified@@ -203,7 +203,7 @@ func (c *Core) filterGroupPoliciesByNS(ctx context.Context, tokenNS *namespace.N if err != nil && err != ErrNoApplicablePolicies { return nil, err } - filteredPolicies = strutil.RemoveDuplicates(filteredPolicies, false) + filteredPolicies = strutil.RemoveDuplicates(filteredPolicies, true /* lowercase */) if len(filteredPolicies) != 0 { policies[nsID] = append(policies[nsID], filteredPolicies...) }
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
8- github.com/advisories/GHSA-vf84-mxrq-crqcghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2025-54996ghsaADVISORY
- nvd.nist.gov/vuln/detail/cve-2025-5999ghsaADVISORY
- discuss.hashicorp.com/t/hcsec-2025-13-vault-root-namespace-operator-may-elevate-token-privileges/76032ghsaWEB
- github.com/openbao/openbao/commit/9b0b5d4f345fdfb1065956f042b12cbd86cd6e0fghsaWEB
- github.com/openbao/openbao/pull/1627ghsax_refsource_MISCWEB
- github.com/openbao/openbao/releases/tag/v2.3.2ghsax_refsource_MISCWEB
- github.com/openbao/openbao/security/advisories/GHSA-vf84-mxrq-crqcghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.