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.

intermediateupdated 2026-09-13RustHound-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.
On this page

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.