ldapsearch-ad.py detailled usage

February 8, 2024 · View on GitHub

-h could help

$ ./ldapsearch-ad.py -h
usage: ldapsearch-ad.py [-h] [-l LDAP_SERVER] [-ssl] [-t REQUEST_TYPE] [-d DOMAIN] [-u USERNAME] [-p PASSWORD]
                        [-H HASHES] [-s SEARCH_FILTER] [-z SIZE_LIMIT] [-o OUTPUT_FILE] [-v] [--version]
                        [search_attributes [search_attributes ...]]

Active Directory LDAP Enumerator

positional arguments:
  search_attributes     LDAP attributes to look for (default is all).

optional arguments:
  -h, --help            show this help message and exit
  -l LDAP_SERVER, --server LDAP_SERVER
                        IP address of the LDAP server.
  -ssl, --ssl           Force an SSL connection?.
  -t REQUEST_TYPE, --type REQUEST_TYPE
                        Request type: info, whoami, search, trusts, pass-pols, admins, show-user, show-user-list,
                        kerberoast, search-spn, asreproast, goldenticket, search-delegation, createsid, all
  -d DOMAIN, --domain DOMAIN
                        Authentication account's FQDN. Example: "contoso.local".
  -u USERNAME, --username USERNAME
                        Authentication account's username.
  -p PASSWORD, --password PASSWORD
                        Authentication account's password.
  -H HASHES, -hashes HASHES
                        NTLM hashes, format is LMHASH:NTHASH
  -s SEARCH_FILTER, --search-filter SEARCH_FILTER
                        Search filter (use LDAP format).
  -z SIZE_LIMIT, --size_limit SIZE_LIMIT
                        Size limit (default is 100, or server' own limit).
  -o OUTPUT_FILE, --output OUTPUT_FILE
                        Write results in specified file too.
  -v, --verbose         Turn on debug mode
  --version             Show version and exit

Actions

-t info

Retrieve server information without credentials using -t info. Usefull to get the FQDN and the functionality levels.

$ ./ldapsearch-ad.py -l 192.168.56.20 -t info
### Server infos ###
[+] Forest functionality level = Windows 2012 R2
[+] Domain functionality level = Windows 2012 R2
[+] Domain controller functionality level = Windows 2012 R2
[+] rootDomainNamingContext = DC=evilcorp,DC=lab2
[+] defaultNamingContext = DC=evilcorp,DC=lab2
[+] ldapServiceName = evilcorp.lab2:mtldc1$@EVILCORP.LAB2
[+] naming_contexts = ['DC=evilcorp,DC=lab2', 'CN=Configuration,DC=evilcorp,DC=lab2', 'CN=Schema,CN=Configuration,DC=evilcorp,DC=lab2', 'DC=DomainDnsZones,DC=evilcorp,DC=lab2', 'DC=ForestDnsZones,DC=evilcorp,DC=lab2']

-t whoami

Check authentication using -t whoami. Usefull to attempt an authentication to verify a password.

$ ./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u bbilly -p 'P@$$w0rd' -t whoami
### Result of "whoami" command ###
[+] u:EVILCORP\bbilly

-t trusts

List trusts attributes using -t trusts (user account needed). Usefull to get a full view of the Active Directory overall architecture.

$ ./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -p 'P@$$word' -t trusts
### Result of "trusts" command ###
[+] + fra.evilcorp.lab2 (FRA)
[+] |__ trustAttributes = ['TRUST_ATTRIBUTE_WITHIN_FOREST']
[+] |__ trustDirection = Bidirectional
[+] |__ trustType = The trusted domain is a Windows domain running Active Directory.
[+] |__ trustPartner = fra.evilcorp.lab2
[+] |__ securityIdentifier = S-1-5-21-2894840767-735700-3593130334
[+] |__ whenCreated = 2019-03-09 04:57:15+00:00
[+] |__ whenChanged = 2019-03-09 04:57:15+00:00
[+] + total.lab2 (TOTAL)
[+] |__ trustAttributes = []
[+] |__ trustDirection = Outbound
[+] |__ trustType = The trusted domain is a Windows domain running Active Directory.
[+] |__ trustPartner = total.lab2
[+] |__ securityIdentifier = S-1-5-21-2894840767-735700-3503349313
[+] |__ whenCreated = 2018-11-05 11:51:18+00:00
[+] |__ whenChanged = 2022-02-11 20:23:40+00:00

-t pass-pols

List password policies using -t pass-pols (user account needed for default password policy / admin account needed for fine grained password policies). Usefull to prepare our next password spraying attack ;)

$ ./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -p 'P@$$word' -t pass-pols
### Result of "pass-pols" command ###
[+] Default password policy:
[+] |__ Minimum password length = 10
[+] |__ Password complexity = Enabled
[*] |__ Lockout threshold = 12
[+] |__   Lockout duration = 30 minutes, 0 seconds
[+] |__   Lockout observation window = 30 minutes, 0 seconds
[*] |__ Password history length = 8
[+] |__ Max password age = 120 days, 0 hours, 0 minutes, 0 seconds
[+] |__ Min password age = 0 seconds
[+] No fine grained password policy found (high privileges are required).

-t admins

Show the admins (members of domain admins, enterprise admins, administrators, and their french equivalent) and their most interesting flags using -t admins (user account needed). Usefull to find juicy targets.

$ ./ldapsearch-ad.py -l 192.168.56.2 -d evilcorp.lab2 -u jjohnny -p 'P@$$word' -t admins
### Result of "admins" command ###
[+] All members of group "Administrateurs":
[*]     admbilly (DONT_EXPIRE_PASSWORD)
[+]     Administrateur
[+] All members of group "Admins du domaine":
[+]     Administrateur
[+] All members of group "Administrateurs de l’entreprise":
[*]     admbilly (DONT_EXPIRE_PASSWORD)

-t kerberoast

Show the user accounts vunerable to kerberoast attacks.

$ ./ldapsearch-ad.py -l 192.168.56.2 -d evilcorp.lab2 -u jjohnny -p 'P@$$word' -t kerberoast
### Result of "kerberoast" command ###
[*] admDupond : MSSQLSvc/srv-db-01.evilcorp.lab2:18739, MSSQLSvc/srv-db-01.evilcorp.lab2:65239
[*] admDurand : HTTP/192.168.56.212, HTTP/srv-web-01.evilcorp.lab2, HTTP/srv-web-01

References:

-t asreproast

Show the user accounts vunerable to asreproast attacks.

I cannot create the proper documentation for the moment.

$ ./ldapsearch-ad.py -l 192.168.56.2 -d evilcorp.lab2 -u jjohnny -p 'P@$$word' -t asreproast
### Result of "asreproast" command ###
TO BE COMPLETED

-t search-spn

Search of account having specific XXX. It is possible to search for services starting with the -s parameter.

Example to look for HTTP services:

$ ./ldapsearch-ad.py -l 192.168.56.2 -d evilcorp.lab2 -u jjohnny -p 'P@$$word' -t search-spn -s 'http'
### Result of "search-spn" command ###
[*] admDupond : HTTP/192.168.56.221, HTTP/srv-web-01.evilcorp.lab2, HTTP/srv-web-01
[*] admDeschamps : HTTP/192.168.56.222, HTTP/srv-web-02.evilcorp.lab2, HTTP/srv-web-02

-t goldenticket

-t goldenticket retrieves the last time the krbtgt password changed.

$ ./ldapsearch-ad.py -l 192.168.56.2 -d evilcorp.lab2 -u jjohnny -p 'P@$$word' -t goldenticket
### Result of "goldenticket" command ###
[+] krbtgt password changed at 2021-02-26 19:25:41

-t search-delegation

-t search-delegation list all the account which are trusted for delegation (TRUSTED_FOR_DELEGATION flag set).

$ ./ldapsearch-ad.py -l 192.168.56.2 -d evilcorp.lab2 -u jjohnny -p 'P@$$word' -t search-delegation
### Result of "search-delegation" command ###
[*] srv-ad-01$
[*] srv-ad-02$
[*] admbilly

References:

-t createsid

Get info about createsid from ms-ds-creatorsid.

I need to fully understand what it looks for before making this documentation.

-t all

Retrieve all interesting information with a simple user account using -t all.

$ ./ldapsearch-ad.py -l 192.168.56.2 -d evilcorp.lab2 -u jjohnny -p 'P@$$word' -t all
### Server infos ###
[+] Forest functionality level = Windows 2012 R2
[+] Domain functionality level = Windows 2012 R2
[+] Domain controller functionality level = Windows 2012 R2
[+] rootDomainNamingContext = DC=evilcorp,DC=lab2
[+] defaultNamingContext = DC=evilcorp,DC=lab2
[+] ldapServiceName = evilcorp.lab2:win2012-dc01$@EVILCORP.LAB2
[+] naming_contexts = ['DC=evilcorp,DC=lab2', 'CN=Configuration,DC=evilcorp,DC=lab2', 'CN=Schema,CN=Configuration,DC=evilcorp,DC=lab2', 'DC=DomainDnsZones,DC=evilcorp,DC=lab2', 'DC=ForestDnsZones,DC=evilcorp,DC=lab2']
### Result of "trusts" command ###
[+] + fra.evilcorp.lab2 (FRA)
[+] |__ trustAttributes = ['TRUST_ATTRIBUTE_WITHIN_FOREST']
[+] |__ trustDirection = Bidirectional
[+] |__ trustType = The trusted domain is a Windows domain running Active Directory.
[+] |__ trustPartner = fra.evilcorp.lab2
[+] |__ securityIdentifier = S-1-5-21-2894840767-735700-3593130334
[+] |__ whenCreated = 2019-03-09 04:57:15+00:00
[+] |__ whenChanged = 2019-03-09 04:57:15+00:00
[+] + total.lab2 (TOTAL)
[+] |__ trustAttributes = []
[+] |__ trustDirection = Outbound
[+] |__ trustType = The trusted domain is a Windows domain running Active Directory.
[+] |__ trustPartner = total.lab2
[+] |__ securityIdentifier = S-1-5-21-2894840767-735700-3503349313
[+] |__ whenCreated = 2018-11-05 11:51:18+00:00
[+] |__ whenChanged = 2022-02-11 20:23:40+00:00
### Result of "pass-pols" command ###
[+] Default password policy:
[+] |__ Minimum password length = 10
[+] |__ Password complexity = Enabled
[*] |__ Lockout threshold = 12
[+] |__   Lockout duration = 30 minutes, 0 seconds
[+] |__   Lockout observation window = 30 minutes, 0 seconds
[*] |__ Password history length = 8
[+] |__ Max password age = 120 days, 0 hours, 0 minutes, 0 seconds
[+] |__ Min password age = 0 seconds
[+] No fine grained password policy found (high privileges are required).
### Result of "admins" command ###
[+] All members of group "Administrateurs":
[*]     admbilly (DONT_EXPIRE_PASSWORD)
[+]     Administrateur
[+] All members of group "Admins du domaine":
[+]     Administrateur
[+] All members of group "Administrateurs de l’entreprise":
[*]     admbilly (DONT_EXPIRE_PASSWORD)
### Result of "kerberoast" command ###
[*] admDupond : MSSQLSvc/srv-db-01.evilcorp.lab2:18739, MSSQLSvc/srv-db-01.evilcorp.lab2:65239
[*] admDurand : HTTP/192.168.56.212, HTTP/srv-web-01.evilcorp.lab2, HTTP/srv-web-01
### Result of "asreqroast" command ###
### Result of "goldenticket" command ###
[+] krbtgt password changed at 2021-02-26 19:25:41

-t show-user

Show the most interesting attributes of a user using -t show-user (user account needed). Usefull to get more information about a juicy target.

$ ./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -p 'P@$$word' -t show-user -s '(samaccountname=adm*)'
### Result of "show-user" command ###
[+] admbilly
[+] |__ type: user
[+] |__ description = ['First admin']
[*] |__ The adminCount is set to 1
[+] |__ userAccountControl = ACCOUNTDISABLE, NORMAL_ACCOUNT
[+] |__ sAMAccountType = SAM_USER_OBJECT
[+] |__ memberOf = grp-gods, grp-passwords-never-expire
[+] admwilly
[+] |__ type: user
[+] |__ displayName = admwilly
[+] |__ description = ['Second admin']
[*] |__ The adminCount is set to 1
[+] |__ userAccountControl = NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
[+] |__ sAMAccountType = SAM_USER_OBJECT
[+] |__ memberOf = grp-gods, grp-vmware-admins

or even computers or groups. Everything depend of the search parameter -s.

$ ./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -p 'P@$$word' -t show-user -s '(samaccountname=srv-ad*)'
### Result of "show-user" command ###
[+] srv-ad-01$
[+] |__ type: computer
[+] |__ description = ['First DC']
[+] |__ userAccountControl = SERVER_TRUST_ACCOUNT, TRUSTED_FOR_DELEGATION
[+] |__ sAMAccountType = SAM_MACHINE_ACCOUNT
[+] srv-ad-02$
[+] |__ type: computer
[+] |__ displayName = SVP1-AD01273-1$
[+] |__ description = ['First RODC']
[+] |__ userAccountControl = WORKSTATION_TRUST_ACCOUNT, TRUSTED_TO_AUTH_FOR_DELEGATION, PARTIAL_SECRETS_ACCOUNT
[+] |__ sAMAccountType = SAM_MACHINE_ACCOUNT
[+] |__ memberOf = grp-rodc

$ ./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -p 'P@$$word' -t show-user -s '(cn=bad admins)'
### Result of "show-user" command ###
[+] bad_admins
[+] |__ type: group
[+] |__ description = ['Bad Admins']
[+] |__ sAMAccountType = SAM_GROUP_OBJECT
[+] |__ memberOf = Domain Admins

-t member-of

List the users member of a specified group.

Exception the search filter argument have to be the groupe CN, instead of a valid LDAP filter as usual.

$ ./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -p 'P@$$word' -t member-of -s 'grp-bad-admins' -z 2000
### Result of "member-of" command ###
[+] All members of group "grp-bad-admins":
[+]     wwilly
[+]     jjohnny
[+]     jjacky
[+]     jdupond
[…]

Note: it is also possible to look for multiple groups by using wildcard (e.g. -s 'grp-admin*'), but it is limited to the first 100 groups for the moment.

-t search-foreign-security-principals

List Security Principals (Users, Computers and groups) in external or forest trusts that are members of domain local scope groups in the current forest by requesting the global catalog on port 3268.

$ ./ldapsearch-ad.py -l 192.168.56.20 -n 3268 -d evilcorp -u jjohnny -p 'P@$$word' -t search-foreign-security-principals
### Result of "search-foreign-security-principals" command ###
[+] name = S-1-5-4
[+] |___objectSid = S-1-5-4
[+] |___distinguishedName = CN=S-1-5-4,CN=ForeignSecurityPrincipals,DC=evilcorp>
[+] |___objectClass = ['top', 'foreignSecurityPrincipal']}
[…]

Authenticate with credentials in a file instead of in the commandline

./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u username.txt -p username.txt -t info
./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u username.txt -H hash.txt -t info

Authenticate with an NTLM hash instead of a password

Because sometimes the compromise is still on-going.

./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -hashes :32ed87bdb5fdc5e9cba88547376818d4 -t show-admins

Append output to a file

It is possible to append the output in a file by using -o <filename>. For instance:

./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -p 'P@$$word' -t info -o info_from_DC.log

Search for any information using the powerful ldap filter syntax with -t search: Only a few attributes are not shown because they seem useless, and a few other are interpreted.

It is possible to set the attributes to retrive at the end of the command line to reduce the search load.

$ ./ldapsearch-ad.py -l 192.168.56.20 -d evilcorp -u jjohnny -p 'P@$$word' -t search -s '(samaccountname=bbilly)' cn samaccountname useraccountcontrol memberof displayname samaccounttype
### Result of "search" command ###
[+] |__ cn = bbilly
[+] |__ displayName = BILLY Billy
[+] |__ memberOf = ['CN=grp-gods,OU=Admins,DC=evilcorp,DC=lab2', 'CN=grp-another-example,OU=Groups,DC=evilcorp,DC=lab2']
[+] |__ sAMAccountName = bbilly
[+] |__ sAMAccountType = SAM_USER_OBJECT
[+] |__ userAccountControl = NORMAL_ACCOUNT