Skip to content

Commit

Permalink
chore: steel thread cleanup (#365)
Browse files Browse the repository at this point in the history
* BED-3858 - Fix not ingesting tenant count during azure analysis (#328)

Co-authored-by: Irshad Ahmed <[email protected]>

* ESC9a Edge Composition (#354)

* feat: esc9a post

* test: add esc9 test

* chore: add harness files

* fix: regen schema after merge

* chore: fix small nits

* chore: cleanup cert template new function

* chore: add missing props

* wip: 9a composition

* fix: treat failure to grab properties as true

* wip: esc9a composition

* wip: esc9a composition

* feat+chore: add depth controls to dawgs patterns

* wip: esc9a composition

* fix: do not drop the current segment if the next pattern is optional

* wip: esc9a composition

* fix: update other continuations to respect depth correctly

* wip: edge comp

* fix: swap

* chore: remove unnecessary logs

* feat: esc9a post

* test: add esc9 test

* chore: fix small nits

* wip: 9a composition

* wip: esc9a composition

* wip: esc9a composition

* feat+chore: add depth controls to dawgs patterns

* wip: esc9a composition

* fix: do not drop the current segment if the next pattern is optional

* wip: esc9a composition

* fix: update other continuations to respect depth correctly

* wip: edge comp

* fix: swap

* chore: remove unnecessary logs

* test: add test covering esc9a edge comp

* chore: revert random re-ordering

* chore: handle negative min/max depth on continuations

---------

Co-authored-by: John Hopper <[email protected]>

* docs: Add to ESC3 abuse info (#350)

* docs: add note in ESC6 abuse info (#356)

* feat: Add ADCS pre-built queries (#342)

Co-authored-by: Rohan Vazarkar <[email protected]>

* feat: esc6a edge composition (#359)

* feat: esc6a edge composition

* chore: allow composition accordion to show for 6a

* fix: add trustedby rel to path4 pattern, use outboundwithdepth for optional memberof traversal

* chore: update dcfor pattern to use outboundwithdepth for optional group membership

* ESC10a Post Processing (#360)

* wip: initial ESC10a post

* test: all the tests for esc10a

* chore: add edges to post processed

* chore: add harnessgen script

* test: remove edges from harness

* chore: don't exit loop if we hit an error, continue instead

* chore: log and continue

* feat: filter out ESC3 false positives (#351)

* feat: filter out ESC3 false positives

* fix: handle esc3 filtering without retraversal

* fix: handle esc3 filtering without retraversal

* fix: handle esc3 filtering without retraversal

* chore: rename function for re-use

* chore: log and continue

---------

Co-authored-by: rvazarkar <[email protected]>
Co-authored-by: Rohan Vazarkar <[email protected]>

* chore: patch EULAAcceptance bypass to only run if the current user is set to false

* fix: incorrect usage of RemoteAddr
fix: unnecessary AuditData() calls
fix: use pointers for AuditEntry.Model assignments so successful actions can record updated fields like ID

---------

Co-authored-by: mistahj67 <[email protected]>
Co-authored-by: Irshad Ahmed <[email protected]>
Co-authored-by: Rohan Vazarkar <[email protected]>
Co-authored-by: John Hopper <[email protected]>
Co-authored-by: Jonas Bülow Knudsen <[email protected]>
Co-authored-by: Ulises Rangel <[email protected]>
Co-authored-by: rvazarkar <[email protected]>
  • Loading branch information
8 people authored Jan 30, 2024
1 parent 602d04b commit 6fe1f94
Show file tree
Hide file tree
Showing 55 changed files with 6,284 additions and 207 deletions.
343 changes: 341 additions & 2 deletions cmd/api/src/analysis/ad/adcs_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ package ad_test

import (
"context"

"github.com/specterops/bloodhound/analysis"
"github.com/specterops/bloodhound/analysis/impact"
"github.com/specterops/bloodhound/graphschema"

ad2 "github.com/specterops/bloodhound/analysis/ad"
Expand Down Expand Up @@ -639,6 +639,33 @@ func TestADCSESC9a(t *testing.T) {
}
return nil
})

db.ReadTransaction(context.Background(), func(tx graph.Transaction) error {
if results, err := ops.FetchRelationships(tx.Relationships().Filterf(func() graph.Criteria {
return query.Kind(query.Relationship(), ad.ADCSESC9a)
})); err != nil {
t.Fatalf("error fetching esc9a edges in integration test; %v", err)
} else {
assert.Equal(t, 1, len(results))
edge := results[0]

if edgeComp, err := ad2.GetEdgeCompositionPath(context.Background(), db, edge); err != nil {
t.Fatalf("error getting edge composition for esc9: %v", err)
} else {
nodes := edgeComp.AllNodes().Slice()
assert.Contains(t, nodes, harness.ESC9AHarness.Attacker)
assert.Contains(t, nodes, harness.ESC9AHarness.Victim)
assert.Contains(t, nodes, harness.ESC9AHarness.Domain)
assert.Contains(t, nodes, harness.ESC9AHarness.NTAuthStore)
assert.Contains(t, nodes, harness.ESC9AHarness.RootCA)
assert.Contains(t, nodes, harness.ESC9AHarness.DC)
assert.Contains(t, nodes, harness.ESC9AHarness.EnterpriseCA)
assert.Contains(t, nodes, harness.ESC9AHarness.CertTemplate)
}
}

return nil
})
})

}
Expand Down Expand Up @@ -682,7 +709,6 @@ func TestADCSESC6a(t *testing.T) {
})
}
}

}
operation.Done()

Expand Down Expand Up @@ -812,6 +838,58 @@ func TestADCSESC6a(t *testing.T) {
require.Contains(t, names, "Group1", "Group2")
}

if edge, err := tx.Relationships().Filterf(func() graph.Criteria {
return query.And(
query.Kind(query.Relationship(), ad.ADCSESC6a),
query.Equals(query.StartProperty(common.Name.String()), "Group1"),
)
}).First(); err != nil {
t.Fatalf("error fetching esc6a edges in integration test; %v", err)
} else {
composition, err := ad2.GetADCSESC6aEdgeComposition(context.Background(), db, edge)
require.Nil(t, err)
names := []string{}
for _, node := range composition.AllNodes() {
name, _ := node.Properties.Get(common.Name.String()).String()
names = append(names, name)
}
require.Equal(t, 8, len(composition.AllNodes()))
require.Contains(t, names, "Group1")
require.Contains(t, names, "Group0")
require.Contains(t, names, "CertTemplate1")
require.Contains(t, names, "EnterpriseCA")
require.Contains(t, names, "RootCA")
require.Contains(t, names, "NTAuthStore")
require.Contains(t, names, "DC")
require.Contains(t, names, "Domain")
}

if edge, err := tx.Relationships().Filterf(func() graph.Criteria {
return query.And(
query.Kind(query.Relationship(), ad.ADCSESC6a),
query.Equals(query.StartProperty(common.Name.String()), "Group2"),
)
}).First(); err != nil {
t.Fatalf("error fetching esc6a edges in integration test; %v", err)
} else {
composition, err := ad2.GetADCSESC6aEdgeComposition(context.Background(), db, edge)
require.Nil(t, err)
names := []string{}
for _, node := range composition.AllNodes() {
name, _ := node.Properties.Get(common.Name.String()).String()
names = append(names, name)
}
require.Equal(t, 8, len(composition.AllNodes()))
require.Contains(t, names, "Group2")
require.Contains(t, names, "Group0")
require.Contains(t, names, "CertTemplate2")
require.Contains(t, names, "EnterpriseCA")
require.Contains(t, names, "RootCA")
require.Contains(t, names, "NTAuthStore")
require.Contains(t, names, "DC")
require.Contains(t, names, "Domain")
}

return nil
})
})
Expand Down Expand Up @@ -878,3 +956,264 @@ func TestADCSESC6a(t *testing.T) {
})
})
}

func FetchADCSPrereqs(db graph.Database) (impact.PathAggregator, []*graph.Node, []*graph.Node, []*graph.Node, ad2.ADCSCache, error) {
if expansions, err := ad2.ExpandAllRDPLocalGroups(context.Background(), db); err != nil {
return nil, nil, nil, nil, ad2.ADCSCache{}, err
} else if eca, err := ad2.FetchNodesByKind(context.Background(), db, ad.EnterpriseCA); err != nil {
return nil, nil, nil, nil, ad2.ADCSCache{}, err
} else if certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate); err != nil {
return nil, nil, nil, nil, ad2.ADCSCache{}, err
} else if domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain); err != nil {
return nil, nil, nil, nil, ad2.ADCSCache{}, err
} else {
cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, eca, certTemplates)
return expansions, eca, certTemplates, domains, cache, nil
}
}

func TestADCSESC10a(t *testing.T) {
testContext := integration.NewGraphTestContext(t, graphschema.DefaultGraphSchema())

testContext.DatabaseTestWithSetup(func(harness *integration.HarnessDetails) error {
harness.ESC10aPrincipalHarness.Setup(testContext)
return nil
}, func(harness integration.HarnessDetails, db graph.Database) {
operation := analysis.NewPostRelationshipOperation(context.Background(), db, "ADCS Post Process Test - ESC10a")

groupExpansions, enterpriseCertAuthorities, _, domains, cache, err := FetchADCSPrereqs(db)
require.Nil(t, err)

for _, domain := range domains {
innerDomain := domain

for _, enterpriseCA := range enterpriseCertAuthorities {
if cache.DoesCAChainProperlyToDomain(enterpriseCA, innerDomain) {
innerEnterpriseCA := enterpriseCA

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := ad2.PostADCSESC10a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC10a.String(), err)
} else {
return nil
}

return nil
})
}
}
}
err = operation.Done()
require.Nil(t, err)

db.ReadTransaction(context.Background(), func(tx graph.Transaction) error {
if results, err := ops.FetchStartNodes(tx.Relationships().Filterf(func() graph.Criteria {
return query.Kind(query.Relationship(), ad.ADCSESC10a)
})); err != nil {
t.Fatalf("error fetching esc10a edges in integration test; %v", err)
} else {
require.Equal(t, 6, len(results))

require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group1))
require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group2))
require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group3))
require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group4))
require.True(t, results.Contains(harness.ESC10aPrincipalHarness.Group5))
require.True(t, results.Contains(harness.ESC10aPrincipalHarness.User2))

}
return nil
})
})

testContext.DatabaseTestWithSetup(func(harness *integration.HarnessDetails) error {
harness.ESC10aHarness1.Setup(testContext)
return nil
}, func(harness integration.HarnessDetails, db graph.Database) {
operation := analysis.NewPostRelationshipOperation(context.Background(), db, "ADCS Post Process Test - ESC10a")

groupExpansions, enterpriseCertAuthorities, _, domains, cache, err := FetchADCSPrereqs(db)
require.Nil(t, err)

for _, domain := range domains {
innerDomain := domain

for _, enterpriseCA := range enterpriseCertAuthorities {
if cache.DoesCAChainProperlyToDomain(enterpriseCA, innerDomain) {
innerEnterpriseCA := enterpriseCA

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := ad2.PostADCSESC10a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC10a.String(), err)
} else {
return nil
}

return nil
})
}
}
}
err = operation.Done()
require.Nil(t, err)

db.ReadTransaction(context.Background(), func(tx graph.Transaction) error {
if results, err := ops.FetchStartNodes(tx.Relationships().Filterf(func() graph.Criteria {
return query.Kind(query.Relationship(), ad.ADCSESC10a)
})); err != nil {
t.Fatalf("error fetching esc10a edges in integration test; %v", err)
} else {
require.Equal(t, 3, len(results))

require.True(t, results.Contains(harness.ESC10aHarness1.Group1))
require.True(t, results.Contains(harness.ESC10aHarness1.Group2))
require.True(t, results.Contains(harness.ESC10aHarness1.Group3))

}
return nil
})
})

testContext.DatabaseTestWithSetup(func(harness *integration.HarnessDetails) error {
harness.ESC10aHarness2.Setup(testContext)
return nil
}, func(harness integration.HarnessDetails, db graph.Database) {
operation := analysis.NewPostRelationshipOperation(context.Background(), db, "ADCS Post Process Test - ESC10a")

groupExpansions, enterpriseCertAuthorities, _, domains, cache, err := FetchADCSPrereqs(db)
require.Nil(t, err)

for _, domain := range domains {
innerDomain := domain

for _, enterpriseCA := range enterpriseCertAuthorities {
if cache.DoesCAChainProperlyToDomain(enterpriseCA, innerDomain) {
innerEnterpriseCA := enterpriseCA

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := ad2.PostADCSESC10a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC10a.String(), err)
} else {
return nil
}

return nil
})
}
}
}
err = operation.Done()
require.Nil(t, err)

db.ReadTransaction(context.Background(), func(tx graph.Transaction) error {
if results, err := ops.FetchStartNodes(tx.Relationships().Filterf(func() graph.Criteria {
return query.Kind(query.Relationship(), ad.ADCSESC10a)
})); err != nil {
t.Fatalf("error fetching esc10a edges in integration test; %v", err)
} else {
require.Equal(t, 4, len(results))

require.True(t, results.Contains(harness.ESC10aHarness2.Group6))
require.True(t, results.Contains(harness.ESC10aHarness2.Group5))
require.True(t, results.Contains(harness.ESC10aHarness2.Computer5))
require.True(t, results.Contains(harness.ESC10aHarness2.User5))

}
return nil
})
})

testContext.DatabaseTestWithSetup(func(harness *integration.HarnessDetails) error {
harness.ESC10aHarnessECA.Setup(testContext)
return nil
}, func(harness integration.HarnessDetails, db graph.Database) {
operation := analysis.NewPostRelationshipOperation(context.Background(), db, "ADCS Post Process Test - ESC10a")

groupExpansions, enterpriseCertAuthorities, _, domains, cache, err := FetchADCSPrereqs(db)
require.Nil(t, err)

for _, domain := range domains {
innerDomain := domain

for _, enterpriseCA := range enterpriseCertAuthorities {
if cache.DoesCAChainProperlyToDomain(enterpriseCA, innerDomain) {
innerEnterpriseCA := enterpriseCA

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := ad2.PostADCSESC10a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC10a.String(), err)
} else {
return nil
}

return nil
})
}
}
}
err = operation.Done()
require.Nil(t, err)

db.ReadTransaction(context.Background(), func(tx graph.Transaction) error {
if results, err := ops.FetchStartNodes(tx.Relationships().Filterf(func() graph.Criteria {
return query.Kind(query.Relationship(), ad.ADCSESC10a)
})); err != nil {
t.Fatalf("error fetching esc10a edges in integration test; %v", err)
} else {
require.Equal(t, 1, len(results))

require.True(t, results.Contains(harness.ESC10aHarnessECA.Group1))

}
return nil
})
})

testContext.DatabaseTestWithSetup(func(harness *integration.HarnessDetails) error {
harness.ESC10aHarnessVictim.Setup(testContext)
return nil
}, func(harness integration.HarnessDetails, db graph.Database) {
operation := analysis.NewPostRelationshipOperation(context.Background(), db, "ADCS Post Process Test - ESC10a")

groupExpansions, enterpriseCertAuthorities, _, domains, cache, err := FetchADCSPrereqs(db)
require.Nil(t, err)

for _, domain := range domains {
innerDomain := domain

for _, enterpriseCA := range enterpriseCertAuthorities {
if cache.DoesCAChainProperlyToDomain(enterpriseCA, innerDomain) {
innerEnterpriseCA := enterpriseCA

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {
if err := ad2.PostADCSESC10a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC10a.String(), err)
} else {
return nil
}

return nil
})
}
}
}
err = operation.Done()
require.Nil(t, err)

db.ReadTransaction(context.Background(), func(tx graph.Transaction) error {
if results, err := ops.FetchStartNodes(tx.Relationships().Filterf(func() graph.Criteria {
return query.Kind(query.Relationship(), ad.ADCSESC10a)
})); err != nil {
t.Fatalf("error fetching esc10a edges in integration test; %v", err)
} else {
require.Equal(t, 2, len(results))

require.True(t, results.Contains(harness.ESC10aHarnessVictim.Group1))
require.True(t, results.Contains(harness.ESC10aHarnessVictim.Group2))

}
return nil
})
})
}
Loading

0 comments on commit 6fe1f94

Please sign in to comment.