Nmap ldap-brute NSE Script


This page contains detailed information about how to use the ldap-brute NSE script. For list of all NSE scripts, visit the Nmap NSE Library.

Select:
Overview
Error Messages

Script Overview


Script source code: https://github.com/nmap/nmap/tree/master/scripts/ldap-brute.nse
Script categories: intrusive, brute
Target service / protocol: ldap, ldapssl
Target network port(s): 389, 636
List of CVEs: -

Script Description


The ldap-brute.nse script attempts to brute-force LDAP authentication. By default it uses the built-in username and password lists. In order to use your own lists use the userdb and passdb script arguments.

This script does not make any attempt to prevent account lockout! If the number of passwords in the dictionary exceed the amount of allowed tries, accounts will be locked out. This usually happens very quickly.

Authenticating against Active Directory using LDAP does not use the Windows user name but the user accounts distinguished name. LDAP on Windows 2003 allows authentication using a simple user name rather than using the fully distinguished name. E.g., "Patrik Karlsson" vs. "cn=Patrik Karlsson,cn=Users,dc=cqure,dc=net" This type of authentication is not supported on e.g. OpenLDAP.

This script uses some AD-specific support and optimizations:

  • LDAP on Windows 2003/2008 reports different error messages depending on whether an account exists or not. If the script receives an error indicating that the username does not exist it simply stops guessing passwords for this account and moves on to the next.
  • The script attempts to authenticate with the username only if no LDAP base is specified. The benefit of authenticating this way is that the LDAP path of each account does not need to be known in advance as it's looked up by the server. This technique will only find a match if the account Display Name matches the username being attempted.

Ldap-brute NSE Script Arguments


This is a full list of arguments supported by the ldap-brute.nse script:

ldap.base

If set, the script will use it as a base for the password guessing attempts. If both ldap.base and ldap.upnsuffix are unset the user list must either contain the distinguished name of each user or the server must support authentication using a simple user name. See the AD discussion in the description. DO NOT use ldap.upnsuffix in conjunction with ldap.base as attempts to login will fail.

ldap.saveprefix

If set, the script will save the output to a file beginning with the specified path and name. The file suffix will automatically be added based on the output type selected.

ldap.savetype

If set, the script will save the passwords in the specified format. The current formats are CSV, verbose and plain. In both verbose and plain records are separated by colons. The difference between the two is that verbose includes the credential state. When ldap.savetype is used without ldap.saveprefix then ldap-brute will be prefixed to all output filenames.

ldap.upnsuffix

If set, the script will append this suffix value to the username to create a User Principle Name (UPN). For example if the ldap.upnsuffix value were 'mycompany.com' and the username being tested was 'pete' then this script would attempt to login as '[email protected]'. This setting should only have value when running the script against a Microsoft Active Directory LDAP implementation. When the UPN is known using this setting should provide more reliable results against domains that have been organized into various OUs or child domains. If both ldap.base and ldap.upnsuffix are unset the user list must either contain the distinguished name of each user or the server must support authentication using a simple user name. See the AD discussion in the description. DO NOT use ldap.upnsuffix in conjunction with ldap.base as attempts to login will fail.

creds.global

Credentials to be returned by Credentials.getCredentials regardless of the service.

creds.[service]

Credentials to be returned by Credentials.getCredentials for [service]. E.g. creds.http=admin:password

passdb

The filename of an alternate password database. Default: nselib/data/passwords.lst

unpwdb.passlimit

The maximum number of passwords passwords will return (default unlimited).

unpwdb.timelimit

The maximum amount of time that any iterator will run before stopping. The value is in seconds by default and you can follow it with ms, s, m, or h for milliseconds, seconds, minutes, or hours. For example, unpwdb.timelimit=30m or unpwdb.timelimit=.5h for 30 minutes. The default depends on the timing template level (see the module description). Use the value 0 to disable the time limit.

unpwdb.userlimit

The maximum number of usernames usernames will return (default unlimited).

userdb

The filename of an alternate username database. Default: nselib/data/usernames.lst

- - -
To use these script arguments, add them to the Nmap command line using the --script-args arg1=value,[arg2=value,..] syntax. For example:

nmap --script=ldap-brute --script-args ldap.base=value,ldap.saveprefix=value <target>

Ldap-brute NSE Script Example Usage


Here's an example of how to use the ldap-brute.nse script:

nmap -p 389 --script ldap-brute --script-args ldap.base='"cn=users,dc=cqure,dc=net"' <host>

Ldap-brute NSE Script Example Output


Here's a sample output from the ldap-brute.nse script:

389/tcp open  ldap
| ldap-brute:
|_  ldaptest:ldaptest => Valid credentials
|   restrict.ws:restricted1 => Valid credentials, account cannot log in from current host
|   restrict.time:restricted1 => Valid credentials, account cannot log in at current time
|   valid.user:valid1 => Valid credentials
|   expired.user:expired1 => Valid credentials, account expired
|   disabled.user:disabled1 => Valid credentials, account disabled
|_  must.change:need2change => Valid credentials, password must be changed at next logon

Ldap-brute NSE Script Example XML Output


There is no sample XML output for this module. However, by providing the -oX <file> option, Nmap will produce a XML output and save it in the file.xml file.

Author


  • Patrik Karlsson

References


See Also


Related NSE scripts to the ldap-brute.nse script:

Visit Nmap NSE Library for more scripts.

The ldap-brute.nse script may fail with the following error messages. Check for the possible causes by using the code snippets highlighted below found in the script source code. This can often times help in identifying the root cause of the problem.

Failed to retrieve namingContext


Here is a relevant code snippet related to the "Failed to retrieve namingContext" error message:

169:	  end
170:	
171:	  context = get_naming_context(socket)
172:	
173:	  if not context then
174:	    stdnse.debug1("Failed to retrieve namingContext")
175:	    socket:close()
176:	    return
177:	  end
178:	
179:	  status, usernames = unpwdb.usernames()

%s returned: "Invalid DN"


Here is a relevant code snippet related to the "%s returned: "Invalid DN"" error message:

209:	      stdnse.debug1( "Trying %s/%s ...", fq_username, password )
210:	      status, response = ldap.bindRequest( socket, { version=3, ['username']=fq_username, ['password']=password} )
211:	
212:	      -- if the DN (username) does not exist, break loop
213:	      if not status and response:match("invalid DN") then
214:	        stdnse.debug1( "%s returned: "Invalid DN"", fq_username )
215:	        invalid_account_cnt = invalid_account_cnt + 1
216:	        break
217:	      end
218:	
219:	      -- Is AD telling us the account does not exist?

%s:%s => Valid credentials, account cannot log in at current time


Here is a relevant code snippet related to the "%s:%s => Valid credentials, account cannot log in at current time" error message:

254:	        break
255:	      end
256:	
257:	      -- Login correct, user account logon time restricted
258:	      if not status and response:match("AcceptSecurityContext error, data 530,") then
259:	        table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "<empty>" ) )
260:	        stdnse.verbose2("%s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "<empty>")
261:	        credTable:add(fq_username,password, creds.State.TIME_RESTRICTED)
262:	        break
263:	      end
264:	

%s:%s => Valid credentials, account cannot log in at current time


Here is a relevant code snippet related to the "%s:%s => Valid credentials, account cannot log in at current time" error message:

255:	      end
256:	
257:	      -- Login correct, user account logon time restricted
258:	      if not status and response:match("AcceptSecurityContext error, data 530,") then
259:	        table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "<empty>" ) )
260:	        stdnse.verbose2("%s:%s => Valid credentials, account cannot log in at current time", fq_username, password:len()>0 and password or "<empty>")
261:	        credTable:add(fq_username,password, creds.State.TIME_RESTRICTED)
262:	        break
263:	      end
264:	
265:	      -- Login correct, user account can only log in from certain workstations

%s:%s => Valid credentials, account cannot log in from current host


Here is a relevant code snippet related to the "%s:%s => Valid credentials, account cannot log in from current host" error message:

262:	        break
263:	      end
264:	
265:	      -- Login correct, user account can only log in from certain workstations
266:	      if not status and response:match("AcceptSecurityContext error, data 531,") then
267:	        table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "<empty>" ) )
268:	        stdnse.verbose2("%s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "<empty>")
269:	        credTable:add(fq_username,password, creds.State.HOST_RESTRICTED)
270:	        break
271:	      end
272:	

%s:%s => Valid credentials, account cannot log in from current host


Here is a relevant code snippet related to the "%s:%s => Valid credentials, account cannot log in from current host" error message:

263:	      end
264:	
265:	      -- Login correct, user account can only log in from certain workstations
266:	      if not status and response:match("AcceptSecurityContext error, data 531,") then
267:	        table.insert( valid_accounts, string.format("%s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "<empty>" ) )
268:	        stdnse.verbose2("%s:%s => Valid credentials, account cannot log in from current host", fq_username, password:len()>0 and password or "<empty>")
269:	        credTable:add(fq_username,password, creds.State.HOST_RESTRICTED)
270:	        break
271:	      end
272:	
273:	      --Login, correct

WARNING: All usernames were invalid. Invalid LDAP base?


Here is a relevant code snippet related to the "WARNING: All usernames were invalid. Invalid LDAP base?" error message:

291:	  end
292:	
293:	  stdnse.debug1( "Finished brute against LDAP, total tries: %d, tps: %.f", tot_tries, ( tot_tries / ( ( nmap.clock_ms() - clock_start ) / 1000 ) ) )
294:	
295:	  if ( invalid_account_cnt == user_cnt and base_dn ~= nil ) then
296:	    return "WARNING: All usernames were invalid. Invalid LDAP base?"
297:	  end
298:	
299:	
300:	
301:	  if output_prefix then

Version


This page has been created based on Nmap version 7.92.

Go back to menu.