VYPR
Medium severity5.3GHSA Advisory· Published May 28, 2026· Updated May 28, 2026

OpenBao's Kerberos Auth Method Accumulates Unaccessible Tokens

CVE-2026-46405

Description

Impact

In OpenBao's Kerberos auth method on the GET handler, or when an Authorization: Negotiate header is supplied, the response is includes a logical.Auth object in addition to an error message. This results in tokens being created with only the default policy, default TTL, and no entity information, which are hidden by the returned error message. No access to these tokens by the caller occurs and the authentication token is not ever made accessible outside of sys/raw. At most this could cause storage usage.

Patches

This is fixed in OpenBao v2.5.4.

Workarounds

Users may set a rate limit quota to limit the creation of these paths. As the path is unauthenticated, it isn't possible to deny access to it.

Reporter

This was discovered by an anonymous reporter.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

In OpenBao ≤2.5.3, the Kerberos auth method's GET handler returns an empty `logical.Auth` object with an error, causing unaccessible default-token creation and storage bloat.

Vulnerability

In OpenBao's Kerberos auth method, the GET handler (or any request with an Authorization: Negotiate header) returns a logical.Response containing an empty logical.Auth{} object at the same time as an error message [1][3]. This code path is reachable by any unauthenticated caller without special configuration. Affected versions are OpenBao up to and including v2.5.3 [3]. The issue was discovered by an anonymous reporter [3].

Exploitation

An attacker only needs network access to an OpenBao instance with the Kerberos auth method enabled; no authentication or user interaction is required [3]. The attacker sends a GET request or supplies an Authorization: Negotiate header to the Kerberos login endpoint. The server processes the request and, due to the bug, returns both an error and a logical.Auth object. The response error hides the fact that a token was created behind the scenes [1][4]. The token is never provided to the caller and can only be discovered via sys/raw [3].

Impact

Each such request creates an orphaned token with only the default policy, default TTL, and no entity information [3]. The caller never receives or gains control of the token, but the token consumes storage space in the backend [3]. While the impact is limited to storage usage bloat, repeated exploitation could lead to excessive storage consumption and potential denial of service through exhaustion of storage capacity [3][4].

Mitigation

This vulnerability is fixed in OpenBao v2.5.4, released on 2026-05-20 [2][3]. Users should upgrade to v2.5.4 or later. For users who cannot upgrade immediately, setting a rate limit quota on the Kerberos auth path can limit the rate of token creation; however, because the path is unauthenticated, it is not possible to deny access entirely [3].

AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

2
  • Openbao/OpenbaoGHSA2 versions
    <= 2.5.3+ 1 more
    • (no CPE)range: <= 2.5.3
    • (no CPE)range: <2.5.4

Patches

1
0d82e0a5a3b6

Prevent errors from creating orphaned tokens (#3150)

https://github.com/openbao/openbaoAlexander ScheelMay 20, 2026via github-commit-search
5 files changed · +123 2
  • builtin/credential/kerberos/path_login.go+0 1 modified
    @@ -66,7 +66,6 @@ func parseKeytab(b64EncodedKt string) (*keytab.Keytab, error) {
     
     func (b *backend) pathLoginGet(ctx context.Context, req *logical.Request, d *framework.FieldData) (*logical.Response, error) {
     	return &logical.Response{
    -		Auth: &logical.Auth{},
     		Headers: map[string][]string{
     			"www-authenticate": {"Negotiate"},
     		},
    
  • changelog/3150.txt+6 0 added
    @@ -0,0 +1,6 @@
    +```release-note:security
    +core: Prevent hidden default token issuance from auth plugin endpoints returning both a `logical.Auth{}` response object and an error. GHSA-7j6w-vvw2-5f9c / CVE-2026-46405.
    +```
    +```release-note:bug
    +auth/kerberos: Do not return `logical.Auth{}` response during initial negotiation at the same time as an error.
    +```
    
  • tools/semgrep/ci/non-nil-err-auth-response.yml+59 0 added
    @@ -0,0 +1,59 @@
    +# Copyright (c) 2026 OpenBao a Series of LF Projects, LLC
    +# SPDX-License-Identifier: MPL-2.0
    +
    +rules:
    +  - id: non-nil-err-auth-response
    +    patterns:
    +      - pattern-either:
    +          - pattern: |
    +              return &logical.Response{
    +                Auth: &logical.Auth{ ... },
    +                ...
    +              }, err
    +          - pattern: |
    +              return &logical.Response{
    +                Auth: &logical.Auth{ ... },
    +                ...
    +              }, logical.CodedError(...)
    +          - pattern: |
    +              return &logical.Response{
    +                Auth: &logical.Auth{ ... },
    +                ...
    +              }, fmt.Errorf(...)
    +          - pattern: |
    +              return &logical.Response{
    +                Auth: &logical.Auth{ ... },
    +                ...
    +              }, errors.New(...)
    +          - pattern: |
    +              $FOO = &logical.Response{
    +                Auth: &logical.Auth{ ... },
    +                ...
    +              }
    +              ...
    +              return $FOO, err
    +          - pattern: |
    +              $FOO = &logical.Response{
    +                Auth: &logical.Auth{ ... },
    +                ...
    +              }
    +              ...
    +              return $FOO, logical.CodedError(...)
    +          - pattern: |
    +              $FOO = &logical.Response{
    +                Auth: &logical.Auth{ ... },
    +                ...
    +              }
    +              ...
    +              return $FOO, fmt.Errorf(...)
    +          - pattern: |
    +              $FOO = &logical.Response{
    +                Auth: &logical.Auth{ ... },
    +                ...
    +              }
    +              ...
    +              return $FOO, errors.New(...)
    +    message: a non-nil error should not include an auth response
    +    languages:
    +      - go
    +    severity: ERROR
    
  • vault/request_handling.go+9 1 modified
    @@ -1389,7 +1389,11 @@ func (c *Core) handleRequest(ctx context.Context, req *logical.Request) (retResp
     	}
     
     	// Only the token store is allowed to return an auth block, for any
    -	// other request this is an internal error.
    +	// other request this is an internal error. When the request fails,
    +	// do not generate a token even if the backend returned an auth block.
    +	if resp != nil && resp.Auth != nil && routeErr != nil {
    +		resp.Auth = nil
    +	}
     	if resp != nil && resp.Auth != nil {
     		if !strings.HasPrefix(req.Path, "auth/token/") {
     			c.logger.Error("unexpected auth response for non-token backend", "request_path", req.Path)
    @@ -1672,6 +1676,10 @@ func (c *Core) handleLoginRequest(ctx context.Context, req *logical.Request) (re
     	}
     
     	// If the response generated an authentication, then generate the token
    +	// only if it did not also err as the err would shadow the token.
    +	if resp != nil && resp.Auth != nil && routeErr != nil {
    +		resp.Auth = nil
    +	}
     	if resp != nil && resp.Auth != nil && req.Path != "sys/mfa/validate" {
     		// When the request path is part of a logical backend (and not a
     		// credential backend), reject the token creation.
    
  • vault/request_handling_test.go+49 0 modified
    @@ -5,6 +5,7 @@ package vault
     
     import (
     	"context"
    +	"errors"
     	"fmt"
     	"strings"
     	"testing"
    @@ -828,3 +829,51 @@ func TestRequestHandling_DisallowLogicalTokenCreation(t *testing.T) {
     	}
     	require.Error(t, err, ErrInternalError)
     }
    +
    +func TestRequestHandling_DisallowAuthErrorTokenCreation(t *testing.T) {
    +	t.Parallel()
    +
    +	core, _, root := TestCoreUnsealed(t)
    +
    +	if err := core.loadMounts(namespace.RootContext(t.Context()), false); err != nil {
    +		t.Fatalf("err: %v", err)
    +	}
    +
    +	core.credentialBackends["test"] = func(ctx context.Context, conf *logical.BackendConfig) (logical.Backend, error) {
    +		b := &backendTest.Noop{
    +			Login:       []string{"login"},
    +			BackendType: logical.TypeCredential,
    +			RequestHandler: func(ctx context.Context, req *logical.Request) (*logical.Response, error) {
    +				return &logical.Response{
    +					Auth: &logical.Auth{},
    +				}, errors.New("erring for test")
    +			},
    +		}
    +		if err := b.Setup(ctx, conf); err != nil {
    +			return nil, err
    +		}
    +		return b, nil
    +	}
    +
    +	req := &logical.Request{
    +		Path:        "sys/auth/test",
    +		ClientToken: root,
    +		Operation:   logical.UpdateOperation,
    +		Data: map[string]interface{}{
    +			"type": "test",
    +		},
    +	}
    +	resp, err := core.HandleRequest(namespace.RootContext(t.Context()), req)
    +	require.NoError(t, err)
    +	require.Nil(t, resp)
    +
    +	req = &logical.Request{
    +		Path:      "auth/test/login",
    +		Operation: logical.ReadOperation,
    +	}
    +	resp, err = core.HandleRequest(namespace.RootContext(t.Context()), req)
    +	if resp != nil {
    +		require.Nil(t, resp.Auth)
    +	}
    +	require.Error(t, err, ErrInternalError)
    +}
    

Vulnerability mechanics

Root cause

"The Kerberos auth method returns both a `logical.Auth{}` object and a non-nil error, causing the core framework to create an orphaned token that is hidden from the caller by the error response."

Attack vector

An unauthenticated attacker sends a GET request to the Kerberos auth endpoint, or supplies an `Authorization: Negotiate` header, triggering the initial negotiation path. The handler returns both a `logical.Auth{}` object (which triggers token creation) and an error message. The error is returned to the caller, hiding the fact that a token was created with only the default policy, default TTL, and no entity information [ref_id=1]. The attacker never receives the token itself, so the impact is limited to storage consumption from orphaned tokens [ref_id=1].

Affected code

The Kerberos auth method's `pathLoginGet` function in `builtin/credential/kerberos/path_login.go` returned a `logical.Response` containing an empty `Auth: &logical.Auth{}` alongside an error [patch_id=2973534]. The core request handling in `vault/request_handling.go` did not strip the `Auth` block when the backend also returned a non-nil error, allowing token creation despite the error [patch_id=2973534].

What the fix does

The patch makes two changes. First, in `builtin/credential/kerberos/path_login.go`, the empty `Auth: &logical.Auth{}` is removed from the response returned during initial negotiation, preventing the auth method from ever returning an auth block with an error [patch_id=2973534]. Second, in `vault/request_handling.go`, both `handleRequest` and `handleLoginRequest` now check if `resp.Auth` is non-nil while `routeErr` is also non-nil, and if so, set `resp.Auth = nil` before token creation proceeds [patch_id=2973534]. This ensures that even if another backend makes the same mistake, the core framework will not create a token from a failed request. A new Semgrep rule (`non-nil-err-auth-response`) was also added to catch this pattern at code review time [patch_id=2973534].

Preconditions

  • authNo authentication required — the Kerberos auth endpoint is unauthenticated
  • networkAttacker must be able to reach the OpenBao HTTP API
  • configThe Kerberos auth method must be enabled on the OpenBao instance

Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.