
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 queryuser one at a time. With no built-in RID-cycling loop it
can't recover computers or work around restricted enumeration. See
Doing it by hand.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.
| 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. |
# 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/: