From 7a1edf21bcdde918f5899dd00aa276bc09884c1c Mon Sep 17 00:00:00 2001 From: jknudsen Date: Wed, 15 May 2024 09:22:49 +0200 Subject: [PATCH] feat: featureflag for auto tagging T0 AD group members --- cmd/api/src/daemons/datapipe/agi.go | 15 +++++++++++---- cmd/api/src/daemons/datapipe/analysis.go | 2 +- cmd/api/src/model/appcfg/flag.go | 8 ++++++++ packages/go/analysis/ad/queries.go | 14 ++++++++------ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/cmd/api/src/daemons/datapipe/agi.go b/cmd/api/src/daemons/datapipe/agi.go index 3a002e34b..0baf2e9ee 100644 --- a/cmd/api/src/daemons/datapipe/agi.go +++ b/cmd/api/src/daemons/datapipe/agi.go @@ -32,6 +32,7 @@ import ( "github.com/specterops/bloodhound/log" "github.com/specterops/bloodhound/src/database" "github.com/specterops/bloodhound/src/model" + "github.com/specterops/bloodhound/src/model/appcfg" "github.com/specterops/bloodhound/src/services/agi" ) @@ -154,20 +155,26 @@ func ParallelTagAzureTierZero(ctx context.Context, db graph.Database) error { return nil } -func TagActiveDirectoryTierZero(ctx context.Context, db graph.Database) error { +func TagActiveDirectoryTierZero(ctx context.Context, db database.Database, graphDB graph.Database) error { defer log.Measure(log.LevelInfo, "Finished tagging Active Directory Tier Zero")() - if domains, err := adAnalysis.FetchAllDomains(ctx, db); err != nil { + autoTagMembersFlag, err := db.GetFlagByKey(ctx, appcfg.FeatureAutoTagT0ADMembers) + if err != nil { + log.Errorf("error getting AutoTagT0ADMembers feature flag: %w", err) + return err + } + + if domains, err := adAnalysis.FetchAllDomains(ctx, graphDB); err != nil { return err } else { for _, domain := range domains { - if roots, err := adAnalysis.FetchActiveDirectoryTierZeroRoots(ctx, db, domain); err != nil { + if roots, err := adAnalysis.FetchActiveDirectoryTierZeroRoots(ctx, graphDB, domain, autoTagMembersFlag.Enabled); err != nil { return err } else { properties := graph.NewProperties() properties.Set(common.SystemTags.String(), ad.AdminTierZero) - if err := db.WriteTransaction(ctx, func(tx graph.Transaction) error { + if err := graphDB.WriteTransaction(ctx, func(tx graph.Transaction) error { return tx.Nodes().Filter(query.InIDs(query.Node(), roots.IDs()...)).Update(properties) }); err != nil { return err diff --git a/cmd/api/src/daemons/datapipe/analysis.go b/cmd/api/src/daemons/datapipe/analysis.go index 0b8861687..9b38cc58f 100644 --- a/cmd/api/src/daemons/datapipe/analysis.go +++ b/cmd/api/src/daemons/datapipe/analysis.go @@ -60,7 +60,7 @@ func RunAnalysisOperations(ctx context.Context, db database.Database, graphDB gr collectedErrors = append(collectedErrors, fmt.Errorf("asset group isolation tagging failed: %w", err)) } - if err := TagActiveDirectoryTierZero(ctx, graphDB); err != nil { + if err := TagActiveDirectoryTierZero(ctx, db, graphDB); err != nil { collectedErrors = append(collectedErrors, fmt.Errorf("active directory tier zero tagging failed: %w", err)) } diff --git a/cmd/api/src/model/appcfg/flag.go b/cmd/api/src/model/appcfg/flag.go index 406770749..edd00498f 100644 --- a/cmd/api/src/model/appcfg/flag.go +++ b/cmd/api/src/model/appcfg/flag.go @@ -32,6 +32,7 @@ const ( FeatureAdcs = "adcs" FeatureClearGraphData = "clear_graph_data" FeatureRiskExposureNewCalculation = "risk_exposure_new_calculation" + FeatureAutoTagT0ADMembers = "auto_tag_t0_ad_members" ) // AvailableFlags returns a FeatureFlagSet of expected feature flags. Feature flag defaults introduced here will become the initial @@ -101,6 +102,13 @@ func AvailableFlags() FeatureFlagSet { Enabled: false, UserUpdatable: false, }, + FeatureAutoTagT0ADMembers: { + Key: FeatureAutoTagT0ADMembers, + Name: "Automatically add members of Tier Zero AD groups to Tier Zero", + Description: "Members incl. nested members of AD Tier Zero groups are automatically added to Tier Zero during analysis.", + Enabled: true, + UserUpdatable: true, + }, } } diff --git a/packages/go/analysis/ad/queries.go b/packages/go/analysis/ad/queries.go index 88d71f238..bdc1d44df 100644 --- a/packages/go/analysis/ad/queries.go +++ b/packages/go/analysis/ad/queries.go @@ -91,7 +91,7 @@ func FetchAllDomains(ctx context.Context, db graph.Database) ([]*graph.Node, err }) } -func FetchActiveDirectoryTierZeroRoots(ctx context.Context, db graph.Database, domain *graph.Node) (graph.NodeSet, error) { +func FetchActiveDirectoryTierZeroRoots(ctx context.Context, db graph.Database, domain *graph.Node, autoTagMembersFlag bool) (graph.NodeSet, error) { defer log.LogAndMeasure(log.LevelInfo, "FetchActiveDirectoryTierZeroRoots")() if domainSID, err := domain.Properties.Get(common.ObjectID.String()).String(); err != nil { @@ -116,11 +116,13 @@ func FetchActiveDirectoryTierZeroRoots(ctx context.Context, db graph.Database, d attackPathRoots.AddSet(wellKnownTierZeroNodes) } - // Pull in all group members of attack path roots - if allGroupMembers, err := FetchAllGroupMembers(ctx, db, attackPathRoots); err != nil { - return nil, err - } else { - attackPathRoots.AddSet(allGroupMembers) + if (autoTagMembersFlag) { + // Pull in all group members of attack path roots + if allGroupMembers, err := FetchAllGroupMembers(ctx, db, attackPathRoots); err != nil { + return nil, err + } else { + attackPathRoots.AddSet(allGroupMembers) + } } // Add all enforced GPO nodes to the attack path roots