# NetworkHound: mapping Active Directory as a BloodHound graph

> NetworkHound is a Python tool that connects to Active Directory, discovers computer objects, resolves them to addresses, scans and validates the services on them, and writes the result as OpenGraph JSON that BloodHound can open. It authenticates with a password, an NTLM hash or a Kerberos ticket, and it can find shadow-IT devices on the subnets the domain advertises.

**MorDavid/NetworkHound** — Advanced Active Directory network topology analyzer with SMB validation, multiple authentication methods (password/NTLM/Kerberos), and comprehensive network discovery. Export results as BloodHound‑compatible OpenGraph JSON.

- Repository: https://github.com/MorDavid/NetworkHound
- Stars: 843 · Forks: 100
- Language: Python
- License: not declared
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/mordavid-networkhound

## What the tool gathers, and why it exists

NetworkHound connects to Active Directory domain controllers, discovers computer objects, resolves hostnames to IP addresses using multiple DNS methods, performs network scanning with port scanning and HTTP and HTTPS validation, and discovers shadow-IT devices. It then builds a detailed network topology graph in OpenGraph JSON format compatible with BloodHound.

The list of what it gathers is the clearest description of the scope:

Active Directory objects, meaning computer objects with their SIDs and hostnames. Network infrastructure, meaning sites, subnets, IP addresses and the relationships between them. Port scanning, with open ports on every discovered device and service identification. Web services, with HTTP and HTTPS validation, SSL certificate analysis and website discovery. SMB services, with connectivity validation, share enumeration and server information. Shadow-IT discovery, meaning non-domain devices found by scanning the subnets. And the network topology itself, the complete set of relationships and device locations.

The point of the tool is the last item combined with the first. BloodHound and its successors model the directory. What they do not model is what is on the network next to those machines, the printers, the appliances and the forgotten servers that are not in the domain. NetworkHound exists to put the two pictures in one graph, and the output format is chosen so that the existing tool can draw it.

The project is Python, the default branch is main, the last push was on 2026-05-16, and the version badge in the README says v1.2.1. There are no GitHub releases, and the repository's licence field is empty.

## Three ways to authenticate, and what they imply

The tool authenticates with a password, an NTLM hash, or a Kerberos ticket file, and the README notes that NetworkHound uses impacket for all Active Directory authentication, in the password, NTLM hash and Kerberos ticket cases.

The basic invocation passes the domain controller, the domain name and a user:

```bash
python NetworkHound.py --dc 192.168.0.11 -d company.local -u admin -p password
```

With an NTLM hash, the password argument is replaced by the hashes argument, which takes an LM and NT pair or just the NT hash:

```bash
python NetworkHound.py --dc 192.168.0.11 -d company.local -u admin --hashes aad3b435b51404eeaad3b435b51404ee:5fbc3d5fec8206a30f4b6c473d68ae76
```

And with Kerberos, the ticket comes from a credential cache that the KRB5CCNAME environment variable points at:

```bash
python NetworkHound.py --dc dc.company.local -d company.local -u admin --kerberos
```

That last form is the one to prefer in a careful operation, because it uses the ticket your existing session already has rather than a credential you have to pass on the command line. The first form puts a password in your shell history and in the process list, which is a real problem on a shared machine even in an authorised assessment.

The required arguments are the domain controller, given as a hostname or an address, the domain name, and the user. The three authentication options are the password, the hashes and the Kerberos ticket file, and the Kerberos option requires the KRB5CCNAME environment variable to be set.

## The scan switches, and what a full run costs

The base command enumerates the directory. The heavier command adds the network work:

```bash
python NetworkHound.py --dc 192.168.0.11 -d company.local -u admin -p password --shadow-it --port-scan --valid-http --ssl --valid-smb --scan-threads 50 -Pn
```

Each switch turns on one kind of work. --shadow-it scans the subnet ranges that Active Directory advertises, looking for devices that are not in the domain. --port-scan enables TCP port scanning, and --valid-http tests HTTP and HTTPS connectivity on the open ports, while --ssl extracts detailed certificate information, which the README marks as slower. --valid-smb tests SMB connectivity and enumerates shares on the SMB ports.

The last two arguments are the ones that change the character of the run. --scan-threads sets the number of concurrent scanning threads, here fifty, and the default is ten. -Pn means only port scan hosts that respond to ping, which is a way to reduce the work on a large network at the cost of missing devices that block ping, so it is a coverage trade rather than an optimisation.

The other scanning options have sensible defaults: --ports takes a comma-separated list and defaults to common ports, --scan-timeout defaults to three seconds, and the output file is chosen with -o or --output, defaulting to network_opengraph.json. The DNS server can be set with --dns, and it defaults to the domain controller if you do not set it.

For an assessment, the sensible order is to run the base enumeration first, look at the graph, and then enable the network switches for the subnets that matter, rather than turning everything on for the whole estate at once.

## Four ways to resolve a name, and a DNS mode for proxies

Name resolution is where a lot of the reliability of a discovery tool lives, and NetworkHound tries several methods rather than one.

The methods are socket resolution, which is standard Python hostname resolution, and nslookup, which does command line DNS queries against a specific DNS server. Then dnspython, described as an advanced DNS library with wide record support over TCP and UDP. And getaddrinfo, the system level address resolution. On top of those there is a hostname fallback, which resolves short hostnames, which is the form Active Directory environments tend to use.

Each method is a different way of asking the same question, and the differences matter in practice. A system resolver will honour the machine's search list and its cache, dnspython talks to a server directly, and nslookup gives you something you can read the output of when the results look wrong. The verbose flag prints the detailed resolution methods, which is the setting to use when a host is missing from the graph and you want to know why.

The DNS over TCP option is the one with a security angle as well as a compatibility one. The --dns-tcp flag uses TCP for DNS queries instead of UDP, and the README describes it as useful for firewall bypass and proxy compatibility, which is also how the tool is used from another network altogether:

```bash
python NetworkHound.py --dc dc.company.local -d company.local -u admin -p password --dns 8.8.8.8 --dns-tcp --verbose
```

Pointing the resolver at an external address, with TCP transport and verbose resolution logging, is the combination for a scan that runs from outside the domain's own network.

## The proxychains note is a bug report in miniature

There is one warning in the README that deserves to be read carefully, because it is the kind of detail that costs an evening if you miss it.

When using proxychains, always use the --dns-tcp flag. The README explains why: this prevents multicast IP issues with the 224.* range that occur with UDP DNS through SOCKS proxies, and it points to a BUGFIX_PROXYCHAINS_DNS.md file for details.

The reasoning is that a SOCKS proxy carries the TCP connection to the target resolver, but a UDP DNS query has no such path, so the resolver has to be reached by a broadcast or multicast address on the local segment, which the proxy cannot route and the target may ignore. Switching to TCP makes the query an ordinary proxied connection, and the local machine never has to resolve anything itself.

The invocation looks like this:

```bash
proxychains python NetworkHound.py --dc dc.company.local -d company.local -u admin -p password --dns-tcp --port-scan
```

The interesting part is that the tool ships a document about this specific defect. A separate file for one bug, named after the failure mode, is a good sign about how the maintainer handles the problems that only show up in unusual networks, and it is also a reminder that this class of tool meets a lot of unusual networks.

## The output, and the model file that makes the graph readable

The graph is written in OpenGraph format, the JSON structure that BloodHound reads, and it is hierarchical: domain, then sites, then subnets, then computers and devices. Discovered web services become separate nodes, so the graph distinguishes a machine from a service running on it.

The custom node kinds need icons and styles, and those live in a model.json file that defines them for all the custom kinds. Because BloodHound has to learn those kinds, there is a step before the scan: the model file is uploaded to the BloodHound instance through its API using a script in the repository.

```bash
python update_custom_nodes_to_bloodhound.py -s https://bloodhound.example.com -u admin@domain.com -p "Password!" -m model.json
```

So the workflow is: prepare the BloodHound instance with the custom node definitions, run the scanner, and open the result. The scanner writes a JSON file and the upload script pushes the model, and the two are separate because they happen at different times and against different systems.

The output structure is worth understanding before you read a graph. Domain to sites to subnets to computers is the shape of the Active Directory itself, so the visual layout of a BloodHound canvas will not change much when you add network nodes, and the web service nodes will appear as leaves attached to the machines. If you are presenting results to people who know Active Directory, that mapping is doing the explanatory work.

## The repository, and what an assessment needs from you

The top-level listing is small: NetworkHound.py as the entry point, a core/ directory, utils/, validators/, model.json, requirements.txt, update_custom_nodes_to_bloodhound.py, the logo and the usual repository files.

The requirements file is six lines and describes the stack precisely: impacket for the Active Directory authentication, ldap3 for the directory queries, dnspython for name resolution, requests and urllib3 for the HTTP work, and bloodhound-opengraph for the output format.

The one thing the README does not provide is a licence file or a licence statement. The repository's licence field is empty, which is worth resolving before you copy any of this into a tool of your own.

For an assessment, two practical points are worth stating plainly. First, this tool needs credentials with enough privilege to read the directory and, with the SMB switches enabled, to authenticate to the machines it finds, and the shares it enumerates are read with your account, so the results reflect what you can see rather than what exists. Second, the scan is not free. Port scanning with fifty threads and SMB validation against a large estate is a lot of traffic, and on a network where the domain controller and the endpoints are watched, that traffic is itself a signal.

Used inside an authorised engagement window, with credentials you are permitted to use and a scope you have agreed, the tool does what it says: it turns a directory into a map of the network around it, in a format the people who read such graphs already have open.

## Conclusion

Adopt NetworkHound if you are assessing an Active Directory estate and want the network around the domain in the same graph as the domain itself, because the tool joins the Active Directory objects to the addresses, the open ports and the services on those addresses, and writes the result in a format BloodHound already reads. Do not run it against a network you are not authorised to test, and keep the credentials it needs out of shell history, since the example commands take a password or a hash on the command line. Before you run it, decide which switches to enable, because a full scan with port scanning and service validation is much heavier than the base enumeration, and read the proxychains note if you plan to route the traffic, since the DNS transport has to be switched to TCP for that to work.

## FAQ

### What does NetworkHound do and what does it output?

It connects to Active Directory, discovers computer objects, resolves hostnames, scans and validates services on the discovered hosts, optionally finds shadow-IT devices, and writes the topology as BloodHound-compatible OpenGraph JSON, hierarchically arranged as domain, sites, subnets and computers or devices, with separate nodes for discovered web services.

### Which authentication methods does NetworkHound support?

A password, an NTLM hash given as an LM and NT pair or just the NT hash, and a Kerberos ticket file, which requires KRB5CCNAME to point at a credential cache. The README states that impacket is used for all Active Directory authentication, and that the Kerberos option is the one that avoids passing a secret on the command line.

### How do I scan for devices that are not in the domain?

Use the --shadow-it switch, which discovers non-domain devices in the subnets Active Directory advertises, by ping sweep, and integrates the devices it finds into the same port scanning. The other discovery switches are --port-scan, --valid-http, --ssl and --valid-smb, and --scan-threads sets the concurrency.

### Why does NetworkHound need --dns-tcp with proxychains?

The README warns that when using proxychains you should always use the --dns-tcp flag, because it prevents multicast address issues in the 224.* range that occur with UDP DNS through SOCKS proxies. A separate document, BUGFIX_PROXYCHAINS_DNS.md, describes the defect in detail.

## Sources

- [Issues](https://github.com/MorDavid/NetworkHound/issues)
- [MorDavid/NetworkHound on GitHub](https://github.com/MorDavid/NetworkHound)
- [README](https://github.com/MorDavid/NetworkHound/blob/main/README.md)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/mordavid-networkhound
