LDAPHunter - LDAP Enumeration Tool for Pentesters
A Python tool for automating LDAP enumeration against Active Directory during offensive engagements.
Why I Built It
If you have run a few Active Directory engagements, you already know the rhythm. You land on the internal network, you fire up a handful of enumeration commands, and ten minutes later you are staring at five terminal windows, pasting filters into ldapsearch, re-running crackmapexec with slightly different flags, and copying output into a notes file that grows scarier by the hour.
Most of that is mechanical. Same queries, same attributes, same tables. I got tired of doing it by hand, so I built LDAPHunter: a Python tool that runs the common LDAP enumeration checks against Active Directory and prints the results as clean tables you can paste straight into a report.
Repository: https://github.com/GhnimiWael/LDAPHunter
What It Does
LDAPHunter targets the information you actually care about on a pentest:
- Users: SAM account names, display names, description fields, UAC flags, last logon timestamps, password-never-expires flags.
- Groups and privileged memberships: Domain Admins, Enterprise Admins, Schema Admins, Backup Operators, and anything else that lets you pivot.
- Organizational Units (OUs): the shape of the directory, useful when you need to target a specific business unit.
- Password policies: length, lockout threshold, age. These are the values you need before you even think about password spraying.
- Delegation flags: unconstrained delegation in particular, because those accounts are gifts.
- Non-standard attributes: sometimes admins drop credentials or connection strings into
descriptionor custom fields. The tool surfaces those so you can skim them quickly.
It is not meant to replace BloodHound or PowerView. It runs before them, gives you a snapshot of what is in the directory, and feeds the next steps of the engagement.
Installation
Requirements are light. Python 3 plus two libraries:
1
pip install ldap3 prettytable
Then clone the repo:
1
2
git clone https://github.com/GhnimiWael/LDAPHunter.git
cd LDAPHunter
That is it. No Docker, no virtualenv gymnastics. It runs on Kali, on a WSL shell, on a jumpbox.
Usage
LDAPHunter supports three authentication modes. Pick the one that matches what you have.
Authenticated bind
If you already have creds (phished, sprayed, found in a file share), pass them in:
1
python ldap_hunter.py -s 10.10.10.100 -d domain.lab -u 'domain\user' -P 'Password123!'
The -u flag accepts both DOMAIN\user and [email protected] formats. If one fails, try the other. Some DCs prefer one style over the other depending on how they are configured.
Anonymous bind
Not every DC allows this, but when it does, it can save you an hour during the first phase of an internal pentest:
1
python ldap_hunter.py -s 10.10.10.100 -d domain.lab --no-auth
If the server refuses the bind, you will see it quickly and move on to credential acquisition.
LDAPS (encrypted)
When port 389 is filtered but 636 is open, use LDAPS:
1
python ldap_hunter.py -s 10.10.10.100 -d domain.lab -u 'domain\user' -P 'Password123!' --ssl
What You Get
Output is tabular. Users, groups, OUs, password policies, and delegation findings are printed as readable tables. A few notes on what the tool highlights:
- Accounts with
PasswordNeverExpiresget flagged. These are usually service accounts, and service accounts are usually interesting. LastLogontimestamps help you pick targets that are actively used versus stale accounts that may not be monitored.- When a
descriptionor custom attribute contains something suspicious (long strings, what looks like credentials, connection URIs), the tool calls it out so you do not miss it while scrolling through.
Troubleshooting
Common issues I have hit, and what to check:
- Connection refused on 389: verify the host is actually a DC and the port is open. Try
--sslto fall back to 636. - Attribute errors on custom schemas: some environments extend the schema with attributes that break generic lookups. The tool tries to auto-detect, but if you see one specific attribute failing, open an issue and paste the schema line.
- Anonymous bind rejected: expected on most hardened DCs. Grab creds and retry with
-u / -P. - Auth fails with a password you know is correct: try the other username format (
user@domainvsDOMAIN\user). Also confirm the account is not locked out from prior tooling.
Roadmap
Things on the backlog that I want to ship:
- Kerberoasting output built in (currently you can find SPN-bearing accounts, but you still have to run Rubeus or
GetUserSPNs.pyseparately). - AS-REP roasting identification (
DONT_REQ_PREAUTHflag). - SID bruteforce mode for when only anonymous LDAP is allowed but RID-cycle is worth a shot.
- JSON and CSV output so the results can be fed into BloodHound ingestion or report automation.
If you hit a bug or want a feature, the issue tracker is open.
Get It
- Repo: https://github.com/GhnimiWael/LDAPHunter
- Language: Python 3
- License: see the repo
If it saves you five minutes on your next engagement, it has paid for itself.
