curl · Report #4023506
libcurl LDAP LDIF: entry DN and attribute names written without RFC 2849 base64 encoding, allowing server line injection
How it was found
Summary:
curl 8.22.0 base64-encodes LDAP attribute values that aren't RFC 2849 SAFE-STRINGs (commit 2ba2fe3540, issue #22339), but the entry DN and the attribute names are still written to the LDIF output verbatim. In lib/openldap.c oldap_recv() that's the DN client_write(data, STRCONST("DN: "), bv.bv_val, bv.bv_len, STRCONST("\n")) call and the attribute-name client_write() right after it, neither of which goes through the SAFE-STRING check that values do; lib/ldap.c (WinLDAP/generic backend) has the same pattern in show_vals() and its DN write. So a hostile or MITM LDAP server that puts a CR/LF in a returned DN or attribute name injects its own lines into the LDIF curl prints. RFC 2849 says a DN that isn't a SAFE-STRING must use the dn:: base64 form, and a valid attribute description can't contain a control byte at all, so this is the DN/name counterpart of the value-only fix in #22339. I have a small patch (base64 the DN when it's non-SAFE, reject a control byte in an attribute name) tested on the OpenLDAP backend and can open a PR. AI disclosure: AI assistance was used to help find this and draft the report; I built curl, wrote and ran the mock-server PoC, and applied and tested the fix myself.
Affected version
curl 8.22.0 and current master. Reproduced on Linux with the OpenLDAP backend (lib/openldap.c). Paste your own curl -V when you file.
Steps To Reproduce:
A real directory server won't store a CR/LF in a DN, so the PoC is a small mock LDAP server (attached: mock_ldap.py) that answers an anonymous bind and returns one crafted entry.
- python3 mock_ldap.py 3899 dn # DN = cn=victim,dc=example,dc=com<LF>injected-by-server: OWNED
- curl -s "ldap://127.0.0.1:3899/dc=example,dc=com?cn?sub?(cn=*)" | cat -A
- Output puts injected-by-server: OWNED on its own LDIF line (the server authored it, not curl) and curl exits 0 with no warning. Attribute-name variant: python3 mock_ldap.py 3900 attr. Control: a clean DN prints a single well-formed DN: line with no injected line.
Impact
A caller that points curl at an untrusted or MITM'd LDAP server and then consumes curl's LDIF output gets lines the server authored rather than curl. If that LDIF is piped into an LDIF processor such as ldapmodify, the server can smuggle in add/modify/delete directives it was never asked for; if it's shown in a terminal it's raw control-byte / escape output. The precondition is that the user aimed curl at the malicious server and further processes the output, so I'd rate this Low, but I'll defer the grade to you.