ldapsearch: Complete Guide to LDAP Enumeration and Directory Service Queries Using Kali Linux

ldapsearch: Complete Guide to LDAP Enumeration and Directory Service Queries Using Kali Linux

ldapsearch is a command-line utility from the OpenLDAP project used to query LDAP (Lightweight Directory Access Protocol) directory servers, most commonly Microsoft Active Directory Domain Controllers, but also standalone OpenLDAP, FreeIPA, and other directory services. LDAP is the protocol that underlies Active Directory’s object storage (users, groups, computers, organizational units, group policies), and because many AD environments permit unauthenticated (“anonymous”) binds or low-privilege authenticated binds by default, ldapsearch can extract an enormous amount of domain intelligence directly — often without needing any specialized AD tooling.

During enumeration, ldapsearch is used to:

  • Discover the LDAP base DN (distinguished name) of the directory
  • Enumerate all user accounts, their attributes (sAMAccountName, userPrincipalName, description, memberOf, etc.)
  • Enumerate groups and their membership
  • Enumerate computer objects (workstations/servers joined to the domain)
  • Pull password policy and domain functional level information
  • Search for specific, sensitive attributes (e.g., description fields sometimes contain plaintext passwords left by administrators)

It is a foundational tool for Active Directory enumeration and is frequently the very first thing run against port 389/636 once a Domain Controller is identified.

Installation

# Kali Linux (preinstalled as part of ldap-utils)
sudo apt update
sudo apt install ldap-utils -y

# Verify
ldapsearch -VV
which ldapsearch

Syntax

ldapsearch -x -H ldap://<target> -b "<base DN>" [options] [filter] [attributes]

Command-Line Options

OptionDescription
-xUse simple authentication instead of SASL
-H uriLDAP URI, e.g., ldap://192.168.10.10 or ldaps://192.168.10.10
-b basednBase DN to search from, e.g., "DC=corp,DC=local"
-D binddnDistinguished name to bind with (for authenticated bind)
-w passwordPassword for the bind DN (plaintext, on command line)
-WPrompt interactively for bind password (safer than -w)
-s scopeSearch scope: base, one, or sub (default sub)
-LLLMinimal output: no comments, no version, no line wrapping
-o ldif-wrap=noDisable LDIF line wrapping for cleaner parsing
-Z / -ZZUse StartTLS (optional/required)
-p portSpecify port (default 389, or 636 for LDAPS)
-vVerbose
-d levelDebug level
<filter>LDAP search filter, e.g., "(objectClass=user)"
<attrs>Space-separated list of attributes to return, e.g., sAMAccountName mail

Common useful filters:

FilterPurpose
(objectClass=*)Return everything (used with base scope to get root DSE)
(objectClass=user)All user objects
(objectClass=group)All group objects
(objectClass=computer)All computer objects
(&(objectClass=user)(admincount=1))Privileged accounts (protected by AdminSDHolder)
(userAccountControl:1.2.840.113556.1.4.803:=2)Disabled accounts
(description=*)Objects with a non-empty description field

Basic Usage

Query the root DSE (anonymous bind) to discover the base DN and server capabilities:

ldapsearch -x -H ldap://192.168.10.10 -s base -b "" "(objectClass=*)" namingContexts

Expected output:

# extended LDIF
#
# LDAPv3
# base <> with scope baseObject
# filter: (objectClass=*)
# requesting: namingContexts 
#

dn:
namingContexts: DC=corp,DC=local
namingContexts: CN=Configuration,DC=corp,DC=local
namingContexts: CN=Schema,CN=Configuration,DC=corp,DC=local

# search result
search: 2
result: 0 Success

Practical Examples

Example 1 — Discover base DN via anonymous root DSE query

ldapsearch -x -H ldap://192.168.10.10 -s base -b "" namingContexts
namingContexts: DC=corp,DC=local

Example 2 — Anonymous bind, dump all objects under the base DN

ldapsearch -x -H ldap://192.168.10.10 -b "DC=corp,DC=local"
# extended LDIF
#
# corp.local
dn: DC=corp,DC=local
objectClass: top
objectClass: domain
...
# Users, Users, corp.local
dn: CN=Users,DC=corp,DC=local
objectClass: container

Example 3 — Enumerate all usernames (sAMAccountName)

ldapsearch -x -H ldap://192.168.10.10 -b "DC=corp,DC=local" \
  "(objectClass=user)" sAMAccountName -LLL
dn: CN=Administrator,CN=Users,DC=corp,DC=local
sAMAccountName: Administrator

dn: CN=jdoe,CN=Users,DC=corp,DC=local
sAMAccountName: jdoe

dn: CN=asmith,CN=Users,DC=corp,DC=local
sAMAccountName: asmith

Example 4 — Authenticated bind with domain credentials

ldapsearch -x -H ldap://192.168.10.10 -D "jdoe@corp.local" -W \
  -b "DC=corp,DC=local" "(objectClass=user)" sAMAccountName mail
Enter LDAP Password:
dn: CN=jdoe,CN=Users,DC=corp,DC=local
sAMAccountName: jdoe
mail: jdoe@corp.local

Example 5 — Search for privileged (AdminCount=1) accounts

ldapsearch -x -H ldap://192.168.10.10 -D "jdoe@corp.local" -w 'Passw0rd!' \
  -b "DC=corp,DC=local" "(&(objectClass=user)(admincount=1))" sAMAccountName -LLL
dn: CN=Administrator,CN=Users,DC=corp,DC=local
sAMAccountName: Administrator

dn: CN=svc_backup,CN=Users,DC=corp,DC=local
sAMAccountName: svc_backup

Example 6 — Search description fields for accidentally leaked credentials

ldapsearch -x -H ldap://192.168.10.10 -D "jdoe@corp.local" -w 'Passw0rd!' \
  -b "DC=corp,DC=local" "(description=*)" sAMAccountName description -LLL
dn: CN=tempuser,CN=Users,DC=corp,DC=local
sAMAccountName: tempuser
description: temp pw Summer2026!

Example 7 — Enumerate all computer objects (workstations/servers)

ldapsearch -x -H ldap://192.168.10.10 -D "jdoe@corp.local" -w 'Passw0rd!' \
  -b "DC=corp,DC=local" "(objectClass=computer)" dNSHostName operatingSystem -LLL
dn: CN=DC01,OU=Domain Controllers,DC=corp,DC=local
dNSHostName: dc01.corp.local
operatingSystem: Windows Server 2019 Standard

dn: CN=WKSTN05,CN=Computers,DC=corp,DC=local
dNSHostName: wkstn05.corp.local
operatingSystem: Windows 11 Enterprise

Example 8 — Enumerate all groups and their members

ldapsearch -x -H ldap://192.168.10.10 -D "jdoe@corp.local" -w 'Passw0rd!' \
  -b "DC=corp,DC=local" "(objectClass=group)" cn member -LLL
dn: CN=Domain Admins,CN=Users,DC=corp,DC=local
cn: Domain Admins
member: CN=Administrator,CN=Users,DC=corp,DC=local
member: CN=asmith,CN=Users,DC=corp,DC=local

Example 9 — Find disabled accounts using a bitwise LDAP filter

ldapsearch -x -H ldap://192.168.10.10 -D "jdoe@corp.local" -w 'Passw0rd!' \
  -b "DC=corp,DC=local" "(userAccountControl:1.2.840.113556.1.4.803:=2)" sAMAccountName -LLL
dn: CN=old_svc,CN=Users,DC=corp,DC=local
sAMAccountName: old_svc

Example 10 — Using LDAPS (encrypted, port 636)

ldapsearch -x -H ldaps://192.168.10.10:636 -D "jdoe@corp.local" -w 'Passw0rd!' \
  -b "DC=corp,DC=local" "(objectClass=user)" sAMAccountName -LLL
dn: CN=jdoe,CN=Users,DC=corp,DC=local
sAMAccountName: jdoe

Common Use Cases

  • Building a full domain username list for password spraying, from either anonymous or low-privilege authenticated LDAP binds
  • Mapping Active Directory group structure and privileged group membership (Domain Admins, Enterprise Admins) for attack-path planning
  • Hunting for credentials accidentally stored in description/info/comment attributes on user objects
  • Enumerating all domain-joined computers, including operating system versions, to identify unpatched or legacy systems
  • Confirming whether anonymous LDAP bind is enabled — itself a significant misconfiguration finding to report

Automation with Bash

#!/bin/bash
# ldap_userlist.sh - Extract a clean sAMAccountName list from a Domain Controller
DC="192.168.10.10"
BASEDN="DC=corp,DC=local"
BINDDN="jdoe@corp.local"
BINDPW='Passw0rd!'
OUT="domain_users.txt"

ldapsearch -x -H "ldap://$DC" -D "$BINDDN" -w "$BINDPW" \
    -b "$BASEDN" "(objectClass=user)" sAMAccountName -LLL \
    | grep "^sAMAccountName:" | awk '{print $2}' | sort -u > "$OUT"

echo "[+] Extracted $(wc -l < "$OUT") usernames to $OUT"

Tips and Best Practices

  • Always test an anonymous bind first (omit -D/-w); many AD environments still permit anonymous LDAP reads, especially the root DSE / base DN.
  • Use -LLL and -o ldif-wrap=no together for clean, script-parseable output free of comments and line wraps.
  • Prefer -W (interactive password prompt) over -w on the command line during manual testing, since -w exposes the password in shell history/process listing.
  • Use the admincount=1 filter as a fast way to shortlist high-value accounts protected by AdminSDHolder, which typically includes Domain/Enterprise Admins and built-in service accounts.
  • Always try LDAPS (-H ldaps://... port 636) if plain LDAP (389) is filtered or refuses simple bind — many environments enforce encrypted-only LDAP.

Troubleshooting

ProblemLikely CauseSolution
ldap_bind: Invalid credentials (49)Wrong bind DN format or passwordTry user@domain.tld format, or full CN=...,DC=...,DC=... DN
ldap_sasl_bind(SIMPLE): Can't contact LDAP server (-1)Port blocked or wrong protocol/port comboConfirm with nmap -p389,636 <ip>; try ldaps:// on 636
Anonymous bind returns nothingAnonymous LDAP disabled (default on modern AD)Requires valid domain credentials (even a low-privilege account is usually enough)
ldap_bind: Strong(er) authentication required (8)Server enforces signed/encrypted LDAPUse -Z for StartTLS or connect via ldaps://
Search returns partial results onlyDefault LDAP result size/time limits on the serverAdd -z 0 (no size limit, if permitted) or narrow search scope/filter

References

  • OpenLDAP ldapsearch manual: https://www.openldap.org/software/man.cgi?query=ldapsearch
  • Kali Linux tool page: https://www.kali.org/tools/ldap-utils/
  • Microsoft LDAP filter syntax reference: https://learn.microsoft.com/en-us/windows/win32/adsi/search-filter-syntax
Total
0
Shares

Leave a Reply

Previous Post
rpcclient: Complete Guide to Windows RPC Enumeration and Active Directory Information Gathering Using Kali Linux

rpcclient: Complete Guide to Windows RPC Enumeration and Active Directory Information Gathering Using Kali Linux

Next Post
snmpwalk: Complete Guide to SNMP Enumeration and Network Device Information Gathering Using Kali Linux

snmpwalk: Complete Guide to SNMP Enumeration and Network Device Information Gathering Using Kali Linux

Related Posts