Partial stack address info leak via ber_printf type confusion
- CVE ID
- CVE-2026-11785
- Product
- 389-ds-base
- Severity
- Moderate (4.3)
- CVSS Vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N
- CWE
- CWE-843
- Red Hat CVE
- Red Hat CVE page
Summary
A type confusion bug in the 389 Directory Server’s SSO token handler passes a stack pointer where an integer value is expected, causing the server to encode 32 bits of a stack address into every SSO token response. Any authenticated non-administrator user can extract this partial address from the standard LDAP response. The SSO token feature is enabled by default with an auto-generated secret, requiring no manual configuration. The leaked information reduces stack address entropy but does not constitute a full ASLR bypass.
Affected Versions
All versions of 389-ds-base that include the SSO token feature (introduced via upstream Issue #1797, extop_handle_ldapssotoken_request() in extendop.c) are affected. The ber_printf type confusion has been present since the feature was first implemented.
| Package | Series | NVR | Status |
|---|---|---|---|
| 389-ds-base (upstream) | 3.x | main branch (HEAD) | Vulnerable (confirmed — source code reviewed) |
| 389-ds-base (RHEL 9) | 2.x | 389-ds-base-2.7.0-10.el9_7 | Vulnerable (code present — extop_handle_ldapssotoken_request() present in 2.x source) |
| 389-ds-base (RHEL 10.0 EUS) | 3.x | 389-ds-base-3.0.6-17.el10_0 | Vulnerable (code present — SSO token feature present in 3.x source) |
| 389-ds-base (RHEL 10.1) | 3.x | 389-ds-base-3.1.3-7.el10_1 | Vulnerable (code present — SSO token feature present in 3.x source) |
| 389-ds-base (Fedora 42) | 3.x | 389-ds-base-3.1.2-4.fc42 | Vulnerable (code present, untested) |
| 389-ds-base (Fedora 43) | 3.x | 389-ds-base-3.1.4-7.fc43 | Vulnerable (code present, untested) |
| 389-ds-base (CentOS Stream 10) | 3.x | 389-ds-base-3.2.0-4.el10 | Vulnerable (code present, untested) |
Affected Products
| Product | Version | Status | Verification |
|---|---|---|---|
| 389 Directory Server (upstream) | main branch, 3.x | Affected | Source code review of extendop.c on GitHub main; ber_printf type confusion at line 233 |
| Red Hat Directory Server 13 | 13.x (RHEL 10) | Affected | RHDS 13 is based on 389-ds-base-3.x per RHDS 13 release notes; SSO token feature present in 3.x |
| RHEL 9 (IdM/FreeIPA) | 9.0+ | Affected | 389-ds-base-2.x ships in RHEL 9 AppStream; SSO token feature enabled by default (libglobs.c:2094) |
| RHEL 10 (IdM/FreeIPA) | 10.0+ | Affected | 389-ds-base-3.x ships in RHEL 10 AppStream; SSO token feature present |
| Fedora | 42, 43 | Affected | extop_handle_ldapssotoken_request() present in shipped versions |
| CentOS Stream | 9, 10 | Affected | Tracks RHEL 9/10 respectively |
Architecture Validation
| Architecture | Tested | Result |
|---|---|---|
| x86_64 | YES | Vulnerability confirmed — PoC executed, INTEGER=0x8e7faed8 leaked in BER response |
| aarch64 | NO | Not tested — type confusion behavior is architecture-independent on all LP64 platforms (pointer > int) |
| s390x | NO | Not tested — type confusion behavior is architecture-independent on all LP64 platforms |
| ppc64le | NO | Not tested — type confusion behavior is architecture-independent on all LP64 platforms |
Note: This is a type confusion in variadic argument handling. On all 64-bit platforms (LP64 model), pointers are 8 bytes and ber_int_t (int) is 4 bytes. The va_arg(ap, int) call reads only the low 32 bits regardless of architecture. The bug is architecture-independent.
32-bit platforms: On 32-bit systems where int and pointers are both 4 bytes, the full pointer value would be leaked, constituting a complete stack address disclosure. However, 389-ds-base is primarily deployed on 64-bit systems.
Root Cause
File: ldap/servers/slapd/extendop.c:233
Function: extop_handle_ldapssotoken_request()
Verified against: 389-ds-base upstream main branch, 2026-04-22
At line 233 of extendop.c, in extop_handle_ldapssotoken_request():
int32_t rc = 0;
rc = ber_printf(ber, "{is}", &rc, token);
// ^^^ BUG: passes &rc (pointer to stack)
// instead of rc (integer value)
The ber_printf format string {is} specifies:
i— aber_int_tvalue (passed by value)s— achar *string (passed as a pointer)
The code passes &rc (a int32_t*, an 8-byte pointer on x86_64) instead of rc (a 4-byte integer value). OpenLDAP’s ber_printf implementation reads va_arg(ap, ber_int_t) for the i format, where ber_int_t is int (4 bytes) — defined in lber_types.h as #define LBER_INT_T int; typedef LBER_INT_T ber_int_t;.
Because ber_int_t is int (4 bytes), va_arg(ap, int) reads only the low 32 bits of the 8-byte pointer, encoding them as the BER INTEGER in the response. This is a type confusion (CWE-843): the function treats a pointer as an integer, and the variadic calling convention prevents compiler warnings because ber_printf lacks __attribute__((format)) annotation.
Proposed Fix
Replace &rc with the integer value rc (or a new variable) in the ber_printf call:
// BEFORE (buggy -- passes stack pointer address):
rc = ber_printf(ber, "{is}", &rc, token);
// AFTER (fixed -- passes integer value):
ber_int_t result_code = 0;
rc = ber_printf(ber, "{is}", result_code, token);
The fix is a one-line change. The key insight is that ber_printf i format expects a ber_int_t value, not a pointer. The original code likely intended to encode 0 (the initial value of rc) as the integer field, but the & operator caused the address to be passed instead.
Proof of Concept
PoC source code: CVE-2026-11785 on GitHub
Compiled PoC (varargs behavior demonstration)
The C program poc/poc-ber-printf-info-leak.c demonstrates the type confusion by simulating ber_printf’s i format handler. It confirms that passing a pointer to va_arg(ap, int) yields only the low 32 bits.
Build and run:
gcc -Wall -o poc-ber-printf-info-leak poc/poc-ber-printf-info-leak.c
./poc-ber-printf-info-leak
Expected result on test system (x86_64):
Address of rc: 0x7fffe38ec2b4
va_arg(ap,int) returned: 0xe38ec2b4
*** CONFIRMED: Low 32 bits of stack address leaked ***
LDAP PoC (network reproduction)
Prerequisites: A 389-ds-base instance with default configuration (SSO token is enabled by default; secret is auto-generated at startup). Any authenticated non-root user account.
-
Install the Python
python-ldapandpyasn1libraries:pip install python-ldap pyasn1 -
Send an ExtendedRequest with OID
2.16.840.1.113730.3.5.14and decode the BER INTEGER from the response. The INTEGER field contains the low 32 bits of a stack address. -
Expected result:
[+] Leaked low 32 bits of stack address: 0x8e7faed8 [+] Stack page offset (low 32): 0x8e7fa000 [+] ~20 bits of stack ASLR entropy revealed [!] Note: high 32 bits (0x7fXX...) are NOT leaked [+] Finding 018 confirmed: low 32 bits 0x8e7faed8 leaked in SSO token response [+] Partial stack entropy disclosed (~20 bits) -- not a full ASLR bypass
System tested: Fedora 42, 389-ds-base-3.1.4-6.fc42, x86_64 Flags used: Default configuration (no manual SSO token configuration required)
Impact
CVSS 3.1: 4.3 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:N/A:N)
| Component | Value | Justification |
|---|---|---|
| AV (Attack Vector) | Network | The LDAP ExtendedRequest is sent over the network (ports 389/636) |
| AC (Attack Complexity) | Low | No special conditions — SSO token feature is enabled by default with auto-generated secret |
| PR (Privileges Required) | Low | Requires any authenticated non-root user (simple bind, Kerberos, DIGEST-MD5) |
| UI (User Interaction) | None | Automated — no user action required beyond initial authentication |
| S (Scope) | Unchanged | The leak occurs within the 389-ds-base process context |
| C (Confidentiality) | Low | Only the low 32 bits of a stack address are disclosed, not full address or sensitive data |
| I (Integrity) | None | No data modification capability |
| A (Availability) | None | No denial of service — the server continues operating normally |
Severity rationale: Moderate is appropriate. The leak is real and deterministic but only discloses 32 bits of a stack address (approximately 20 bits of effective entropy after accounting for page alignment). This provides partial ASLR entropy reduction but does not constitute a full ASLR bypass. The information alone does not enable exploitation without additional vulnerabilities and significant additional work.
Blast Radius
389-ds-base is the core LDAP directory server for Red Hat’s identity management stack. It provides the backend for:
- Red Hat Identity Management (IdM/FreeIPA): All RHEL deployments using centralized identity management rely on 389-ds-base. The SSO token feature is enabled by default in all configurations.
- Red Hat Directory Server (RHDS): Enterprise LDAP deployments on RHEL use 389-ds-base directly.
- CentOS Stream and Fedora: Community distributions ship the same package.
The information leak is limited to partial stack address disclosure (32 bits). It does not directly compromise user data, credentials, or directory entries. The primary concern is as a building block for exploitation chains — specifically, it provides partial ASLR entropy reduction that could theoretically assist in exploiting separate memory corruption vulnerabilities. However, the chain from partial stack leak to reliable exploitation requires multiple additional steps (brute-forcing remaining high address bits, deriving heap layout from partial stack address, crafting valid heap metadata).
Any application or service that authenticates against a 389-ds-base instance with a non-root account can trigger the leak. This includes FreeIPA clients, SSSD, Apache mod_ldap, and any LDAP-aware application.
Workaround
Option 1 (Recommended): Disable the SSO token feature entirely:
dsconf <instance> config replace nsslapd-enable-ldapssotoken=off
This prevents the vulnerable code path from being reached. Note: this disables SSO token functionality for all users.
Option 2: Restrict network access to LDAP ports (389/636) to trusted networks via firewall rules. This limits who can trigger the leak but does not eliminate the vulnerability for users with legitimate access.
Note: Removing the SSO token secret from the configuration does not mitigate the vulnerability — the server auto-generates a new secret at startup (fernet_generate_new_key() at libglobs.c:2095).
Exploitation in the Wild
No evidence of exploitation in the wild. 389-ds-base does not appear in the CISA Known Exploited Vulnerabilities (KEV) catalog for this issue.
Timeline
| Date | Event |
|---|---|
| 2026-04-14 | Discovered during 389-ds-base security assessment |
| 2026-04-15 | Reported to vendor |
| TBD | Patch released |
| TBD | Public disclosure |
References
- Red Hat CVE page
- NVD
- 389ds/389-ds-base (GitHub)
- SSO token feature design (Issue #1797)
- Token auth design document
- CWE-843: Access of Resource Using Incompatible Type
- CWE-200: Exposure of Sensitive Information
- CVE-2020-35518: 389-ds-base information disclosure during DN binding
Credits
Discovered by Ian Murphy