GetADUsers.py Inpacket: Enumerate Active Directory Users

Impacket's GetADUsers.py guide to enumerate Active Directory users via LDAP with password, NTLM hash, AES or Kerberos key and parse pwdLastSet and lastLogon.
- Posted on 2026-07-24
- Reading time: 6 min
GetADUsers.py — Enumerate Active Directory Users with Impacket #
GetADUsers.py is part of Packet and queries a Domain Controller via LDAP to obtain a list of users with four attributes: account name, email, last password change date, and last logon date. It's a deliberately minimal tool—no groups, no descriptions, no SPNs. It's useful as a quick first step after obtaining your first domain credentials, not as a replacement for BloodHound or a full LDAP query.
Note: GetADUsers.py It always requires valid credentials, even minimal ones. If you don't have any yet, you can first try unauthenticated username enumeration via Kerberos (port 88):
nmap -p 88 --script=krb5-enum-users --script-args krb5-enum-users.realm='corp.local',userdb=usernames.txt 10.10.10.5Official reference: fortra/impacket — GetADUsers.py (verified identical also on the stable release impacket_0_13_1 )
What it really lists (and what it doesn't) #
The code performs a paginated LDAP search by requesting exactly four attributes: sAMAccountName, pwdLastSet, mail, lastLogon. Nothing else.
| Information | Supported |
|---|---|
sAMAccountName (username) | Yes |
Email (mail) | Yes |
pwdLastSet | Yes |
lastLogon | Yes |
| Account Description | No |
| Groups | No |
| Group members | No |
| SPN (Kerberoasting) | No |
userAccountControl itinerary | No |
DONT_REQUIRE_PREAUTH (AS-REP Roasting) | No |
badPwdCount | No |
adminCount | No |
| OU and Distinguished Name | No |
If you need any of this “No” information, GetADUsers.py is not the right tool — you need other tools (see them later in this article).
Syntax and real options #
impacket-GetADUsers [opzioni] dominio[/utente:password]| Option | Description |
|---|---|
-user USERNAME | Requests data for a specific user |
-all | Returns all users, including disabled and email-less accounts |
-hashes LM:NT | Pass the Hash |
-k | Kerberos authentication (ccache via KRB5CCNAME) |
-no-pass | With -k, does not ask for password |
-aesKey KEY | AES key (128 or 256 bits) for Kerberos authentication |
-dc-ip IP | Domain Controller IP |
-dc-host HOSTNAME | Domain Controller hostname |
-ts | Adds timestamps to log messages |
-debug | Diagnostic output |
They do not exist -users, -groups, -port o -outputfile in this version of the tool — if you see them mentioned elsewhere, they are wrong.
Default behavior vs. -all
#
This distinction is the most important thing to understand before using the tool.
Without -all: LDAP filter requires users with the attribute mail enhanced e not disabled (excludes the flag UF_ACCOUNTDISABLE). Short or empty output doesn't mean the query failed—it just means that few (or no) accounts have email set up.
impacket-GetADUsers -dc-ip 10.10.10.5 corp.local/user:'Password123!'With -all: The filter removes the email and disabled requirement — returns all users, including disabled and email-less accounts.
impacket-GetADUsers -all -dc-ip 10.10.10.5 corp.local/user:'Password123!'Enumerate a specific user #
# Utile ad esempio per controllare rapidamente il pwdLastSet di krbtgt
impacket-GetADUsers -user krbtgt -all -dc-ip 10.10.10.5 corp.local/user:'Password123!'Authentication: password, NTLM hash, AES key #
# Pass-the-Hash
impacket-GetADUsers -all -hashes ':NTHASH' -dc-ip 10.10.10.5 corp.local/user
# Con chiave AES
impacket-GetADUsers -all -aesKey 'AES256_KEY' -dc-ip 10.10.10.5 corp.local/userKerberos with ccache #
export KRB5CCNAME=/path/to/user.ccache
impacket-GetADUsers -all -k -no-pass \
-dc-host DC01.corp.local \
-dc-ip 10.10.10.5 \
corp.local/user-dc-host This matters in Kerberos scenarios because the tool must construct the LDAP principal with a consistent hostname — if you omit it, it uses the FQDN part of the passed target, which may not match the DC's actual host.
Save the output #
-outputfile It doesn't exist. Use redirect or tee:
impacket-GetADUsers -all -dc-ip 10.10.10.5 corp.local/user:'Password123!' | tee /tmp/getadusers.txtOutput and how to read it #
Name Email PasswordLastSet LastLogon
-------------------- ----------------- ------------------- -------------------
Administrator 2024-01-15 10:23:11 2024-03-01 09:00:00
john.doe jdoe@corp.local 2021-08-01 08:00:00 2024-02-28 15:30:00
svc_sql 2019-03-10 12:00:00 <never>Correct interpretation of indicators:
| Indicator | What can you conclude? |
|---|---|
pwdLastSet very old | The password hasn't been changed in a long time — it doesn't prove it's weak, just that it's not rotated |
pwdLastSet = <never> | Password never set in the usual way, or flag that requires change at next logon |
Timestamp pwdLastSet identical on multiple accounts | Possible provisioning at the same time — does not prove that the password is the same |
lastLogon = <never> | Il Domain Controller queried has not registered a login — does not mean inactive account |
Name starting with svc_ | Possible service account, to be verified with a real SPN check |
| Email present | Account with attribute mail enhanced |
Beware of lastLogon: this attribute It is not replicated between Domain ControllersThe value shown is only the one recorded by the DC you queried—the user could have authenticated through another DC at a different time. For reliable data, you would need to query all DCs and get the most recent value.
A legitimate use of <never>: It doesn't tell you anything about AS-REP Roasting, but it's still useful information in other ways. Accounts like Guest, krbtgt, or old service accounts with lastLogon <never> They don't have a behavioral baseline — if you decide to rely on one of these for persistence, occasional use is less likely to stand out from a “normal” pattern (which simply doesn't exist for that account).
From GetADUsers to Kerberoasting and AS-REP Roasting #
A name like svc_sql It's just a clue, not proof. An account is Kerberoastable only if he has a servicePrincipalName — GetADUsers.py doesn't check it. You need a dedicated tool to check SPNs (e.g., Impacket's GetUserSPNs.py).
Similarly, an account is AS-REP Roastable only if it has the flag DONT_REQUIRE_PREAUTH active — GetADUsers.py doesn't check this either. LastLogon = <never> It is not an indicator of AS-REP Roasting, they are two completely independent things.
# 1. Enumerazione generale
impacket-GetADUsers -all -dc-ip 10.10.10.5 corp.local/user:'Password123!' | tee getadusers.txt
# 2. Verifica reale degli account senza pre-authentication (AS-REP Roasting)
impacket-GetNPUsers corp.local/user:'Password123!' -dc-ip 10.10.10.5 -request -format hashcat
# 3. Verifica reale degli account con SPN (Kerberoasting) — vedi anche [Kerberoasting](https://hackita.it/articoli/kerberos/)
impacket-GetUserSPNs -dc-ip 10.10.10.5 corp.local/user:'Password123!'Groups and descriptions: the right tools #
GetADUsers.py doesn't list groups or descriptions . Other tools are needed for that:
# Gruppi e membri, con netexec
nxc ldap 10.10.10.5 -u user -p 'Password123!' --groups
nxc ldap 10.10.10.5 -u user -p 'Password123!' --groups 'Domain Admins'
# Descrizioni utente (dove a volte finiscono password dimenticate)
nxc ldap 10.10.10.5 -u user -p 'Password123!' -M get-desc-users
# Query LDAP libera su attributi specifici
nxc ldap 10.10.10.5 -u user -p 'Password123!' --query '(objectCategory=person)' 'sAMAccountName description'For a complete picture of the relationships between users, groups, and permissions in the domain, the right tool remains BloodHound ; for custom line-by-line LDAP queries, ldapsearch.
Realistic offensive workflow #
GetADUsers.py -all
│
├── Elenco username
│ └── Password spraying, solo dopo aver verificato la lockout policy
│
├── Utenti con naming da servizio (svc_*)
│ └── GetUserSPNs.py → verifica SPN reale prima di dare per Kerberoastable
│
├── Verifica pre-authentication
│ └── GetNPUsers.py → individua i veri candidati AS-REP Roastable
│
├── Descrizioni e attributi aggiuntivi
│ └── ldapsearch / nxc --query / nxc -M get-desc-users
│
└── Gruppi e privilegi
└── nxc --groups / BloodHoundGetADUsers vs. alternatives #
| tool | Protocol | Detail | Note |
|---|---|---|---|
GetADUsers.py | LDAP | Minimum (4 attributes) | Fast, zero installation, clean output |
| ldapsearch | LDAP | Eventing | Customizable queries, raw output |
| blood hound | LDAP + SMB | Very high | Relation graph, find attack path |
nxc ldap | LDAP | High | Groups, descriptions, free queries, forms |
Common mistakes #
| Error | Probable cause | Verifica |
|---|---|---|
invalidCredentials | Incorrect password, hash, or domain | Check the format dominio/utente |
strongerAuthRequired | The DC requires a secure LDAP channel | Impacket automatically retries LDAPS when it receives this error — no flag is needed -port manual (does not exist) |
NTLM negotiation failed | NTLM disabled on DC | Use Kerberos with -k |
| Error on hostname/KDC | Inconsistent Kerberos IP and hostname | Use together -dc-host e -dc-ip |
Empty output without -all | No enabled account has mail enhanced | Add -all |
KRB_AP_ERR_SKEW | Clock not synchronized with DC | Synchronize the time |
| Ticket not found | Missing cache or inconsistent principal | Check it out KRB5CCNAME e klist |
| Connection refused | LDAP/LDAPS filtered or incorrect DC | Check ports 389/636 with Nmap |
Detection and mitigation #
What the monitor can notice:
- Massive LDAP query of all user objects from an account that normally does not administer
- Repeated queries on the same attributes from unusual workstations
- Event ID 1644 (Field Engineering / Expensive and Inefficient Searches) — when enabled, logs expensive or high-volume LDAP searches, and is more specific than 4662 alone for detecting a mass user dump
- Event ID 4662 — but only if a SACL is configured on the affected objects, otherwise it is not generated
- Microsoft Defender for Identity correlates LDAP reconnaissance activity across multiple signals, not a single query
- Honey-token accounts (fake Domain Admin or service accounts) used as bait: any LDAP/Kerberos/NTLM query that hits them is a high-confidence signal
Realistic mitigations:
- Don't leave passwords or operating information in account descriptions
- Verify and remove truly inactive accounts by checking
lastLogonsu all the DCs, not just one - Restrict read rights on the most sensitive attributes where the architecture allows it
- LDAP signing and channel binding protect against tampering and relaying — they do not prevent a normal authenticated LDAP query like this
Cheat sheet #
# Comportamento default (solo utenti abilitati con email)
impacket-GetADUsers -dc-ip DC_IP corp.local/user:pass
# Tutti gli utenti (inclusi disabilitati e senza email)
impacket-GetADUsers -all -dc-ip DC_IP corp.local/user:pass
# Utente specifico
impacket-GetADUsers -user krbtgt -all -dc-ip DC_IP corp.local/user:pass
# Pass-the-Hash
impacket-GetADUsers -all -hashes :NThash -dc-ip DC_IP corp.local/user
# Chiave AES
impacket-GetADUsers -all -aesKey AES256KEY -dc-ip DC_IP corp.local/user
# Kerberos
export KRB5CCNAME=ticket.ccache
impacket-GetADUsers -all -k -no-pass -dc-host DC01.corp.local -dc-ip DC_IP corp.local/user
# Salva output
impacket-GetADUsers -all -dc-ip DC_IP corp.local/user:pass | tee getadusers.txt
# Segue: verifica SPN e pre-auth reali
impacket-GetUserSPNs -dc-ip DC_IP corp.local/user:pass
impacket-GetNPUsers corp.local/user:pass -dc-ip DC_IP -request -format hashcat
# Gruppi (non fatto da GetADUsers)
nxc ldap DC_IP -u user -p pass --groupsFAQ #
What is GetADUsers.py used for?
Enumerate Active Directory user accounts via LDAP and display name, email, pwdLastSet e lastLogon. Does not enumerate groups, descriptions, SPNs, or AS-REP flags.
Does GetADUsers.py also enumerate groups?
No. For groups you need another tool - for example nxc ldap --groups, or BloodHound for the full relationship picture.
What is the difference between the basic command and -all?
Without -all You only see enabled accounts with email set. With -all See all users, including disabled and email-less users.
lastLogon Is it reliable for determining if an account is inactive?
No, because it's not replicated between domain controllers. The value only reflects the queried DC—the user may have authenticated elsewhere.
How do I find Roastable AS-REP accounts?
Not with GetADUsers.py. You need GetNPUsers.py, which actually checks the flag. DONT_REQUIRE_PREAUTH.
How do I find Kerberoastable accounts?
Not based on the name (svc_*). It is necessary to check the presence of a servicePrincipalName real, with GetUserSPNs.py.
Related Articles #
- Impacket: complete suite
- Active Directory: exploitation guide
- ldapsearch — advanced LDAP queries
- BloodHound — Map AD and find attack paths
- Kerber-roasting
- AS-REP Roasting
- Pass the Hash
For use in authorized environments only.








