DEV Community

Pastukhov Aleksey
Pastukhov Aleksey

Posted on

Your AD graph can't see the service that owns your domain

"Redteams" around the world are marking the 10th anniversary of BloodHound. Congrats. A brilliant tool. BloodHound changed how we think about Active Directory.
Instead of staring at group memberships, you look at a graph and ask one question: who can reach Domain Admin, and by what path? It's the right question, and the tools that answer it are excellent.

But a reachability graph has a blind spot, and it's a big one. The graph knows about principals and the edges between them - users, groups, computers, ACLs, delegation. It does not know that one of those computer objects is your Exchange server, that another publishes SCCM policy to every workstation, or that your DNS zone quietly lets any authenticated user create records. To the graph, those are just nodes. To an attacker, they're the whole game.

Kestrel v1.1 is about that blind spot. The core idea is almost embarrassingly simple:

Kestrel doesn't scan your network. It reads what your network already told the directory about itself.

The services already confessed

Here's the thing nobody exploits often enough on the defensive side: major services write themselves into Active Directory. They have to - that's how clients find them and how Kerberos authenticates to them. Which means the directory already holds a detailed, security-relevant description of your service estate, readable by any ordinary domain user, without sending a single packet to the service itself.

Kestrel v1.1 reads that description. Four new passes, all read-only, all from AD:

Exchange. Exchange extends the schema and dumps an enormous amount into the Configuration partition - every server, its version, its build. So Kestrel can tell you that you're running an end-of-life Exchange that will never be patched again, without touching OWA. More importantly, it reads the domain object's ACL and checks what the Exchange security groups actually hold there. The classic Exchange-to-Domain-Admin path - a group with WriteDACL on the domain that can grant itself DCSync - is a permission sitting in the directory right now, or it isn't. The graph doesn't model it. Kestrel reads it directly.

SCCM / MECM. SCCM publishes its site servers and management points into a container called System Management. Its site servers legitimately hold Full Control there - which is exactly how you identify them. The danger is a principal that shouldn't have write access to that container: a broad group, an unexpected user. Anyone who can write there can publish rogue configuration to managed machines. Kestrel reads the container's ACL and tells you who can take the hierarchy over.

DNS / ADIDNS. This is my favorite, because it's a default. When DNS is AD-integrated, the standard zone ACL grants Authenticated Users the right to create records. Read that sentence again: any domain account can inject DNS records into your environment. Combine that with a wildcard or a WPAD entry and you have domain-wide traffic interception. This misconfiguration ships by default, survives migrations untouched, and almost nobody reviews it, because it's an ACL on a zone object, not an edge in anyone's graph. Kestrel walks the zone DACLs and flags it.

Domain hardening flags. Two single-attribute checks that everybody forgets after the domain is built. dSHeuristics is a fixed-position string where one character enables anonymous LDAP (unauthenticated enumeration of your whole directory) and another quietly excludes privileged groups from AdminSDHolder protection, a persistence lever that means an attacker's ACL edits are never reverted. And the Pre-Windows 2000 Compatible Access group: if Everyone or Anonymous is a member, broad principals get legacy read access to account attributes across the domain.

Why the graph misses all of this

It's not that BloodHound is wrong. It's that a reachability graph answers "who can reach what," and these problems aren't reachability — they're posture. A zone ACL that grants record creation isn't an edge to Domain Admin; it's a lateral-movement and interception primitive. An end-of-life Exchange isn't a path; it's an exposure. An anonymous-LDAP flag isn't a privilege; it's a discovery surface. The graph is built to collapse the domain into paths, and posture is exactly what gets collapsed away.

Kestrel takes the opposite view. It doesn't collapse anything. It reads the specific artifacts, container ACL, the zone DACL, the heuristics string, the schema-published server object and reports each as what it is, ranked by severity, with the fix and now with the MITRE ATT&CK technique attached so it drops straight into your detection language.

What it is, and what it deliberately isn't

Kestrel is a single native Windows executable. No server, no Python, no agent, no dependencies. It runs as an ordinary domain user, no special privilege, no replication rights, no connection to any service it audits. It is read-only by design, and its exact footprint is documented rather than hidden, because a defensive tool should be detectable by design.

That last constraint is the whole philosophy, and it's why v1.1 stops where it does. Kestrel will read what Exchange, SCCM, and DNS published to the directory. It will not connect to a SQL server, sniff a session, or send a crafted request to a domain controller - the moment it does that, it stops being a passive auditor and becomes something you have to think twice about running in production. Reading what's already published is a different, quieter kind of power, and it turns out there's an enormous amount of it lying around.

Red teams get a map of the persistence and escalation surface the graph doesn't express, exportable alongside their existing tooling. Blue teams get a prioritized, remediated, ATT&CK-tagged report they can run on any domain-joined box in seconds.

v1.1 is out now. Free, open source, Apache 2.0.

github.com/ssteelfactor-oss/Kestrel


The core of Kestrel - 20-plus checks on Active Directory itself, has been stable since v1.0. v1.1 adds the passive service-posture layer described here without changing any of it: --all still runs exactly the core AD audit it always did, and the service passes are opt-in.

Top comments (0)