Skip to content

Conversation

@q-roland
Copy link

@q-roland q-roland commented Nov 6, 2025

Description

This pull requests aims to integrate Active Directory Sites into the data collected from the configuration partition.
This pull request goes with the following Bloodhound PR: SpecterOps/BloodHound#2031
It is also accompanied by the following SharpHoundCommon PR: SpecterOps/SharpHoundCommon#257
A more complete description on why we thought this could be a good idea is described in details in the BloodHound PR, as well as the following article that we just released: https://www.synacktiv.com/en/publications/site-unseen-enumerating-and-attacking-active-directory-sites

Motivation and Context

Motivation and context are explained in the linked article.

How Has This Been Tested?

This has for the moment been tested in local lab instances. The lab instance was composed of various Active Directory site configurations:

  • Single site
  • 2 sites with associated subnets and 1 server by site
  • 3 sites with several servers by site.

Screenshots (if appropriate):

See the BloodHound PR and linked article for screenshots.

Types of changes

  • Chore (a change that does not modify the application functionality)
  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

Checklist:

  • Documentation updates are needed, and have been made accordingly.
  • I have added and/or updated tests to cover my changes.
  • All new and existing tests passed.
  • My changes include a database migration.

As was mentioned in the BloodHound pull request, this is of course only an implementation suggestion, and I remain open to discussion about the choices that were made, requests for implementation changes and so on!

Cheers,

Summary by CodeRabbit

  • New Features
    • Added support for Site as a collection method option
    • Enabled processing and output of Site, SiteServer, and SiteSubnet objects
    • Implemented ACL handling and containment relationship tracking for site-level data

* Process data associated with sites, sites servers, sites subnets
* Add collection method "site", included in default collection methods
@coderabbitai
Copy link

coderabbitai bot commented Nov 6, 2025

Walkthrough

Active Directory Site objects (Sites, SiteServers, SiteSubnets) are now supported as a collection method. Changes include enum registration, command-line option parsing, runtime object processing with ACL handling and containment logic, and corresponding output writers.

Changes

Cohort / File(s) Summary
Enum and Options Configuration
src/Client/Enums.cs, src/Options.cs
Added Site enum member to CollectionMethodOptions; extended options parsing to recognize and map the Site collection method with updated help text.
Object Processing
src/Runtime/ObjectProcessors.cs
Introduced SiteProcessor dependency; extended ProcessObject to handle Site, SiteServer, and SiteSubnet labels; implemented three new private handlers (ProcessSiteObject, ProcessSiteServerObject, ProcessSiteSubnetObject) with ACL reading, property merging, GP link handling, and containment/relationship deduction.
Output Writing
src/Runtime/OutputWriter.cs
Added JsonDataWriter instances for Site, SiteServer, and SiteSubnet; extended StartWriter dispatch logic; integrated new writers into flush and archive operations.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI/Options
    participant Proc as ObjectProcessors
    participant ACL as ACL Handler
    participant Writer as OutputWriter
    
    CLI->>CLI: Parse Site collection method
    CLI->>Proc: Route Site/SiteServer/SiteSubnet objects
    
    rect rgb(200, 230, 255)
    Note over Proc,ACL: Object Processing Phase
    Proc->>ACL: Read ACLs
    Proc->>Proc: Merge object properties
    Proc->>Proc: Deduce containment relationships
    end
    
    Proc->>Writer: Pass processed objects
    
    rect rgb(230, 200, 255)
    Note over Writer: Output Phase
    Writer->>Writer: Write Site objects
    Writer->>Writer: Write SiteServer objects
    Writer->>Writer: Write SiteSubnet objects
    end
    
    Writer->>Writer: Flush writers & create archive
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20–25 minutes

  • Key areas requiring attention:
    • ACL handling logic in ProcessSiteObject, ProcessSiteServerObject, and ProcessSiteSubnetObject — verify correctness of flag assignments and containment relationship deduction
    • Site containment deduction in ProcessSiteSubnetObject using the site processor — ensure proper null/edge-case handling
    • Property merging patterns across the three new handlers — confirm consistency with existing object processing patterns
    • Wiring of new writers in constructor and dispatch methods — validate initialization order and flush/archive sequencing

Possibly related issues

Poem

🐰 A warren of Sites now takes form,
With Servers and Subnets in swarm,
ACLs unwind through each new bind,
Containment traced, relationships signed!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding collection of Active Directory Sites ACLs and data, which aligns perfectly with the raw summary and changeset across all modified files.
Description check ✅ Passed The PR description follows the template structure with all major sections completed: Description with context and linked resources, Motivation and Context, How Has This Been Tested with lab configurations, and Types of changes properly marked. The description is substantive and provides clear context.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/Runtime/OutputWriter.cs (1)

219-220: Minor formatting: missing space after comma.

Line 219 is missing a space between _certTemplateOutput.GetFilename(), and _issuancePolicyOutput.GetFilename().

Apply this diff:

-                _certTemplateOutput.GetFilename(),_issuancePolicyOutput.GetFilename(),
+                _certTemplateOutput.GetFilename(), _issuancePolicyOutput.GetFilename(),
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dc420a6 and ebc3743.

📒 Files selected for processing (4)
  • src/Client/Enums.cs (1 hunks)
  • src/Options.cs (2 hunks)
  • src/Runtime/ObjectProcessors.cs (4 hunks)
  • src/Runtime/OutputWriter.cs (6 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/Runtime/ObjectProcessors.cs (2)
src/Runtime/OutputWriter.cs (2)
  • Task (108-170)
  • Task (172-193)
src/Client/Context.cs (2)
  • Task (72-72)
  • Dictionary (23-27)
src/Runtime/OutputWriter.cs (1)
src/Writers/JsonDataWriter.cs (3)
  • JsonDataWriter (19-119)
  • JsonDataWriter (33-47)
  • GetFilename (115-118)
🔇 Additional comments (7)
src/Options.cs (1)

17-17: LGTM! Site collection method properly integrated.

The Site option is correctly added to the help text and mapped in the switch statement, consistent with existing collection methods.

Also applies to: 211-211

src/Client/Enums.cs (1)

34-34: LGTM! Enum member correctly positioned.

The Site enum member is properly placed and aligns with the mapping in Options.cs.

src/Runtime/OutputWriter.cs (1)

38-40: LGTM! Site-related output writers properly integrated.

The three new writers (Site, SiteServer, SiteSubnet) follow the established pattern consistently across field declarations, constructor initialization, routing in StartWriter, and flushing.

Also applies to: 64-66, 155-163, 187-189

src/Runtime/ObjectProcessors.cs (4)

42-42: LGTM! SiteProcessor properly initialized.

The SiteProcessor field and constructor initialization follow the established pattern used by other processors.

Also applies to: 64-64


100-105: LGTM! Switch cases properly route Site-related labels.

The three new case statements correctly dispatch Site, SiteServer, and SiteSubnet objects to their respective processing methods.


1049-1052: Verify: ContainedBy logic nested under ObjectProps rather than Container flag.

The containing site for subnets is determined inside the ObjectProps conditional block (lines 1037-1053) rather than a Container check. While the comment on lines 1047-1048 explains this requires reading the siteObject property, this creates an inconsistency: the ContainedBy field won't be populated if ObjectProps isn't enabled, even when Container collection is active.

Consider whether the site lookup could be moved under a Container flag check, or if ObjectProps should implicitly enable the required property reads for containment.


970-970: Verify: GP link reading inconsistency in ProcessSiteObject requires developer confirmation

The inconsistency identified in the review is confirmed. ProcessDomainObject (line 530) and ProcessOUObject (line 603) both guard GP link reading with if (_methods.HasFlag(CollectionMethod.Container)), but ProcessSiteObject (line 970) reads GP links unconditionally without checking the Container flag.

Other conditional properties in ProcessSiteObject (ACL, ObjectProps) follow the flag-based pattern, making this inconsistency stand out. No comments explain why Site differs.

This requires developer verification: is the unconditional GP link reading for Site intentional (perhaps Site objects always require links regardless of collection settings), or is it a bug that should follow the Domain/OU pattern?

Comment on lines +986 to +989
if (await _siteProcessor.GetContainingSiteForServer(entry) is (true, var container))
{
ret.ContainedBy = container;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify: Should containing site lookup be conditional on Container collection method?

Lines 986-989 get the containing site for the server unconditionally. Other similar methods only retrieve containment relationships when the Container collection method is enabled:

  • ProcessUserObject (lines 205-212) checks Container flag
  • ProcessComputerObject (lines 274-279) checks Container flag
  • ProcessGroupObject (lines 482-487) checks Container flag

Run the following script to examine containment patterns:


🏁 Script executed:

#!/bin/bash
# Search for ContainedBy assignment patterns to verify conditional checks

rg -nP -B5 'ret\.ContainedBy\s*=' --type=cs | rg -A5 -B5 'ProcessObject|HasFlag.*Container'

Length of output: 6055




🏁 Script executed:

#!/bin/bash
# View ProcessSiteServerObject method around lines 975-1005 to verify Container check presence
sed -n '975,1005p' src/Runtime/ObjectProcessors.cs | cat -n

Length of output: 74


🏁 Script executed:

#!/bin/bash
# View ProcessSiteServerObject method around lines 975-1005
sed -n '975,1005p' src/Runtime/ObjectProcessors.cs | cat -n

Length of output: 1717


🏁 Script executed:

#!/bin/bash
# View ProcessSiteSubnetObject method around lines 1016-1056 to verify its containment pattern
sed -n '1016,1056p' src/Runtime/ObjectProcessors.cs | cat -n

Length of output: 2208


Wrap containing site lookups in Container collection method checks

Verification confirms the issue. All 12 other object processors guard ContainedBy assignments with HasFlag(CollectionMethod.Container) checks, but:

  1. ProcessSiteServerObject (lines 986-989): Retrieves containing site unconditionally—needs Container flag guard
  2. ProcessSiteSubnetObject (lines 1049-1052): Retrieves containing site inside the ObjectProps block instead of a Container block—should be conditional on Container flag (the comment about needing siteObject property explains data dependencies, but architectural responsibility for containment should follow Container flag, not ObjectProps)

Both deviations break consistency with ProcessUserObject, ProcessComputerObject, ProcessGroupObject, and all domain/certificate processors.

🤖 Prompt for AI Agents
In src/Runtime/ObjectProcessors.cs around lines 986-989 and 1049-1052, the code
retrieves the containing site unconditionally (lines 986-989) and inside the
ObjectProps block (lines 1049-1052); wrap these containing-site lookups and the
assignments to ret.ContainedBy with a guard that checks
entry.Methods.HasFlag(CollectionMethod.Container) (i.e., only perform the
_siteProcessor.GetContainingSiteForServer / GetContainingSiteForSubnet calls and
set ret.ContainedBy when the Container flag is present), and move the subnet
lookup out of the ObjectProps block into the Container-guarded section so
containment logic matches the other processors.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant