Skip to content

Commit

Permalink
chore: move post local groups to bhce packages for use in BHE, run li…
Browse files Browse the repository at this point in the history
…cense generation for headers (#209)
  • Loading branch information
urangel authored Nov 15, 2023
1 parent b09e04d commit 4bd8319
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 121 deletions.
122 changes: 1 addition & 121 deletions cmd/api/src/analysis/ad/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,133 +18,13 @@ package ad

import (
"context"
"fmt"

"github.com/specterops/bloodhound/analysis"
adAnalysis "github.com/specterops/bloodhound/analysis/ad"
"github.com/specterops/bloodhound/analysis/impact"
"github.com/specterops/bloodhound/dawgs/graph"
"github.com/specterops/bloodhound/dawgs/util/channels"
"github.com/specterops/bloodhound/graphschema/ad"
"github.com/specterops/bloodhound/log"
)

func PostLocalGroups(ctx context.Context, db graph.Database, localGroupExpansions impact.PathAggregator) (*analysis.AtomicPostProcessingStats, error) {
var (
adminGroupSuffix = "-544"
psRemoteGroupSuffix = "-580"
dcomGroupSuffix = "-562"
)

if computers, err := adAnalysis.FetchComputers(ctx, db); err != nil {
return &analysis.AtomicPostProcessingStats{}, err
} else {
var (
threadSafeLocalGroupExpansions = impact.NewThreadSafeAggregator(localGroupExpansions)
operation = analysis.NewPostRelationshipOperation(ctx, db, "LocalGroup Post Processing")
)

for idx, computer := range computers.ToArray() {
computerID := graph.ID(computer)

if idx > 0 && idx%10000 == 0 {
log.Infof("Post processed %d active directory computers", idx)
}

if err := operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if entities, err := adAnalysis.FetchLocalGroupBitmapForComputer(tx, computerID, dcomGroupSuffix); err != nil {
return err
} else {
for _, admin := range entities.Slice() {
nextJob := analysis.CreatePostRelationshipJob{
FromID: graph.ID(admin),
ToID: computerID,
Kind: ad.ExecuteDCOM,
}

if !channels.Submit(ctx, outC, nextJob) {
return nil
}
}

return nil
}
}); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed submitting reader for operation involving computer %d: %w", computerID, err)
}

if err := operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if entities, err := adAnalysis.FetchLocalGroupBitmapForComputer(tx, computerID, psRemoteGroupSuffix); err != nil {
return err
} else {
for _, admin := range entities.Slice() {
nextJob := analysis.CreatePostRelationshipJob{
FromID: graph.ID(admin),
ToID: computerID,
Kind: ad.CanPSRemote,
}

if !channels.Submit(ctx, outC, nextJob) {
return nil
}
}

return nil
}
}); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed submitting reader for operation involving computer %d: %w", computerID, err)
}

if err := operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if entities, err := adAnalysis.FetchLocalGroupBitmapForComputer(tx, computerID, adminGroupSuffix); err != nil {
return err
} else {
for _, admin := range entities.Slice() {
nextJob := analysis.CreatePostRelationshipJob{
FromID: graph.ID(admin),
ToID: computerID,
Kind: ad.AdminTo,
}

if !channels.Submit(ctx, outC, nextJob) {
return nil
}
}

return nil
}
}); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed submitting reader for operation involving computer %d: %w", computerID, err)
}

if err := operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if entities, err := adAnalysis.FetchRDPEntityBitmapForComputerWithUnenforcedURA(tx, computerID, threadSafeLocalGroupExpansions); err != nil {
return err
} else {
for _, rdp := range entities.Slice() {
nextJob := analysis.CreatePostRelationshipJob{
FromID: graph.ID(rdp),
ToID: computerID,
Kind: ad.CanRDP,
}

if !channels.Submit(ctx, outC, nextJob) {
return nil
}
}
}

return nil
}); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed submitting reader for operation involving computer %d: %w", computerID, err)
}
}

log.Infof("Finished post-processing %d active directory computers", computers.GetCardinality())
return &operation.Stats, operation.Done()
}
}

func Post(ctx context.Context, db graph.Database) (*analysis.AtomicPostProcessingStats, error) {
aggregateStats := analysis.NewAtomicPostProcessingStats()
if stats, err := analysis.DeleteTransitEdges(ctx, db, ad.Entity, ad.Entity, adAnalysis.PostProcessedRelationships()...); err != nil {
Expand All @@ -155,7 +35,7 @@ func Post(ctx context.Context, db graph.Database) (*analysis.AtomicPostProcessin
return &aggregateStats, err
} else if groupExpansions, err := adAnalysis.ExpandAllRDPLocalGroups(ctx, db); err != nil {
return &aggregateStats, err
} else if localGroupStats, err := PostLocalGroups(ctx, db, groupExpansions); err != nil {
} else if localGroupStats, err := adAnalysis.PostLocalGroups(ctx, db, groupExpansions); err != nil {
return &aggregateStats, err
} else if adcsStats, err := adAnalysis.PostADCS(ctx, db, groupExpansions); err != nil {
return &aggregateStats, err
Expand Down
117 changes: 117 additions & 0 deletions packages/go/analysis/ad/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package ad

import (
"context"
"fmt"

"github.com/RoaringBitmap/roaring/roaring64"
"github.com/specterops/bloodhound/analysis"
Expand Down Expand Up @@ -190,6 +191,122 @@ func getLAPSComputersForDomain(tx graph.Transaction, domain *graph.Node) ([]grap
}
}

func PostLocalGroups(ctx context.Context, db graph.Database, localGroupExpansions impact.PathAggregator) (*analysis.AtomicPostProcessingStats, error) {
var (
adminGroupSuffix = "-544"
psRemoteGroupSuffix = "-580"
dcomGroupSuffix = "-562"
)

if computers, err := FetchComputers(ctx, db); err != nil {
return &analysis.AtomicPostProcessingStats{}, err
} else {
var (
threadSafeLocalGroupExpansions = impact.NewThreadSafeAggregator(localGroupExpansions)
operation = analysis.NewPostRelationshipOperation(ctx, db, "LocalGroup Post Processing")
)

for idx, computer := range computers.ToArray() {
computerID := graph.ID(computer)

if idx > 0 && idx%10000 == 0 {
log.Infof("Post processed %d active directory computers", idx)
}

if err := operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if entities, err := FetchLocalGroupBitmapForComputer(tx, computerID, dcomGroupSuffix); err != nil {
return err
} else {
for _, admin := range entities.Slice() {
nextJob := analysis.CreatePostRelationshipJob{
FromID: graph.ID(admin),
ToID: computerID,
Kind: ad.ExecuteDCOM,
}

if !channels.Submit(ctx, outC, nextJob) {
return nil
}
}

return nil
}
}); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed submitting reader for operation involving computer %d: %w", computerID, err)
}

if err := operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if entities, err := FetchLocalGroupBitmapForComputer(tx, computerID, psRemoteGroupSuffix); err != nil {
return err
} else {
for _, admin := range entities.Slice() {
nextJob := analysis.CreatePostRelationshipJob{
FromID: graph.ID(admin),
ToID: computerID,
Kind: ad.CanPSRemote,
}

if !channels.Submit(ctx, outC, nextJob) {
return nil
}
}

return nil
}
}); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed submitting reader for operation involving computer %d: %w", computerID, err)
}

if err := operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if entities, err := FetchLocalGroupBitmapForComputer(tx, computerID, adminGroupSuffix); err != nil {
return err
} else {
for _, admin := range entities.Slice() {
nextJob := analysis.CreatePostRelationshipJob{
FromID: graph.ID(admin),
ToID: computerID,
Kind: ad.AdminTo,
}

if !channels.Submit(ctx, outC, nextJob) {
return nil
}
}

return nil
}
}); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed submitting reader for operation involving computer %d: %w", computerID, err)
}

if err := operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if entities, err := FetchRDPEntityBitmapForComputerWithUnenforcedURA(tx, computerID, threadSafeLocalGroupExpansions); err != nil {
return err
} else {
for _, rdp := range entities.Slice() {
nextJob := analysis.CreatePostRelationshipJob{
FromID: graph.ID(rdp),
ToID: computerID,
Kind: ad.CanRDP,
}

if !channels.Submit(ctx, outC, nextJob) {
return nil
}
}
}

return nil
}); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed submitting reader for operation involving computer %d: %w", computerID, err)
}
}

log.Infof("Finished post-processing %d active directory computers", computers.GetCardinality())
return &operation.Stats, operation.Done()
}
}

func ExpandLocalGroupMembership(tx graph.Transaction, candidates graph.NodeSet) (graph.NodeSet, error) {
if paths, err := ExpandLocalGroupMembershipPaths(tx, candidates); err != nil {
return nil, err
Expand Down
15 changes: 15 additions & 0 deletions packages/go/dawgs/graph/mocks/graph.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 4bd8319

Please sign in to comment.