Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ESC10a Post Processing #360

Merged
merged 11 commits into from
Jan 30, 2024
264 changes: 262 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 @@ -682,7 +682,6 @@ func TestADCSESC6a(t *testing.T) {
})
}
}

}
operation.Done()

Expand Down Expand Up @@ -878,3 +877,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
})
})
}
2 changes: 2 additions & 0 deletions cmd/api/src/test/integration/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,7 @@ func (s *GraphTestContext) NewActiveDirectoryCertTemplate(name, domainSID string
ad.SubjectAltRequireSPN: data.SubjectAltRequireSPN,
ad.SubjectAltRequireDNS: data.SubjectAltRequireDNS,
ad.SubjectAltRequireDomainDNS: data.SubjectAltRequireDomainDNS,
ad.SubjectAltRequireEmail: data.SubjectAltRequireEmail,
}), ad.Entity, ad.CertTemplate)
}

Expand All @@ -442,6 +443,7 @@ type CertTemplateData struct {
SubjectAltRequireSPN bool
SubjectAltRequireDNS bool
SubjectAltRequireDomainDNS bool
SubjectAltRequireEmail bool
NoSecurityExtension bool
SchemaVersion float64
AuthorizedSignatures float64
Expand Down
Loading
Loading