Skip to content

Commit

Permalink
Merge branch 'adcs-poc' into adcs-feature-flag
Browse files Browse the repository at this point in the history
  • Loading branch information
juggernot325 committed Nov 15, 2023
2 parents b722bdd + 10c9713 commit cf2c335
Show file tree
Hide file tree
Showing 11 changed files with 204 additions and 131 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/jira-issue-transfer.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,14 @@

name: Jira Issue Transfer

runs-on: self-hosted

on:
issues:
types:
- opened

jobs:
build:
runs-on: ubuntu-latest
runs-on: self-hosted
steps:
- name: Login
uses: atlassian/gajira-login@v3
Expand Down
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, adcsEnabled bool) (*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, adcsEnabled bool) (*analysis.A
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, adcsEnabled); err != nil {
return &aggregateStats, err
Expand Down
1 change: 1 addition & 0 deletions cmd/api/src/daemons/datapipe/convertors.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func convertEnterpriseCAData(data []ein.EnterpriseCA) ConvertedData {

for _, enterpriseca := range data {
converted.NodeProps = append(converted.NodeProps, ein.ConvertObjectToNode(enterpriseca.IngestBase, ad.EnterpriseCA))
converted.NodeProps = append(converted.NodeProps, ein.ParseCARegistryProperties(enterpriseca))
converted.RelProps = append(converted.RelProps, ein.ParseEnterpriseCAMiscData(enterpriseca)...)

if rel := ein.ParseObjectContainer(enterpriseca.IngestBase, ad.EnterpriseCA); rel.IsValid() {
Expand Down
3 changes: 2 additions & 1 deletion cmd/ui/src/ducks/entityinfo/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ export interface EnterpriseCAInfo extends EntityInfo {
enrollmentagentrestrictionscollected: boolean;
flags: 10;
hasbasicconstraints: boolean;
isuserspecifiessanenabled: boolean;
hasenrollmentagentrestrictions?: boolean;
isuserspecifiessanenabled?: boolean;
isuserspecifiessanenabledcollected: boolean;
description?: string;
};
Expand Down
16 changes: 16 additions & 0 deletions packages/cue/bh/ad/ad.cue
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,27 @@ CASecurityCollected: types.#StringEnum & {
representation: "casecuritycollected"
}

HasEnrollmentAgentRestrictions: types.#StringEnum & {
symbol: "HasEnrollmentAgentRestrictions"
schema: "ad"
name: "Has Enrollment Agent Restrictions"
representation: "hasenrollmentagentrestrictions"
}

EnrollmentAgentRestrictionsCollected: types.#StringEnum & {
symbol: "EnrollmentAgentRestrictionsCollected"
schema: "ad"
name: "Enrollment Agent Restrictions Collected"
representation: "enrollmentagentrestrictionscollected"
}

IsUserSpecifiesSanEnabled: types.#StringEnum & {
symbol: "IsUserSpecifiesSanEnabled"
schema: "ad"
name: "Is User Specifies San Enabled"
representation: "isuserspecifiessanenabled"
}

IsUserSpecifiesSanEnabledCollected: types.#StringEnum & {
symbol: "IsUserSpecifiesSanEnabledCollected"
schema: "ad"
Expand Down Expand Up @@ -462,7 +476,9 @@ Properties: [
CertName,
CertThumbprint,
CertThumbprints,
HasEnrollmentAgentRestrictions,
EnrollmentAgentRestrictionsCollected,
IsUserSpecifiesSanEnabled,
IsUserSpecifiesSanEnabledCollected,
HasBasicConstraints,
BasicConstraintPathLength,
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.

Loading

0 comments on commit cf2c335

Please sign in to comment.