Heap out-of-bounds read in LDIF parser str2entry_state_information_from_type
- CVE ID
- CVE-2026-11786
- Product
- 389-ds-base
- Severity
- Low (1.9)
- CVSS Vector
- CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:L/I:N/A:N
- CWE
- CWE-125
- Red Hat CVE
- Red Hat CVE page
Summary
A heap out-of-bounds read vulnerability exists in the 389 Directory Server’s LDIF parser. When an administrator imports an LDIF file containing attribute types with trailing semicolons, the server reads 1 to 16 bytes past the end of a heap allocation. The bug requires local administrator access and does not crash production binaries, limiting the impact to a low-severity memory safety defect detectable only under instrumentation.
Affected Versions
| Package | Version | NVR | Status |
|---|---|---|---|
| 389-ds-base | 2.6.1 | 389-ds-base-2.6.1-6.el9 | Vulnerable (confirmed — ASan-proven, aarch64) |
| 389-ds-base | 1.3.11.1 | 389-ds-base-1.3.11.1-5.el7_9 | Vulnerable (confirmed — code path reached via ldif2db) |
| 389-ds-base | 2.7.0 | 389-ds-base-2.7.0-10.el9_7 | Vulnerable (confirmed — source unchanged per git history) |
| 389-ds-base | 3.1.3 | 389-ds-base-3.1.3-7.el10_1 | Vulnerable (confirmed — source unchanged per git history) |
| 389-ds-base | 3.0.6 | 389-ds-base-3.0.6-17.el10_0 | Vulnerable (confirmed — source unchanged per git history) |
| 389-ds-base | 3.1.4 | 389-ds-base-3.1.4-6.fc42 | Vulnerable (confirmed — debug container on test system) |
| 389-ds-base | HEAD | upstream current | Vulnerable (confirmed — source verified on test system) |
Affected Products
| Product | Version | Status | Verification |
|---|---|---|---|
| Red Hat Enterprise Linux 7 | 389-ds-base-1.3.11.1-5.el7_9 | Affected | Code path confirmed via ldif2db import on EL7 container; deletedattribute string present in libslapd.so |
| Red Hat Enterprise Linux 9 (CentOS Stream 9) | 389-ds-base-2.6.1-6.el9 | Affected | ASan-proven heap-buffer-overflow on aarch64 instrumented build |
| Red Hat Enterprise Linux 9.7 | 389-ds-base-2.7.0-10.el9_7 | Affected | Source unchanged per upstream git history; str2entry_state_information_from_type() identical |
| Red Hat Enterprise Linux 10.0 EUS | 389-ds-base-3.0.6-17.el10_0 | Affected | Source unchanged per upstream git history |
| Red Hat Enterprise Linux 10.1 | 389-ds-base-3.1.3-7.el10_1 | Affected | Source unchanged per upstream git history |
| Fedora 42 | 389-ds-base-3.1.4-6.fc42 | Affected | Verified via debug container on test system |
| Red Hat Directory Server (RHDS) | All versions shipping 389-ds-base | Affected | RHDS is 389-ds-base with management tooling; same vulnerable entry.c code |
| Red Hat Identity Management (IdM) / FreeIPA | All versions shipping 389-ds-base | Affected | IdM uses 389-ds-base as its LDAP backend; same vulnerable entry.c code. The ldif2db import path is available to IdM administrators. |
| UBI 7 / UBI 9 | Matches corresponding RHEL | Affected | UBI ships the same 389-ds-base packages as the corresponding RHEL version |
Architecture Validation
| Architecture | Tested | Result |
|---|---|---|
| aarch64 | YES | ASan heap-buffer-overflow confirmed (389-ds-base-2.6.1-6.el9 instrumented build) |
| x86_64 | YES | Standalone PoC triggers ASan heap-buffer-overflow on test system; production binary processes entry without crash |
Root Cause
File: ldap/servers/slapd/entry.c:119-170
Function: str2entry_state_information_from_type()
Three distinct OOB read sites share the same root cause: PL_strchr()/strchr() locates a semicolon in the attribute type string, but no bounds check verifies that sufficient bytes remain before subsequent memory accesses.
Site 1: entry.c:122-123 — 4-byte indexed access (ASan-proven)
p = PL_strchr(atype->bv_val, ';');
// ...
while (p != NULL) {
if (p[3] == 'c' && p[4] == 's' && p[5] == 'n' && p[6] == '-') {
// CSN parsing -- reads p[3] through p[6] without bounds check
When the semicolon is within the last 6 bytes of the heap allocation, p[3] through p[6] read past the boundary.
Site 2: entry.c:164 — 16-byte strncmp read
} else if (strncmp(p + 1, "deletedattribute", 16) == 0) {
When fewer than 17 bytes remain after the semicolon, strncmp(p+1, ...) reads up to 16 bytes past the valid allocation. This is the largest OOB read window in the function.
Site 3: entry.c:170 — 7-byte strncmp read
} else if (strncmp(p + 1, "deleted", 7) == 0) {
When fewer than 8 bytes remain after the semicolon, strncmp(p+1, ...) reads up to 7 bytes past the valid allocation.
Allocation context
The buffer is allocated by dup_ldif_line() via slapi_ch_realloc() at util.c:1785, sized exactly to the attribute type length with no padding beyond the NUL terminator.
Proof of Concept
PoC source code: CVE-2026-11786 on GitHub
Two PoC programs and an LDIF trigger file are provided in the poc/ directory.
PoC 1: OOB Read Trigger (poc/poc-str2entry-oob.c)
Replicates the vulnerable logic from entry.c:119-124 where p[3]..p[6] are accessed after finding a semicolon via PL_strchr without bounds checking.
Build and run:
gcc -fsanitize=address -g -o poc-017 poc/poc-str2entry-oob.c
./poc-017
Expected result: AddressSanitizer reports heap-buffer-overflow READ of size 1 in check_state_info at the p[3] access. Exit code is non-zero.
PoC 2: Behavioral Oracle (poc/poc-str2entry-heap-oracle.c)
The p[3..6] comparison against 'c','s','n','-' in str2entry_state_information_from_type() creates a behavioral oracle over adjacent heap content. By varying attribute type length (shifting the semicolon position), an attacker slides the 4-byte probe window across adjacent heap objects.
Oracle outputs:
0=p[3] != 'c'(1 byte probed)1=p[3]='c', p[4]!='s'(2 bytes probed)2=p[3..4]='cs', p[5]!='n'(3 bytes probed)3=p[3..5]='csn', p[6]!='-'(4 bytes probed)4=p[3..6]='csn-'— CSN parsing path taken (different behavior)
Build and run:
# Without ASan (to see oracle behavior without crash):
gcc -g -o poc-017-oracle poc/poc-str2entry-heap-oracle.c
./poc-017-oracle
# With ASan (to prove OOB):
gcc -fsanitize=address -g -o poc-017-oracle poc/poc-str2entry-heap-oracle.c
ASAN_OPTIONS=halt_on_error=0 ./poc-017-oracle
LDIF Trigger File (poc/trigger.ldif)
dn: uid=oobtest017,dc=example,dc=com
objectClass: person
cn: x
sn: x
userCertificate;binary;: dGVzdA==
The trailing semicolon in userCertificate;binary; causes PL_strchr to find the second ; near the allocation boundary, making p[3] through p[6] read past the buffer.
Production Reproduction via ldif2db
Import the LDIF trigger file via ldif2db (requires root or Directory Manager):
ns-slapd ldif2db -D /etc/dirsrv/slapd-<instance> -i /tmp/017-oob.ldif -n userRoot
Import completes successfully (exit code 0). No crash on production (non-ASan) binary — the system allocator provides padding that absorbs the small overread. The code path through slapi_str2entry() -> str2entry_fast() -> str2entry_state_information_from_type() is confirmed reached by successful entry processing.
Impact
CVSS 3.1: 1.9 (CVSS:3.1/AV:L/AC:H/PR:H/UI:N/S:U/C:L/I:N/A:N)
| Component | Value | Justification |
|---|---|---|
| AV | L (Local) | The only confirmed external input path is ldif2db, a local admin tool. The LDAP wire protocol (do_add, do_modify, do_modrdn) constructs entries via BER decoding, not slapi_str2entry(). |
| AC | H (High) | Requires crafting a specific LDIF with a trailing semicolon positioned within the last bytes of the attribute type allocation. |
| PR | H (High) | ldif2db requires root or Directory Manager privileges on the server host. |
| UI | N (None) | No user interaction required beyond the admin running the import. |
| S | U (Unchanged) | The vulnerability stays within the ns-slapd process. |
| C | L (Low) | Reads 1-16 bytes of adjacent heap data. Values are compared against fixed constants ('c', 's', 'n', '-', "deletedattribute", "deleted") and are not returned to the caller, limiting information disclosure. |
| I | N (None) | Read-only access — no data modification. |
| A | N (None) | Production (non-ASan) binaries do not crash. The system allocator provides padding that absorbs the small overread. ASan-instrumented builds abort, but this is not a production scenario. |
Comparable CVEs
| CVE | Product | Type | CVSS | Notes |
|---|---|---|---|---|
| CVE-2024-1062 | 389-ds-base | Heap overflow in log_entry_attr (CWE-122) | ~5.5 (Moderate) | Local, low complexity, DoS via crash. Higher than this finding because it causes an actual crash on production binaries. |
| CVE-2018-1054 | 389-ds-base | OOB read in SetUnicodeStringFromUTF_8 (CWE-125) | 7.5 (High) | Network-reachable (AV:N), unauthenticated (PR:N), DoS via crash. Much higher because it is remotely exploitable and causes a crash. |
| CVE-2025-14905 | 389-ds-base | Heap overflow in schema.c (CWE-122) | Moderate | Different function (schema_attr_enum_callback), different vulnerability class. |
Blast Radius
389-ds-base is a foundational component serving as the LDAP backend for both Red Hat Directory Server (RHDS) and Red Hat Identity Management (IdM/FreeIPA). It is not supported as a standalone LDAP server on RHEL.
Downstream consumers:
- Red Hat Directory Server (RHDS): Ships 389-ds-base with management tooling. Any RHDS deployment where an administrator imports LDIF files is potentially affected.
- Red Hat Identity Management (IdM) / FreeIPA: Uses 389-ds-base as its LDAP backend. IdM administrators who use
ldif2dbfor data recovery or migration are affected. - Dogtag Certificate System: Uses 389-ds-base for its internal LDAP store. Affected if LDIF import is used for certificate database management.
Blast radius assessment: Limited. The vulnerability requires local administrator access, deliberate import of a malformed LDIF file via ldif2db, and the overread is silent on production binaries (no crash, no observable effect). An administrator who can run ldif2db already has full control over the directory data. The vulnerability does not enable privilege escalation or lateral movement.
Exploitation in the Wild
No evidence found. Searches performed across NVD, Red Hat Bugzilla, GitHub 389ds/389-ds-base issues, Debian security tracker, and CISA KEV catalog returned no matching entries. The OOB read is small (1-16 bytes), compared against fixed constants, and not returned to the caller, making information disclosure impractical.
Workaround
Validate LDIF files for malformed attribute types before importing. Reject any LDIF entry containing attribute types with trailing semicolons (e.g., userCertificate;binary;).
Pre-import validation check:
grep -nE '^[a-zA-Z][a-zA-Z0-9-]*;[^:]*;:' input.ldif && echo "REJECT: trailing semicolon in attribute type"
This does not protect against the replication changelog path (corrupted stored data), but that path requires a pre-existing database corruption condition, not external input.
Proposed Fix
Add bounds checking before all three access sites. Compute remaining length once after strchr returns and gate each branch on the required minimum length.
while (p != NULL) {
size_t remaining = strlen(p); /* includes the ';' at p[0] */
if (remaining >= 7 && p[3] == 'c' && p[4] == 's' && p[5] == 'n' && p[6] == '-') {
/* ... existing CSN parsing (line 123) ... */
} else if (remaining >= 17 && strncmp(p + 1, "deletedattribute", 16) == 0) {
/* ... existing deleted-attribute handling (line 164) ... */
} else if (remaining >= 8 && strncmp(p + 1, "deleted", 7) == 0) {
/* ... existing deleted-value handling (line 170) ... */
}
p = strchr(p + 1, ';');
}
The remaining >= N checks ensure that all indexed accesses and strncmp reads stay within the allocation. The minimum lengths are: 7 for p[3..6] (CSN), 17 for p+1 + 16-byte strncmp (deletedattribute), and 8 for p+1 + 7-byte strncmp (deleted).
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
- CWE-125: Out-of-bounds Read
- CVSS 3.1 Calculator
- 389-ds-base upstream repository
- CVE-2024-1062 (related) — Heap overflow in
log_entry_attr - CVE-2018-1054 (related) — OOB read in
SetUnicodeStringFromUTF_8
Credits
Discovered by Ian Murphy