logo

Port 389 – LDAP (Lightweight Directory Access Protocol)

Service:

OpenLDAP (slapd)Active Directory LDAP389 Directory Server

Protocol:

TCP/UDP

Port:

389

Used for:

Cleartext directory lookups, authentication, and identity queries in Active Directory and LDAP directories

Port 389 is the default port for LDAP (Lightweight Directory Access Protocol), the protocol that clients use to query and authenticate against a directory service — users, groups, computers, and organizational units. On this port the traffic is cleartext by default (or upgraded in place with StartTLS); the fully encrypted variant, LDAPS, runs on port 636. Because every Active Directory domain controller answers LDAP, an open port 389 is one of the fastest ways to fingerprint a domain controller — and one of the richest sources of information a pentester can pull from a network.

Why It’s Open

LDAP is the backbone of enterprise identity, so it’s almost always open where directories live. Every Active Directory domain controller exposes port 389 — you can’t run a domain without it — and it’s the primary channel clients use to look up accounts, resolve group memberships, and locate services. Outside Windows you’ll find it on OpenLDAP (slapd), the 389 Directory Server, FreeIPA, and any app that binds to a directory for single sign-on. Where port 389 is open, the rest of the domain controller is usually one scan away — check for Kerberos on port 88, LDAPS on port 636, SMB on port 445, RPC on port 135, the kpasswd service on port 464, and the Global Catalog over SSL on port 3269.

Common Risks

  • Anonymous / null bind enumeration. Many directories allow an unauthenticated (anonymous) or null bind, handing over the entire org structure — users, groups, computers, OUs, and descriptions — to anyone who asks. LDAP is BloodHound’s primary collection channel for exactly this reason.
  • Passwords left in directory attributes. Admins routinely stash credentials in the description, info, or userPassword fields. If anonymous bind is on, those come out in a single query.
  • Cleartext credential exposure. A plain simple bind on port 389 sends the bind DN and password unencrypted, so anyone on the path can sniff a full login with Wireshark. StartTLS fixes this only if it’s enforced.
  • NTLM relay to LDAP. If LDAP signing and channel binding aren’t enforced, an attacker who coerces and relays a machine or user’s NTLM authentication to a DC’s LDAP service can act as that principal — often a straight path to escalation (RBCD, shadow credentials, or AD CS abuse).
  • LDAP injection. Web apps that build LDAP search filters from unsanitized user input let an attacker rewrite the filter to bypass authentication or read arbitrary directory data.

Want to save time on reporting?

Let PentestPad generate, track, and export your reports - automatically.

logo-cta

Enumeration & Testing

Detect the service and grab the RootDSE

Terminal window
nmap -sV -p 389 <target>
nmap -p 389 --script "ldap-rootdse,ldap-search" <target>

nmap labels the service ldap; ldap-rootdse dumps the naming contexts, domain functional level, and DC hostname anonymously, and ldap-search pulls directory entries.

Anonymous bind with ldapsearch

Terminal window
# Read the RootDSE (naming contexts) with an anonymous simple bind
ldapsearch -x -H ldap://<target> -s base namingContexts
# Dump everything under the base DN if anonymous bind is allowed
ldapsearch -x -H ldap://<target> -b "dc=example,dc=com"
# Hunt for passwords left in attributes
ldapsearch -x -H ldap://<target> -b "dc=example,dc=com" "(|(description=*pass*)(userPassword=*))"

Enumerate an AD directory with windapsearch and ldapdomaindump

Terminal window
# Null-bind user dump
windapsearch.py --dc-ip <target> -u "" -U
# Full authenticated dump to HTML/JSON/Greppable
ldapdomaindump ldap://<target> -u 'DOMAIN\user' -p 'Password123'

NetExec / CrackMapExec

Terminal window
nxc ldap <target> -u user -p 'Password123' --users --groups
nxc ldap <target> -u '' -p '' --users # null session
nxc ldap <target> -u user -p 'Password123' --password-not-required
nxc ldap <target> -u user -p 'Password123' --bloodhound -c all --dns-server <target>

Metasploit modules

Terminal window
msfconsole -q
use auxiliary/gather/ldap_hashdump # anonymous-bind dump of userPassword and friends
set RHOSTS <target>
run
use auxiliary/gather/ldap_query # run arbitrary LDAP queries
use auxiliary/scanner/ldap/ldap_login # test binds (plaintext, NTLM, Kerberos, SChannel)
use auxiliary/gather/ldap_esc_vulnerable_cert_finder # find AD CS ESC misconfigs over LDAP

Collect for BloodHound and test the relay path

Terminal window
# BloodHound collects almost entirely over LDAP
bloodhound-python -d domain.local -u user -p 'Password123' -c all -ns <target>
# Relay coerced NTLM auth to LDAP when signing isn't enforced
ntlmrelayx.py -t ldap://<dc> --escalate-user <lowpriv_user>

Log every anonymous bind, exposed attribute, and relay path you confirm so it lands in the final pentest report instead of a scratch file.

What to Look For

Checkpoint What it means
Anonymous / null bind allowed Full directory enumeration with no credentials
Passwords in description / info / userPassword Instant credential harvest via one query
Simple bind offered without StartTLS Bind DN and password sniffable in cleartext
LDAP signing / channel binding not enforced NTLM relay to LDAP → escalation (RBCD, shadow creds, ESC8)
userAccountControl shows “password not required” Weak or blank passwords to spray
Service accounts with SPNs visible over LDAP Kerberoasting targets (crack via port 88)
Unfiltered LDAP filters in a web app LDAP injection → auth bypass or data disclosure

Known CVEs and Exploits

  • CVE-2017-8563 — Windows LDAP elevation of privilege (CVSS 8.1). When a client’s Kerberos authentication falls back to NTLM and LDAP signing isn’t enforced, an attacker who relays that NTLM auth to a domain controller’s LDAP service acts with the victim’s privileges — the canonical “NTLM relay to LDAP” path to domain admin. Microsoft’s fix added the LdapEnforceChannelBinding setting, and the 2020 hardening (ADV190023) moved to enforce LDAP signing and channel binding by default. Affects Windows 7 SP1 through Server 2016.
  • CVE-2019-1040 — NTLM MIC bypass, “drop-the-MIC” (CVSS 5.9). A man-in-the-middle strips the Message Integrity Check from an NTLM exchange without invalidating the message, re-opening relay to services that would otherwise reject tampered authentication. Chained with ntlmrelayx.py it revives NTLM-relay-to-LDAP even where some protections were assumed. Affects Windows 7 through Server 2019.
  • CVE-2022-29155 — OpenLDAP SQL injection (CVSS 9.8, critical). An unauthenticated attacker injects SQL through an LDAP search filter against slapd’s experimental back-sql backend in OpenLDAP before 2.5.12 and 2.6.x before 2.6.2. It only bites deployments that enable back-sql, but where present it’s pre-auth SQL execution straight from an LDAP query.
  • CVE-2022-0918 — 389 Directory Server denial of service (CVSS 7.5). An unauthenticated attacker who can reach the LDAP port sends a single crafted message that segfaults the ns-slapd process, taking directory services (and any auth that depends on them) offline.
  • CVE-2018-14648 — 389 Directory Server search DoS (CVSS 7.5). A specially crafted search query drives the do_search() function into runaway CPU consumption — an unauthenticated, network-reachable denial of service against Red Hat / Fedora directory servers.
  • CVE-2020-25709 — OpenLDAP reachable assertion (CVSS 7.5). A malicious packet processed by slapd triggers an assertion failure that crashes the service — one of a family of pre-auth OpenLDAP DoS bugs in versions up to 2.4.56.
  • CVE-2021-44228 — “Log4Shell” (CVSS 10.0). Worth naming precisely: the flaw is in Apache Log4j2, not in the LDAP service. A JNDI lookup string in attacker-controlled log data makes a vulnerable Java app connect out to an attacker’s LDAP server, which returns a malicious object that runs in the app’s context. LDAP is only the delivery channel — which is why blocking outbound LDAP to untrusted hosts helps contain it, but patching log4j is the real fix.

Mitigation

  • Disable anonymous and null binds. Require authentication for directory reads, and audit what an unauthenticated bind can still see.
  • Enforce LDAP signing and channel binding. On domain controllers, require signing and set channel binding to enforced (ADV190023) so NTLM relay to LDAP fails; this is the single most important AD hardening for port 389.
  • Use StartTLS or move to LDAPS on 636. Never allow a cleartext simple bind that carries a password — encrypt the session so credentials can’t be sniffed.
  • Purge secrets from directory attributes. Never store passwords in description, info, or userPassword; scan for them the way an attacker would.
  • Sanitize LDAP filters in applications. Escape user input before building search filters to shut down LDAP injection.
  • Patch the directory server. Keep OpenLDAP, 389-DS, and Windows current, and avoid the experimental back-sql backend unless you need it.
  • Firewall port 389 to trusted networks. A domain controller should never answer LDAP from the internet — restrict it to internal management ranges.

Real-World Example

The most damaging LDAP attacks in Active Directory don’t exploit a bug in LDAP itself — they abuse a configuration. For years, domain controllers accepted NTLM authentication over LDAP without requiring signing or channel binding. An attacker on the internal network coerces a victim (a user, or a computer account via a printer-bug-style trigger) into authenticating, then relays that NTLM handshake to the DC’s LDAP service with a tool like ntlmrelayx.py. Because the DC never verified the channel, it treats the relayed session as the victim — and the attacker writes to the directory: granting themselves resource-based constrained delegation, adding shadow credentials, or (chained with AD CS) relaying to the certificate endpoint for a domain-admin certificate. CVE-2017-8563 is the flaw that formalized this class, and Microsoft’s 2020 push to enforce LDAP signing and channel binding by default (ADV190023) exists precisely because a single unsigned LDAP session on port 389 could hand over the domain. It’s the clearest reason to treat every reachable port 389 as a high-value target.

FAQ

What port does LDAP use?

LDAP uses port 389 by default for standard (cleartext or StartTLS) directory traffic, and port 636 for LDAPS — LDAP wrapped in SSL/TLS from the first byte. Active Directory adds the Global Catalog on ports 3268 (LDAP) and 3269 (LDAP over SSL) for forest-wide searches. The core directory that clients hit constantly is port 389.

Is port 389 TCP or UDP?

Mostly TCP. LDAP queries, binds, and searches all run over TCP 389. UDP 389 exists for the older Connectionless LDAP (CLDAP) used for lightweight rootDSE “ping” lookups — which is also why exposed CLDAP servers get abused as UDP amplification reflectors in DDoS attacks. Scans and firewall rules should cover both.

What is port 389 used for?

Port 389 carries LDAP: clients bind to a directory and query it for users, groups, computers, and organizational units, or authenticate a login against it. It’s on every Active Directory domain controller and on OpenLDAP, 389 Directory Server, and FreeIPA hosts — the lookup service that underpins single sign-on across an enterprise.

What is the difference between port 389 and port 636?

Port 389 is standard LDAP: cleartext by default, optionally upgraded in place with StartTLS. Port 636 is LDAPS — the session is wrapped in SSL/TLS from the start, so credentials and queries are never exposed. If you need encryption end to end, use LDAPS on 636; a simple bind on 389 without StartTLS sends the password in the clear.

Can port 389 be exploited?

The port itself isn’t a vulnerability, but a normally configured LDAP service exposes a lot: anonymous-bind enumeration of the whole directory, passwords left in attributes, credential sniffing on cleartext binds, and NTLM relay to LDAP where signing isn’t enforced — which can escalate to domain admin. Unpatched OpenLDAP and 389-DS add denial-of-service and SQL-injection bugs on top. Treat any reachable port 389 as a high-value target to enumerate and lock down.

How do I secure or close port 389?

You can’t close it on a domain controller — Active Directory needs LDAP — so reduce what an attacker can do: disable anonymous binds, enforce LDAP signing and channel binding, require StartTLS or move to LDAPS, strip secrets out of directory attributes, sanitize application filters, patch the server, and firewall port 389 to trusted subnets. On non-DC hosts that don’t need a directory, stop the LDAP service and confirm the port is closed with a rescan.

TL;DR

  • Service: LDAP (Lightweight Directory Access Protocol) — cleartext / StartTLS directory access
  • Default port: 389 TCP/UDP (encrypted variant: LDAPS on 636; Global Catalog on 3268/3269)
  • Biggest risk: anonymous-bind enumeration, cleartext credential exposure, and NTLM relay to LDAP when signing isn’t enforced
  • Mitigation: disable anonymous binds, enforce LDAP signing + channel binding, use StartTLS/LDAPS, purge secrets from attributes, patch, and firewall port 389