tools

GetADUsers.py Inpacket: Enumerate Active Directory Users

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):

bash
nmap -p 88 --script=krb5-enum-users --script-args krb5-enum-users.realm='corp.local',userdb=usernames.txt 10.10.10.5

Official 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.

InformationSupported
sAMAccountName (username)Yes
Email (mail)Yes
pwdLastSetYes
lastLogonYes
Account DescriptionNo
GroupsNo
Group membersNo
SPN (Kerberoasting)No
userAccountControl itineraryNo
DONT_REQUIRE_PREAUTH (AS-REP Roasting)No
badPwdCountNo
adminCountNo
OU and Distinguished NameNo

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 #

bash
impacket-GetADUsers [opzioni] dominio[/utente:password]
OptionDescription
-user USERNAMERequests data for a specific user
-allReturns all users, including disabled and email-less accounts
-hashes LM:NTPass the Hash
-kKerberos authentication (ccache via KRB5CCNAME)
-no-passWith -k, does not ask for password
-aesKey KEYAES key (128 or 256 bits) for Kerberos authentication
-dc-ip IPDomain Controller IP
-dc-host HOSTNAMEDomain Controller hostname
-tsAdds timestamps to log messages
-debugDiagnostic 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.

bash
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.

bash
impacket-GetADUsers -all -dc-ip 10.10.10.5 corp.local/user:'Password123!'

Enumerate a specific user #

bash
# 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 #

bash
# 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/user

Kerberos with ccache #

bash
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:

bash
impacket-GetADUsers -all -dc-ip 10.10.10.5 corp.local/user:'Password123!' | tee /tmp/getadusers.txt

Output and how to read it #

text
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:

IndicatorWhat can you conclude?
pwdLastSet very oldThe 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 accountsPossible 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 presentAccount 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.

bash
# 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:

bash
# 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 #

text
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 / BloodHound

GetADUsers vs. alternatives #

toolProtocolDetailNote
GetADUsers.pyLDAPMinimum (4 attributes)Fast, zero installation, clean output
ldapsearchLDAPEventingCustomizable queries, raw output
blood houndLDAP + SMBVery highRelation graph, find attack path
nxc ldapLDAPHighGroups, descriptions, free queries, forms

Common mistakes #

ErrorProbable causeVerifica
invalidCredentialsIncorrect password, hash, or domainCheck the format dominio/utente
strongerAuthRequiredThe DC requires a secure LDAP channelImpacket automatically retries LDAPS when it receives this error — no flag is needed -port manual (does not exist)
NTLM negotiation failedNTLM disabled on DCUse Kerberos with -k
Error on hostname/KDCInconsistent Kerberos IP and hostnameUse together -dc-host e -dc-ip
Empty output without -allNo enabled account has mail enhancedAdd -all
KRB_AP_ERR_SKEWClock not synchronized with DCSynchronize the time
Ticket not foundMissing cache or inconsistent principalCheck it out KRB5CCNAME e klist
Connection refusedLDAP/LDAPS filtered or incorrect DCCheck 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 lastLogon su 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 #

bash
# 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 --groups

FAQ #

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 #

For use in authorized environments only.

#user-enumeration #windows-enumeration #kerberos #pwdlastset #lastlogon

leave a message

I'm not a robot