AD ^: Active Directory

RustHound-CE

Cross-platform BloodHound Community Edition collector written in Rust. Fast LDAP-based AD enumeration with pass-the-hash, Kerberos, certificate auth and ADCS-aware output.

intermediate updated 2026-09-13 RustHound-CE · BloodHound · SharpHound · Impacket

🦀 RustHound-CE Cheatsheet

Complete guide to using RustHound-CE for BloodHound Community Edition data collection.

RustHound-CE is a fast, cross-platform BloodHound Community Edition collector written in Rust, maintained by g0h4n. It talks to a Domain Controller over LDAP/LDAPS from Linux, macOS or Windows and emits CE-schema JSON (optionally zipped) for ingestion into the BloodHound CE web UI.

[!warning] CE-only output schema RustHound-CE produces BloodHound CE JSON, which is not interchangeable with legacy BloodHound. For legacy BloodHound, use the separate NeverHack-maintained RustHound (original), bloodhound-python, or SharpHound v1. Uploading the wrong schema silently fails or mis-parses.

Related notes: SharpHound, bloodhound-ce-python, BloodHound, NetExec, Impacket, faketime, Kerberos Tickets.


📋 Table of Contents


🎯 Overview

RustHound-CE enumerates Active Directory over LDAP to map attack paths, ACLs, sessions, trusts and (optionally) ADCS relationships for BloodHound CE.

Key features

  • ✅ Single static binary, no .NET / Python runtime needed
  • ✅ Cross-platform: Linux, macOS, Windows, ARM
  • ✅ Runs remotely from a non-domain-joined attacker host
  • ✅ Password, NTLM hash (pass-the-hash), Kerberos ccache, and client-certificate auth
  • ✅ LDAPS support and custom LDAP filters
  • ✅ On-disk caching and --resume for large / flaky environments
  • ✅ Outputs CE-compatible JSON, optionally zipped for direct upload

Compatibility

  • Targets: BloodHound Community Edition only.
  • Legacy BloodHound: use the original RustHound (NeverHack) or bloodhound-python.
  • Always match collector output to your BloodHound server version; check the CE web UI under Settings → Download Collectors for the expected format.

📦 Installation

cargo install rusthound-ce
# binary lands in ~/.cargo/bin/rusthound-ce

Build dependencies (Debian/Ubuntu/Kali)

sudo apt install -y gcc clang libclang-dev \
  libgssapi-krb5-2 libkrb5-dev libsasl2-modules-gssapi-mit \
  musl-tools gcc-mingw-w64-x86-64

The Kerberos and SASL packages are needed for -k (GSSAPI) auth; the musl and mingw toolchains are only needed for static / cross-compiled builds.

From source

git clone https://github.com/g0h4n/RustHound-CE
cd RustHound-CE

make release        # optimized build for the current system
# or
cargo build --release

Makefile cross-compile targets

make windows        # cross-compile a Windows .exe
make linux_musl     # static Linux binary (portable, no libc deps)
make linux_aarch64  # ARM64 Linux
make armv7          # ARMv7
make macos          # macOS
make install        # install locally  /  make uninstall to remove

Docker

# build the image, then run against the current dir as output volume
docker run --rm -v "$PWD":/usr/src/rusthound-ce rusthound-ce \
  -d domain.local -u user@domain.local -p 'Password123' -o /usr/src/rusthound-ce -z

🧰 CLI Options Reference

rusthound-ce [OPTIONS] --domain <domain>
FlagLong formDescription
-d--domain <domain>Required. Domain FQDN, e.g. DOMAIN.LOCAL
-u--ldapusername <user>LDAP username, e.g. user@domain.local
-p--ldappassword <pass>LDAP password
-H--hashes <hashes>NT hash for pass-the-hash (NTLM)
-k--kerberosKerberos auth via ccache (KRB5CCNAME)
-f--ldapfqdn <fqdn>DC FQDN, e.g. DC01.DOMAIN.LOCAL or just DC01
-i--ldapip <ip>Domain Controller IP address
-P--ldapport <port>LDAP port (default 389, or 636 with --ldaps)
--ldapsForce LDAPS (TLS) for requests
--dns-tcpUse TCP instead of UDP for DNS queries
-n--name-server <ip>Alternative DNS server IP
--pfx <pfx>PFX / PKCS#12 client certificate
--pfx-pass <pass>PFX file password (optional)
--crt <crt>PEM client certificate
--key <key>PEM private key
-o--output <dir>Output directory (default ./)
-z--zipCompress JSON files into a single archive
-c--collectionmethod [<M>]All (LDAP,SMB,HTTP) or DCOnly (default All)
--cacheCache LDAP results to disk
--cache-buffer <n>Buffer size when caching (default 1000)
--resumeResume from last saved state
--ldap-filter <filter>Custom LDAP filter (default (objectClass=*))
--fqdn-resolverModule to resolve computer IP addresses
-v-v...Increase verbosity (-v, -vv, -vvv)
-h--helpPrint help
-V--versionPrint version

🚀 Basic Usage

# Minimal: password auth, zip the output
rusthound-ce -d domain.local -u user@domain.local -p 'Password123' -o output/ -z

# Point at a specific DC by IP (skip DNS discovery)
rusthound-ce -d domain.local -u user@domain.local -p 'Password123' \
  -i 10.10.10.10 -o output/ -z

# Verbose (trace what LDAP queries run and why something is missing)
rusthound-ce -d domain.local -u user@domain.local -p 'Password123' \
  -o output/ -z -vv

Output is a set of *_users.json, *_computers.json, *_groups.json, *_gpos.json, *_ous.json, *_domains.json, *_containers.json files (plus ADCS/cert objects when reachable). With -z they are bundled into a timestamped zip ready for upload.


🔐 Authentication Methods

Username + password

rusthound-ce -d domain.local -u user@domain.local -p 'Password123' -o output/ -z

Pass-the-hash (NTLM)

# NT hash only; leave the LM half empty
rusthound-ce -d domain.local -u user -H :2b576acbe6bcfda7294d6bd18041b8fe -o output/ -z

Kerberos (ccache)

# 1) Obtain a TGT (impacket) and export the ccache
getTGT.py domain.local/user:'Password123' -dc-ip 10.10.10.10
export KRB5CCNAME=$PWD/user.ccache

# 2) Collect with -k; -f names the DC so the SPN resolves
rusthound-ce -d domain.local -k -f DC01.domain.local -o output/ -z

On a domain-joined Windows host you can just run rusthound-ce -d domain.local -k -f DC01 to reuse the current logon session.

Client certificate (PKINIT / LDAPS mTLS)

# PEM cert + key
rusthound-ce -d DOMAIN.LOCAL -f DC01.DOMAIN.LOCAL \
  --crt user.crt --key user.key --ldaps -o output/ -z

# PFX bundle
rusthound-ce -d DOMAIN.LOCAL -f DC01.DOMAIN.LOCAL \
  --pfx user.pfx --pfx-pass 'certpass' --ldaps -o output/ -z

Useful after an ADCS/Shadow-Credentials attack where you hold a certificate but no cleartext password.


🗂️ Collection Methods

# All (default): LDAP + SMB + HTTP — full session/local-group data
rusthound-ce -c All -d domain.local -u user@domain.local -p 'pass' -o output/ -z

# DCOnly: LDAP against the DC only — no SMB/HTTP touch to member hosts
rusthound-ce -c DCOnly -d domain.local -u user@domain.local -p 'pass' -o output/ -z
  • All — LDAP objects plus SMB/HTTP calls to member computers for sessions and local-group membership. Louder, needs reachability to hosts, but yields richer attack paths.
  • DCOnly — talks only to the DC over LDAP. Quietest option: no direct contact with workstations/servers, so no per-host sessions or local admin data, but full objects, ACLs, trusts and GPOs.

Custom LDAP filter

# Narrow the collection (e.g. one OU or object class)
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
  --ldap-filter '(objectClass=user)' -o output/ -z

⚡ Performance & Reliability

# Cache LDAP results to disk with a large buffer (big domains)
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
  -o output/ --cache --cache-buffer 10000 -z

# Resume a collection that was interrupted (flaky link / disconnect)
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
  -o output/ --cache --resume -z

--cache writes intermediate results so --resume can pick up where a dropped run left off. Raise --cache-buffer for very large directories to reduce disk churn.


🌐 DNS & Host Resolution

# Resolve computer IPs during collection
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
  --fqdn-resolver -o output/ -z

# Force TCP for DNS (UDP blocked/filtered) and use a specific resolver
rusthound-ce -d domain.local -u user@domain.local -p 'pass' \
  --fqdn-resolver --dns-tcp -n 10.10.10.10 -o output/ -z

--fqdn-resolver maps computer objects to IP addresses; combine with --dns-tcp when UDP/53 is filtered, and -n to point at the internal DNS server (usually the DC).


⏰ Time Skew & Kerberos Gotchas

Kerberos rejects tickets when the clock differs from the DC by more than ~5 minutes (KRB_AP_ERR_SKEW). Sync or fake your clock:

# Read DC time, then wrap the collector with faketime
sudo ntpdate 10.10.10.10                      # if allowed
# or
faketime "$(date -u -d "$(net time -S 10.10.10.10)" '+%Y-%m-%d %H:%M:%S')" \
  rusthound-ce -d domain.local -k -f DC01.domain.local -o output/ -z

See the faketime note for the reliable one-liner pattern.


📥 Ingesting into BloodHound CE

# Output is already CE-schema JSON; -z gives one zip to upload
rusthound-ce -d domain.local -u user@domain.local -p 'pass' -o output/ -z
  1. Open the BloodHound CE web UI.
  2. Go to Administration / File Ingest (or drag-and-drop onto the graph).
  3. Upload the .zip (or the individual .json files).
  4. Wait for the ingest job to finish, then run built-in queries (Shortest Paths to Domain Admins, Kerberoastable users, etc.).

[!tip] Automated upload You can push the JSON straight into CE with the bloodhound-cli / API or tools like bhcli, avoiding the web upload for repeatable pipelines.


🔄 SharpHound / bloodhound-python Equivalents

GoalRustHound-CESharpHound (v2, CE)bloodhound-ce-python
Full collection, zipped-c All ... -z-c All --zippassword ...-c All --zip
DC-only / quiet-c DCOnly-c DCOnly-c DCOnly
Pass-the-hash-H :<nt>(via runas/pth)--hashes :<nt>
Kerberos ccache-k -f DC01--kerberos-k
Target DC-i <ip> / -f <fqdn>--domaincontroller-dc <fqdn> -ns <ip>
LDAPS--ldaps--secureldap--use-ldaps
Output dir-o <dir>--outputdirectory-op / cwd

RustHound-CE fills the same niche as bloodhound-ce-python (remote, Linux-friendly) but as a fast native binary rather than a Python tool. See both companion notes.


🛠️ Troubleshooting

SymptomLikely causeFix
KRB_AP_ERR_SKEWClock drift vs DCSync time or wrap with faketime
Empty / tiny outputWrong domain or no LDAP reachVerify -d, add -i <DC IP>, check port 389/636
TLS / cert errors on --ldapsUntrusted DC cert / wrong FQDNUse correct -f FQDN; confirm CA trust
Missing sessions / local adminsRan DCOnly, or hosts unreachableUse -c All and ensure SMB/HTTP reachability
Hangs on large domainsNo caching, big directoryAdd --cache --cache-buffer 10000
Computer IPs missingResolver disabledAdd --fqdn-resolver (+ --dns-tcp -n <DNS>)
Auth fails with hashLM half includedUse -H :<nthash> (empty LM)
“wrong schema” on uploadLegacy BloodHound serverUse a CE server, or a legacy collector instead

Add -v, -vv, or -vvv to see the exact LDAP queries and where collection stalls.


🕶️ OPSEC Notes

Context — Authorised engagements / lab use only.

  • LDAP enumeration against a DC is high-signal to defenders (BloodHound-style query patterns are widely alerted on). Prefer -c DCOnly when you only need objects/ACLs and want to avoid touching member hosts.
  • -c All reaches out to workstations/servers over SMB/HTTP for sessions and local groups — noisier and leaves host-side artifacts.
  • LDAPS (--ldaps) encrypts the query traffic but does not hide the volume or pattern of queries.
  • Throttle / scope with --ldap-filter and target a single DC (-i/-f) to keep the footprint small.
  • Certificate and Kerberos auth avoid sending a cleartext password over the wire; pair with proper ticket hygiene.