Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 

Repository files navigation

NSPIder

NSPI-based domain enumeration and BloodHound-CE collector.

NSPIder collects domain principals via the Name Service Provider Interface (MS-NSPI) directly against a Global Catalog, producing BloodHound-CE v6 JSON output. It runs two collection passes on every execution: a browse walk that respects address-book filtering as a baseline, and a MId-range scan that reads raw DIT positions and bypasses that filtering entirely.

Exchange is required. The NSPI endpoint on a GC only registers after Exchange's AD preparation runs. NSPIder targets the GC directly over ncacn_ip_tcp — not the Exchange server. Any valid domain credential works. No elevated privileges required.


Why NSPI?

Standard BloodHound collectors reach the directory over LDAP, which is heavily monitored in mature environments — MDI has dedicated SharpHound analytics, expensive-query thresholds fire on bulk enumeration, and LDAP-signature auditing catches the traffic pattern even when individual queries are benign.

NSPI is a different interface to the same data. It's what every Outlook client uses for address-book lookups, so moderate query volume is indistinguishable from normal mail client activity. Direct GC connections over ncacn_ip_tcp generate zero 4662 events under default DC audit configuration and fewer 4769 events than an equivalent LDAP session. The MId-range scan — 400 RPC calls that traverse the full DIT — produces a single logon event.

Beyond stealth, the MId-range scan surfaces data that LDAP-first collectors and every existing NSPI tool miss:

Control Browse (container-filtered) MId-range scan
msExchHideFromAddressLists Respected Bypassed
Address Book Policy segmentation Respected (browse) Bypassed
Mail-enabled-only filter Applied Bypassed — full domain population
Non-mail AD groups (Domain Admins, etc.) Not present Surfaced with SIDs

Objects not visible in any address book — hidden service accounts, restricted DLs, built-in AD groups, Exchange service mailboxes, machine accounts — appear in the MId scan output and are tagged nspi_hidden: true in the JSON so analysts can immediately spot what standard tooling would miss.


Installation

pip install impacket
git clone https://github.com/cbev0x/NSPIder
cd NSPIder

Python 3.8+. No additional dependencies beyond Impacket.


Usage

NSPIder [[domain/]user[:pass]@]<DC IP or hostname> [options]

Basic collection

# Password auth
python3 NSPIder.py reflect.lab/jdoe:'Password1'@10.10.20.10

# NT hash
python3 NSPIder.py reflect.lab/jdoe@10.10.20.10 -hashes :8846f7eaee8fb117ad06bdd830b7586c

# Kerberos (from ccache)
python3 NSPIder.py reflect.lab/jdoe@dc01.reflect.lab -k

Output lands in NSPIder_<domain>_<timestamp>/ by default — two files ready for BloodHound-CE import:

NSPIder_reflect.lab_20260826055903/
├── 20260826055903_users.json    # 65 users (62 hidden)
└── 20260826055903_groups.json   # 110 groups (109 hidden)

Drag-drop both into BloodHound-CE's upload interface. Nodes key on objectSid and merge cleanly with SharpHound or other collector output.

OPSEC tuning

# Add inter-batch jitter (200ms max) and narrow the MId range
python3 NSPIder.py reflect.lab/jdoe:'Password1'@10.10.20.10 \
  --jitter 200 --mid-start 4000 --mid-end 15000

# Increase batch size (faster, slightly louder)
python3 NSPIder.py reflect.lab/jdoe:'Password1'@10.10.20.10 --batch 100

The default batch size of 50 matches typical Outlook address-book paging. Jitter adds a random sleep between batches to flatten the request rate. The MId range 1-20000 covers most environments; adjust for very large directories.

LDAP enrichment (group membership edges)

By default NSPIder is pure NSPI — no LDAP traffic. The --enrich flag adds a targeted LDAP query for group membership, populating MemberOf edges in the output. This opens an LDAP session (additional logon event, signing negotiation).

# Enrich all discovered groups (targeted to NSPI-collected groups only)
python3 NSPIder.py reflect.lab/jdoe:'Password1'@10.10.20.10 --enrich

# Enrich only groups matching a name substring (more targeted)
python3 NSPIder.py reflect.lab/jdoe:'Password1'@10.10.20.10 \
  --enrich --enrich-filter "domain admins"

Enrichment scope is always limited to groups discovered during the NSPI collection pass. It is not a full LDAP group dump.

BOFHound-compatible output

python3 NSPIder.py reflect.lab/jdoe:'Password1'@10.10.20.10 --bofhound

Emits a single combined JSON file compatible with BOFHound v2+ ingestion.

Full options

authentication:
  -hashes LM:NT         NTLM hashes (:NT for NT-only)
  -k                    Kerberos auth (from ccache or -aesKey)
  -aesKey hex           AES-128/256 key for Kerberos
  -dc-ip ip             DC/GC IP (overrides target host for EPM resolution)

collection:
  --mid-start INT       MId-cycle start (default: 1)
  --mid-end   INT       MId-cycle end   (default: 20000)
  --batch     INT       QueryRows batch size (default: 50)
  --jitter    ms        Random inter-batch jitter in milliseconds

enrichment (adds LDAP session to footprint):
  --enrich              Enable LDAP membership pull for discovered groups
  --enrich-filter STR   Only enrich groups whose name contains STR

output:
  -o, --output DIR      Output directory (default: NSPIder_<domain>_<ts>)
  --bofhound            BOFHound-compatible JSON instead of BH-CE format
  -debug                Verbose debug output

What gets collected

Browse walk (container-filtered, runs first):

  • Mail-enabled users and distribution groups visible in the Default GAL
  • Establishes the visible-object baseline

MId-range scan (bypasses all container filtering):

  • Everything in the browse walk, plus hidden mailboxes and DLs
  • Non-mail-enabled AD users and groups (Domain Admins, Schema Admins, built-ins)
  • Exchange service mailboxes (HealthMailbox, SystemMailbox, federation accounts)
  • Machine accounts that surface through NSPI
  • Any AD object at a scanned DIT position that has a display name and SID

Per object, NSPIder collects: objectSid, objectGUID, displayName, mail, mailNickname, title, department, and displayType for node classification. Objects without a SID are discarded (no BH node key).

Enrichment (optional): member attribute pulled from LDAP for collected groups, added as Members entries in the group JSON.


Architecture and limitations

NSPI on a GC exposes two collection primitives. Browse-walk uses the address book container, which is exchange-managed and filtered. MId-range scan uses NspiQueryRows with an explicit MId table (lpETable), which reads DIT positions directly and ignores container-level filtering. Both run over ncacn_ip_tcp to the GC, resolved via the endpoint mapper on port 135.

The following operations are not available on the direct-GC path and are out of scope for this tool:

  • NspiResolveNamesW (ANR / name resolution) — requires the Exchange address-book service (ncacn_http via EXCH01)
  • NspiGetMatches (restriction/filter queries) — same requirement
  • Group membership proptags (PidTagAddressBookMember etc.) — absent from the GC's exposed proptag set; hence the LDAP enrichment option

These live in the Exchange resolution plane (EXCH01 via RPC-over-HTTP). That path is outside NSPIder's scope but is worth a separate tool.

NSPI only registers on the GC after Exchange AD preparation has run. Running NSPIder against a domain with no Exchange installation will produce ept_s_not_registered.


Footprint

Default run (no enrichment):

Event Count
4662 (DS object access) 0 (default SACLs have no read ACEs)
4624 (logon) ~1
4769 (Kerberos TGS) 0-1
5145 (pipe/share access) 0 (transport is ncacn_ip_tcp, not named pipe)

Traffic pattern: one authenticated RPC bind followed by batched NspiQueryRows calls. Indistinguishable from an Outlook client opening the address book. A tuned SOC with MDI will see a "NSPI RPC Browse Requests/sec" spike on the DC during the MId-cycle pass — the tell is rate, not protocol. Jitter (--jitter) flattens this.

With --enrich: adds one LDAP session (one additional 4624, one 4769 for ldap/DC).


Research

This tool came out of a characterization of the MS-NSPI attack surface for offensive security research. The full writeup with empirical lab results, footprint tables, and a breakdown of the two-plane NSPI architecture (GC data plane vs Exchange resolution plane) is at cbev0x.github.io.

Key findings documented there:

  • msExchHideFromAddressLists is a presentation-layer control only — direct MId queries bypass it
  • Address Book Policy segmentation is not enforced on the direct-GC NSPI path
  • The MId-range scan surfaces the full domain user population, not just Exchange recipients
  • legacyExchangeDN (LDAP-readable at any privilege) enables targeted single-object access via NspiDNToMId with a ~2-event footprint vs 400 calls for bulk discovery
  • Exchange infrastructure details (server name, mailbox DB GUID, service topology) are recoverable from HealthMailbox objects visible in the MId scan

Credits

Built on Impacket's dcerpc.v5.nspi implementation by mohemiv (PT SWARM), which provides the MS-NSPI / MS-OXNSPI / MS-OXABREF protocol layer. NSPIder adds the BloodHound-graph layer, the MId-bypass collection mode, and the OPSEC-aware tooling on top.

Inspired by BOFHound and SOAPHound for the "quiet collector" framing.

About

NSPI-based domain enumeration and BloodHound-CE collector - bypasses GAL hiding, ABP segmentation, and the mail-enabled filter via direct GC MId-range scanning

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages