
One-pass anonymous Active Directory enumeration over SAMR and LSARPC — null session, no credentials, with structured reusable output.
A single-pass Active Directory enumerator for an anonymous (null) session,
using the \samr and \lsarpc named pipes. No credentials required.
It doesn't do anything the classic tools can't. What it does is gather it all in
one run and lay the results out cleanly. On an engagement you land with no
domain account, find the DC through DNS, and want the whole picture, accounts,
per-user detail, groups, policy, and the LSA/DNS bits, without stitching together
rpcclient sessions or several separate scripts and then parsing their stdout by
hand. This does that pass for you and writes structured, reusable output:
manifest.json, ready-to-use userlist.txt /
computerlist.txt, and a saved RID list you can reuse instead of brute-forcing
twice.Please only run this against systems you own or are explicitly authorized to test. Enumerating someone else's directory without permission is not okay, and depending on where you are it may be illegal.
There are two versions that behave identically. Use whichever library you have:
adnullenum.py — built on impacket.$) are
separated out from real users.It also flags interesting accounts for you: things like "password not required", "password never expires", AS-REP-roastable, unconstrained delegation, never logged on, or a description that mentions a password.
A couple of things that fall naturally out of this:
Everything here is standard anonymous SAMR/LSARPC, and the established tools already reach the same data. This is a convenience wrapper, not a new capability, so it's worth knowing the alternatives:
--rid-brute, --users,
--pass-pol) covers the same enumeration inside a much larger framework.samrdump.py (users/groups/aliases +
per-user detail) and lookupsid.py (RID cycling over LSA). No single run, no
SAMR→LSA fallback, stdout only.rpcclient exposes the underlying calls (queryuser, enumdomusers,
querydominfo, lsaquery, lookupsids, …) but interactively and stdout only.
Its enumdomusers lists standard users by name, no machine () accounts, and
full detail is one at a time. With no built-in RID-cycling loop it
can't recover computers or work around restricted enumeration. See
.What adnullenum adds is packaging: one null-session run instead of several tools, SAMR-first with an opt-in LSA fallback, curated interesting-account flags, and tidy multi-format output you can hand straight to the next step or re-open later. If that workflow fits how you work, use it; if you'd rather a battle-tested tool, reach for enum4linux-ng or NetExec.
You need Python 3.9+ and one SMB library. A virtual environment keeps it tidy:
# clone / copy the files, then from the project folder:
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install impacket # for adnullenum.py
That's it. Run python3 adnullenum.py --help to see everything.
Everything runs through a single --mode. The default is recon, which is the
safe thing to run first.
# 1. Is anonymous access open, and how big is this domain?
python3 adnullenum.py 10.0.0.10
# 2. Grab everything
python3 adnullenum.py 10.0.0.10 --mode full
# 3. Just the account inventory, wider net for a big domain
python3 adnullenum.py 10.0.0.10 --mode rids --start-rid 1000 --max-rid 200000
# 4. Go slower to stay quiet
python3 adnullenum.py 10.0.0.10 --mode users --sleep 1 --jitter 40
# 5. Also write JSON (txt and csv are always written)
python3 adnullenum.py 10.0.0.10 --mode full --json
If the domain won't let you list users directly (common), the tool falls back to RID cycling: it asks the DC to translate account IDs to names in batches of 1000. By default it sweeps upward until it has found everything the DC says exists, or until 5000 IDs in a row come back empty. You can pin it to an exact range instead:
python3 adnullenum.py 10.0.0.10 --mode rids --range 500-50000
Some DCs refuse anonymous SAMR entirely (enumdomains and RID translation come
back ACCESS_DENIED). The run does not give up:
\lsarpc, which frequently answers when SAMR does not.--lsa-fallback, the tool retries the same sweep over LSA
LsarLookupSids using the domain SID, which still gives you the account
inventory (names and types, including computers). It is off by default because
it is a second brute-force pass; without the flag the run stops at recon and
says so.Finding the accounts is the slow part, so the tool saves the list (rids.txt) in
every run's output folder. Point a later run at that folder with --session and
it skips the brute and reuses what you already found:
python3 adnullenum.py 10.0.0.10 --mode rids
# -> output/20260917-1530_10.0.0.10_rids/
python3 adnullenum.py 10.0.0.10 --mode users \
--session output/20260917-1530_10.0.0.10_rids
--session takes the folder of a previous run. (--rid-file does the same
from a plain text file with one RID per line.)
Each run drops a timestamped folder under output/:
output/20260917-1530_10.0.0.10_full/
├── rids.txt # the account IDs found (reuse with --session)
├── manifest.json # summary: counts, interesting accounts, run details
├── access_denied.txt # only if something was denied/restricted (with a timestamp)
├── output
├──── lists/ # ready-to-use lists (see below)
├──── txt/ # easy to read
└──── csv/ # easy to grep / open in a spreadsheet
txt and csv are always written; add --json for a json/ folder too.
The lists/ folder holds two plain files made for feeding other tools directly:
lists/userlist.txt — drop straight into AS-REP roasting, password spraying,
or a cracker's --users list. In users/full mode it contains enabled
accounts only; in rids mode (no per-user detail) it's every user.lists/computerlist.txt — machine-account names with the $ kept (the
sAMAccountName), for pre-Windows 2000 machine-account checks.Forgot to look at them, or only ran rids and now want the lists? Point at the
run's folder and they're rebuilt from the CSVs, no network needed:
python3 adnullenum.py --lists-from output/20260917-1530_10.0.0.10_full
On screen you get a capped preview so a big domain doesn't flood your terminal
(--limit, default 100 per section) — the files always have the complete data.
Colour is on when you're at a terminal; --no-color turns it off.
A full run against a test DC (sanitized). On screen you get recon, the RID
sweep, the interesting-account summary, and the list of files written:
$ python3 adnullenum.py 10.0.0.10 --mode full
[*] Connecting to 10.0.0.10:445 anonymously ...
[+] SAM domain: CORP (S-1-5-21-1111111111-2222222222-3333333333)
...
[ COUNTS ]
Users: 43
Groups: 0
Aliases: 17
[ PASSWORD POLICY ]
Min length: 7
Max age: 42 days
Properties: COMPLEX
[*] Bruting RIDs via SAMR (chunk=1000, open-ended 500-100000, stop-after 5000)
[*] up to RID 1498: 31 resolved, gap=0
[*] stop: 5000 consecutive empty RIDs (--stop-after 5000)
[+] Resolved: 5 users, 4 computers, 22 groups, 0 other
[*] Deep-querying 5 users (skipping 4 computers)
[!] Administrator (RID 500) PWD_NEVER_EXPIRES
[!] Guest (RID 501) (disabled) PWD_NOT_REQUIRED, PWD_NEVER_EXPIRES
[!] svc_sql (RID 1104) ASREP_ROASTABLE
[+] 5 users: 3 enabled, 2 disabled, 3 interesting (full detail in the users file)
[ FILES WRITTEN in output/20260101-120000_10.0.0.10_full ]
txt/recon.txt / csv/recon.csv domain info + password/lockout policy
txt/inventory.txt / csv/inventory.csv all resolved accounts
txt/users.txt / csv/users.csv full per-user detail
txt/groups.txt / csv/groups.csv groups + aliases with members
rids.txt existing RIDs (reuse with --session)
lists/userlist.txt usernames, one per line
lists/computerlist.txt machine accounts (with $)
manifest.json run metadata + counts
A single record from txt/users.txt (the primary group and direct groups are
resolved to names):
[+] RID 500
Enabled: True
SamAccountName: Administrator
SID: S-1-5-21-1111111111-2222222222-3333333333-500
Description/AdminComment: Built-in account for administering the computer/domain
Last Logon: 2026-09-03 17:58:26+09:00
Password Last Set: 2026-01-23 05:14:35+09:00
Primary Group: Domain Users (513)
Account Control: NORMAL_ACCOUNT | PASSWORD_NEVER_EXPIRES
Logon Count: 92
Direct Domain Groups: Domain Users; Administrators; Domain Admins
>> INTERESTING: PWD_NEVER_EXPIRES
Everything here is standard SAMR/LSARPC, so you can pull the same facts one call
at a time with Samba's rpcclient. Connect anonymously first:
rpcclient -U "" -N 10.0.0.10
enumdomusers returns standard user accounts by name only; machine ($) accounts
are filtered out, and full detail means running queryuser one account at a time.
rpcclient has no built-in RID-cycling loop, so it can't recover the machine
accounts (or anything else) when server-side enumeration is restricted; you would
script lookupsids over a range yourself. It also has no lockout-policy command
and does not return the LSA DNS/forest/GUID or trusts, which is why this tool
queries PolicyDnsDomainInformation directly.
For scripted equivalents and how this tool relates to them, see How it compares.
If you're on the blue side and want to shut this down, the anonymous path is the thing to close:
RestrictAnonymousSAM / RestrictAnonymous
settings).None of this stops a logged-in user from reading the same data — that's normal and expected. It only closes the no-credentials path.
\samr, or policy/lookup calls against \lsarpc, in a short time.--sleep and --jitter can spread the calls out, alert on the total
count over a window rather than on the rate alone.--lsa-fallback to try
LSA LsarLookupSids for the inventory (see "When SAMR is locked down"). If LSA
lookups are blocked too, only recon (domain name + SID) is possible.MIT. See LICENSE.
$queryuser| Mode | What you get |
|---|
recon | Domain info, counts, password/lockout policy, and the LSA/DNS details. No brute-forcing. Just a look around. |
rids | Finds which accounts exist and sorts them into users / computers / groups. |
users | The above, plus full detail on every real user and the "interesting accounts" summary. Add --include-groups to also list each user's groups. |
groups | Domain groups and builtin aliases with their members. |
full | All of it. |
| This tool | rpcclient equivalent |
|---|
recon (domain info + counts) | querydominfo |
recon (password policy) | getdompwinfo |
recon (LSA domain + SID) | lsaquery, lsaenumsid |
rids (name/SID translation) | lookupnames <name>, lookupsids <SID> |
rids (server-side list) | enumdomusers (standard users only — no $ machine accounts) |
users (per-user detail) | queryuser <rid> or queryuser <name>, querydispinfo |
groups | enumdomgroups, querygroupmem <rid>, enumalsgroups builtin, queryaliasmem builtin <rid> |