Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
621b04e
feat: Collect data for Active Directory sites from configuration part…
q-roland Nov 6, 2025
95ac4d1
Merge branch 'v4' into pr-257-ad-sites
JonasBK Jun 12, 2026
9214843
Guard against null siteObject
JonasBK Jun 12, 2026
a615dce
avoid ACL edges to SiteServer and SiteSubnet
JonasBK Jun 12, 2026
6fd6029
add codex to gitignore
JonasBK Jun 12, 2026
20d164a
tests for site objects
JonasBK Jun 12, 2026
7946b56
Add IsServer
JonasBK Jun 12, 2026
4e96f6a
fix many things
JonasBK Jun 12, 2026
b81e13a
add more containers and objectClass
JonasBK Jun 12, 2026
8634f87
fix test and add Site to DCOnly
JonasBK Jun 12, 2026
c29e4dc
fix builtin contains
JonasBK Jun 15, 2026
5deab29
fix builtin container id
JonasBK Jun 15, 2026
78536b6
avoid SiteServers outside Sites container
JonasBK Jun 22, 2026
f3b0463
Merge branch 'v4' into AD_sites
rvazarkar Aug 12, 2026
55553d8
lowercase properties
JonasBK Aug 13, 2026
0d1feec
mv overload up
JonasBK Aug 13, 2026
4938653
mv ReadGPLink logic to helper file
JonasBK Aug 13, 2026
774f53a
fix LdapProducerQueryGenerator
JonasBK Aug 13, 2026
c337e00
add site props to ReservedAttributes
JonasBK Aug 13, 2026
6df5e10
Fix standalone site ACL collection
JonasBK Aug 19, 2026
4328b4c
Fix subnet site containment lookup
JonasBK Aug 19, 2026
393da92
Guard invalid subnet site references
JonasBK Aug 19, 2026
1efa94b
add comment
JonasBK Aug 19, 2026
1e26dd4
Include site properties in broad config queries
JonasBK Aug 19, 2026
f9354ef
Fix object class property casing assertions
JonasBK Aug 19, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -603,3 +603,6 @@ FodyWeavers.xsd
# JetBrains Rider
.idea/
*.sln.iml

# Codex local workspace state
.codex
9 changes: 8 additions & 1 deletion src/CommonLib/DirectoryObjects/DirectoryObjectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public static bool IsGMSA(this IDirectoryObject directoryObject) {
}

public static bool GetObjectIdentifier(this IDirectoryObject directoryObject, out string objectIdentifier) {
// Builtin container has a SID (always "S-1-5-32"). We use the ObjectGUID as ID like for other containers
if (directoryObject.TryGetArrayProperty(LDAPProperties.ObjectClass, out var objectClasses) &&
objectClasses.Contains(ObjectClass.BuiltinDomainClass, StringComparer.OrdinalIgnoreCase) &&
directoryObject.TryGetGuid(out objectIdentifier) && !string.IsNullOrWhiteSpace(objectIdentifier)) {
return true;
}

if (directoryObject.TryGetSecurityIdentifier(out objectIdentifier) && !string.IsNullOrWhiteSpace(objectIdentifier)) {
return true;
}
Expand Down Expand Up @@ -68,4 +75,4 @@ public static bool HasLAPS(this IDirectoryObject directoryObject) {

return false;
}
}
}
5 changes: 3 additions & 2 deletions src/CommonLib/Enums/CollectionMethod.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,15 @@ public enum CollectionMethod {
WebClientService = 1 << 21,
SmbInfo = 1 << 22,
NTLMRegistry = 1 << 23,
Site = 1 << 24,
//TODO: Re-introduce this when we're ready for Event Log collection
//EventLogs = 1 << 23,
LocalGroups = DCOM | RDP | LocalAdmin | PSRemote,
ComputerOnly = LocalGroups | Session | UserRights | CARegistry | DCRegistry | WebClientService | SmbInfo | NTLMRegistry,
DCOnly = ACL | Container | Group | ObjectProps | Trusts | GPOLocalGroup | CertServices,
DCOnly = ACL | Container | Group | ObjectProps | Trusts | GPOLocalGroup | CertServices | Site,

Default = Group | Session | Trusts | ACL | ObjectProps | LocalGroups | SPNTargets | Container | CertServices |
LdapServices | SmbInfo | WebClientService,
LdapServices | SmbInfo | WebClientService | Site,

All = Default | LoggedOn | GPOLocalGroup | UserRights | CARegistry | DCRegistry | WebClientService |
LdapServices | NTLMRegistry
Expand Down
3 changes: 3 additions & 0 deletions src/CommonLib/Enums/DataType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,8 @@ public static class DataType
public const string EnterpriseCAs = "enterprisecas";
public const string CertTemplates = "certtemplates";
public const string IssuancePolicies = "issuancepolicies";
public const string Sites = "sites";
public const string SiteServers = "siteservers";
public const string SiteSubnets = "sitesubnets";
}
}
3 changes: 2 additions & 1 deletion src/CommonLib/Enums/DirectoryPaths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public static class DirectoryPaths
public const string NTAuthStoreLocation = "CN=NTAuthCertificates,CN=Public Key Services,CN=Services";
public const string PKILocation = "CN=Public Key Services,CN=Services";
public const string ConfigLocation = "CN=Configuration";
public const string SitesLocation = "CN=Sites";
public const string OIDContainerLocation = "CN=OID,CN=Public Key Services,CN=Services";
}
}
}
3 changes: 2 additions & 1 deletion src/CommonLib/Enums/EdgeNames.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public static class EdgeNames
public const string WriteGPLink = "WriteGPLink";
public const string WriteAltSecurityIdentities = "WriteAltSecurityIdentities";
public const string WritePublicInformation = "WritePublicInformation";
public const string ServerIs = "ServerIs";

//CertAbuse edges
public const string WritePKIEnrollmentFlag = "WritePKIEnrollmentFlag";
Expand All @@ -32,4 +33,4 @@ public static class EdgeNames
public const string ManageCertificates = "ManageCertificates";
public const string Enroll = "Enroll";
}
}
}
2 changes: 2 additions & 0 deletions src/CommonLib/Enums/LDAPProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,5 +96,7 @@ public static class LDAPProperties
public const string LockOutObservationWindow = "lockoutobservationwindow";
public const string PrincipalName = "msds-principalname";
public const string GroupType = "grouptype";
public const string ServerReference = "serverreference";
public const string SiteObject = "siteobject";
}
}
5 changes: 4 additions & 1 deletion src/CommonLib/Enums/Labels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public enum Label
AIACA,
EnterpriseCA,
NTAuthStore,
IssuancePolicy
IssuancePolicy,
Site,
SiteServer,
SiteSubnet
}
}
7 changes: 6 additions & 1 deletion src/CommonLib/Enums/ObjectClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,15 @@ public static class ObjectClass {
public const string DomainClass = "domain";
public const string ContainerClass = "container";
public const string ConfigurationClass = "configuration";
public const string BuiltinDomainClass = "builtinDomain";
public const string SitesContainerClass = "sitesContainer";
public const string PKICertificateTemplateClass = "pKICertificateTemplate";
public const string PKIEnrollmentServiceClass = "pKIEnrollmentService";
public const string CertificationAuthorityClass = "certificationAuthority";
public const string OIDContainerClass = "msPKI-Enterprise-Oid";
public const string GMSAClass = "msds-groupmanagedserviceaccount";
public const string MSAClass = "msds-managedserviceaccount";
}
public const string SiteClass = "site";
public const string SiteServerClass = "server";
public const string SiteSubnetClass = "subnet";
}
41 changes: 40 additions & 1 deletion src/CommonLib/Helpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using SharpHoundCommonLib.Enums;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
using SharpHoundCommonLib.OutputTypes;

namespace SharpHoundCommonLib {
public static class Helpers {
Expand Down Expand Up @@ -79,6 +80,44 @@ public static IEnumerable<ParsedGPLink> SplitGPLinkProperty(string linkProp, boo
}
}

/// <summary>
/// Reads the "gplink" property from a directory object and converts the links into the acceptable SharpHound format
/// </summary>
/// <param name="entry"></param>
/// <param name="utils"></param>
/// <returns></returns>
public static IAsyncEnumerable<GPLink> ReadGPLinks(IDirectoryObject entry, ILdapUtils utils) {
if (entry.TryGetProperty(LDAPProperties.GPLink, out var links)) {
return ReadGPLinks(links, utils);
}

return AsyncEnumerable.Empty<GPLink>();
}

/// <summary>
/// Reads the "gplink" property and converts the links into the acceptable SharpHound format
/// </summary>
/// <param name="gpLink"></param>
/// <param name="utils"></param>
/// <returns></returns>
public static async IAsyncEnumerable<GPLink> ReadGPLinks(string gpLink, ILdapUtils utils) {
if (gpLink == null)
yield break;

foreach (var link in SplitGPLinkProperty(gpLink)) {
var enforced = link.Status.Equals("2");

var res = await utils.ResolveDistinguishedName(link.DistinguishedName);

if (res.Success) {
yield return new GPLink {
GUID = res.Principal.ObjectIdentifier,
IsEnforced = enforced
};
}
}
}

/// <summary>
/// Attempts to convert a SamAccountType value to the appropriate type enum
/// </summary>
Expand Down Expand Up @@ -304,4 +343,4 @@ public class ParsedGPLink {
public string DistinguishedName { get; set; }
public string Status { get; set; }
}
}
}
47 changes: 41 additions & 6 deletions src/CommonLib/LdapProducerQueryGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ public static GeneratedLdapParameters GenerateDefaultPartitionParameters(Collect

if (methods.HasFlag(CollectionMethod.ObjectProps) || methods.HasFlag(CollectionMethod.ACL) ||
methods.HasFlag(CollectionMethod.Container)) {
filter = filter.AddComputers().AddDomains().AddUsers().AddContainers().AddGPOs().AddOUs().AddGroups();
filter = filter.AddComputers().AddDomains().AddUsers().AddContainers().AddBuiltinDomains().AddGPOs()
.AddOUs().AddGroups();

if (methods.HasFlag(CollectionMethod.Container)) {
properties.AddRange(CommonProperties.ContainerProps);
Expand Down Expand Up @@ -103,13 +104,38 @@ public static GeneratedLdapParameters GenerateConfigurationPartitionParameters(C
properties.AddRange(CommonProperties.BaseQueryProps);
properties.AddRange(CommonProperties.TypeResolutionProps);

if (methods.HasFlag(CollectionMethod.ACL) || methods.HasFlag(CollectionMethod.ObjectProps) ||
methods.HasFlag(CollectionMethod.Container) || methods.HasFlag(CollectionMethod.CertServices)) {
filter = filter.AddContainers().AddConfiguration().AddCertificateTemplates().AddCertificateAuthorities()
.AddEnterpriseCertificationAuthorities().AddIssuancePolicies();
// These methods collect all supported object types from the configuration partition
var collectBroadConfigObjects = methods.HasFlag(CollectionMethod.ACL) ||
Comment thread
JonasBK marked this conversation as resolved.
methods.HasFlag(CollectionMethod.ObjectProps) ||
methods.HasFlag(CollectionMethod.Container);

if (collectBroadConfigObjects || methods.HasFlag(CollectionMethod.CertServices) ||
methods.HasFlag(CollectionMethod.Site)) {
filter = filter.AddContainers()
.AddConfiguration()
.AddSitesContainer();

if (collectBroadConfigObjects || methods.HasFlag(CollectionMethod.CertServices)) {
filter.AddCertificateTemplates()
.AddCertificateAuthorities()
.AddEnterpriseCertificationAuthorities()
.AddIssuancePolicies();
}
else if (methods.HasFlag(CollectionMethod.CARegistry)) {
filter.AddEnterpriseCertificationAuthorities();
}

if (collectBroadConfigObjects || methods.HasFlag(CollectionMethod.Site)) {
filter.AddSites()
.AddSiteServers()
.AddSiteSubnets();
}

if (methods.HasFlag(CollectionMethod.ObjectProps)) {
properties.AddRange(CommonProperties.ObjectPropsProps);
properties.AddRange(CommonProperties.SiteProps);
properties.AddRange(CommonProperties.SiteServerProps);
properties.AddRange(CommonProperties.SiteSubnetProps);
}

if (methods.HasFlag(CollectionMethod.ACL)) {
Expand All @@ -118,6 +144,7 @@ public static GeneratedLdapParameters GenerateConfigurationPartitionParameters(C

if (methods.HasFlag(CollectionMethod.Container)) {
properties.AddRange(CommonProperties.ContainerProps);
properties.AddRange(CommonProperties.SiteSubnetProps);
}

if (methods.HasFlag(CollectionMethod.CertServices)) {
Expand All @@ -131,6 +158,14 @@ public static GeneratedLdapParameters GenerateConfigurationPartitionParameters(C
properties.AddRange(CommonProperties.CertAbuseProps);
Comment thread
JonasBK marked this conversation as resolved.
}

if (methods.HasFlag(CollectionMethod.Site))
{
properties.AddRange(CommonProperties.SiteProps);
properties.AddRange(CommonProperties.SiteServerProps);
properties.AddRange(CommonProperties.SiteSubnetProps);
properties.AddRange(CommonProperties.ACLProps);
}

return new GeneratedLdapParameters {
Filter = filter,
Attributes = properties.Distinct().ToArray()
Expand All @@ -152,4 +187,4 @@ public static GeneratedLdapParameters GenerateConfigurationPartitionParameters(C
public class GeneratedLdapParameters {
public string[] Attributes { get; set; }
public LdapFilter Filter { get; set; }
}
}
24 changes: 22 additions & 2 deletions src/CommonLib/LdapQueries/CommonProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ public static class CommonProperties
LDAPProperties.Flags
};

public static readonly string[] ObjectID = { LDAPProperties.ObjectSID, LDAPProperties.ObjectGUID };
public static readonly string[] ObjectID =
{ LDAPProperties.ObjectSID, LDAPProperties.ObjectGUID, LDAPProperties.ObjectClass };
public static readonly string[] ObjectSID = { LDAPProperties.ObjectSID };
public static readonly string[] GPCFileSysPath = { LDAPProperties.GPCFileSYSPath };

Expand Down Expand Up @@ -98,5 +99,24 @@ public static class CommonProperties
public static readonly string[] StealthProperties = {
LDAPProperties.HomeDirectory, LDAPProperties.ScriptPath, LDAPProperties.ProfilePath
};

public static readonly string[] SiteProps =
{
LDAPProperties.DisplayName, LDAPProperties.Name, LDAPProperties.ObjectGUID, LDAPProperties.GPLink,
LDAPProperties.GroupPolicyOptions, LDAPProperties.ObjectClass, LDAPProperties.Description,
LDAPProperties.WhenCreated
};

public static readonly string[] SiteServerProps =
{
LDAPProperties.DisplayName, LDAPProperties.Name, LDAPProperties.ObjectGUID, LDAPProperties.ObjectClass, LDAPProperties.DNSHostName,
LDAPProperties.ServerReference
};

public static readonly string[] SiteSubnetProps =
{
LDAPProperties.DisplayName, LDAPProperties.Name, LDAPProperties.CanonicalName, LDAPProperties.ObjectGUID, LDAPProperties.ObjectClass,
LDAPProperties.SiteObject
};
}
}
}
63 changes: 62 additions & 1 deletion src/CommonLib/LdapQueries/LdapFilter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,17 @@ public LdapFilter AddContainers(params string[] conditions) {
return this;
}

/// <summary>
/// Add a filter that will include Builtin domain container objects.
/// </summary>
/// <param name="conditions"></param>
/// <returns></returns>
public LdapFilter AddBuiltinDomains(params string[] conditions) {
_filterParts.Add(BuildString("(objectClass=builtinDomain)", conditions));

return this;
}

/// <summary>
/// Add a filter that will include Configuration objects
/// </summary>
Expand All @@ -140,6 +151,17 @@ public LdapFilter AddConfiguration(params string[] conditions) {
return this;
}

/// <summary>
/// Add a filter that will include Sites container objects.
/// </summary>
/// <param name="conditions"></param>
/// <returns></returns>
public LdapFilter AddSitesContainer(params string[] conditions) {
_filterParts.Add(BuildString("(objectClass=sitesContainer)", conditions));

return this;
}

/// <summary>
/// Add a filter that will include Computer objects
///
Expand Down Expand Up @@ -215,6 +237,45 @@ public LdapFilter AddComputersNoMSAs(params string[] conditions) {
return this;
}

/// <summary>
/// Add a filter that will match Active Directory sites
/// </summary>
/// <param name="conditions"></param>
/// <returns></returns>
public LdapFilter AddSites(params string[] conditions)
{
_filterParts.Add(BuildString(
"(objectClass=site)",
conditions));
return this;
}

/// <summary>
/// Add a filter that will match Active Directory site servers
/// </summary>
/// <param name="conditions"></param>
/// <returns></returns>
public LdapFilter AddSiteServers(params string[] conditions)
{
_filterParts.Add(BuildString(
"(objectClass=server)",
conditions));
return this;
}

/// <summary>
/// Add a filter that will match Active Directory site subnets
/// </summary>
/// <param name="conditions"></param>
/// <returns></returns>
public LdapFilter AddSiteSubnets(params string[] conditions)
{
_filterParts.Add(BuildString(
"(objectClass=subnet)",
conditions));
return this;
}

/// <summary>
/// Adds a generic user specified filter
/// </summary>
Expand Down Expand Up @@ -274,4 +335,4 @@ public IEnumerable<string> GetFilterList() {
}
}
}
}
}
Loading
Loading