enum4linux-ng

enum4linux-ng turns an unknown Windows or Samba host into a clear picture: which SMB shares exist, which users and groups are visible, what the password policy is, and what OS clues leak, all before you touch anything by hand. Reach for it the moment Nmap shows 139 or 445 open, whether you are triaging a CTF box, inventorying your own Samba server, or starting authorized internal enumeration. It is the Python rewrite of the classic enum4linux, with cleaner output and structured JSON/YAML exports.

Mental model: enum4linux-ng [options] TARGET. It wraps smbclient, rpcclient, and nmblookup, then parses their output into one structured report.

New to SMB enumeration? Core concepts
  • SMB / CIFS: the Windows file-sharing protocol on 445 (and legacy NetBIOS on 139)
  • Share: a named, network-exposed directory (IPC$, ADMIN$, C$, plus custom ones); a readable or writable one is the prize
  • Null / anonymous session: an unauthenticated logon (-u "" -p "", the default) showing what any stranger can see
  • RID: a relative identifier appended to the domain SID to name an account; 500 is the built-in Administrator
  • RID cycling: walking RID ranges to resolve account names when direct user enum (-U) is blocked (-R)
  • RPC: the remote procedure calls (via rpcclient) behind the users, groups, policy, and OS queries
  • Workgroup vs domain: standalone hosts sit in a workgroup; domain members answer to a domain controller over LDAP too
  • Password policy: lockout threshold, minimum length, complexity - read it BEFORE any authenticated attempt
When to reach for enum4linux-ng (and when not)

Reach for it to get one structured picture of a single host: shares, users, groups, password policy, and OS over SMB and RPC in a single pass, to confirm whether anonymous access leaks anything, or to validate that a hardening change actually closed null-session enumeration.

Reach for something else when SMB is closed (enumerate the services that are open), when you already know the share and just want to read it (smbclient), when you need to sweep many hosts or validate credentials at scale (NetExec / nxc), or when you are mapping a whole domain from a foothold (BloodHound, ldapsearch).

Install, update, verify#

sudo apt install -y enum4linux-ng               # Debian / Ubuntu / Kali (pulls in the Samba tools)
pipx install git+https://github.com/cddmp/enum4linux-ng   # isolated install on non-Kali Linux
git clone https://github.com/cddmp/enum4linux-ng ~/tools/enum4linux-ng   # latest upstream

sudo apt install --only-upgrade -y enum4linux-ng   # update (package)
pipx upgrade enum4linux-ng                          # update (pipx)

command -v enum4linux-ng        # confirm it is on PATH
enum4linux-ng -h | head -n 20   # self-test: prints usage, no Python traceback

If you cloned instead of installing a package, install the Samba client tools it shells out to and call the script directly:

sudo apt install -y smbclient samba-common-bin ldap-utils
python3 ~/tools/enum4linux-ng/enum4linux-ng.py -h

A ModuleNotFoundError means a missing dependency: run pip install -r requirements.txt inside the clone.

Flags you’ll actually use#

Skim these first; the cheat sheet below is these flags combined.

FlagDoesFlagDoes
-AAll simple checks (default)-R / -rRID cycling / its RID range
-AsAll simple, skip NetBIOS lookup-NNetBIOS names lookup (nbtstat-style)
-UUsers via RPC-u / -pUsername / password for an authed session
-G / -GmGroups / groups with members-wSet workgroup or domain manually
-SShares via RPC-dDetailed user/group info (with -U/-G/-R)
-PPassword policy-kKnown users for lookupsid SID discovery
-OOS information-sBrute-force share names from a wordlist
-IPrinter info via RPC-tConnection timeout, seconds (default 5)
-LExtra domain info via LDAP (DCs only)-vVerbose: show the raw samba commands
-oJ / -oYWrite JSON / YAML-oAWrite both JSON and YAML

Cheat sheet#

Every block starts simple and adds one flag at a time, so you can stop at the line that does the job.

# Build up a first pass: stop at the depth you need
enum4linux-ng TARGET_IP                        # 1. default: all simple checks, anonymous
enum4linux-ng -A TARGET_IP                     # 2. same, explicit
enum4linux-ng -A TARGET_IP -oA scans/anon      # 3. + save JSON and YAML
enum4linux-ng -As TARGET_IP                    # lighter: skip the NetBIOS lookup

# Anonymous vs authenticated (same flags, more depth with creds)
enum4linux-ng -A TARGET_IP                                 # null session (default -u "" -p "")
enum4linux-ng -A -u USERNAME -p 'PASSWORD' TARGET_IP       # authenticated
enum4linux-ng -A -w DOMAIN -u USERNAME -p 'PASSWORD' TARGET_IP   # scope to a domain

# Targeted single checks
enum4linux-ng -S TARGET_IP                     # shares only
enum4linux-ng -U TARGET_IP                     # users only
enum4linux-ng -G TARGET_IP                     # groups only
enum4linux-ng -Gm TARGET_IP                    # groups + their members
enum4linux-ng -P TARGET_IP                     # password / lockout policy
enum4linux-ng -O TARGET_IP                     # OS and Samba version
enum4linux-ng -I TARGET_IP                     # printer info via RPC
enum4linux-ng -N TARGET_IP                     # NetBIOS names (nbtstat-style)

# When direct user enum is blocked
enum4linux-ng TARGET_IP -R                     # RID cycling, default ranges
enum4linux-ng -R -r 500-2000 TARGET_IP         # RID cycling, custom range (-r)
enum4linux-ng -U -d TARGET_IP                  # add detail (SIDs, descriptions) to a check

# Domain controllers
enum4linux-ng -L -u USERNAME -p 'PASSWORD' TARGET_IP   # extra domain info via LDAP

# Guess shares when -S comes back empty
enum4linux-ng -s /usr/share/wordlists/shares.txt TARGET_IP

# Output and note taking
enum4linux-ng -A TARGET_IP -oJ scans/host      # JSON only
enum4linux-ng -A TARGET_IP -oY scans/host      # YAML only
enum4linux-ng -A TARGET_IP -oA scans/host 2>&1 | tee notes/host.txt   # both + terminal log

# Parse the JSON export
jq -r '.shares | keys[]?' scans/anon.json                       # list share names
jq -r '.users[]?.username // empty' scans/anon.json | sort -u   # list usernames

# Slow or noisy networks
enum4linux-ng -A -t 15 TARGET_IP               # longer connection timeout
enum4linux-ng -A -v TARGET_IP                  # verbose: see each smbclient/rpcclient call

Command breakdowns#

Each block below explains one situation, the exact command, and what to expect back.

Run a first anonymous pass on a new host#

enum4linux-ng -A TARGET_IP

Runs every simple check over a null session: target info, NetBIOS, sessions, shares, users, groups, and policy in one report. With no creds this is exactly what an unauthenticated stranger can see. -A is the default, so bare enum4linux-ng TARGET_IP does the same.

Save the output as JSON and YAML for your notes#

enum4linux-ng -A TARGET_IP -oA scans/anon

Writes scans/anon.json and scans/anon.yaml, both holding the same structured results. The files are the source of truth; terminal scrollback is easy to lose. Use -oJ or -oY when you only want one format.

Run a lighter, lower-noise first pass#

enum4linux-ng -As TARGET_IP

The same simple checks as -A but without the NetBIOS names lookup, so it is a touch faster and quieter. Good when you want shares and basic info without the extra probes.

Add credentials for deeper enumeration#

enum4linux-ng -A -u USERNAME -p 'PASSWORD' TARGET_IP -oA scans/authed

A valid session usually unlocks user, group, and policy detail a null session cannot read. Quote the password so the shell does not eat special characters. Re-run the anonymous pass first so you can diff what credentials actually added.

List only the shares#

enum4linux-ng -S TARGET_IP

A share list with read/write hints. IPC$ and ADMIN$ are standard plumbing; a readable custom share is the real find. Confirm and browse anything interesting with smbclient //TARGET_IP/share.

List users to build a username list#

enum4linux-ng -U TARGET_IP -oJ scans/users

Account names parsed into JSON you can grep or feed to later authorized work. Empty output does not mean there are no users: enumeration may be blocked while a session is still allowed (see RID cycling).

Map groups and their members#

enum4linux-ng -Gm TARGET_IP

-G lists groups; -Gm also resolves each group’s members, so you see who sits in Domain Admins or a custom privileged group. Add -d for descriptions and SIDs.

Read the password and lockout policy before any auth attempt#

enum4linux-ng -P TARGET_IP

Prints the lockout threshold, minimum length, and complexity. Read the lockout threshold BEFORE any spray or authenticated retry, or you will lock accounts and burn the engagement.

Fingerprint the OS and Samba version#

enum4linux-ng -O TARGET_IP

Returns OS and Samba or Windows version strings gathered over RPC. Useful for narrowing which host you are looking at and which behaviors to expect; treat version banners as a lead, not gospel.

RID-cycle when direct user enumeration is blocked#

enum4linux-ng TARGET_IP -R
enum4linux-ng -R -r 500-2000 TARGET_IP

Walks RID ranges and resolves each to an account name, listing users even when -U returns empty but a null session is allowed. -R optionally takes a lookup size, so it cannot sit directly before the target: put the target first, or follow -R with -r START-END to set a custom range instead of the default common accounts. It is noisy, so reach for it only when you need it.

Scope enumeration to a specific domain or workgroup#

enum4linux-ng -A -w DOMAIN TARGET_IP

enum4linux-ng usually detects the workgroup, but -w pins it when detection is wrong or the host is domain-joined and you want results scoped to DOMAIN.

Pull extra domain info from a domain controller (LDAP)#

enum4linux-ng -L -u USERNAME -p 'PASSWORD' TARGET_IP

-L queries LDAP/LDAPS for domain detail and only makes sense against a domain controller. On a non-DC host these checks fail and you can ignore them.

Brute-force share names when -S comes back empty#

enum4linux-ng -s /usr/share/wordlists/shares.txt TARGET_IP

Guesses share names from a wordlist when RPC share enumeration is blocked but the shares still exist. A hit tells you a hidden share is there to try with smbclient.

Parse the JSON export into clean notes#

jq -r '.shares | keys[]?' scans/anon.json
jq -r '.users[]?.username // empty' scans/anon.json | sort -u

Pull structured fields straight from the export instead of copying from scrollback. Keep one JSON/YAML pair per host so you can diff and compare across an environment.

See the raw samba commands enum4linux-ng runs#

enum4linux-ng -A -v TARGET_IP

-v prints each underlying smbclient, rpcclient, net, and nmblookup call. The fastest way to understand what the wrapper is doing, or to copy one call out and run it by hand.

Reading the output#

You seeMeaningDo next
A readable/writable shareAction itemBrowse it with smbclient //TARGET_IP/share
Users or groups listedLeads, not proofVerify access separately; save for authorized use
access denied on one checkNormal, per-checkOther checks in the same run may still succeed
Empty users but session worksEnum blocked, not accessTry -R RID cycling
Policy with a low lockout countHandle with careDo not spray; respect the threshold
Connection errorSMB not reachableRe-check 139/445 and the IP with Nmap
LDAP section failedHost is not a DCIgnore it

The structured JSON/YAML export is the record you trust; treat the terminal scroll as disposable.

Troubleshooting#

ProblemCauseFix
command not foundNot installed / stale PATHReinstall, or call the script by full path in a fresh shell
ModuleNotFoundError on launchMissing Python dependencypip install -r requirements.txt in the clone
smbclient/rpcclient not foundSamba client tools missingsudo apt install -y smbclient samba-common-bin
Connection refused / timeoutSMB closed or host downConfirm 139/445 with Nmap; re-check the IP
Access denied on every checkNull session rejectedAdd -u/-p, or try -R RID cycling
Empty users/groups, session okEnumeration blocked, not accessUse -R to cycle RIDs
LDAP checks all failTarget is not a DCExpected; ignore those sections
Hangs on a slow linkDefault 5s timeout too shortRaise it with -t 15
Output differs from a tutorialGuide used the old Perl enum4linuxFlags and output differ; check enum4linux-ng -h

Gotchas#

  • enum4linux and enum4linux-ng are not interchangeable. The old Perl tool takes different flags and prints different output, so commands from a pre-2020 tutorial will not map cleanly onto the -ng rewrite.
  • A thin anonymous result is not “nothing here.” Modern Windows blocks null-session user enum by default, so -U can come back empty while -R RID cycling or a single valid credential lights the host up.
  • Check the lockout policy before, not after. -P is cheap, and reading it first is the difference between a clean spray and locking every account you were about to test.
  • The JSON is the record, the terminal is not. Always pass -oJ or -oA; a run you did not save is a run you will repeat, and RID cycling is slow.
  • It opens real logons. Every null and credentialed session lands in the target’s Security event log, and RID cycling fires a burst of lookups defenders alert on, so it is not a stealthy tool.
  • -A is on by default, but a stray flag disables it. The moment you pass a specific check like -S, you get only that check; combine flags explicitly when you want more than one.

Pairs with#

nmap -p 139,445 confirms SMB is open before you enumerate at all. Once enum4linux-ng points the way, smbclient browses and reads the shares it reports, rpcclient digs into specific RPC queries it only summarizes, and ldapsearch or BloodHound take over when the target is a domain controller and you have a foothold. For sweeping many hosts or validating credentials at scale reach for NetExec (formerly CrackMapExec) instead; for a quick share-permission map smbmap is faster, but enum4linux-ng stays the best single-host, all-in-one SMB picture.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop