rpcclient

rpcclient is Samba’s hands-on client for the Windows MS-RPC interfaces over SMB: point it at a host and ask direct questions, which users exist, what groups they hold, the password policy, the shares, and the domain SID. Reach for it when an automated scanner reported something and you want to confirm exactly what one account (or a null session) can actually read.

Mental model: rpcclient [auth] -c "command" TARGET_IP. Pick a session, run one RPC call, read the answer.

How rpcclient works: core concepts
  • Transport: MS-RPC rides SMB named pipes, so it needs TCP 445 (or 139) reachable on the target.
  • Session type: authenticated (-U USERNAME), null/anonymous (-U "" -N), or pass-the-hash (--pw-nt-hash); what you can read depends entirely on this.
  • Interactive vs one-shot: bare rpcclient drops you at an rpcclient $> prompt; -c "cmd" runs a command (or several with ;) and exits, which is what you script.
  • RID: a Relative ID appended to the domain SID; every account is <domain-SID>-<RID>, and ...-500 is always the built-in Administrator.
  • SID: the full S-1-5-21-... identifier; lookupnames and lookupsids convert between names and SIDs.
  • Named pipes: calls hit different interfaces, SAMR (users/groups), LSARPC (SIDs/policy/privileges), SRVSVC (shares), and each has its own permission gate.
  • Permission-bound output: the same command can return full data, partial data, or NT_STATUS_ACCESS_DENIED, purely as a function of the account you used.
When to reach for rpcclient (and when not)

Reach for it to confirm by hand exactly what a credential or null session can enumerate, pull a clean user list for later steps, read the password policy before any spraying, map SIDs and RID-cycle, or verify a single finding that enum4linux-ng or NetExec already reported.

Reach for something else to browse or download share contents (smbclient), to sweep many hosts or protocols at once (NetExec), to get a full automated SMB report in one pass (enum4linux-ng), or to pipe RPC results straight into code (Impacket samrdump / lookupsid).

Install, update, verify#

sudo apt install -y smbclient                  # Debian / Ubuntu / Kali (rpcclient ships with it)
sudo dnf install -y samba-client               # Fedora / RHEL
brew install samba                             # macOS (Homebrew)
sudo apt install --only-upgrade -y smbclient   # update

rpcclient --version       # Samba version
command -v rpcclient      # confirm it is on PATH
rpcclient --help | less   # skim the connection options and pipe commands

rpcclient is part of the Samba client tools, so you install smbclient and get it alongside. There is no network self-test: rpcclient --help runs offline and proves the binary works, while a real connection returns either an rpcclient $> prompt or an NT_STATUS_* error.

Flags you’ll actually use#

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

FlagDoesFlagDoes
-U USERSet the session user (USER%PASS inline)-c "cmd"Run RPC command(s) then exit
-U ""Empty username (null session)-A fileRead user/pass/domain from a file
-NNo password prompt (null/anon)--pw-nt-hashTreat the password as an NT hash
-W DOMAINSet the workgroup / domain-kAuthenticate with Kerberos
-p PORTNon-standard SMB port-d 3Raise debug level for diagnosis
-I IPTarget IP (when host is a NetBIOS name)--option='...'Set any smb.conf option (legacy protocol)

Cheat sheet#

Every connection block builds up one flag at a time, so you can stop at the line that logs you in. The query blocks default to a null session; swap -U "" -N for -U USERNAME when anonymous access is refused.

# Verify the tool
command -v rpcclient                                # is it on PATH
rpcclient --version                                 # Samba version

# Build up a connection: stop at the line that logs you in
rpcclient -U "" -N TARGET_IP                         # 1. null session (empty user, no password)
rpcclient -U USERNAME TARGET_IP                      # 2. + prompt for a password
rpcclient -U USERNAME -W DOMAIN TARGET_IP            # 3. + set the domain / workgroup
rpcclient -A creds.txt TARGET_IP                     # 4. or read creds from a locked file
rpcclient -U USERNAME --pw-nt-hash TARGET_IP         # 5. or pass an NT hash instead of a password

# Server and domain facts (read-only)
rpcclient -U "" -N -c "srvinfo" TARGET_IP            # OS version, server type, platform
rpcclient -U "" -N -c "querydominfo" TARGET_IP       # domain/workgroup, role, user count
rpcclient -U "" -N -c "getdompwinfo" TARGET_IP       # password policy: min length, complexity
rpcclient -U "" -N -c "lsaquery" TARGET_IP           # the domain name and its SID
rpcclient -U "" -N -c "enumdomains" TARGET_IP        # domains this server knows

# Users
rpcclient -U "" -N -c "enumdomusers" TARGET_IP           # every user + RID
rpcclient -U "" -N -c "querydispinfo" TARGET_IP          # users with full name + description
rpcclient -U "" -N -c "queryuser 0x457" TARGET_IP        # one account by RID (flags, logon)
rpcclient -U "" -N -c "queryusergroups 0x457" TARGET_IP  # the RIDs of a user's groups

# Groups
rpcclient -U "" -N -c "enumdomgroups" TARGET_IP          # domain groups + RIDs
rpcclient -U "" -N -c "querygroup 0x200" TARGET_IP       # one group's attributes
rpcclient -U "" -N -c "querygroupmem 0x200" TARGET_IP    # member RIDs of a group
rpcclient -U "" -N -c "enumalsgroups builtin" TARGET_IP  # built-in local groups (Administrators, ...)

# Identity mapping and SIDs
rpcclient -U "" -N -c "lookupnames USERNAME" TARGET_IP        # name -> SID (+ RID)
rpcclient -U "" -N -c "lookupsids S-1-5-21-...-500" TARGET_IP # SID -> name
rpcclient -U "" -N -c "lsaenumsid" TARGET_IP                  # SIDs the LSA has cached

# RID cycle when enumdomusers is blocked but lookupsids is not
for i in $(seq 500 1100); do \
  rpcclient -U "" -N -c "lookupsids S-1-5-21-1001-1001-1001-$i" TARGET_IP \
  | grep -v UNKNOWN; done

# Privileges and rights
rpcclient -U USERNAME -c "enumprivs" TARGET_IP                          # privilege names the server defines
rpcclient -U USERNAME -c "lsaenumacctrights S-1-5-21-...-500" TARGET_IP # rights held by one SID

# Shares over RPC (a different surface than smbclient -L)
rpcclient -U "" -N -c "netshareenumall" TARGET_IP        # every share, including hidden
rpcclient -U "" -N -c "netsharegetinfo SHARE" TARGET_IP  # one share's path and info

# Chain, save, and parse
rpcclient -U "" -N -c "querydominfo; enumdomusers; enumdomgroups" TARGET_IP
rpcclient -U "" -N -c "enumdomusers" TARGET_IP 2>&1 | tee loot/users.txt
grep -oP 'user:\[\K[^\]]+' loot/users.txt                # one clean username per line

# Change a password (WRITE op; only where you are explicitly authorized)
rpcclient -U USERNAME -c "setuserinfo2 TARGET_USER 23 NEWPASS" TARGET_IP

Command breakdowns#

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

Open a null session#

rpcclient -U "" -N TARGET_IP

Empty username, no password. Success is an rpcclient $> prompt (a finding in itself); a hardened box answers NT_STATUS_ACCESS_DENIED or NT_STATUS_LOGON_FAILURE. This is always the cheapest first attempt.

Authenticate with a username and password#

rpcclient -U USERNAME -W DOMAIN TARGET_IP

Prompts for the password so it stays out of shell history. Add -W DOMAIN on domain-joined targets. To avoid the prompt in scripts, put the creds in a file and use -A creds.txt.

Read the server and domain basics#

rpcclient -U "" -N -c "srvinfo; querydominfo" TARGET_IP

srvinfo returns the OS build and server type; querydominfo returns the workgroup or domain, the server role, and the total user count. This is your orientation before any per-object query.

Check the password policy before spraying#

rpcclient -U "" -N -c "getdompwinfo" TARGET_IP

Returns the minimum password length and the complexity flags. Read this first: it tells you the lockout risk and whether a spray candidate even meets the policy, so you do not lock accounts blindly.

List every domain user and RID#

rpcclient -U "" -N -c "enumdomusers" TARGET_IP

Prints lines like user:[bob] rid:[0x3e9]. This is the workhorse call. Save the names for spraying or AS-REP checks elsewhere, and keep the RIDs for targeted lookups.

Pull user details and descriptions#

rpcclient -U "" -N -c "querydispinfo" TARGET_IP
rpcclient -U "" -N -c "queryuser 0x457" TARGET_IP

querydispinfo lists every account with its full name and description in one call (passwords sometimes hide in a description). queryuser <RID> drills into one account: flags, last logon, and bad-password count when permitted.

Enumerate groups and their members#

rpcclient -U "" -N -c "enumdomgroups" TARGET_IP
rpcclient -U "" -N -c "querygroupmem 0x200" TARGET_IP

enumdomgroups gives group names and RIDs; feed a group RID to querygroupmem to get the member RIDs, then resolve each with lookupsids. This is how you find who sits in Domain Admins.

Find which groups a user is in#

rpcclient -U "" -N -c "queryusergroups 0x457" TARGET_IP

Takes a user RID and returns the RIDs of the groups that account belongs to. Pair it with enumdomgroups to turn those RIDs into readable group names.

List the built-in local groups#

rpcclient -U "" -N -c "enumalsgroups builtin" TARGET_IP

Enumerates the machine’s built-in aliases (Administrators, Remote Desktop Users, Backup Operators). Use enumalsgroups domain for domain-local aliases. Membership here often matters more than domain-group membership.

Map a name to a SID and back#

rpcclient -U "" -N -c "lookupnames USERNAME" TARGET_IP
rpcclient -U "" -N -c "lookupsids S-1-5-21-...-500" TARGET_IP

lookupnames resolves a name to its full SID (and exposes the domain SID prefix); lookupsids does the reverse. Together they are the plumbing behind RID cycling and group resolution.

Recover the domain SID#

rpcclient -U "" -N -c "lsaquery" TARGET_IP

Returns the domain name and its S-1-5-21-... SID. You need this prefix to build SIDs for lookupsids, so run it before any RID-cycling loop.

RID-cycle when enumdomusers is blocked#

for i in $(seq 500 1100); do \
  rpcclient -U "" -N -c "lookupsids S-1-5-21-1001-1001-1001-$i" TARGET_IP \
  | grep -v UNKNOWN; done

When enumdomusers is denied but lookupsids is not, walk the RID range against the real domain SID from lsaquery. Known RIDs resolve to names; unknown ones say *unknown*, which grep -v UNKNOWN filters out.

Enumerate shares over RPC#

rpcclient -U "" -N -c "netshareenumall" TARGET_IP

Lists every share the SRVSVC pipe reports, including hidden $ shares, which is a different code path than smbclient -L. Follow an interesting name with netsharegetinfo SHARE, then switch to smbclient to actually read it.

Authenticate with an NT hash (pass-the-hash)#

rpcclient -U USERNAME --pw-nt-hash -c "enumdomusers" TARGET_IP

When you hold an NT hash instead of a cleartext password, --pw-nt-hash tells rpcclient the value you supply at the prompt is the hash. Only against systems you are explicitly authorized to test.

Run several queries in one connection#

rpcclient -U "" -N -c "querydominfo; enumdomusers; enumdomgroups" TARGET_IP 2>&1 \
  | tee "loot/rpc-$(date +%Y%m%d-%H%M).txt"

Semicolons chain commands over a single login, so you authenticate once and get everything in order. Piping through tee timestamps the output and keeps a copy instead of re-running noisy enumeration later.

Reset a user’s password (authorized only)#

rpcclient -U USERNAME -c "setuserinfo2 TARGET_USER 23 NEWPASS" TARGET_IP

A write operation: level 23 sets a new password for TARGET_USER when your account holds the reset right. It changes real state, so run it only where a scope explicitly permits it, and note the change.

Reading the output#

OutputMeaningDo next
rpcclient $> promptSession acceptedYou are in; run commands, and note if it was a null session
user:[bob] rid:[0x3e9]A user and its RIDSave the name; feed the RID to queryuser
NT_STATUS_ACCESS_DENIEDService answered, account lacks rightsChange the account, not the host
NT_STATUS_LOGON_FAILUREWrong username or passwordRe-check creds; try a null session
NT_STATUS_CONNECTION_REFUSED / _RESETSMB not open or filteredRe-scan 139/445
NT_STATUS_ACCOUNT_LOCKED_OUTToo many bad logonsStop; you tripped lockout, back off
Empty user or group listStripped box, guest session, or hidden by policyNot proof of “nothing there”; try credentials

RIDs print in hex (rid:[0x457]), and queryuser accepts hex or decimal, so keep the notation consistent. Every SID you build for lookupsids needs the domain prefix from lsaquery, not just the trailing RID.

Troubleshooting#

ProblemCauseFix
command not foundSamba client not installed / stale PATHInstall smbclient; open a fresh shell
NT_STATUS_CONNECTION_REFUSEDSMB not open or filteredConfirm 445/139 with a port scan first
NT_STATUS_LOGON_FAILUREWrong username or passwordRe-check creds; try -U "" -N
NT_STATUS_ACCESS_DENIEDAccount lacks rights for that callUse valid creds; expect partial output
Hangs with no promptWrong port or host unreachableAdd -p, verify the IP, try -d 3
Null session rejectedAnonymous access disabledSupply real credentials instead
protocol negotiation failedModern client refuses legacy SMB/NTLMAdd --option='client min protocol=NT1'
Output differs from a writeupSamba version or flag changesCheck rpcclient --version and --help

Gotchas#

  • RPC commands go at the rpcclient $> prompt or after -c, never at the system shell. Typing enumdomusers in bash just gives command not found.
  • Permissions are per-call, not per-session. A null session that answers srvinfo can still deny enumdomusers, so one refusal does not mean the whole session is useless.
  • -U USERNAME%PASSWORD leaks the password into shell history and the process list. Prefer the interactive prompt or -A creds.txt (then chmod 600 it).
  • Modern Samba disables SMB1 and legacy auth by default, so an old “easy null session” can fail with a negotiation error that is your client’s doing, not the target’s; force the old protocol only when you must.
  • lookupsids needs the full domain SID from lsaquery, not a bare RID; a RID-cycle loop built on the wrong prefix silently resolves nothing.
  • A failed null session says nothing about credentialed access. They are separate paths, so do not skip -U USERNAME just because -U "" -N was denied.

Pairs with#

nmap confirms 139/445 are open before you ever launch rpcclient. enum4linux-ng runs the same SAMR and LSARPC calls automatically for a fast overview, then rpcclient verifies any single finding with one precise call. NetExec sweeps many hosts and protocols and does password checks at scale, where rpcclient is deliberately one host at a time. Once you find a share, hand it to smbclient to browse and download; when you want RPC output piped into code, reach for Impacket’s samrdump and lookupsid.

Find us elsewhere

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