diff --git a/cmd/api/src/analysis/ad/adcs_integration_test.go b/cmd/api/src/analysis/ad/adcs_integration_test.go index bda73f3ce8..fb828d9ea8 100644 --- a/cmd/api/src/analysis/ad/adcs_integration_test.go +++ b/cmd/api/src/analysis/ad/adcs_integration_test.go @@ -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" @@ -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 + }) }) } @@ -682,7 +709,6 @@ func TestADCSESC6a(t *testing.T) { }) } } - } operation.Done() @@ -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 }) }) @@ -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 + }) + }) +} diff --git a/cmd/api/src/analysis/azure/queries.go b/cmd/api/src/analysis/azure/queries.go index 75bf58402a..6c27548dd5 100644 --- a/cmd/api/src/analysis/azure/queries.go +++ b/cmd/api/src/analysis/azure/queries.go @@ -1,17 +1,17 @@ // Copyright 2023 Specter Ops, Inc. -// +// // Licensed under the Apache License, Version 2.0 // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -// +// // SPDX-License-Identifier: Apache-2.0 package azure @@ -21,7 +21,6 @@ import ( "fmt" "sync" - "github.com/specterops/bloodhound/src/model" "github.com/gofrs/uuid" "github.com/specterops/bloodhound/analysis" "github.com/specterops/bloodhound/dawgs/graph" @@ -30,6 +29,7 @@ import ( "github.com/specterops/bloodhound/graphschema/azure" "github.com/specterops/bloodhound/graphschema/common" "github.com/specterops/bloodhound/log" + "github.com/specterops/bloodhound/src/model" ) func GraphStats(ctx context.Context, db graph.Database) (model.AzureDataQualityStats, model.AzureDataQualityAggregation, error) { @@ -48,7 +48,7 @@ func GraphStats(ctx context.Context, db graph.Database) (model.AzureDataQualityS runID = newUUID.String() } - return stats, aggregation, db.ReadTransaction(ctx, func(tx graph.Transaction) error { + err := db.ReadTransaction(ctx, func(tx graph.Transaction) error { if tenants, err := ops.FetchNodes(tx.Nodes().Filterf(func() graph.Criteria { return query.Kind(query.Node(), azure.Tenant) })); err != nil { @@ -58,6 +58,8 @@ func GraphStats(ctx context.Context, db graph.Database) (model.AzureDataQualityS if tenantObjectID, err := tenant.Properties.Get(common.ObjectID.String()).String(); err != nil { log.Errorf("Tenant node %d does not have a valid %s property: %v", tenant.ID, common.ObjectID, err) } else { + aggregation.Tenants++ + var ( stat = model.AzureDataQualityStat{ RunID: runID, @@ -163,4 +165,6 @@ func GraphStats(ctx context.Context, db graph.Database) (model.AzureDataQualityS return nil }) + + return stats, aggregation, err } diff --git a/cmd/api/src/analysis/azure/queries_test.go b/cmd/api/src/analysis/azure/queries_test.go new file mode 100644 index 0000000000..4afde925aa --- /dev/null +++ b/cmd/api/src/analysis/azure/queries_test.go @@ -0,0 +1,52 @@ +// Copyright 2023 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +//go:build integration +// +build integration + +package azure_test + +import ( + "context" + "github.com/specterops/bloodhound/dawgs/graph" + schema "github.com/specterops/bloodhound/graphschema" + azure2 "github.com/specterops/bloodhound/src/analysis/azure" + "github.com/specterops/bloodhound/src/test/integration" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "testing" +) + +func TestAnalysisAzure_GraphStats(t *testing.T) { + testCtx := integration.NewGraphTestContext(t, schema.DefaultGraphSchema()) + testCtx.DatabaseTest(func(harness integration.HarnessDetails, db graph.Database) { + + _, agg, err := azure2.GraphStats(context.TODO(), testCtx.Graph.Database) + require.Nil(t, err) + assert.NotZero(t, agg.Tenants) + assert.NotZero(t, agg.Users) + assert.NotZero(t, agg.Groups) + assert.NotZero(t, agg.Apps) + assert.NotZero(t, agg.ServicePrincipals) + assert.NotZero(t, agg.Devices) + assert.NotZero(t, agg.ManagementGroups) + assert.NotZero(t, agg.Subscriptions) + assert.NotZero(t, agg.ResourceGroups) + assert.NotZero(t, agg.VMs) + assert.NotZero(t, agg.KeyVaults) + assert.NotZero(t, agg.Relationships) + }) +} diff --git a/cmd/api/src/api/middleware/middleware.go b/cmd/api/src/api/middleware/middleware.go index 1b33a910ed..a7858b7379 100644 --- a/cmd/api/src/api/middleware/middleware.go +++ b/cmd/api/src/api/middleware/middleware.go @@ -153,16 +153,10 @@ func parseUserIP(r *http.Request) string { if ipAddress := r.Header.Get("X-Forwarded-For"); ipAddress != "" { res += "X-Forwarded-For: " + ipAddress + "; " } else { - log.Errorf("No data found in X-Forwarded-For") + log.Warnf("No data found in X-Forwarded-For, possible upstream misconfig?") } - if parsedUrl, err := url.Parse(r.RemoteAddr); err != nil { - log.Errorf("Error parsing IP address from RemoteAddr: %s", err) - } else if hostName := parsedUrl.Hostname(); hostName == "" { - log.Errorf("Hostname not found in URL: %s", parsedUrl.String()) - } else { - res += "Remote Address: " + parsedUrl.Hostname() - } + res += "Remote Address: " + r.RemoteAddr return res } diff --git a/cmd/api/src/api/middleware/middleware_internal_test.go b/cmd/api/src/api/middleware/middleware_internal_test.go index 941ad84bd9..398977f4ed 100644 --- a/cmd/api/src/api/middleware/middleware_internal_test.go +++ b/cmd/api/src/api/middleware/middleware_internal_test.go @@ -84,30 +84,13 @@ func TestParseUserIP_XForwardedForMissing(t *testing.T) { req, err := http.NewRequest("GET", "/teapot", nil) require.Nil(t, err) - req.RemoteAddr = "http://www.google.com/0.0.0.0:3000" + req.RemoteAddr = "0.0.0.0:3000" res := parseUserIP(req) require.NotContains(t, res, "X-Forwarded-For") require.Contains(t, res, "Remote Address") } -func TestParseUserIP_RemoteAddrError(t *testing.T) { - req, err := http.NewRequest("GET", "/teapot", nil) - require.Nil(t, err) - - ip1 := "192.168.1.1:8080" - ip2 := "192.168.1.2" - ip3 := "192.168.1.3" - req.Header.Set("X-Forwarded-For", strings.Join([]string{ip1, ip2, ip3}, ",")) - req.RemoteAddr = "0.0.0.0:3000" - - res := parseUserIP(req) - require.Contains(t, res, "X-Forwarded-For") - require.Contains(t, res, ip1) - require.Contains(t, res, ip2) - require.NotContains(t, res, "Remote Address") -} - func TestParseUserIP_Success(t *testing.T) { req, err := http.NewRequest("GET", "/teapot", nil) require.Nil(t, err) @@ -117,7 +100,7 @@ func TestParseUserIP_Success(t *testing.T) { ip3 := "192.168.1.3" req.Header.Set("X-Forwarded-For", strings.Join([]string{ip1, ip2, ip3}, ",")) - req.RemoteAddr = "http://www.google.com/0.0.0.0:3000" + req.RemoteAddr = "0.0.0.0:3000" res := parseUserIP(req) require.Contains(t, res, "X-Forwarded-For") @@ -125,6 +108,7 @@ func TestParseUserIP_Success(t *testing.T) { require.Contains(t, res, ip2) require.Contains(t, res, ip3) require.Contains(t, res, "Remote Address") + require.Contains(t, res, req.RemoteAddr) } func TestParsePreferHeaderWait(t *testing.T) { diff --git a/cmd/api/src/api/v2/auth/login.go b/cmd/api/src/api/v2/auth/login.go index 322c727861..1892ce161b 100644 --- a/cmd/api/src/api/v2/auth/login.go +++ b/cmd/api/src/api/v2/auth/login.go @@ -91,12 +91,13 @@ func (s LoginResource) Login(response http.ResponseWriter, request *http.Request func (s LoginResource) patchEULAAcceptance(ctx context.Context, username string) error { if user, err := s.db.LookupUser(username); err != nil { return err - } else { + } else if !user.EULAAccepted { user.EULAAccepted = true if err = s.db.UpdateUser(ctx, user); err != nil { return err } } + return nil } diff --git a/cmd/api/src/database/agi.go b/cmd/api/src/database/agi.go index d3a748dd99..1f2824c2fd 100644 --- a/cmd/api/src/database/agi.go +++ b/cmd/api/src/database/agi.go @@ -37,7 +37,7 @@ func (s *BloodhoundDB) CreateAssetGroup(ctx context.Context, name, tag string, s auditEntry = model.AuditEntry{ Action: "CreateAssetGroup", - Model: assetGroup.AuditData(), + Model: &assetGroup, // Pointer is required to ensure success log contains updated fields after transaction } ) @@ -54,7 +54,7 @@ func (s *BloodhoundDB) DeleteAssetGroup(ctx context.Context, assetGroup model.As var ( auditEntry = model.AuditEntry{ Action: "DeleteAssetGroup", - Model: assetGroup.AuditData(), + Model: &assetGroup, // Pointer is required to ensure success log contains updated fields after transaction } ) diff --git a/cmd/api/src/database/audit.go b/cmd/api/src/database/audit.go index 055dcacac2..8d2933e74a 100644 --- a/cmd/api/src/database/audit.go +++ b/cmd/api/src/database/audit.go @@ -128,7 +128,7 @@ func (s *BloodhoundDB) AuditableTransaction(ctx context.Context, auditEntry mode } if err := s.AppendAuditLog(ctx, auditEntry); err != nil { - return fmt.Errorf("could not append end status to audit log: %w", err) + return fmt.Errorf("could not append %s to audit log: %w", auditEntry.Status, err) } return err diff --git a/cmd/api/src/database/auth.go b/cmd/api/src/database/auth.go index 4f88e24e4b..be9627956b 100644 --- a/cmd/api/src/database/auth.go +++ b/cmd/api/src/database/auth.go @@ -365,7 +365,7 @@ func (s *BloodhoundDB) UpdateUser(ctx context.Context, user model.User) error { var ( auditEntry = model.AuditEntry{ Action: "UpdateUser", - Model: user.AuditData(), + Model: &user, // Pointer is required to ensure success log contains updated fields after transaction } ) @@ -611,7 +611,7 @@ func (s *BloodhoundDB) DeleteSAMLProvider(ctx context.Context, provider model.SA var ( auditEntry = model.AuditEntry{ Action: "DeleteSAMLProvider", - Model: provider.AuditData(), + Model: &provider, // Pointer is required to ensure success log contains updated fields after transaction } ) diff --git a/cmd/api/src/test/integration/graph.go b/cmd/api/src/test/integration/graph.go index b1459ba1cf..ea39683a93 100644 --- a/cmd/api/src/test/integration/graph.go +++ b/cmd/api/src/test/integration/graph.go @@ -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) } @@ -442,6 +443,7 @@ type CertTemplateData struct { SubjectAltRequireSPN bool SubjectAltRequireDNS bool SubjectAltRequireDomainDNS bool + SubjectAltRequireEmail bool NoSecurityExtension bool SchemaVersion float64 AuthorizedSignatures float64 @@ -461,6 +463,7 @@ func (s *GraphTestContext) setupAzure() { s.Harness.AZMGRoleManagementReadWriteDirectoryHarness.Setup(s) s.Harness.AZMGServicePrincipalEndpointReadWriteAllHarness.Setup(s) s.Harness.AZInboundControlHarness.Setup(s) + s.Harness.AZManagementGroup.Setup(s) } func (s *GraphTestContext) setupActiveDirectory() { diff --git a/cmd/api/src/test/integration/harnesses.go b/cmd/api/src/test/integration/harnesses.go index 9b11946519..4bbfba80a5 100644 --- a/cmd/api/src/test/integration/harnesses.go +++ b/cmd/api/src/test/integration/harnesses.go @@ -753,16 +753,41 @@ type AZGroupMembershipHarness struct { func (s *AZGroupMembershipHarness) Setup(testCtx *GraphTestContext) { tenantID := RandomObjectID(testCtx.testCtx) + s.Tenant = testCtx.NewAzureTenant(tenantID) s.UserA = testCtx.NewAzureUser("UserA", "UserA", "", RandomObjectID(testCtx.testCtx), "", tenantID, false) s.UserB = testCtx.NewAzureUser("UserB", "UserB", "", RandomObjectID(testCtx.testCtx), "", tenantID, false) s.UserC = testCtx.NewAzureUser("UserC", "UserC", "", RandomObjectID(testCtx.testCtx), "", tenantID, false) s.Group = testCtx.NewAzureGroup("Group", RandomObjectID(testCtx.testCtx), tenantID) + testCtx.NewRelationship(s.Tenant, s.Group, azure.Contains) + testCtx.NewRelationship(s.UserA, s.Group, azure.MemberOf) testCtx.NewRelationship(s.UserB, s.Group, azure.MemberOf) testCtx.NewRelationship(s.UserC, s.Group, azure.MemberOf) } +type AZManagementGroupHarness struct { + Tenant *graph.Node + UserA *graph.Node + UserB *graph.Node + UserC *graph.Node + Group *graph.Node +} + +func (s *AZManagementGroupHarness) Setup(testCtx *GraphTestContext) { + tenantID := RandomObjectID(testCtx.testCtx) + s.Tenant = testCtx.NewAzureTenant(tenantID) + s.UserA = testCtx.NewAzureUser("Batman", "Batman", "", RandomObjectID(testCtx.testCtx), "", tenantID, false) + s.UserB = testCtx.NewAzureUser("Wonder Woman", "Wonder Woman", "", RandomObjectID(testCtx.testCtx), "", tenantID, false) + s.UserC = testCtx.NewAzureUser("Flash", "Flash", "", RandomObjectID(testCtx.testCtx), "", tenantID, false) + s.Group = testCtx.NewAzureManagementGroup("Justice League", RandomObjectID(testCtx.testCtx), tenantID) + testCtx.NewRelationship(s.Tenant, s.Group, azure.Contains) + + testCtx.NewRelationship(s.UserA, s.Group, azure.ManagementGroup) + testCtx.NewRelationship(s.UserB, s.Group, azure.ManagementGroup) + testCtx.NewRelationship(s.UserC, s.Group, azure.ManagementGroup) +} + type AZEntityPanelHarness struct { Application *graph.Node Device *graph.Node @@ -1030,6 +1055,7 @@ func (s *AZMGServicePrincipalEndpointReadWriteAllHarness) Setup(testCtx *GraphTe } type AZInboundControlHarness struct { + AZTenant *graph.Node ControlledAZUser *graph.Node AZAppA *graph.Node AZGroupA *graph.Node @@ -1042,6 +1068,7 @@ type AZInboundControlHarness struct { func (s *AZInboundControlHarness) Setup(testCtx *GraphTestContext) { tenantID := RandomObjectID(testCtx.testCtx) + s.AZTenant = testCtx.NewAzureTenant(tenantID) s.ControlledAZUser = testCtx.NewAzureUser("Controlled AZUser", "Controlled AZUser", "", RandomObjectID(testCtx.testCtx), HarnessUserLicenses, tenantID, HarnessUserMFAEnabled) s.AZAppA = testCtx.NewAzureApplication("AZAppA", RandomObjectID(testCtx.testCtx), tenantID) s.AZGroupA = testCtx.NewAzureGroup("AZGroupA", RandomObjectID(testCtx.testCtx), tenantID) @@ -1051,6 +1078,8 @@ func (s *AZInboundControlHarness) Setup(testCtx *GraphTestContext) { s.AZServicePrincipalA = testCtx.NewAzureServicePrincipal("AZServicePrincipalA", RandomObjectID(testCtx.testCtx), tenantID) s.AZServicePrincipalB = testCtx.NewAzureServicePrincipal("AZServicePrincipalB", RandomObjectID(testCtx.testCtx), tenantID) + testCtx.NewRelationship(s.AZTenant, s.AZGroupA, azure.Contains) + testCtx.NewRelationship(s.AZUserA, s.AZGroupA, azure.MemberOf) testCtx.NewRelationship(s.AZServicePrincipalB, s.AZGroupB, azure.MemberOf) @@ -2080,7 +2109,7 @@ func initHarnessNodeProperties(c *GraphTestContext, nodeMap map[string]*graph.No } //This is an exception for schemaVersion which should not be a boolean - if value == "1" || value == "0" { + if value == "1" || value == "0" || value == "2" { intValue, _ := strconv.ParseInt(value, 10, 32) nodeMap[node.ID].Properties.Set(strings.ToLower(key), float64(intValue)) } else if boolValue, err := strconv.ParseBool(value); err != nil { @@ -2127,6 +2156,650 @@ func (s *ESC6aHarnessTemplate2) Setup(c *GraphTestContext) { setupHarnessFromArrowsJson(c, "esc6a-template2") } +type ESC10aPrincipalHarness struct { + Domain *graph.Node + NTAuthStore *graph.Node + RootCA *graph.Node + EnterpriseCA *graph.Node + DC *graph.Node + CertTemplate *graph.Node + User1 *graph.Node + Group1 *graph.Node + Group2 *graph.Node + Group6 *graph.Node + Group3 *graph.Node + Group4 *graph.Node + Group5 *graph.Node + User2 *graph.Node + Group0 *graph.Node +} + +func (s *ESC10aPrincipalHarness) Setup(graphTestContext *GraphTestContext) { + domainSid := RandomDomainSID() + s.Domain = graphTestContext.NewActiveDirectoryDomain("Domain", domainSid, false, true) + s.NTAuthStore = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore", domainSid) + s.RootCA = graphTestContext.NewActiveDirectoryRootCA("RootCA", domainSid) + s.EnterpriseCA = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA", domainSid) + s.DC = graphTestContext.NewActiveDirectoryComputer("DC", domainSid) + s.CertTemplate = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.User1 = graphTestContext.NewActiveDirectoryUser("User1", domainSid) + s.Group1 = graphTestContext.NewActiveDirectoryGroup("Group1", domainSid) + s.Group2 = graphTestContext.NewActiveDirectoryGroup("Group2", domainSid) + s.Group6 = graphTestContext.NewActiveDirectoryGroup("Group6", domainSid) + s.Group3 = graphTestContext.NewActiveDirectoryGroup("Group3", domainSid) + s.Group4 = graphTestContext.NewActiveDirectoryGroup("Group4", domainSid) + s.Group5 = graphTestContext.NewActiveDirectoryGroup("Group5", domainSid) + s.User2 = graphTestContext.NewActiveDirectoryUser("User2", domainSid) + s.Group0 = graphTestContext.NewActiveDirectoryGroup("Group0", domainSid) + graphTestContext.NewRelationship(s.RootCA, s.Domain, ad.RootCAFor) + graphTestContext.NewRelationship(s.EnterpriseCA, s.RootCA, ad.IssuedSignedBy) + graphTestContext.NewRelationship(s.NTAuthStore, s.Domain, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.EnterpriseCA, s.NTAuthStore, ad.TrustedForNTAuth) + graphTestContext.NewRelationship(s.EnterpriseCA, s.DC, ad.CanAbuseUPNCertMapping) + graphTestContext.NewRelationship(s.DC, s.Domain, ad.DCFor) + graphTestContext.NewRelationship(s.CertTemplate, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.Group1, s.User1, ad.GenericAll) + graphTestContext.NewRelationship(s.Group2, s.User1, ad.GenericWrite) + graphTestContext.NewRelationship(s.Group6, s.User1, ad.AllExtendedRights) + graphTestContext.NewRelationship(s.Group3, s.User1, ad.WriteDACL) + graphTestContext.NewRelationship(s.Group4, s.User1, ad.WriteOwner) + graphTestContext.NewRelationship(s.Group5, s.User1, ad.WriteOwner) + graphTestContext.NewRelationship(s.User2, s.User2, ad.GenericAll) + graphTestContext.NewRelationship(s.User1, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User2, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.Group0, s.CertTemplate, ad.Enroll) + graphTestContext.NewRelationship(s.Group0, s.EnterpriseCA, ad.Enroll) +} + +type ESC10aHarness1 struct { + CertTemplate1 *graph.Node + CertTemplate2 *graph.Node + CertTemplate3 *graph.Node + CertTemplate4 *graph.Node + CertTemplate5 *graph.Node + CertTemplate6 *graph.Node + CertTemplate7 *graph.Node + DC *graph.Node + Domain *graph.Node + EnterpriseCA *graph.Node + Group0 *graph.Node + Group1 *graph.Node + Group2 *graph.Node + Group3 *graph.Node + Group4 *graph.Node + Group5 *graph.Node + Group6 *graph.Node + Group7 *graph.Node + NTAuthStore *graph.Node + RootCA *graph.Node + User1 *graph.Node + User2 *graph.Node + User3 *graph.Node + User4 *graph.Node + User5 *graph.Node + User6 *graph.Node + User7 *graph.Node +} + +func (s *ESC10aHarness1) Setup(graphTestContext *GraphTestContext) { + domainSid := RandomDomainSID() + s.CertTemplate1 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate1", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 2, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate2 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate2", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: true, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate3 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate3", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireSPN: true, + SubjectAltRequireUPN: false, + }) + s.CertTemplate4 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate4", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: true, + SchemaVersion: 1, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate5 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate5", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: false, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate6 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate6", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 1, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 2, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate7 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate7", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: false, + }) + s.DC = graphTestContext.NewActiveDirectoryComputer("DC", domainSid) + s.Domain = graphTestContext.NewActiveDirectoryDomain("Domain", domainSid, false, true) + s.EnterpriseCA = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA", domainSid) + s.Group0 = graphTestContext.NewActiveDirectoryGroup("Group0", domainSid) + s.Group1 = graphTestContext.NewActiveDirectoryGroup("Group1", domainSid) + s.Group2 = graphTestContext.NewActiveDirectoryGroup("Group2", domainSid) + s.Group3 = graphTestContext.NewActiveDirectoryGroup("Group3", domainSid) + s.Group4 = graphTestContext.NewActiveDirectoryGroup("Group4", domainSid) + s.Group5 = graphTestContext.NewActiveDirectoryGroup("Group5", domainSid) + s.Group6 = graphTestContext.NewActiveDirectoryGroup("Group6", domainSid) + s.Group7 = graphTestContext.NewActiveDirectoryGroup("Group7", domainSid) + s.NTAuthStore = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore", domainSid) + s.RootCA = graphTestContext.NewActiveDirectoryRootCA("RootCA", domainSid) + s.User1 = graphTestContext.NewActiveDirectoryUser("User1", domainSid) + s.User2 = graphTestContext.NewActiveDirectoryUser("User2", domainSid) + s.User3 = graphTestContext.NewActiveDirectoryUser("User3", domainSid) + s.User4 = graphTestContext.NewActiveDirectoryUser("User4", domainSid) + s.User5 = graphTestContext.NewActiveDirectoryUser("User5", domainSid) + s.User6 = graphTestContext.NewActiveDirectoryUser("User6", domainSid) + s.User7 = graphTestContext.NewActiveDirectoryUser("User7", domainSid) + graphTestContext.NewRelationship(s.CertTemplate2, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.RootCA, s.Domain, ad.RootCAFor) + graphTestContext.NewRelationship(s.EnterpriseCA, s.RootCA, ad.IssuedSignedBy) + graphTestContext.NewRelationship(s.NTAuthStore, s.Domain, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.EnterpriseCA, s.NTAuthStore, ad.TrustedForNTAuth) + graphTestContext.NewRelationship(s.EnterpriseCA, s.DC, ad.CanAbuseUPNCertMapping) + graphTestContext.NewRelationship(s.DC, s.Domain, ad.DCFor) + graphTestContext.NewRelationship(s.User3, s.CertTemplate3, ad.Enroll) + graphTestContext.NewRelationship(s.CertTemplate3, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.CertTemplate4, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.User4, s.CertTemplate4, ad.Enroll) + graphTestContext.NewRelationship(s.User2, s.CertTemplate2, ad.Enroll) + graphTestContext.NewRelationship(s.Group0, s.EnterpriseCA, ad.Enroll) + graphTestContext.NewRelationship(s.User2, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User3, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User4, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.CertTemplate5, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.User5, s.CertTemplate5, ad.Enroll) + graphTestContext.NewRelationship(s.User5, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User6, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User6, s.CertTemplate6, ad.Enroll) + graphTestContext.NewRelationship(s.CertTemplate6, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.CertTemplate1, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.User1, s.CertTemplate1, ad.Enroll) + graphTestContext.NewRelationship(s.User1, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.CertTemplate7, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.User7, s.CertTemplate7, ad.Enroll) + graphTestContext.NewRelationship(s.User7, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.Group7, s.User7, ad.GenericAll) + graphTestContext.NewRelationship(s.Group6, s.User6, ad.GenericAll) + graphTestContext.NewRelationship(s.Group5, s.User5, ad.GenericAll) + graphTestContext.NewRelationship(s.Group4, s.User4, ad.GenericAll) + graphTestContext.NewRelationship(s.Group3, s.User3, ad.GenericAll) + graphTestContext.NewRelationship(s.Group2, s.User2, ad.GenericAll) + graphTestContext.NewRelationship(s.Group1, s.User1, ad.GenericAll) +} + +type ESC10aHarness2 struct { + CertTemplate1 *graph.Node + CertTemplate2 *graph.Node + CertTemplate3 *graph.Node + Computer1 *graph.Node + Computer2 *graph.Node + Computer3 *graph.Node + Computer4 *graph.Node + Computer5 *graph.Node + Computer6 *graph.Node + DC *graph.Node + Domain *graph.Node + EnterpriseCA *graph.Node + Group0 *graph.Node + Group1 *graph.Node + Group2 *graph.Node + Group3 *graph.Node + Group4 *graph.Node + Group5 *graph.Node + Group6 *graph.Node + NTAuthStore *graph.Node + RootCA *graph.Node + User1 *graph.Node + User2 *graph.Node + User3 *graph.Node + User4 *graph.Node + User5 *graph.Node + User6 *graph.Node +} + +func (s *ESC10aHarness2) Setup(graphTestContext *GraphTestContext) { + domainSid := RandomDomainSID() + s.CertTemplate1 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate1", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 2, + SubjectAltRequireDNS: false, + SubjectAltRequireDomainDNS: false, + SubjectAltRequireEmail: true, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate2 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate2", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 2, + SubjectAltRequireDNS: true, + SubjectAltRequireDomainDNS: false, + SubjectAltRequireEmail: true, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate3 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate3", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 2, + SubjectAltRequireDNS: false, + SubjectAltRequireDomainDNS: true, + SubjectAltRequireEmail: true, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.Computer1 = graphTestContext.NewActiveDirectoryComputer("Computer1", domainSid) + s.Computer2 = graphTestContext.NewActiveDirectoryComputer("Computer2", domainSid) + s.Computer3 = graphTestContext.NewActiveDirectoryComputer("Computer3", domainSid) + s.Computer4 = graphTestContext.NewActiveDirectoryComputer("Computer4", domainSid) + s.Computer5 = graphTestContext.NewActiveDirectoryComputer("Computer5", domainSid) + s.Computer6 = graphTestContext.NewActiveDirectoryComputer("Computer6", domainSid) + s.DC = graphTestContext.NewActiveDirectoryComputer("DC", domainSid) + s.Domain = graphTestContext.NewActiveDirectoryDomain("Domain", domainSid, false, true) + s.EnterpriseCA = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA", domainSid) + s.Group0 = graphTestContext.NewActiveDirectoryGroup("Group0", domainSid) + s.Group1 = graphTestContext.NewActiveDirectoryGroup("Group1", domainSid) + s.Group2 = graphTestContext.NewActiveDirectoryGroup("Group2", domainSid) + s.Group3 = graphTestContext.NewActiveDirectoryGroup("Group3", domainSid) + s.Group4 = graphTestContext.NewActiveDirectoryGroup("Group4", domainSid) + s.Group5 = graphTestContext.NewActiveDirectoryGroup("Group5", domainSid) + s.Group6 = graphTestContext.NewActiveDirectoryGroup("Group6", domainSid) + s.NTAuthStore = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore", domainSid) + s.RootCA = graphTestContext.NewActiveDirectoryRootCA("RootCA", domainSid) + s.User1 = graphTestContext.NewActiveDirectoryUser("User1", domainSid) + s.User2 = graphTestContext.NewActiveDirectoryUser("User2", domainSid) + s.User3 = graphTestContext.NewActiveDirectoryUser("User3", domainSid) + s.User4 = graphTestContext.NewActiveDirectoryUser("User4", domainSid) + s.User5 = graphTestContext.NewActiveDirectoryUser("User5", domainSid) + s.User6 = graphTestContext.NewActiveDirectoryUser("User6", domainSid) + graphTestContext.NewRelationship(s.RootCA, s.Domain, ad.RootCAFor) + graphTestContext.NewRelationship(s.EnterpriseCA, s.RootCA, ad.IssuedSignedBy) + graphTestContext.NewRelationship(s.NTAuthStore, s.Domain, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.EnterpriseCA, s.NTAuthStore, ad.TrustedForNTAuth) + graphTestContext.NewRelationship(s.EnterpriseCA, s.DC, ad.CanAbuseUPNCertMapping) + graphTestContext.NewRelationship(s.DC, s.Domain, ad.DCFor) + graphTestContext.NewRelationship(s.Group0, s.EnterpriseCA, ad.Enroll) + graphTestContext.NewRelationship(s.CertTemplate1, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.Group1, s.CertTemplate1, ad.Enroll) + graphTestContext.NewRelationship(s.Group1, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.CertTemplate2, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.CertTemplate3, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.Computer1, s.CertTemplate1, ad.Enroll) + graphTestContext.NewRelationship(s.Computer1, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User1, s.CertTemplate1, ad.Enroll) + graphTestContext.NewRelationship(s.User1, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.Group2, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.Computer2, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User2, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User2, s.CertTemplate2, ad.Enroll) + graphTestContext.NewRelationship(s.Computer2, s.CertTemplate2, ad.Enroll) + graphTestContext.NewRelationship(s.Group2, s.CertTemplate2, ad.Enroll) + graphTestContext.NewRelationship(s.Group3, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.Computer3, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User3, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.Group3, s.CertTemplate3, ad.Enroll) + graphTestContext.NewRelationship(s.Computer3, s.CertTemplate3, ad.Enroll) + graphTestContext.NewRelationship(s.User3, s.CertTemplate3, ad.Enroll) + graphTestContext.NewRelationship(s.User6, s.User3, ad.GenericAll) + graphTestContext.NewRelationship(s.User5, s.Computer3, ad.GenericAll) + graphTestContext.NewRelationship(s.User4, s.Group3, ad.GenericAll) + graphTestContext.NewRelationship(s.Computer6, s.User2, ad.GenericAll) + graphTestContext.NewRelationship(s.Computer5, s.Computer2, ad.GenericAll) + graphTestContext.NewRelationship(s.Computer4, s.Group2, ad.GenericAll) + graphTestContext.NewRelationship(s.Group6, s.User1, ad.GenericAll) + graphTestContext.NewRelationship(s.Group5, s.Computer1, ad.GenericAll) + graphTestContext.NewRelationship(s.Group4, s.Group1, ad.GenericAll) +} + +type ESC10aHarnessECA struct { + CertTemplate1 *graph.Node + CertTemplate2 *graph.Node + CertTemplate3 *graph.Node + CertTemplate4 *graph.Node + CertTemplate5 *graph.Node + DC1 *graph.Node + DC2 *graph.Node + DC3 *graph.Node + DC4 *graph.Node + DC5 *graph.Node + Domain1 *graph.Node + Domain2 *graph.Node + Domain3 *graph.Node + Domain4 *graph.Node + Domain5 *graph.Node + EnterpriseCA1 *graph.Node + EnterpriseCA2 *graph.Node + EnterpriseCA3 *graph.Node + EnterpriseCA4 *graph.Node + EnterpriseCA5 *graph.Node + Group1 *graph.Node + Group2 *graph.Node + Group3 *graph.Node + Group4 *graph.Node + Group5 *graph.Node + NTAuthStore1 *graph.Node + NTAuthStore2 *graph.Node + NTAuthStore3 *graph.Node + NTAuthStore4 *graph.Node + NTAuthStore5 *graph.Node + RootCA1 *graph.Node + RootCA2 *graph.Node + RootCA3 *graph.Node + RootCA4 *graph.Node + RootCA5 *graph.Node + User1 *graph.Node + User2 *graph.Node + User3 *graph.Node + User4 *graph.Node + User5 *graph.Node +} + +func (s *ESC10aHarnessECA) Setup(graphTestContext *GraphTestContext) { + domainSid1 := RandomDomainSID() + domainSid2 := RandomDomainSID() + domainSid3 := RandomDomainSID() + domainSid4 := RandomDomainSID() + domainSid5 := RandomDomainSID() + s.CertTemplate1 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate1", domainSid1, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireEmail: false, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate2 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate2", domainSid2, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireEmail: false, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate3 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate3", domainSid3, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireEmail: false, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate4 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate4", domainSid4, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireEmail: false, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.CertTemplate5 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate5", domainSid5, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireEmail: false, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.DC1 = graphTestContext.NewActiveDirectoryComputer("DC1", domainSid1) + s.DC2 = graphTestContext.NewActiveDirectoryComputer("DC2", domainSid2) + s.DC3 = graphTestContext.NewActiveDirectoryComputer("DC3", domainSid3) + s.DC4 = graphTestContext.NewActiveDirectoryComputer("DC4", domainSid4) + s.DC5 = graphTestContext.NewActiveDirectoryComputer("DC5", domainSid5) + s.Domain1 = graphTestContext.NewActiveDirectoryDomain("Domain1", domainSid1, false, true) + s.Domain2 = graphTestContext.NewActiveDirectoryDomain("Domain2", domainSid2, false, true) + s.Domain3 = graphTestContext.NewActiveDirectoryDomain("Domain3", domainSid3, false, true) + s.Domain4 = graphTestContext.NewActiveDirectoryDomain("Domain4", domainSid4, false, true) + s.Domain5 = graphTestContext.NewActiveDirectoryDomain("Domain5", domainSid5, false, true) + s.EnterpriseCA1 = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA1", domainSid1) + s.EnterpriseCA2 = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA2", domainSid2) + s.EnterpriseCA3 = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA3", domainSid3) + s.EnterpriseCA4 = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA4", domainSid4) + s.EnterpriseCA5 = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA5", domainSid5) + s.Group1 = graphTestContext.NewActiveDirectoryGroup("Group1", domainSid1) + s.Group2 = graphTestContext.NewActiveDirectoryGroup("Group2", domainSid2) + s.Group3 = graphTestContext.NewActiveDirectoryGroup("Group3", domainSid3) + s.Group4 = graphTestContext.NewActiveDirectoryGroup("Group4", domainSid4) + s.Group5 = graphTestContext.NewActiveDirectoryGroup("Group5", domainSid5) + s.NTAuthStore1 = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore1", domainSid1) + s.NTAuthStore2 = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore2", domainSid2) + s.NTAuthStore3 = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore3", domainSid3) + s.NTAuthStore4 = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore4", domainSid4) + s.NTAuthStore5 = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore5", domainSid5) + s.RootCA1 = graphTestContext.NewActiveDirectoryRootCA("RootCA1", domainSid1) + s.RootCA2 = graphTestContext.NewActiveDirectoryRootCA("RootCA2", domainSid2) + s.RootCA3 = graphTestContext.NewActiveDirectoryRootCA("RootCA3", domainSid3) + s.RootCA4 = graphTestContext.NewActiveDirectoryRootCA("RootCA4", domainSid4) + s.RootCA5 = graphTestContext.NewActiveDirectoryRootCA("RootCA5", domainSid5) + s.User1 = graphTestContext.NewActiveDirectoryUser("User1", domainSid1) + s.User2 = graphTestContext.NewActiveDirectoryUser("User2", domainSid2) + s.User3 = graphTestContext.NewActiveDirectoryUser("User3", domainSid3) + s.User4 = graphTestContext.NewActiveDirectoryUser("User4", domainSid4) + s.User5 = graphTestContext.NewActiveDirectoryUser("User5", domainSid5) + graphTestContext.NewRelationship(s.RootCA1, s.Domain1, ad.RootCAFor) + graphTestContext.NewRelationship(s.NTAuthStore1, s.Domain1, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.DC1, s.Domain1, ad.DCFor) + graphTestContext.NewRelationship(s.CertTemplate1, s.EnterpriseCA1, ad.PublishedTo) + graphTestContext.NewRelationship(s.EnterpriseCA1, s.RootCA1, ad.IssuedSignedBy) + graphTestContext.NewRelationship(s.EnterpriseCA1, s.NTAuthStore1, ad.TrustedForNTAuth) + graphTestContext.NewRelationship(s.User1, s.EnterpriseCA1, ad.Enroll) + graphTestContext.NewRelationship(s.EnterpriseCA1, s.DC1, ad.CanAbuseUPNCertMapping) + graphTestContext.NewRelationship(s.User1, s.CertTemplate1, ad.Enroll) + graphTestContext.NewRelationship(s.RootCA2, s.Domain2, ad.RootCAFor) + graphTestContext.NewRelationship(s.NTAuthStore2, s.Domain2, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.DC2, s.Domain2, ad.DCFor) + graphTestContext.NewRelationship(s.CertTemplate2, s.EnterpriseCA2, ad.PublishedTo) + graphTestContext.NewRelationship(s.EnterpriseCA2, s.RootCA2, ad.IssuedSignedBy) + graphTestContext.NewRelationship(s.EnterpriseCA2, s.NTAuthStore2, ad.TrustedForNTAuth) + graphTestContext.NewRelationship(s.User2, s.EnterpriseCA2, ad.Enroll) + graphTestContext.NewRelationship(s.User2, s.CertTemplate2, ad.Enroll) + graphTestContext.NewRelationship(s.RootCA3, s.Domain3, ad.RootCAFor) + graphTestContext.NewRelationship(s.NTAuthStore3, s.Domain3, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.DC3, s.Domain3, ad.DCFor) + graphTestContext.NewRelationship(s.CertTemplate3, s.EnterpriseCA3, ad.PublishedTo) + graphTestContext.NewRelationship(s.EnterpriseCA3, s.RootCA3, ad.IssuedSignedBy) + graphTestContext.NewRelationship(s.User3, s.EnterpriseCA3, ad.Enroll) + graphTestContext.NewRelationship(s.EnterpriseCA3, s.DC3, ad.CanAbuseUPNCertMapping) + graphTestContext.NewRelationship(s.User3, s.CertTemplate3, ad.Enroll) + graphTestContext.NewRelationship(s.RootCA4, s.Domain4, ad.RootCAFor) + graphTestContext.NewRelationship(s.NTAuthStore4, s.Domain4, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.DC4, s.Domain4, ad.DCFor) + graphTestContext.NewRelationship(s.CertTemplate4, s.EnterpriseCA4, ad.PublishedTo) + graphTestContext.NewRelationship(s.EnterpriseCA4, s.NTAuthStore4, ad.TrustedForNTAuth) + graphTestContext.NewRelationship(s.User4, s.EnterpriseCA4, ad.Enroll) + graphTestContext.NewRelationship(s.EnterpriseCA4, s.DC4, ad.CanAbuseUPNCertMapping) + graphTestContext.NewRelationship(s.User4, s.CertTemplate4, ad.Enroll) + graphTestContext.NewRelationship(s.RootCA5, s.Domain5, ad.RootCAFor) + graphTestContext.NewRelationship(s.NTAuthStore5, s.Domain5, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.DC5, s.Domain5, ad.DCFor) + graphTestContext.NewRelationship(s.EnterpriseCA5, s.RootCA5, ad.IssuedSignedBy) + graphTestContext.NewRelationship(s.EnterpriseCA5, s.NTAuthStore5, ad.TrustedForNTAuth) + graphTestContext.NewRelationship(s.User5, s.EnterpriseCA5, ad.Enroll) + graphTestContext.NewRelationship(s.EnterpriseCA5, s.DC5, ad.CanAbuseUPNCertMapping) + graphTestContext.NewRelationship(s.User5, s.CertTemplate5, ad.Enroll) + graphTestContext.NewRelationship(s.Group1, s.User1, ad.GenericAll) + graphTestContext.NewRelationship(s.Group2, s.User2, ad.GenericAll) + graphTestContext.NewRelationship(s.Group5, s.User5, ad.GenericAll) + graphTestContext.NewRelationship(s.Group4, s.User4, ad.GenericAll) + graphTestContext.NewRelationship(s.Group3, s.User3, ad.GenericAll) +} + +type ESC10aHarnessVictim struct { + CertTemplate1 *graph.Node + DC *graph.Node + Domain *graph.Node + EnterpriseCA *graph.Node + Group0 *graph.Node + Group1 *graph.Node + Group2 *graph.Node + Group3 *graph.Node + Group4 *graph.Node + NTAuthStore *graph.Node + RootCA *graph.Node + User1 *graph.Node + User2 *graph.Node + User3 *graph.Node + User4 *graph.Node +} + +func (s *ESC10aHarnessVictim) Setup(graphTestContext *GraphTestContext) { + domainSid := RandomDomainSID() + s.CertTemplate1 = graphTestContext.NewActiveDirectoryCertTemplate("CertTemplate1", domainSid, CertTemplateData{ + ApplicationPolicies: []string{}, + AuthenticationEnabled: true, + AuthorizedSignatures: 0, + EKUS: []string{}, + EnrolleeSuppliesSubject: false, + NoSecurityExtension: false, + RequiresManagerApproval: false, + SchemaVersion: 1, + SubjectAltRequireEmail: false, + SubjectAltRequireSPN: false, + SubjectAltRequireUPN: true, + }) + s.DC = graphTestContext.NewActiveDirectoryComputer("DC", domainSid) + s.Domain = graphTestContext.NewActiveDirectoryDomain("Domain", domainSid, false, true) + s.EnterpriseCA = graphTestContext.NewActiveDirectoryEnterpriseCA("EnterpriseCA", domainSid) + s.Group0 = graphTestContext.NewActiveDirectoryGroup("Group0", domainSid) + s.Group1 = graphTestContext.NewActiveDirectoryGroup("Group1", domainSid) + s.Group2 = graphTestContext.NewActiveDirectoryGroup("Group2", domainSid) + s.Group3 = graphTestContext.NewActiveDirectoryGroup("Group3", domainSid) + s.Group4 = graphTestContext.NewActiveDirectoryGroup("Group4", domainSid) + s.NTAuthStore = graphTestContext.NewActiveDirectoryNTAuthStore("NTAuthStore", domainSid) + s.RootCA = graphTestContext.NewActiveDirectoryRootCA("RootCA", domainSid) + s.User1 = graphTestContext.NewActiveDirectoryUser("User1", domainSid) + s.User2 = graphTestContext.NewActiveDirectoryUser("User2", domainSid) + s.User3 = graphTestContext.NewActiveDirectoryUser("User3", domainSid) + s.User4 = graphTestContext.NewActiveDirectoryUser("User4", domainSid) + graphTestContext.NewRelationship(s.RootCA, s.Domain, ad.RootCAFor) + graphTestContext.NewRelationship(s.EnterpriseCA, s.RootCA, ad.IssuedSignedBy) + graphTestContext.NewRelationship(s.NTAuthStore, s.Domain, ad.NTAuthStoreFor) + graphTestContext.NewRelationship(s.EnterpriseCA, s.NTAuthStore, ad.TrustedForNTAuth) + graphTestContext.NewRelationship(s.EnterpriseCA, s.DC, ad.CanAbuseUPNCertMapping) + graphTestContext.NewRelationship(s.DC, s.Domain, ad.DCFor) + graphTestContext.NewRelationship(s.Group0, s.EnterpriseCA, ad.Enroll) + graphTestContext.NewRelationship(s.CertTemplate1, s.EnterpriseCA, ad.PublishedTo) + graphTestContext.NewRelationship(s.User1, s.CertTemplate1, ad.GenericAll) + graphTestContext.NewRelationship(s.User1, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User2, s.CertTemplate1, ad.AllExtendedRights) + graphTestContext.NewRelationship(s.User2, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User3, s.CertTemplate1, ad.GenericWrite) + graphTestContext.NewRelationship(s.User3, s.Group0, ad.MemberOf) + graphTestContext.NewRelationship(s.User4, s.CertTemplate1, ad.Enroll) + graphTestContext.NewRelationship(s.Group1, s.User1, ad.GenericAll) + graphTestContext.NewRelationship(s.Group2, s.User2, ad.GenericAll) + graphTestContext.NewRelationship(s.Group3, s.User3, ad.GenericAll) + graphTestContext.NewRelationship(s.Group4, s.User4, ad.GenericAll) +} + type ShortcutHarness struct { Group1 *graph.Node Group2 *graph.Node @@ -2198,6 +2871,7 @@ type HarnessDetails struct { Completeness CompletenessHarness AZBaseHarness AZBaseHarness AZGroupMembership AZGroupMembershipHarness + AZManagementGroup AZManagementGroupHarness AZEntityPanelHarness AZEntityPanelHarness AZMGApplicationReadWriteAllHarness AZMGApplicationReadWriteAllHarness AZMGAppRoleManagementReadWriteAllHarness AZMGAppRoleManagementReadWriteAllHarness @@ -2225,4 +2899,9 @@ type HarnessDetails struct { ESC6aHarnessTemplate1 ESC6aHarnessTemplate1 ESC6aHarnessTemplate2 ESC6aHarnessTemplate2 ESC9AHarness ESC9AHarness + ESC10aPrincipalHarness ESC10aPrincipalHarness + ESC10aHarness1 ESC10aHarness1 + ESC10aHarness2 ESC10aHarness2 + ESC10aHarnessECA ESC10aHarnessECA + ESC10aHarnessVictim ESC10aHarnessVictim } diff --git a/cmd/api/src/test/integration/harnesses/azinboundcontrolharness.json b/cmd/api/src/test/integration/harnesses/AZInboundControlHarness.json similarity index 77% rename from cmd/api/src/test/integration/harnesses/azinboundcontrolharness.json rename to cmd/api/src/test/integration/harnesses/AZInboundControlHarness.json index b57cb1b592..8cfdb5e1f5 100644 --- a/cmd/api/src/test/integration/harnesses/azinboundcontrolharness.json +++ b/cmd/api/src/test/integration/harnesses/AZInboundControlHarness.json @@ -54,10 +54,10 @@ }, "nodes": [ { - "id": "n7", + "id": "n0", "position": { "x": 1059.8976569191977, - "y": 1346.027889910619 + "y": 501.6907518623764 }, "caption": "Controlled AZUser", "labels": [], @@ -67,10 +67,10 @@ } }, { - "id": "n8", + "id": "n1", "position": { "x": 604.0284270522187, - "y": 894.3371380482427 + "y": 50 }, "caption": "AZGroup A", "labels": [], @@ -80,10 +80,10 @@ } }, { - "id": "n9", + "id": "n2", "position": { "x": 604.0284270522187, - "y": 1838.502464519107 + "y": 994.1653264708643 }, "caption": "AZGroup B", "labels": [], @@ -93,10 +93,10 @@ } }, { - "id": "n10", + "id": "n3", "position": { "x": 604.0284270522187, - "y": 1475.6758276195499 + "y": 631.3386895713072 }, "caption": "AZServicePrincipal A", "labels": [], @@ -106,10 +106,10 @@ } }, { - "id": "n11", + "id": "n4", "position": { "x": 604.0284270522187, - "y": 1229.2947464229803 + "y": 384.9576083747377 }, "caption": "AZUser B", "labels": [], @@ -119,10 +119,10 @@ } }, { - "id": "n12", + "id": "n5", "position": { "x": 75, - "y": 894.3371380482427 + "y": 50 }, "caption": "AZUser A", "labels": [], @@ -132,10 +132,10 @@ } }, { - "id": "n13", + "id": "n6", "position": { "x": 75, - "y": 1838.502464519107 + "y": 994.1653264708643 }, "caption": "AZServicePrincipal B", "labels": [], @@ -145,14 +145,27 @@ } }, { - "id": "n14", + "id": "n7", "position": { "x": 75, - "y": 1475.6758276195499 + "y": 631.3386895713072 }, "caption": "AZApp A", + "labels": [], + "properties": {}, "style": { "border-color": "#d33115" + } + }, + { + "id": "n8", + "position": { + "x": 379.0709828688383, + "y": 274.95744418338046 + }, + "caption": "AZTenant", + "style": { + "border-color": "#aea1ff" }, "labels": [], "properties": {} @@ -160,74 +173,84 @@ ], "relationships": [ { - "id": "n8", + "id": "n0", + "fromId": "n3", + "toId": "n0", "type": "AZResetPassword", + "properties": {}, "style": { "arrow-color": "#68bc00" - }, - "properties": {}, - "fromId": "n10", - "toId": "n7" + } }, { - "id": "n9", + "id": "n1", + "fromId": "n4", + "toId": "n0", "type": "AZResetPassword", + "properties": {}, "style": { "arrow-color": "#68bc00" - }, - "properties": {}, - "fromId": "n11", - "toId": "n7" + } }, { - "id": "n10", + "id": "n2", + "fromId": "n5", + "toId": "n1", "type": "AZMemberOf", + "properties": {}, "style": { "arrow-color": "#68bc00" - }, - "properties": {}, - "fromId": "n12", - "toId": "n8" + } }, { - "id": "n11", + "id": "n3", + "fromId": "n6", + "toId": "n2", "type": "AZMemberOf", + "properties": {}, "style": { "arrow-color": "#68bc00" - }, - "properties": {}, - "fromId": "n13", - "toId": "n9" + } }, { - "id": "n13", + "id": "n4", + "fromId": "n1", + "toId": "n0", "type": "AZResetPassword", + "properties": {}, "style": { "arrow-color": "#68bc00" - }, - "properties": {}, - "fromId": "n8", - "toId": "n7" + } }, { - "id": "n16", + "id": "n5", + "fromId": "n2", + "toId": "n0", "type": "AZResetPassword", + "properties": {}, "style": { "arrow-color": "#68bc00" - }, - "properties": {}, - "fromId": "n9", - "toId": "n7" + } }, { - "id": "n17", + "id": "n6", + "fromId": "n7", + "toId": "n3", "type": "AZRunsAs", + "properties": {}, "style": { "arrow-color": "#d33115" + } + }, + { + "id": "n7", + "type": "AZContains", + "style": { + "arrow-color": "#68bc00" }, "properties": {}, - "fromId": "n14", - "toId": "n10" + "fromId": "n8", + "toId": "n1" } ] } \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/AZInboundControlHarness.svg b/cmd/api/src/test/integration/harnesses/AZInboundControlHarness.svg new file mode 100644 index 0000000000..3c0d8c6ce5 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/AZInboundControlHarness.svg @@ -0,0 +1 @@ +AZResetPasswordAZResetPasswordAZMemberOfAZMemberOfAZResetPasswordAZResetPasswordAZRunsAsAZContainsControlledAZUserAZGroupAAZGroupBAZServicePrincipalAAZUserBAZUserAAZServicePrincipalBAZAppAAZTenant \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/AZManagementGroup.json b/cmd/api/src/test/integration/harnesses/AZManagementGroup.json new file mode 100644 index 0000000000..1a52ec9798 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/AZManagementGroup.json @@ -0,0 +1,156 @@ +{ + "nodes": [ + { + "id": "n0", + "position": { + "x": -55, + "y": 85.2904443080064 + }, + "caption": "UserA", + "labels": [ + "AZUser" + ], + "properties": {}, + "style": {} + }, + { + "id": "n1", + "position": { + "x": -55, + "y": 253.81479412258489 + }, + "caption": "UserB", + "labels": [ + "AZUser" + ], + "properties": {}, + "style": {} + }, + { + "id": "n2", + "position": { + "x": -55, + "y": 422.3391439371636 + }, + "caption": "UserC", + "labels": [ + "AZUser" + ], + "properties": {}, + "style": {} + }, + { + "id": "n3", + "position": { + "x": 449.99999999999994, + "y": 40.19237886466851 + }, + "caption": "Tenant", + "labels": [ + "AZTenant" + ], + "properties": {}, + "style": {} + }, + { + "id": "n4", + "position": { + "x": 300, + "y": 253.81479412258489 + }, + "caption": "Group", + "labels": [ + "AZGroup" + ], + "properties": {}, + "style": {} + } + ], + "relationships": [ + { + "id": "n0", + "fromId": "n2", + "toId": "n4", + "type": "AZManagementGroup", + "properties": {}, + "style": {} + }, + { + "id": "n1", + "fromId": "n1", + "toId": "n4", + "type": "AZManagementGroup", + "properties": {}, + "style": {} + }, + { + "id": "n2", + "fromId": "n0", + "toId": "n4", + "type": "AZManagementGroup", + "properties": {}, + "style": {} + }, + { + "id": "n3", + "fromId": "n3", + "toId": "n4", + "type": "AZContains", + "properties": {}, + "style": {} + } + ], + "style": { + "font-family": "sans-serif", + "background-color": "#ffffff", + "background-image": "", + "background-size": "100%", + "node-color": "#ffffff", + "border-width": 4, + "border-color": "#68bc00", + "radius": 50, + "node-padding": 5, + "node-margin": 2, + "outside-position": "auto", + "node-icon-image": "", + "node-background-image": "", + "icon-position": "inside", + "icon-size": 64, + "caption-position": "inside", + "caption-max-width": 200, + "caption-color": "#000000", + "caption-font-size": 50, + "caption-font-weight": "normal", + "label-position": "inside", + "label-display": "pill", + "label-color": "#000000", + "label-background-color": "#ffffff", + "label-border-color": "#000000", + "label-border-width": 4, + "label-font-size": 40, + "label-padding": 5, + "label-margin": 4, + "directionality": "directed", + "detail-position": "inline", + "detail-orientation": "parallel", + "arrow-width": 5, + "arrow-color": "#aea1ff", + "margin-start": 5, + "margin-end": 5, + "margin-peer": 20, + "attachment-start": "normal", + "attachment-end": "normal", + "relationship-icon-image": "", + "type-color": "#000000", + "type-background-color": "#ffffff", + "type-border-color": "#000000", + "type-border-width": 0, + "type-font-size": 16, + "type-padding": 5, + "property-position": "outside", + "property-alignment": "colon", + "property-color": "#000000", + "property-font-size": 16, + "property-font-weight": "normal" + } +} diff --git a/cmd/api/src/test/integration/harnesses/AZManagementGroup.svg b/cmd/api/src/test/integration/harnesses/AZManagementGroup.svg new file mode 100644 index 0000000000..1a7ac2bbb2 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/AZManagementGroup.svg @@ -0,0 +1 @@ +AZManagementGroupAZManagementGroupAZManagementGroupAZContainsUserAAZUserUserBAZUserUserCAZUserTenantAZTenantGroupAZGroup \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/azmembership.json b/cmd/api/src/test/integration/harnesses/azgroupmembership.json similarity index 77% rename from cmd/api/src/test/integration/harnesses/azmembership.json rename to cmd/api/src/test/integration/harnesses/azgroupmembership.json index e011237996..c281b8294f 100644 --- a/cmd/api/src/test/integration/harnesses/azmembership.json +++ b/cmd/api/src/test/integration/harnesses/azgroupmembership.json @@ -54,36 +54,36 @@ }, "nodes": [ { - "id": "n0", + "id": "n1", "position": { - "x": 0, - "y": 0 + "x": 505.40943703856345, + "y": 89.79253144038367 }, "caption": "Group", - "style": {}, "labels": [ "AZGroup" ], - "properties": {} + "properties": {}, + "style": {} }, { - "id": "n1", + "id": "n2", "position": { - "x": -276.40943703856345, - "y": -159.58506288076748 + "x": 129, + "y": -4 }, "caption": "UserA", - "style": {}, "labels": [ "AZUser" ], - "properties": {} + "properties": {}, + "style": {} }, { - "id": "n2", + "id": "n3", "position": { - "x": -276.40943703856345, - "y": 0 + "x": 129, + "y": 155.58506288076748 }, "caption": "UserB", "labels": [ @@ -93,10 +93,10 @@ "style": {} }, { - "id": "n3", + "id": "n4", "position": { - "x": -276.40943703856345, - "y": 159.58506288076748 + "x": 129, + "y": 365.17012576153496 }, "caption": "UserC", "labels": [ @@ -104,32 +104,51 @@ ], "properties": {}, "style": {} + }, + { + "id": "n5", + "position": { + "x": 129, + "y": -185.5850628807675 + }, + "caption": "Tenant", + "style": {}, + "labels": [], + "properties": {} } ], "relationships": [ { "id": "n0", + "fromId": "n2", + "toId": "n1", "type": "AZMemberOf", - "style": {}, "properties": {}, - "fromId": "n1", - "toId": "n0" + "style": {} }, { "id": "n1", + "fromId": "n3", + "toId": "n1", "type": "AZMemberOf", - "style": {}, "properties": {}, - "fromId": "n2", - "toId": "n0" + "style": {} }, { "id": "n2", + "fromId": "n4", + "toId": "n1", "type": "AZMemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n3", + "type": "AZContains", "style": {}, "properties": {}, - "fromId": "n3", - "toId": "n0" + "fromId": "n5", + "toId": "n1" } ] } \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/azgroupmembership.svg b/cmd/api/src/test/integration/harnesses/azgroupmembership.svg new file mode 100644 index 0000000000..d0f558b8a2 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/azgroupmembership.svg @@ -0,0 +1 @@ +AZMemberOfAZMemberOfAZMemberOfAZContainsGroupAZGroupUserAAZUserUserBAZUserUserCAZUserTenant \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/azinboundcontrolharness.svg b/cmd/api/src/test/integration/harnesses/azinboundcontrolharness.svg deleted file mode 100644 index ecadd95685..0000000000 --- a/cmd/api/src/test/integration/harnesses/azinboundcontrolharness.svg +++ /dev/null @@ -1,18 +0,0 @@ - -AZResetPasswordAZResetPasswordAZMemberOfAZMemberOfAZResetPasswordAZResetPasswordAZRunsAsControlledAZUserAZGroupAAZGroupBAZServicePrincipalAAZUserBAZUserAAZServicePrincipalBAZAppA diff --git a/cmd/api/src/test/integration/harnesses/azmembership.svg b/cmd/api/src/test/integration/harnesses/azmembership.svg deleted file mode 100644 index c34f860236..0000000000 --- a/cmd/api/src/test/integration/harnesses/azmembership.svg +++ /dev/null @@ -1,18 +0,0 @@ - -AZMemberOfAZMemberOfAZMemberOfGroupAZGroupUserAAZUserUserBAZUserUserCAZUser diff --git a/cmd/api/src/test/integration/harnesses/esc10aharness1.json b/cmd/api/src/test/integration/harnesses/esc10aharness1.json new file mode 100644 index 0000000000..60c77c8965 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aharness1.json @@ -0,0 +1,807 @@ +{ + "style": { + "font-family": "sans-serif", + "background-color": "#ffffff", + "background-image": "", + "background-size": "100%", + "node-color": "#ffffff", + "border-width": 4, + "border-color": "#000000", + "radius": 50, + "node-padding": 5, + "node-margin": 2, + "outside-position": "auto", + "node-icon-image": "", + "node-background-image": "", + "icon-position": "inside", + "icon-size": 64, + "caption-position": "inside", + "caption-max-width": 200, + "caption-color": "#000000", + "caption-font-size": 50, + "caption-font-weight": "normal", + "label-position": "inside", + "label-display": "pill", + "label-color": "#000000", + "label-background-color": "#ffffff", + "label-border-color": "#000000", + "label-border-width": 4, + "label-font-size": 40, + "label-padding": 5, + "label-margin": 4, + "directionality": "directed", + "detail-position": "inline", + "detail-orientation": "parallel", + "arrow-width": 5, + "arrow-color": "#000000", + "margin-start": 5, + "margin-end": 5, + "margin-peer": 20, + "attachment-start": "normal", + "attachment-end": "normal", + "relationship-icon-image": "", + "type-color": "#000000", + "type-background-color": "#ffffff", + "type-border-color": "#000000", + "type-border-width": 0, + "type-font-size": 16, + "type-padding": 5, + "property-position": "outside", + "property-alignment": "colon", + "property-color": "#000000", + "property-font-size": 16, + "property-font-weight": "normal" + }, + "nodes": [ + { + "id": "n0", + "position": { + "x": 1166.2401531581963, + "y": 1669.717944661677 + }, + "caption": "Domain", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n1", + "position": { + "x": 1763.8169426399654, + "y": 1545.8922712261758 + }, + "caption": "NTAuthStore", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n2", + "position": { + "x": 1763.8169426399654, + "y": 1669.7179446616767 + }, + "caption": "RootCA", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n3", + "position": { + "x": 2343.4991450315265, + "y": 1270.461309154399 + }, + "caption": "EnterpriseCA", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n4", + "position": { + "x": 1979.2258547112808, + "y": 313.8361415302825 + }, + "caption": "CertTemplate2", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True", + "NoSecurityExtension": "True" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n5", + "position": { + "x": 1763.8169426399654, + "y": 1420.0681651505495 + }, + "caption": "DC", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n6", + "position": { + "x": 698.5658018046843, + "y": 973.4264953559922 + }, + "caption": "User4", + "labels": [], + "properties": {}, + "style": { + "border-color": "#000000", + "node-color": "#a4dd00" + } + }, + { + "id": "n7", + "position": { + "x": 1979.2258547112808, + "y": 445.75421229542417 + }, + "caption": "CertTemplate3", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "False", + "SubjectAltRequireSPN": "True" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n8", + "position": { + "x": 542.6743513535134, + "y": 973.4264953559922 + }, + "caption": "User3", + "labels": [], + "properties": {}, + "style": { + "border-color": "#000000", + "node-color": "#a4dd00" + } + }, + { + "id": "n9", + "position": { + "x": 386.7829009023419, + "y": 973.4264953559922 + }, + "caption": "User2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00" + } + }, + { + "id": "n10", + "position": { + "x": 1979.2258547112808, + "y": 577.6722830605653 + }, + "caption": "CertTemplate4", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "True", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff", + "caption-position": "inside", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n11", + "position": { + "x": 1166.2401531581963, + "y": 1270.461309154399 + }, + "caption": "Group0", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n12", + "position": { + "x": 1979.2258547112808, + "y": 709.5903538257065 + }, + "caption": "CertTemplate5", + "labels": [], + "properties": { + "AuthenticationEnabled": "False", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n13", + "position": { + "x": 854.4572522558553, + "y": 973.4264953559922 + }, + "caption": "User5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n14", + "position": { + "x": 1979.2258547112808, + "y": 841.5084245908482 + }, + "caption": "CertTemplate6", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "2", + "SubjectAltRequireUPN": "True", + "AuthorizedSignatures": "1" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n15", + "position": { + "x": 1979.2258547112808, + "y": 181.91807076514067 + }, + "caption": "CertTemplate1", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "2", + "SubjectAltRequireUPN": "True", + "AuthorizedSignatures": "0" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n16", + "position": { + "x": 1010.348702707026, + "y": 973.4264953559904 + }, + "caption": "User6", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n17", + "position": { + "x": 230.89145045117098, + "y": 973.4264953559922 + }, + "caption": "User1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00" + } + }, + { + "id": "n18", + "position": { + "x": 1979.2258547112808, + "y": 973.4264953559904 + }, + "caption": "CertTemplate7", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "False" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n19", + "position": { + "x": 1166.2401531581963, + "y": 973.4264953559904 + }, + "caption": "User7", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n23", + "position": { + "x": 1166.2401531581959, + "y": 49.99999999999923 + }, + "caption": "Group7", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n24", + "position": { + "x": 1010.348702707025, + "y": 49.99999999999923 + }, + "caption": "Group6", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n25", + "position": { + "x": 854.4572522558542, + "y": 49.99999999999923 + }, + "caption": "Group5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n26", + "position": { + "x": 698.5658018046829, + "y": 49.99999999999923 + }, + "caption": "Group4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n27", + "position": { + "x": 542.6743513535134, + "y": 1420.0681651505477 + }, + "caption": "Group3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#000000" + } + }, + { + "id": "n28", + "position": { + "x": 386.7829009023419, + "y": 1545.892271226174 + }, + "caption": "Group2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#000000" + } + }, + { + "id": "n29", + "position": { + "x": 230.89145045117098, + "y": 1669.7179446616767 + }, + "caption": "Group1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#000000" + } + } + ], + "relationships": [ + { + "id": "n0", + "fromId": "n4", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n1", + "fromId": "n2", + "toId": "n0", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n2", + "fromId": "n3", + "toId": "n2", + "type": "IssuedSignedBy", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n3", + "fromId": "n1", + "toId": "n0", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n4", + "fromId": "n3", + "toId": "n1", + "type": "TrustedForNTAuth", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n5", + "fromId": "n3", + "toId": "n5", + "type": "CanAbuseUPNCertMapping", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n6", + "fromId": "n5", + "toId": "n0", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n7", + "fromId": "n8", + "toId": "n7", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n8", + "fromId": "n7", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": {} + }, + { + "id": "n9", + "fromId": "n10", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n10", + "fromId": "n6", + "toId": "n10", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n11", + "fromId": "n9", + "toId": "n4", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n12", + "fromId": "n11", + "toId": "n3", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n13", + "fromId": "n9", + "toId": "n11", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n14", + "fromId": "n8", + "toId": "n11", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n15", + "fromId": "n6", + "toId": "n11", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n16", + "fromId": "n12", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n17", + "fromId": "n13", + "toId": "n12", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n18", + "fromId": "n13", + "toId": "n11", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n19", + "fromId": "n16", + "toId": "n11", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n20", + "fromId": "n16", + "toId": "n14", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n21", + "fromId": "n14", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": {} + }, + { + "id": "n22", + "fromId": "n15", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": {} + }, + { + "id": "n23", + "fromId": "n17", + "toId": "n15", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n24", + "fromId": "n17", + "toId": "n11", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n25", + "fromId": "n18", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n26", + "fromId": "n19", + "toId": "n18", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n27", + "fromId": "n19", + "toId": "n11", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n32", + "fromId": "n23", + "toId": "n19", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n33", + "fromId": "n24", + "toId": "n16", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n34", + "fromId": "n25", + "toId": "n13", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n35", + "fromId": "n26", + "toId": "n6", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n36", + "fromId": "n27", + "toId": "n8", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n37", + "fromId": "n28", + "toId": "n9", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n38", + "fromId": "n29", + "toId": "n17", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n39", + "fromId": "n27", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n40", + "fromId": "n28", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n41", + "fromId": "n29", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + } + ] +} \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aharness1.svg b/cmd/api/src/test/integration/harnesses/esc10aharness1.svg new file mode 100644 index 0000000000..b2f3b82ef0 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aharness1.svg @@ -0,0 +1 @@ +PublishedToRootCAForIssuedSignedByNTAuthStoreForTrustedForNTAuthCanAbuseUPNCertMappingDCForEnrollPublishedToPublishedToEnrollEnrollEnrollMemberOfMemberOfMemberOfPublishedToEnrollMemberOfMemberOfEnrollPublishedToPublishedToEnrollMemberOfPublishedToEnrollMemberOfGenericAllGenericAllGenericAllGenericAllGenericAllGenericAllGenericAllADCSESC10aADCSESC10aADCSESC10aDomainNTAuthStoreRootCAEnterpriseCACertTemplate2AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueNoSecurityExtension:TrueDCUser4CertTemplate3AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:FalseSubjectAltRequireSPN:TrueUser3User2CertTemplate4AuthenticationEnabled:TrueRequireManagerApproval:TrueSchemaVersion:1SubjectAltRequireUPN:TrueGroup0CertTemplate5AuthenticationEnabled:FalseRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueUser5CertTemplate6AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:2SubjectAltRequireUPN:TrueAuthorizedSignatures:1CertTemplate1AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:2SubjectAltRequireUPN:TrueAuthorizedSignatures:0User6User1CertTemplate7AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:FalseUser7Group7Group6Group5Group4Group3Group2Group1 \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aharness2.json b/cmd/api/src/test/integration/harnesses/esc10aharness2.json new file mode 100644 index 0000000000..062fb24dbc --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aharness2.json @@ -0,0 +1,797 @@ +{ + "style": { + "font-family": "sans-serif", + "background-color": "#ffffff", + "background-image": "", + "background-size": "100%", + "node-color": "#ffffff", + "border-width": 4, + "border-color": "#000000", + "radius": 50, + "node-padding": 5, + "node-margin": 2, + "outside-position": "auto", + "node-icon-image": "", + "node-background-image": "", + "icon-position": "inside", + "icon-size": 64, + "caption-position": "inside", + "caption-max-width": 200, + "caption-color": "#000000", + "caption-font-size": 50, + "caption-font-weight": "normal", + "label-position": "inside", + "label-display": "pill", + "label-color": "#000000", + "label-background-color": "#ffffff", + "label-border-color": "#000000", + "label-border-width": 4, + "label-font-size": 40, + "label-padding": 5, + "label-margin": 4, + "directionality": "directed", + "detail-position": "inline", + "detail-orientation": "parallel", + "arrow-width": 5, + "arrow-color": "#000000", + "margin-start": 5, + "margin-end": 5, + "margin-peer": 20, + "attachment-start": "normal", + "attachment-end": "normal", + "relationship-icon-image": "", + "type-color": "#000000", + "type-background-color": "#ffffff", + "type-border-color": "#000000", + "type-border-width": 0, + "type-font-size": 16, + "type-padding": 5, + "property-position": "outside", + "property-alignment": "colon", + "property-color": "#000000", + "property-font-size": 16, + "property-font-weight": "normal" + }, + "nodes": [ + { + "id": "n1", + "position": { + "x": 129, + "y": 1327.41620651926 + }, + "caption": "Domain", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n2", + "position": { + "x": 1664.8135268077572, + "y": 1118.6584948427476 + }, + "caption": "NTAuthStore", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n3", + "position": { + "x": 1734.0761748485493, + "y": 1226.5718804439516 + }, + "caption": "RootCA", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n4", + "position": { + "x": 2089.3408145645117, + "y": 922.3660942769798 + }, + "caption": "EnterpriseCA", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n5", + "position": { + "x": 1855.4850232261435, + "y": 1327.41620651926 + }, + "caption": "DC", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n6", + "position": { + "x": 1324.7762805968055, + "y": 922.3660942769798 + }, + "caption": "Group0", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcc400" + } + }, + { + "id": "n7", + "position": { + "x": 1855.4850232261435, + "y": 649.2340527998417 + }, + "caption": "CertTemplate1", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "2", + "AuthorizedSignatures": "0", + "SubjectAltRequireEmail": "True", + "SubjectAltRequireUPN": "True", + "SubjectAltRequireDNS": "False", + "SubjectAltRequireDomainDNS": "False" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n8", + "position": { + "x": 973.639047876434, + "y": 727.2392440430749 + }, + "caption": "Group1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcc400" + } + }, + { + "id": "n9", + "position": { + "x": 1855.4850232261435, + "y": 366.352007001999 + }, + "caption": "CertTemplate2", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "2", + "AuthorizedSignatures": "0", + "SubjectAltRequireEmail": "True", + "SubjectAltRequireUPN": "True", + "SubjectAltRequireDNS": "True", + "SubjectAltRequireDomainDNS": "False" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n10", + "position": { + "x": 1855.4850232261435, + "y": 74.00519124323318 + }, + "caption": "CertTemplate3", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "2", + "AuthorizedSignatures": "0", + "SubjectAltRequireEmail": "True", + "SubjectAltRequireUPN": "True", + "SubjectAltRequireDNS": "False", + "SubjectAltRequireDomainDNS": "True" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n11", + "position": { + "x": 883.4172386161655, + "y": 649.2340527998417 + }, + "caption": "Computer1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b" + } + }, + { + "id": "n12", + "position": { + "x": 973.639047876434, + "y": 571.2288615566085 + }, + "caption": "User1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00" + } + }, + { + "id": "n13", + "position": { + "x": 973.639047876434, + "y": 444.3571982452322 + }, + "caption": "Group2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcc400" + } + }, + { + "id": "n14", + "position": { + "x": 883.4172386161655, + "y": 366.352007001999 + }, + "caption": "Computer2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b" + } + }, + { + "id": "n15", + "position": { + "x": 973.639047876434, + "y": 288.3468157587656 + }, + "caption": "User2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n16", + "position": { + "x": 973.639047876434, + "y": 152.01038248646637 + }, + "caption": "Group3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcc400" + } + }, + { + "id": "n17", + "position": { + "x": 883.4172386161655, + "y": 74.00519124323318 + }, + "caption": "Computer3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b" + } + }, + { + "id": "n18", + "position": { + "x": 973.639047876434, + "y": -4 + }, + "caption": "User3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n19", + "position": { + "x": 695.1130959232657, + "y": 727.2392440430749 + }, + "caption": "Group4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcc400", + "border-color": "#73d8ff" + } + }, + { + "id": "n20", + "position": { + "x": 619.2141547864899, + "y": 649.2340527998417 + }, + "caption": "Group5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcc400" + } + }, + { + "id": "n21", + "position": { + "x": 541.4379963358472, + "y": 571.2288615566085 + }, + "caption": "Group6", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcc400" + } + }, + { + "id": "n22", + "position": { + "x": 498.952275954523, + "y": 444.3571982452322 + }, + "caption": "Computer4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "border-color": "#73d8ff" + } + }, + { + "id": "n23", + "position": { + "x": 419.9345466228922, + "y": 366.352007001999 + }, + "caption": "Computer5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b" + } + }, + { + "id": "n24", + "position": { + "x": 343.33133116590204, + "y": 288.3468157587656 + }, + "caption": "Computer6", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "border-color": "#73d8ff" + } + }, + { + "id": "n25", + "position": { + "x": 282.15189381679534, + "y": 152.01038248646637 + }, + "caption": "User4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#73d8ff" + } + }, + { + "id": "n26", + "position": { + "x": 206.68013167102274, + "y": 74.00519124323318 + }, + "caption": "User5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00" + } + }, + { + "id": "n27", + "position": { + "x": 129, + "y": -4 + }, + "caption": "User6", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#73d8ff" + } + } + ], + "relationships": [ + { + "id": "n0", + "fromId": "n3", + "toId": "n1", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n1", + "fromId": "n4", + "toId": "n3", + "type": "IssuedSignedBy", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n2", + "fromId": "n2", + "toId": "n1", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n3", + "fromId": "n4", + "toId": "n2", + "type": "TrustedForNTAuth", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n4", + "fromId": "n4", + "toId": "n5", + "type": "CanAbuseUPNCertMapping", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n5", + "fromId": "n5", + "toId": "n1", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n6", + "fromId": "n6", + "toId": "n4", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n7", + "fromId": "n7", + "toId": "n4", + "type": "PublishedTo", + "properties": {}, + "style": {} + }, + { + "id": "n8", + "fromId": "n8", + "toId": "n7", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n9", + "fromId": "n8", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n10", + "fromId": "n9", + "toId": "n4", + "type": "PublishedTo", + "properties": {}, + "style": {} + }, + { + "id": "n11", + "fromId": "n10", + "toId": "n4", + "type": "PublishedTo", + "properties": {}, + "style": {} + }, + { + "id": "n12", + "fromId": "n11", + "toId": "n7", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n13", + "fromId": "n11", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n14", + "fromId": "n12", + "toId": "n7", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n15", + "fromId": "n12", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n16", + "fromId": "n13", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n17", + "fromId": "n14", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n18", + "fromId": "n15", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n19", + "fromId": "n15", + "toId": "n9", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n20", + "fromId": "n14", + "toId": "n9", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n21", + "fromId": "n13", + "toId": "n9", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n22", + "fromId": "n16", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n23", + "fromId": "n17", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n24", + "fromId": "n18", + "toId": "n6", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n25", + "fromId": "n16", + "toId": "n10", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n26", + "fromId": "n17", + "toId": "n10", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n27", + "fromId": "n18", + "toId": "n10", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n28", + "fromId": "n27", + "toId": "n18", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n29", + "fromId": "n26", + "toId": "n17", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n30", + "fromId": "n25", + "toId": "n16", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n31", + "fromId": "n24", + "toId": "n15", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n32", + "fromId": "n23", + "toId": "n14", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n33", + "fromId": "n22", + "toId": "n13", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n34", + "fromId": "n21", + "toId": "n12", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n35", + "fromId": "n20", + "toId": "n11", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n36", + "fromId": "n19", + "toId": "n8", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n37", + "fromId": "n26", + "toId": "n1", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n39", + "fromId": "n23", + "toId": "n1", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n41", + "fromId": "n21", + "toId": "n1", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n42", + "fromId": "n20", + "toId": "n1", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + } + ] +} \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aharness2.svg b/cmd/api/src/test/integration/harnesses/esc10aharness2.svg new file mode 100644 index 0000000000..2db2d6938d --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aharness2.svg @@ -0,0 +1 @@ +RootCAForIssuedSignedByNTAuthStoreForTrustedForNTAuthCanAbuseUPNCertMappingDCForEnrollPublishedToEnrollMemberOfPublishedToPublishedToEnrollMemberOfEnrollMemberOfMemberOfMemberOfMemberOfEnrollEnrollEnrollMemberOfMemberOfMemberOfEnrollEnrollEnrollGenericAllGenericAllGenericAllGenericAllGenericAllGenericAllGenericAllGenericAllGenericAllADCSESC10aADCSESC10aADCSESC10aADCSESC10aDomainNTAuthStoreRootCAEnterpriseCADCGroup0CertTemplate1AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:2AuthorizedSignatures:0SubjectAltRequireEmail:TrueSubjectAltRequireUPN:TrueSubjectAltRequireDNS:FalseSubjectAltRequireDomainDNS:FalseGroup1CertTemplate2AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:2AuthorizedSignatures:0SubjectAltRequireEmail:TrueSubjectAltRequireUPN:TrueSubjectAltRequireDNS:TrueSubjectAltRequireDomainDNS:FalseCertTemplate3AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:2AuthorizedSignatures:0SubjectAltRequireEmail:TrueSubjectAltRequireUPN:TrueSubjectAltRequireDNS:FalseSubjectAltRequireDomainDNS:TrueComputer1User1Group2Computer2User2Group3Computer3User3Group4Group5Group6Computer4Computer5Computer6User4User5User6 \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aharnesseca.json b/cmd/api/src/test/integration/harnesses/esc10aharnesseca.json new file mode 100644 index 0000000000..6e3db8ed62 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aharnesseca.json @@ -0,0 +1,1078 @@ +{ + "style": { + "font-family": "sans-serif", + "background-color": "#ffffff", + "background-image": "", + "background-size": "100%", + "node-color": "#ffffff", + "border-width": 4, + "border-color": "#000000", + "radius": 50, + "node-padding": 5, + "node-margin": 2, + "outside-position": "auto", + "node-icon-image": "", + "node-background-image": "", + "icon-position": "inside", + "icon-size": 64, + "caption-position": "inside", + "caption-max-width": 200, + "caption-color": "#000000", + "caption-font-size": 50, + "caption-font-weight": "normal", + "label-position": "inside", + "label-display": "pill", + "label-color": "#000000", + "label-background-color": "#ffffff", + "label-border-color": "#000000", + "label-border-width": 4, + "label-font-size": 40, + "label-padding": 5, + "label-margin": 4, + "directionality": "directed", + "detail-position": "inline", + "detail-orientation": "parallel", + "arrow-width": 5, + "arrow-color": "#000000", + "margin-start": 5, + "margin-end": 5, + "margin-peer": 20, + "attachment-start": "normal", + "attachment-end": "normal", + "relationship-icon-image": "", + "type-color": "#000000", + "type-background-color": "#ffffff", + "type-border-color": "#000000", + "type-border-width": 0, + "type-font-size": 16, + "type-padding": 5, + "property-position": "outside", + "property-alignment": "colon", + "property-color": "#000000", + "property-font-size": 16, + "property-font-weight": "normal" + }, + "nodes": [ + { + "id": "n0", + "position": { + "x": 1838.2521199214677, + "y": 2555.736538757399 + }, + "caption": "Domain1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n1", + "position": { + "x": 1442.701427932503, + "y": 2273.231824786215 + }, + "caption": "NTAuthStore1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n2", + "position": { + "x": 1442.701427932503, + "y": 2428.987863580507 + }, + "caption": "RootCA1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n3", + "position": { + "x": 446.2054422641352, + "y": 2273.231824786215 + }, + "caption": "CertTemplate1", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff" + } + }, + { + "id": "n4", + "position": { + "x": 1442.701427932503, + "y": 2117.4757859919237 + }, + "caption": "DC1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n5", + "position": { + "x": 956.6260599607343, + "y": 2273.231824786215 + }, + "caption": "EnterpriseCA1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n6", + "position": { + "x": 75.00000000000023, + "y": 2555.736538757399 + }, + "caption": "User1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n7", + "position": { + "x": 1838.2521199214673, + "y": 2038.867592259418 + }, + "caption": "Domain2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n8", + "position": { + "x": 1442.7014279325028, + "y": 1756.3628782882338 + }, + "caption": "NTAuthStore2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n9", + "position": { + "x": 1442.7014279325028, + "y": 1912.118917082525 + }, + "caption": "RootCA2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n10", + "position": { + "x": 446.20544226413443, + "y": 1756.3628782882338 + }, + "caption": "CertTemplate2", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff" + } + }, + { + "id": "n11", + "position": { + "x": 1442.7014279325028, + "y": 1600.6068394939425 + }, + "caption": "DC2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n12", + "position": { + "x": 956.6260599607343, + "y": 1756.3628782882338 + }, + "caption": "EnterpriseCA2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n13", + "position": { + "x": 75.00000000000034, + "y": 2038.867592259418 + }, + "caption": "User2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n14", + "position": { + "x": 1838.2521199214673, + "y": 1521.9986457614368 + }, + "caption": "Domain3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n15", + "position": { + "x": 1442.7014279325022, + "y": 1239.4939317902526 + }, + "caption": "NTAuthStore3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n16", + "position": { + "x": 1442.7014279325022, + "y": 1395.249970584544 + }, + "caption": "RootCA3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n17", + "position": { + "x": 446.20544226413415, + "y": 1239.4939317902526 + }, + "caption": "CertTemplate3", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff" + } + }, + { + "id": "n18", + "position": { + "x": 1442.7014279325022, + "y": 1083.7378929959623 + }, + "caption": "DC3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n19", + "position": { + "x": 956.6260599607343, + "y": 1239.4939317902526 + }, + "caption": "EnterpriseCA3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n20", + "position": { + "x": 75, + "y": 1521.9986457614368 + }, + "caption": "User3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n21", + "position": { + "x": 1838.2521199214677, + "y": 1005.1296992634557 + }, + "caption": "Domain4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n22", + "position": { + "x": 1442.7014279325026, + "y": 722.6249852922715 + }, + "caption": "NTAuthStore4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n23", + "position": { + "x": 1442.7014279325026, + "y": 878.3810240865628 + }, + "caption": "RootCA4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n24", + "position": { + "x": 446.2054422641343, + "y": 722.6249852922715 + }, + "caption": "CertTemplate4", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff" + } + }, + { + "id": "n25", + "position": { + "x": 1442.7014279325026, + "y": 566.8689464979814 + }, + "caption": "DC4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n26", + "position": { + "x": 956.6260599607343, + "y": 722.6249852922715 + }, + "caption": "EnterpriseCA4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n27", + "position": { + "x": 75.00000000000023, + "y": 1005.1296992634557 + }, + "caption": "User4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n28", + "position": { + "x": 1838.2521199214682, + "y": 488.26075276547454 + }, + "caption": "Domain5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n29", + "position": { + "x": 1442.7014279325024, + "y": 205.75603879429036 + }, + "caption": "NTAuthStore5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n30", + "position": { + "x": 1442.7014279325024, + "y": 361.51207758858163 + }, + "caption": "RootCA5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n31", + "position": { + "x": 446.2054422641345, + "y": 205.75603879429036 + }, + "caption": "CertTemplate5", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff" + } + }, + { + "id": "n32", + "position": { + "x": 1442.7014279325024, + "y": 50 + }, + "caption": "DC5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n33", + "position": { + "x": 956.6260599607343, + "y": 205.75603879429036 + }, + "caption": "EnterpriseCA5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n34", + "position": { + "x": 75.00000000000034, + "y": 488.26075276547454 + }, + "caption": "User5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n35", + "position": { + "x": -194.9337573355329, + "y": 2761.1687609848414 + }, + "caption": "Group1", + "style": { + "node-color": "#fcdc00" + }, + "labels": [], + "properties": {} + }, + { + "id": "n36", + "position": { + "x": -194.9337573355329, + "y": 2244.299814486861 + }, + "caption": "Group2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n37", + "position": { + "x": -194.9337573355329, + "y": 1727.430867988881 + }, + "caption": "Group3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n38", + "position": { + "x": -194.9337573355329, + "y": 1210.5619214909007 + }, + "caption": "Group4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n39", + "position": { + "x": -194.9337573355329, + "y": 693.6929749929204 + }, + "caption": "Group5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + } + ], + "relationships": [ + { + "id": "n0", + "fromId": "n2", + "toId": "n0", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n1", + "fromId": "n1", + "toId": "n0", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n2", + "fromId": "n4", + "toId": "n0", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n3", + "fromId": "n3", + "toId": "n5", + "type": "PublishedTo", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n4", + "fromId": "n5", + "toId": "n2", + "type": "IssuedSignedBy", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n5", + "fromId": "n5", + "toId": "n1", + "type": "TrustedForNTAuth", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n6", + "fromId": "n6", + "toId": "n5", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n7", + "fromId": "n5", + "toId": "n4", + "type": "CanAbuseUPNCertMapping", + "properties": {}, + "style": {} + }, + { + "id": "n8", + "fromId": "n6", + "toId": "n3", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n9", + "fromId": "n9", + "toId": "n7", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n10", + "fromId": "n8", + "toId": "n7", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n11", + "fromId": "n11", + "toId": "n7", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n12", + "fromId": "n10", + "toId": "n12", + "type": "PublishedTo", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n13", + "fromId": "n12", + "toId": "n9", + "type": "IssuedSignedBy", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n14", + "fromId": "n12", + "toId": "n8", + "type": "TrustedForNTAuth", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n15", + "fromId": "n13", + "toId": "n12", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n16", + "fromId": "n13", + "toId": "n10", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n17", + "fromId": "n16", + "toId": "n14", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n18", + "fromId": "n15", + "toId": "n14", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n19", + "fromId": "n18", + "toId": "n14", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n20", + "fromId": "n17", + "toId": "n19", + "type": "PublishedTo", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n21", + "fromId": "n19", + "toId": "n16", + "type": "IssuedSignedBy", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n22", + "fromId": "n20", + "toId": "n19", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n23", + "fromId": "n19", + "toId": "n18", + "type": "CanAbuseUPNCertMapping", + "properties": {}, + "style": {} + }, + { + "id": "n24", + "fromId": "n20", + "toId": "n17", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n25", + "fromId": "n23", + "toId": "n21", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n26", + "fromId": "n22", + "toId": "n21", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n27", + "fromId": "n25", + "toId": "n21", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n28", + "fromId": "n24", + "toId": "n26", + "type": "PublishedTo", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n29", + "fromId": "n26", + "toId": "n22", + "type": "TrustedForNTAuth", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n30", + "fromId": "n27", + "toId": "n26", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n31", + "fromId": "n26", + "toId": "n25", + "type": "CanAbuseUPNCertMapping", + "properties": {}, + "style": {} + }, + { + "id": "n32", + "fromId": "n27", + "toId": "n24", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n33", + "fromId": "n30", + "toId": "n28", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n34", + "fromId": "n29", + "toId": "n28", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n35", + "fromId": "n32", + "toId": "n28", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n36", + "fromId": "n33", + "toId": "n30", + "type": "IssuedSignedBy", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n37", + "fromId": "n33", + "toId": "n29", + "type": "TrustedForNTAuth", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n38", + "fromId": "n34", + "toId": "n33", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n39", + "fromId": "n33", + "toId": "n32", + "type": "CanAbuseUPNCertMapping", + "properties": {}, + "style": {} + }, + { + "id": "n40", + "fromId": "n34", + "toId": "n31", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n41", + "type": "GenericAll", + "style": {}, + "properties": {}, + "fromId": "n35", + "toId": "n6" + }, + { + "id": "n42", + "type": "ADCSESC10a", + "style": {}, + "properties": {}, + "fromId": "n35", + "toId": "n0" + }, + { + "id": "n43", + "type": "GenericAll", + "style": {}, + "properties": {}, + "fromId": "n36", + "toId": "n13" + }, + { + "id": "n44", + "type": "GenericAll", + "style": {}, + "properties": {}, + "fromId": "n39", + "toId": "n34" + }, + { + "id": "n45", + "type": "GenericAll", + "style": {}, + "properties": {}, + "fromId": "n38", + "toId": "n27" + }, + { + "id": "n46", + "type": "GenericAll", + "style": {}, + "properties": {}, + "fromId": "n37", + "toId": "n20" + } + ] +} \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aharnesseca.svg b/cmd/api/src/test/integration/harnesses/esc10aharnesseca.svg new file mode 100644 index 0000000000..5a9511fdab --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aharnesseca.svg @@ -0,0 +1 @@ +RootCAForNTAuthStoreForDCForPublishedToIssuedSignedByTrustedForNTAuthEnrollCanAbuseUPNCertMappingEnrollRootCAForNTAuthStoreForDCForPublishedToIssuedSignedByTrustedForNTAuthEnrollEnrollRootCAForNTAuthStoreForDCForPublishedToIssuedSignedByEnrollCanAbuseUPNCertMappingEnrollRootCAForNTAuthStoreForDCForPublishedToTrustedForNTAuthEnrollCanAbuseUPNCertMappingEnrollRootCAForNTAuthStoreForDCForIssuedSignedByTrustedForNTAuthEnrollCanAbuseUPNCertMappingEnrollGenericAllADCSESC10aGenericAllGenericAllGenericAllGenericAllDomain1NTAuthStore1RootCA1CertTemplate1AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueDC1EnterpriseCA1User1Domain2NTAuthStore2RootCA2CertTemplate2AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueDC2EnterpriseCA2User2Domain3NTAuthStore3RootCA3CertTemplate3AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueDC3EnterpriseCA3User3Domain4NTAuthStore4RootCA4CertTemplate4AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueDC4EnterpriseCA4User4Domain5NTAuthStore5RootCA5CertTemplate5AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueDC5EnterpriseCA5User5Group1Group2Group3Group4Group5 \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aharnessvictim.json b/cmd/api/src/test/integration/harnesses/esc10aharnessvictim.json new file mode 100644 index 0000000000..2865cd0be9 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aharnessvictim.json @@ -0,0 +1,452 @@ +{ + "style": { + "font-family": "sans-serif", + "background-color": "#ffffff", + "background-image": "", + "background-size": "100%", + "node-color": "#ffffff", + "border-width": 4, + "border-color": "#000000", + "radius": 50, + "node-padding": 5, + "node-margin": 2, + "outside-position": "auto", + "node-icon-image": "", + "node-background-image": "", + "icon-position": "inside", + "icon-size": 64, + "caption-position": "inside", + "caption-max-width": 200, + "caption-color": "#000000", + "caption-font-size": 50, + "caption-font-weight": "normal", + "label-position": "inside", + "label-display": "pill", + "label-color": "#000000", + "label-background-color": "#ffffff", + "label-border-color": "#000000", + "label-border-width": 4, + "label-font-size": 40, + "label-padding": 5, + "label-margin": 4, + "directionality": "directed", + "detail-position": "inline", + "detail-orientation": "parallel", + "arrow-width": 5, + "arrow-color": "#000000", + "margin-start": 5, + "margin-end": 5, + "margin-peer": 20, + "attachment-start": "normal", + "attachment-end": "normal", + "relationship-icon-image": "", + "type-color": "#000000", + "type-background-color": "#ffffff", + "type-border-color": "#000000", + "type-border-width": 0, + "type-font-size": 16, + "type-padding": 5, + "property-position": "outside", + "property-alignment": "colon", + "property-color": "#000000", + "property-font-size": 16, + "property-font-weight": "normal" + }, + "nodes": [ + { + "id": "n0", + "position": { + "x": 337.04748861166024, + "y": 50 + }, + "caption": "Domain", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n1", + "position": { + "x": 2497.3664674686984, + "y": 410.62726890336444 + }, + "caption": "NTAuthStore", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n2", + "position": { + "x": 2381.5025296927042, + "y": 212.5597044346638 + }, + "caption": "RootCA", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n3", + "position": { + "x": 2215.339273936165, + "y": 1016.8790026674126 + }, + "caption": "EnterpriseCA", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n4", + "position": { + "x": 2215.339273936165, + "y": 50 + }, + "caption": "DC", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n5", + "position": { + "x": 531.0993047358514, + "y": 1016.8790026674126 + }, + "caption": "Group0", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n6", + "position": { + "x": 1987.2171233355546, + "y": 529.3782522542996 + }, + "caption": "CertTemplate1", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n7", + "position": { + "x": 1041.8790026674124, + "y": 312.04748861166024 + }, + "caption": "User1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00" + } + }, + { + "id": "n8", + "position": { + "x": 1041.8790026674124, + "y": 456.9346643734199 + }, + "caption": "User2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00" + } + }, + { + "id": "n9", + "position": { + "x": 1041.8790026674124, + "y": 601.8218401351795 + }, + "caption": "User3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n10", + "position": { + "x": 1041.8790026674124, + "y": 746.7090158969393 + }, + "caption": "User4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#000000" + } + }, + { + "id": "n11", + "position": { + "x": 75, + "y": 312.04748861166024 + }, + "caption": "Group1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n12", + "position": { + "x": 158.45112686904184, + "y": 456.9346643734199 + }, + "caption": "Group2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n13", + "position": { + "x": 246.0320350312874, + "y": 601.8218401351795 + }, + "caption": "Group3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n14", + "position": { + "x": 337.04748861166024, + "y": 746.7090158969393 + }, + "caption": "Group4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + } + ], + "relationships": [ + { + "id": "n0", + "fromId": "n2", + "toId": "n0", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n1", + "fromId": "n3", + "toId": "n2", + "type": "IssuedSignedBy", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n2", + "fromId": "n1", + "toId": "n0", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n3", + "fromId": "n3", + "toId": "n1", + "type": "TrustedForNTAuth", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n4", + "fromId": "n3", + "toId": "n4", + "type": "CanAbuseUPNCertMapping", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n5", + "fromId": "n4", + "toId": "n0", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n6", + "fromId": "n5", + "toId": "n3", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n7", + "fromId": "n6", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": {} + }, + { + "id": "n8", + "fromId": "n7", + "toId": "n6", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n9", + "fromId": "n7", + "toId": "n5", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n10", + "type": "AllExtendedRights", + "style": {}, + "properties": {}, + "fromId": "n8", + "toId": "n6" + }, + { + "id": "n11", + "fromId": "n8", + "toId": "n5", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n12", + "fromId": "n9", + "toId": "n6", + "type": "GenericWrite", + "properties": {}, + "style": {} + }, + { + "id": "n13", + "fromId": "n9", + "toId": "n5", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n14", + "fromId": "n10", + "toId": "n6", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n15", + "fromId": "n11", + "toId": "n7", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n16", + "fromId": "n11", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n17", + "fromId": "n12", + "toId": "n8", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n18", + "fromId": "n12", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n19", + "fromId": "n13", + "toId": "n9", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n20", + "fromId": "n14", + "toId": "n10", + "type": "GenericAll", + "properties": {}, + "style": {} + } + ] +} \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aharnessvictim.svg b/cmd/api/src/test/integration/harnesses/esc10aharnessvictim.svg new file mode 100644 index 0000000000..18146eadf7 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aharnessvictim.svg @@ -0,0 +1 @@ +RootCAForIssuedSignedByNTAuthStoreForTrustedForNTAuthCanAbuseUPNCertMappingDCForEnrollPublishedToGenericAllMemberOfAllExtendedRightsMemberOfGenericWriteMemberOfEnrollGenericAllADCSESC10aGenericAllADCSESC10aGenericAllGenericAllDomainNTAuthStoreRootCAEnterpriseCADCGroup0CertTemplate1AuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueUser1User2User3User4Group1Group2Group3Group4 \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.json b/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.json new file mode 100644 index 0000000000..c0c4bd7f8e --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.json @@ -0,0 +1,474 @@ +{ + "style": { + "font-family": "sans-serif", + "background-color": "#ffffff", + "background-image": "", + "background-size": "100%", + "node-color": "#ffffff", + "border-width": 4, + "border-color": "#000000", + "radius": 50, + "node-padding": 5, + "node-margin": 2, + "outside-position": "auto", + "node-icon-image": "", + "node-background-image": "", + "icon-position": "inside", + "icon-size": 64, + "caption-position": "inside", + "caption-max-width": 200, + "caption-color": "#000000", + "caption-font-size": 50, + "caption-font-weight": "normal", + "label-position": "inside", + "label-display": "pill", + "label-color": "#000000", + "label-background-color": "#ffffff", + "label-border-color": "#000000", + "label-border-width": 4, + "label-font-size": 40, + "label-padding": 5, + "label-margin": 4, + "directionality": "directed", + "detail-position": "inline", + "detail-orientation": "parallel", + "arrow-width": 5, + "arrow-color": "#000000", + "margin-start": 5, + "margin-end": 5, + "margin-peer": 20, + "attachment-start": "normal", + "attachment-end": "normal", + "relationship-icon-image": "", + "type-color": "#000000", + "type-background-color": "#ffffff", + "type-border-color": "#000000", + "type-border-width": 0, + "type-font-size": 16, + "type-padding": 5, + "property-position": "outside", + "property-alignment": "colon", + "property-color": "#000000", + "property-font-size": 16, + "property-font-weight": "normal" + }, + "nodes": [ + { + "id": "n0", + "position": { + "x": 24.6412667909101, + "y": 1790.9164380862208 + }, + "caption": "Domain", + "labels": [], + "properties": {}, + "style": { + "node-color": "#68ccca" + } + }, + { + "id": "n1", + "position": { + "x": 915.5043544911281, + "y": 1554.763441047 + }, + "caption": "NTAuthStore", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n2", + "position": { + "x": 915.5043544911281, + "y": 1672.8399395666104 + }, + "caption": "RootCA", + "labels": [], + "properties": {}, + "style": { + "node-color": "#653294", + "caption-color": "#ffffff" + } + }, + { + "id": "n3", + "position": { + "x": 1729.453404980963, + "y": 1396.5299845769014 + }, + "caption": "EnterpriseCA", + "labels": [], + "properties": {}, + "style": { + "node-color": "#194d33", + "caption-color": "#ffffff" + } + }, + { + "id": "n4", + "position": { + "x": 915.5043544911281, + "y": 1790.9164380862208 + }, + "caption": "DC", + "labels": [], + "properties": {}, + "style": { + "node-color": "#f44e3b", + "caption-position": "inside", + "property-alignment": "colon", + "property-position": "outside" + } + }, + { + "id": "n6", + "position": { + "x": 1729.453404980963, + "y": 1082.4385788188413 + }, + "caption": "CertTemplate", + "labels": [], + "properties": { + "AuthenticationEnabled": "True", + "RequireManagerApproval": "False", + "SchemaVersion": "1", + "SubjectAltRequireUPN": "True" + }, + "style": { + "node-color": "#fda1ff", + "outside-position": "left", + "node-margin": 50 + } + }, + { + "id": "n7", + "position": { + "x": 915.5043544911281, + "y": 1082.4385788188413 + }, + "caption": "User1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00", + "border-color": "#73d8ff" + } + }, + { + "id": "n11", + "position": { + "x": 24.6412667909101, + "y": 842.2531080094587 + }, + "caption": "Group1", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n12", + "position": { + "x": 97.83319758088052, + "y": 977.4451369153232 + }, + "caption": "Group2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n14", + "position": { + "x": 432.58749843883385, + "y": 1449.4515969240565 + }, + "caption": "Group6", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00", + "border-color": "#73d8ff" + } + }, + { + "id": "n15", + "position": { + "x": 180.7937059276843, + "y": 1105.4077644506815 + }, + "caption": "Group3", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n16", + "position": { + "x": 259.9130584079944, + "y": 1235.2263846542628 + }, + "caption": "Group4", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n17", + "position": { + "x": 345.4214516200178, + "y": 1344.891677696618 + }, + "caption": "Group5", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + }, + { + "id": "n18", + "position": { + "x": 915.504354491128, + "y": 1396.5299845769018 + }, + "caption": "User2", + "labels": [], + "properties": {}, + "style": { + "node-color": "#a4dd00" + } + }, + { + "id": "n19", + "position": { + "x": 1311.4447947779927, + "y": 1235.2263846542628 + }, + "caption": "Group0", + "labels": [], + "properties": {}, + "style": { + "node-color": "#fcdc00" + } + } + ], + "relationships": [ + { + "id": "n0", + "fromId": "n2", + "toId": "n0", + "type": "RootCAFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n1", + "fromId": "n3", + "toId": "n2", + "type": "IssuedSignedBy", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n2", + "fromId": "n1", + "toId": "n0", + "type": "NTAuthStoreFor", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n3", + "fromId": "n3", + "toId": "n1", + "type": "TrustedForNTAuth", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n4", + "fromId": "n3", + "toId": "n4", + "type": "CanAbuseUPNCertMapping", + "properties": {}, + "style": { + "arrow-color": "#000000" + } + }, + { + "id": "n5", + "fromId": "n4", + "toId": "n0", + "type": "DCFor", + "properties": {}, + "style": { + "type-color": "#000000", + "arrow-color": "#000000" + } + }, + { + "id": "n7", + "fromId": "n6", + "toId": "n3", + "type": "PublishedTo", + "properties": {}, + "style": {} + }, + { + "id": "n17", + "fromId": "n11", + "toId": "n7", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n18", + "fromId": "n11", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n20", + "fromId": "n12", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n21", + "fromId": "n12", + "toId": "n7", + "type": "GenericWrite", + "properties": {}, + "style": {} + }, + { + "id": "n22", + "fromId": "n14", + "toId": "n7", + "type": "AllExtendedRights", + "properties": {}, + "style": {} + }, + { + "id": "n23", + "fromId": "n15", + "toId": "n7", + "type": "WriteDacl", + "properties": {}, + "style": {} + }, + { + "id": "n24", + "fromId": "n15", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n25", + "fromId": "n16", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n26", + "fromId": "n16", + "toId": "n7", + "type": "WriteOwner", + "properties": {}, + "style": {} + }, + { + "id": "n27", + "fromId": "n17", + "toId": "n0", + "type": "ADCSESC10a", + "properties": {}, + "style": {} + }, + { + "id": "n28", + "fromId": "n17", + "toId": "n7", + "type": "WriteOwner", + "properties": {}, + "style": {} + }, + { + "id": "n38", + "fromId": "n18", + "toId": "n18", + "type": "GenericAll", + "properties": {}, + "style": {} + }, + { + "id": "n39", + "type": "ADCSESC10a", + "style": {}, + "properties": {}, + "fromId": "n18", + "toId": "n0" + }, + { + "id": "n40", + "fromId": "n7", + "toId": "n19", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n41", + "fromId": "n18", + "toId": "n19", + "type": "MemberOf", + "properties": {}, + "style": {} + }, + { + "id": "n42", + "fromId": "n19", + "toId": "n6", + "type": "Enroll", + "properties": {}, + "style": {} + }, + { + "id": "n43", + "fromId": "n19", + "toId": "n3", + "type": "Enroll", + "properties": {}, + "style": {} + } + ] +} \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.svg b/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.svg new file mode 100644 index 0000000000..2e5387ed13 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/esc10aprincipalharness.svg @@ -0,0 +1 @@ +RootCAForIssuedSignedByNTAuthStoreForTrustedForNTAuthCanAbuseUPNCertMappingDCForPublishedToGenericAllADCSESC10aADCSESC10aGenericWriteAllExtendedRightsWriteDaclADCSESC10aADCSESC10aWriteOwnerADCSESC10aWriteOwnerGenericAllADCSESC10aMemberOfMemberOfEnrollEnrollDomainNTAuthStoreRootCAEnterpriseCADCCertTemplateAuthenticationEnabled:TrueRequireManagerApproval:FalseSchemaVersion:1SubjectAltRequireUPN:TrueUser1Group1Group2Group6Group3Group4Group5User2Group0 \ No newline at end of file diff --git a/cmd/api/src/test/integration/harnesses/harnessgen.py b/cmd/api/src/test/integration/harnesses/harnessgen.py new file mode 100644 index 0000000000..9b0167ee91 --- /dev/null +++ b/cmd/api/src/test/integration/harnesses/harnessgen.py @@ -0,0 +1,183 @@ +import json +import sys + +with open(sys.argv[1], 'r') as f: + j = json.load(f) + +nodes = {} +relationships = [] + + +class BaseNode: + def __init__(self, name: str): + self.name = name + + +class UserNode(BaseNode): + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.NewActiveDirectoryUser("{self.name}", domainSid)' + + +class GroupNode(BaseNode): + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.NewActiveDirectoryGroup("{self.name}", domainSid)' + + +class ComputerNode(BaseNode): + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.NewActiveDirectoryComputer("{self.name}", domainSid)' + + +class OUNode(BaseNode): + def __init__(self, name: str, blocksInheritance: bool = False): + super().__init__(name) + self.blocksInheritance = blocksInheritance + + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.NewActiveDirectoryOU("{self.name}", domainSid, {json.dumps(self.blocksInheritance)})' + + +class DomainNode(BaseNode): + def __init__(self, name: str, blocksInheritance: bool = False): + super().__init__(name) + self.blocksInheritance = blocksInheritance + + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.NewActiveDirectoryDomain("{self.name}", domainSid, {json.dumps(self.blocksInheritance)}, true)' + + +class NTAuthStore(BaseNode): + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.NewActiveDirectoryNTAuthStore("{self.name}", domainSid)' + + +class RootCA(BaseNode): + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.NewActiveDirectoryRootCA("{self.name}", domainSid)' + + +class EnterpriseCA(BaseNode): + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.NewActiveDirectoryEnterpriseCA("{self.name}", domainSid)' + + +class CertTemplateData: + def __init__(self) -> None: + self.RequiresManagerApproval = False + self.AuthenticationEnabled = False + self.EnrolleeSuppliesSubject = False + self.SubjectAltRequireUPN = False + self.SubjectAltRequireSPN = False + self.NoSecurityExtension = False + self.SchemaVersion = 1 + self.AuthorizedSignatures = 0 + self.EKUS = [] + self.ApplicationPolicies = [] + self.SubjectAltRequireEmail = False + + def toJSON(self): + return json.dumps(self, default=lambda o: o.__dict__, + sort_keys=True, indent=4) + + +def create_certtemplate_data(j): + data = CertTemplateData() + properties = j['properties'] + for key in properties: + v = properties[key] + k = key + if k == 'RequireManagerApproval': + k = 'RequiresManagerApproval' + if v == 'True': + data.__dict__[k] = True + elif v == 'False': + data.__dict__[k] = False + else: + try: + i = int(v) + data.__dict__[k] = i + except: + data.__dict__[k] = v + + return data + + +class CertTemplate(BaseNode): + def __init__(self, name: str, data: CertTemplateData): + super().__init__(name) + self.data = data + + def create_creation_statement(self): + d = self.data.toJSON() + d = d.replace(']', '}') + d = d.replace('[', '[]string{') + d = d.replace('"', '') + d = d[:-2] + "," + d[-2:] + b = f's.{self.name} = graphTestContext.NewActiveDirectoryCertTemplate("{self.name}", domainSid, CertTemplateData{d})' + + return b + + +class UnknownNode(BaseNode): + def create_creation_statement(self): + return f's.{self.name} = graphTestContext.REPLACEME("{self.name}", domainSid)' + + +class Relationship: + def __init__(self, fromID: str, toID: str, type: str) -> None: + self.fromID = fromID + self.toID = toID + self.type = type + + def create_statement(self): + return f'graphTestContext.NewRelationship(s.{nodes[self.fromID].name}, s.{nodes[self.toID].name}, ad.{self.type})' + + +for node in j['nodes']: + name = node['caption'] + id = node['id'] + if 'User' in name: + nodes[id] = UserNode(name) + elif 'Group' in name: + nodes[id] = GroupNode(name) + elif 'Computer' in name: + nodes[id] = ComputerNode(name) + elif 'OU' in name: + nodes[id] = OUNode(name) + elif 'NTAuthStore' in name: + nodes[id] = NTAuthStore(name) + elif 'RootCA' in name: + nodes[id] = RootCA(name) + elif 'Domain' in name: + nodes[id] = DomainNode(name) + elif 'EnterpriseCA' in name or 'ECA' in name: + nodes[id] = EnterpriseCA(name) + elif 'CertTemplate' in name: + d = create_certtemplate_data(node) + nodes[id] = CertTemplate(name, d) + else: + print(f'Could not determine type for {name}') + nodes[id] = UnknownNode(name) + +for rel in j['relationships']: + relationships.append(Relationship(rel['fromId'], rel['toId'], rel['type'])) + +harnessName = input("Name this harness: ") +structDef = f"type {harnessName} struct {{\n" +setupFunc = f"func (s *{harnessName}) Setup(graphTestContext *GraphTestContext) {{\n domainSid := RandomDomainSID()\n" + +for k in {k: v for k, v in sorted(nodes.items(), key=lambda item: item[1].name)}: + structDef += f'{nodes[k].name} *graph.Node\n' + setupFunc += nodes[k].create_creation_statement() + "\n" + +structDef += "}" + +for r in relationships: + setupFunc += r.create_statement() + "\n" + +setupFunc += "}" + +print() +print(structDef) +print() +print(setupFunc) diff --git a/cmd/ui/src/views/Explore/EdgeInfo/EdgeInfoContent.tsx b/cmd/ui/src/views/Explore/EdgeInfo/EdgeInfoContent.tsx index bc0e4217bb..28972f3642 100644 --- a/cmd/ui/src/views/Explore/EdgeInfo/EdgeInfoContent.tsx +++ b/cmd/ui/src/views/Explore/EdgeInfo/EdgeInfoContent.tsx @@ -73,7 +73,9 @@ const EdgeInfoContent: FC<{ selectedEdge: NonNullable }> = ({ sele const sendOnChange = (selectedEdge.name === 'GoldenCert' || selectedEdge.name === 'ADCSESC1' || - selectedEdge.name === 'ADCSESC3') && + selectedEdge.name === 'ADCSESC3' || + selectedEdge.name === 'ADCSESC6a' || + selectedEdge.name === 'ADCSESC9a') && section[0] === 'composition'; return ( diff --git a/cmd/ui/src/views/Explore/ExploreSearchCombobox/ExploreSearchCombobox.tsx b/cmd/ui/src/views/Explore/ExploreSearchCombobox/ExploreSearchCombobox.tsx index b4e807795f..a235fd1c4a 100644 --- a/cmd/ui/src/views/Explore/ExploreSearchCombobox/ExploreSearchCombobox.tsx +++ b/cmd/ui/src/views/Explore/ExploreSearchCombobox/ExploreSearchCombobox.tsx @@ -81,7 +81,13 @@ const ExploreSearchCombobox: React.FC<{ }, startAdornment: selectedItem?.type && , }} - {...getInputProps({ onFocus: openMenu, refKey: 'inputRef', onChange: (e) => {handleNodeEdited(e.currentTarget.value)} })} + {...getInputProps({ + onFocus: openMenu, + refKey: 'inputRef', + onChange: (e) => { + handleNodeEdited(e.currentTarget.value); + }, + })} data-testid='explore_search_input-search' />
0 { + if err := enterpriseCAs.Each(func(value uint32) (bool, error) { + for _, segment := range enterpriseCASegments[graph.ID(value)] { + paths.AddPath(segment.Path()) + } + return true, nil + + }); err != nil { + return paths, err + } + } + + return paths, nil +} + func GetADCSESC3EdgeComposition(ctx context.Context, db graph.Database, edge *graph.Relationship) (graph.PathSet, error) { var ( startNode *graph.Node @@ -807,7 +1098,7 @@ func getDelegatedEnrollmentAgentPath(ctx context.Context, startNode, certTemplat func ADCSESC1Path1Pattern(domainID graph.ID) traversal.PatternContinuation { return traversal.NewPattern(). - Outbound(query.And( + OutboundWithDepth(0, 0, query.And( query.Kind(query.Relationship(), ad.MemberOf), query.Kind(query.End(), ad.Group), )). @@ -846,7 +1137,7 @@ func ADCSESC1Path1Pattern(domainID graph.ID) traversal.PatternContinuation { func ADCSESC1Path2Pattern(domainID graph.ID, enterpriseCAs cardinality.Duplex[uint32]) traversal.PatternContinuation { return traversal.NewPattern(). - Outbound(query.And( + OutboundWithDepth(0, 0, query.And( query.Kind(query.Relationship(), ad.MemberOf), query.Kind(query.End(), ad.Group), )). @@ -931,8 +1222,6 @@ func GetADCSESC1EdgeComposition(ctx context.Context, db graph.Database, edge *gr // Render paths from the segments return paths, path1EnterpriseCAs.Each(func(value uint32) (bool, error) { for _, segment := range candidateSegments[graph.ID(value)] { - log.Infof("Found ESC1 Path: %s", graph.FormatPathSegment(segment)) - paths.AddPath(segment.Path()) } @@ -975,3 +1264,248 @@ func getGoldenCertEdgeComposition(tx graph.Transaction, edge *graph.Relationship return finalPaths, nil } } + +func adcsESC9aPath1Pattern(domainID graph.ID) traversal.PatternContinuation { + return traversal.NewPattern(). + OutboundWithDepth( + 1, 1, + query.And( + query.KindIn(query.Relationship(), ad.GenericWrite, ad.GenericAll, ad.Owns, ad.WriteOwner, ad.WriteDACL), + query.KindIn(query.End(), ad.Computer, ad.User), + ), + ). + OutboundWithDepth( + 0, 0, + query.And( + query.Kind(query.Relationship(), ad.MemberOf), + query.Kind(query.End(), ad.Group), + ), + ). + Outbound( + query.And( + query.KindIn(query.Relationship(), ad.GenericAll, ad.Enroll, ad.AllExtendedRights), + query.Kind(query.End(), ad.CertTemplate), + query.Equals(query.EndProperty(ad.RequiresManagerApproval.String()), false), + query.Equals(query.EndProperty(ad.AuthenticationEnabled.String()), true), + query.Equals(query.EndProperty(ad.NoSecurityExtension.String()), true), + query.Equals(query.EndProperty(ad.EnrolleeSuppliesSubject.String()), false), + query.Or( + query.Equals(query.EndProperty(ad.SubjectAltRequireUPN.String()), true), + query.Equals(query.EndProperty(ad.SubjectAltRequireSPN.String()), true), + ), + query.Or( + query.Equals(query.EndProperty(ad.SchemaVersion.String()), 1), + query.And( + query.GreaterThan(query.EndProperty(ad.SchemaVersion.String()), 1), + query.Equals(query.EndProperty(ad.AuthorizedSignatures.String()), 0), + ), + ), + ), + ). + Outbound(query.And( + query.KindIn(query.Relationship(), ad.PublishedTo, ad.IssuedSignedBy), + query.Kind(query.End(), ad.EnterpriseCA), + )). + Outbound(query.And( + query.KindIn(query.Relationship(), ad.IssuedSignedBy, ad.EnterpriseCAFor), + query.Kind(query.End(), ad.RootCA), + )). + Outbound(query.And( + query.KindIn(query.Relationship(), ad.RootCAFor), + query.Equals(query.EndID(), domainID), + )) +} + +func adcsESC9APath2Pattern(caNodes []graph.ID, domainId graph.ID) traversal.PatternContinuation { + return traversal.NewPattern(). + OutboundWithDepth(0, 0, query.And( + query.Kind(query.Relationship(), ad.MemberOf), + query.Kind(query.End(), ad.Group), + )). + Outbound(query.And( + query.Kind(query.Relationship(), ad.Enroll), + query.InIDs(query.End(), caNodes...), + )). + Outbound(query.And( + query.KindIn(query.Relationship(), ad.TrustedForNTAuth), + query.Kind(query.End(), ad.NTAuthStore), + )). + Outbound(query.And( + query.KindIn(query.Relationship(), ad.NTAuthStoreFor), + query.Equals(query.EndID(), domainId), + )) +} + +func adcsESC9APath3Pattern(caIDs []graph.ID) traversal.PatternContinuation { + return traversal.NewPattern(). + Inbound( + query.KindIn(query.Relationship(), ad.DCFor, ad.TrustedBy), + ). + Inbound(query.And( + query.Kind(query.Relationship(), ad.CanAbuseWeakCertBinding), + query.InIDs(query.StartID(), caIDs...), + )) +} + +func GetADCSESC9aEdgeComposition(ctx context.Context, db graph.Database, edge *graph.Relationship) (graph.PathSet, error) { + /* + MATCH (n {objectid:'S-1-5-21-3933516454-2894985453-2515407000-500'})-[:ADCSESC9a]->(d:Domain {objectid:'S-1-5-21-3933516454-2894985453-2515407000'}) + OPTIONAL MATCH p1 = (n)-[:GenericAll|GenericWrite|Owns|WriteOwner|WriteDacl]->(m)-[:MemberOf*0..]->()-[:GenericAll|Enroll|AllExtendedRights]->(ct)-[:PublishedTo]->(ca)-[:IssuedSignedBy|EnterpriseCAFor|RootCAFor*1..]->(d) + WHERE ct.requiresmanagerapproval = false + AND ct.authenticationenabled = true + AND ct.nosecurityextension = true + AND ct.enrolleesuppliessubject = false + AND (ct.subjectaltrequireupn = true OR ct.subjectaltrequirespn = true) + AND ( + (ct.schemaversion > 1 AND ct.authorizedsignatures = 0) + OR ct.schemaversion = 1 + ) + AND ( + m:Computer + OR (m:User AND ct.subjectaltrequiredns = false AND ct.subjectaltrequiredomaindns = false) + ) + OPTIONAL MATCH p2 = (m)-[:MemberOf*0..]->()-[:Enroll]->(ca)-[:TrustedForNTAuth]->(nt)-[:NTAuthStoreFor]->(d) + OPTIONAL MATCH p3 = (ca)-[:CanAbuseWeakCertBinding|DCFor|TrustedBy*1..]->(d) + RETURN p1,p2,p3 + */ + + var ( + startNode *graph.Node + endNode *graph.Node + + traversalInst = traversal.New(db, analysis.MaximumDatabaseParallelWorkers) + paths = graph.PathSet{} + path1CandidateSegments = map[graph.ID][]*graph.PathSegment{} + victimCANodes = map[graph.ID][]graph.ID{} + path2CandidateSegments = map[graph.ID][]*graph.PathSegment{} + path3CandidateSegments = map[graph.ID][]*graph.PathSegment{} + p2canodes = make([]graph.ID, 0) + nodeMap = map[graph.ID]*graph.Node{} + lock = &sync.Mutex{} + ) + + if err := db.ReadTransaction(ctx, func(tx graph.Transaction) error { + if node, err := ops.FetchNode(tx, edge.StartID); err != nil { + return err + } else if eNode, err := ops.FetchNode(tx, edge.EndID); err != nil { + return err + } else { + startNode = node + endNode = eNode + return nil + } + }); err != nil { + return nil, err + } + + //Fully manifest p1 + if err := traversalInst.BreadthFirst(ctx, traversal.Plan{ + Root: startNode, + Driver: adcsESC9aPath1Pattern(edge.EndID).Do(func(terminal *graph.PathSegment) error { + victimNode := terminal.Search(func(nextSegment *graph.PathSegment) bool { + return nextSegment.Depth() == 1 + }) + + if victimNode.Kinds.ContainsOneOf(ad.User) { + certTemplate := terminal.Search(func(nextSegment *graph.PathSegment) bool { + return nextSegment.Node.Kinds.ContainsOneOf(ad.CertTemplate) + }) + + if !certTemplateValidForUserVictim(certTemplate) { + return nil + } + } + + caNode := terminal.Search(func(nextSegment *graph.PathSegment) bool { + return nextSegment.Node.Kinds.ContainsOneOf(ad.EnterpriseCA) + }) + + lock.Lock() + path1CandidateSegments[victimNode.ID] = append(path1CandidateSegments[victimNode.ID], terminal) + nodeMap[victimNode.ID] = victimNode + victimCANodes[victimNode.ID] = append(victimCANodes[victimNode.ID], caNode.ID) + lock.Unlock() + + return nil + }), + }); err != nil { + return nil, err + } + + for victim, p1CANodes := range victimCANodes { + if err := traversalInst.BreadthFirst(ctx, traversal.Plan{ + Root: nodeMap[victim], + Driver: adcsESC9APath2Pattern(p1CANodes, edge.EndID).Do(func(terminal *graph.PathSegment) error { + caNode := terminal.Search(func(nextSegment *graph.PathSegment) bool { + return nextSegment.Node.Kinds.ContainsOneOf(ad.EnterpriseCA) + }) + + lock.Lock() + path2CandidateSegments[caNode.ID] = append(path2CandidateSegments[caNode.ID], terminal) + p2canodes = append(p2canodes, caNode.ID) + lock.Unlock() + + return nil + }), + }); err != nil { + return nil, err + } + } + + if len(p2canodes) > 0 { + if err := traversalInst.BreadthFirst(ctx, traversal.Plan{ + Root: endNode, + Driver: adcsESC9APath3Pattern(p2canodes).Do(func(terminal *graph.PathSegment) error { + caNode := terminal.Search(func(nextSegment *graph.PathSegment) bool { + return nextSegment.Node.Kinds.ContainsOneOf(ad.EnterpriseCA) + }) + + lock.Lock() + path3CandidateSegments[caNode.ID] = append(path3CandidateSegments[caNode.ID], terminal) + lock.Unlock() + return nil + }), + }); err != nil { + return nil, err + } + } + + for _, p1paths := range path1CandidateSegments { + for _, p1path := range p1paths { + caNode := p1path.Search(func(nextSegment *graph.PathSegment) bool { + return nextSegment.Node.Kinds.ContainsOneOf(ad.EnterpriseCA) + }) + + if p2segments, ok := path2CandidateSegments[caNode.ID]; !ok { + continue + } else if p3segments, ok := path3CandidateSegments[caNode.ID]; !ok { + continue + } else { + paths.AddPath(p1path.Path()) + for _, p2 := range p2segments { + paths.AddPath(p2.Path()) + } + + for _, p3 := range p3segments { + paths.AddPath(p3.Path()) + } + } + } + } + + return paths, nil +} + +func certTemplateValidForUserVictim(certTemplate *graph.Node) bool { + if subjectAltRequireDNS, err := certTemplate.Properties.Get(ad.SubjectAltRequireDNS.String()).Bool(); err != nil { + return false + } else if subjectAltRequireDNS { + return false + } else if subjectAltRequireDomainDNS, err := certTemplate.Properties.Get(ad.SubjectAltRequireDomainDNS.String()).Bool(); err != nil { + return false + } else if subjectAltRequireDomainDNS { + return false + } else { + return true + } +} diff --git a/packages/go/analysis/ad/adcs.go b/packages/go/analysis/ad/adcs.go index eb64a1537e..4dae7474c0 100644 --- a/packages/go/analysis/ad/adcs.go +++ b/packages/go/analysis/ad/adcs.go @@ -141,7 +141,13 @@ func PostADCSESC3(ctx context.Context, tx graph.Transaction, outC chan<- analysi cache.EnterpriseCAEnrollers[eca1.ID], cache.EnterpriseCAEnrollers[eca2.ID], delegatedAgents.Slice()) - results.Or(tempResults) + + // Add principals to result set unless it's a user and DNS is required + if filteredResults, err := filterUserDNSResults(tx, tempResults, certTemplateOne); err != nil { + log.Errorf("Error filtering user dns results: %v", err) + } else { + results.Or(filteredResults) + } } } } else { @@ -151,7 +157,12 @@ func PostADCSESC3(ctx context.Context, tx graph.Transaction, outC chan<- analysi cache.CertTemplateControllers[certTemplateTwo.ID], cache.EnterpriseCAEnrollers[eca1.ID], cache.EnterpriseCAEnrollers[eca2.ID]) - results.Or(tempResults) + + if filteredResults, err := filterUserDNSResults(tx, tempResults, certTemplateOne); err != nil { + log.Errorf("Error filtering user dns results: %v", err) + } else { + results.Or(filteredResults) + } } } } @@ -174,6 +185,32 @@ func PostADCSESC3(ctx context.Context, tx graph.Transaction, outC chan<- analysi return nil } +func filterUserDNSResults(tx graph.Transaction, tempResults cardinality.Duplex[uint32], certTemplate *graph.Node) (cardinality.Duplex[uint32], error) { + if userNodes, err := ops.FetchNodeSet(tx.Nodes().Filterf(func() graph.Criteria { + return query.And( + query.KindIn(query.Node(), ad.User), + query.InIDs(query.NodeID(), cardinality.DuplexToGraphIDs(tempResults)...), + ) + })); err != nil { + if !graph.IsErrNotFound(err) { + return nil, err + } + } else if len(userNodes) > 0 { + if subjRequireDns, err := certTemplate.Properties.Get(ad.SubjectAltRequireDNS.String()).Bool(); err != nil { + log.Debugf("Failed to retrieve subjectAltRequireDNS for template %d: %v", certTemplate.ID, err) + tempResults.Xor(cardinality.NodeSetToDuplex(userNodes)) + } else if subjRequireDomainDns, err := certTemplate.Properties.Get(ad.SubjectAltRequireDomainDNS.String()).Bool(); err != nil { + log.Debugf("Failed to retrieve subjectAltRequireDomainDNS for template %d: %v", certTemplate.ID, err) + tempResults.Xor(cardinality.NodeSetToDuplex(userNodes)) + } else if subjRequireDns || subjRequireDomainDns { + //If either of these properties is true, we need to remove all these users from our victims list + tempResults.Xor(cardinality.NodeSetToDuplex(userNodes)) + } + } + + return tempResults, nil +} + func principalControlsCertTemplate(principal, certTemplate *graph.Node, groupExpansions impact.PathAggregator, cache ADCSCache) bool { var ( expandedTemplateControllers = cache.ExpandedCertTemplateControllers[certTemplate.ID] @@ -487,6 +524,14 @@ func PostADCS(ctx context.Context, db graph.Database, groupExpansions impact.Pat return nil }) + + operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error { + if err := PostADCSESC10a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil { + log.Errorf("failed post processing for %s: %v", ad.ADCSESC10a.String(), err) + } + + return nil + }) } } diff --git a/packages/go/analysis/ad/esc10.go b/packages/go/analysis/ad/esc10.go new file mode 100644 index 0000000000..233ff24a9f --- /dev/null +++ b/packages/go/analysis/ad/esc10.go @@ -0,0 +1,150 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +package ad + +import ( + "context" + "errors" + "github.com/specterops/bloodhound/analysis" + "github.com/specterops/bloodhound/analysis/impact" + "github.com/specterops/bloodhound/dawgs/cardinality" + "github.com/specterops/bloodhound/dawgs/graph" + "github.com/specterops/bloodhound/dawgs/ops" + "github.com/specterops/bloodhound/dawgs/query" + "github.com/specterops/bloodhound/dawgs/util/channels" + "github.com/specterops/bloodhound/graphschema/ad" + "github.com/specterops/bloodhound/log" +) + +func PostADCSESC10a(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob, groupExpansions impact.PathAggregator, eca, domain *graph.Node, cache ADCSCache) error { + results := cardinality.NewBitmap32() + + if canAbuseUPNRels, err := FetchCanAbuseUPNCertMappingRels(tx, eca); err != nil { + if graph.IsErrNotFound(err) { + return nil + } + + return err + } else if len(canAbuseUPNRels) == 0 { + return nil + } else if publishedCertTemplates, ok := cache.PublishedTemplateCache[eca.ID]; !ok { + return nil + } else { + for _, template := range publishedCertTemplates { + if valid, err := isCertTemplateValidForESC10a(template); err != nil { + if !errors.Is(err, graph.ErrPropertyNotFound) { + log.Errorf("Error checking cert template validity for template %d: %v", template.ID, err) + } else { + log.Debugf("Error checking cert template validity for template %d: %v", template.ID, err) + } + } else if !valid { + continue + } else if certTemplateControllers, ok := cache.CertTemplateControllers[template.ID]; !ok { + log.Debugf("Failed to retrieve controllers for cert template %d from cache", template.ID) + continue + } else if ecaControllers, ok := cache.EnterpriseCAEnrollers[eca.ID]; !ok { + log.Debugf("Failed to retrieve controllers for enterprise ca %d from cache", eca.ID) + continue + } else { + //Expand controllers for the eca + template completely because we don't do group shortcutting here + var ( + victimBitmap = expandNodeSliceToBitmapWithoutGroups(certTemplateControllers, groupExpansions) + ecaBitmap = expandNodeSliceToBitmapWithoutGroups(ecaControllers, groupExpansions) + ) + victimBitmap.And(ecaBitmap) + //Use our id list to filter down to users + if userNodes, err := ops.FetchNodeSet(tx.Nodes().Filterf(func() graph.Criteria { + return query.And( + query.KindIn(query.Node(), ad.User), + query.InIDs(query.NodeID(), cardinality.DuplexToGraphIDs(victimBitmap)...), + ) + })); err != nil && !graph.IsErrNotFound(err) { + log.Warnf("Error getting user nodes for esc10a attacker nodes: %v", err) + continue + } else if len(userNodes) > 0 { + if subjRequireDns, err := template.Properties.Get(ad.SubjectAltRequireDNS.String()).Bool(); err != nil { + log.Debugf("Failed to retrieve subjectAltRequireDNS for template %d: %v", template.ID, err) + victimBitmap.Xor(cardinality.NodeSetToDuplex(userNodes)) + } else if subjRequireDomainDns, err := template.Properties.Get(ad.SubjectAltRequireDomainDNS.String()).Bool(); err != nil { + log.Debugf("Failed to retrieve subjectAltRequireDomainDNS for template %d: %v", template.ID, err) + victimBitmap.Xor(cardinality.NodeSetToDuplex(userNodes)) + } else if subjRequireDns || subjRequireDomainDns { + //If either of these properties is true, we need to remove all these users from our victims list + victimBitmap.Xor(cardinality.NodeSetToDuplex(userNodes)) + } + } + + if attackers, err := ops.FetchStartNodes(tx.Relationships().Filterf(func() graph.Criteria { + return query.And( + query.KindIn(query.Start(), ad.Group, ad.User, ad.Computer), + query.KindIn(query.Relationship(), ad.GenericAll, ad.GenericWrite, ad.Owns, ad.WriteOwner, ad.WriteDACL), + query.InIDs(query.EndID(), cardinality.DuplexToGraphIDs(victimBitmap)...), + ) + })); err != nil { + log.Warnf("Error getting start nodes for esc10a attacker nodes: %v", err) + continue + } else { + results.Or(cardinality.NodeSetToDuplex(attackers)) + } + } + } + + results.Each(func(value uint32) (bool, error) { + if !channels.Submit(ctx, outC, analysis.CreatePostRelationshipJob{ + FromID: graph.ID(value), + ToID: domain.ID, + Kind: ad.ADCSESC10a, + }) { + return false, nil + } else { + return true, nil + } + }) + + return nil + } +} + +func isCertTemplateValidForESC10a(ct *graph.Node) (bool, error) { + if reqManagerApproval, err := ct.Properties.Get(ad.RequiresManagerApproval.String()).Bool(); err != nil { + return false, err + } else if reqManagerApproval { + return false, nil + } else if authenticationEnabled, err := ct.Properties.Get(ad.AuthenticationEnabled.String()).Bool(); err != nil { + return false, err + } else if !authenticationEnabled { + return false, nil + } else if enrolleeSuppliesSubject, err := ct.Properties.Get(ad.EnrolleeSuppliesSubject.String()).Bool(); err != nil { + return false, err + } else if enrolleeSuppliesSubject { + return false, nil + } else if schemaVersion, err := ct.Properties.Get(ad.SchemaVersion.String()).Float64(); err != nil { + return false, err + } else if authorizedSignatures, err := ct.Properties.Get(ad.AuthorizedSignatures.String()).Float64(); err != nil { + return false, err + } else if schemaVersion > 1 && authorizedSignatures > 0 { + return false, nil + } else if subjectAltRequireUPN, err := ct.Properties.Get(ad.SubjectAltRequireUPN.String()).Bool(); err != nil { + return false, err + } else if subjectAltRequireSPN, err := ct.Properties.Get(ad.SubjectAltRequireSPN.String()).Bool(); err != nil { + return false, err + } else if subjectAltRequireSPN || subjectAltRequireUPN { + return true, nil + } else { + return false, nil + } +} diff --git a/packages/go/analysis/ad/esc6.go b/packages/go/analysis/ad/esc6.go index 1df859d799..fcf00ade3c 100644 --- a/packages/go/analysis/ad/esc6.go +++ b/packages/go/analysis/ad/esc6.go @@ -26,6 +26,7 @@ import ( "github.com/specterops/bloodhound/dawgs/util/channels" "github.com/specterops/bloodhound/errors" "github.com/specterops/bloodhound/graphschema/ad" + "github.com/specterops/bloodhound/log" ) func PostCanAbuseUPNCertMapping(operation analysis.StatTrackedOperation[analysis.CreatePostRelationshipJob], enterpriseCertAuthorities []*graph.Node) error { @@ -49,7 +50,7 @@ func PostCanAbuseUPNCertMapping(operation analysis.StatTrackedOperation[analysis } else { for _, dcForNode := range dcForNodes { if cmmrProperty, err := dcForNode.Properties.Get(ad.CertificateMappingMethodsRaw.String()).Int(); err != nil { - collector.Collect(fmt.Errorf("error in PostCanAbuseUPNCertMapping: unable to fetch %v property for node ID %v: %v", ad.StrongCertificateBindingEnforcementRaw.String(), dcForNode.ID, err)) + log.Warnf("error in PostCanAbuseUPNCertMapping: unable to fetch %v property for node ID %v: %v", ad.StrongCertificateBindingEnforcementRaw.String(), dcForNode.ID, err) continue } else if cmmrProperty&0x04 == 0x04 { if !channels.Submit(ctx, outC, analysis.CreatePostRelationshipJob{ @@ -91,7 +92,7 @@ func PostCanAbuseWeakCertBinding(operation analysis.StatTrackedOperation[analysi } else { for _, dcForNode := range dcForNodes { if strongCertBindingEnforcement, err := dcForNode.Properties.Get(ad.StrongCertificateBindingEnforcementRaw.String()).Int(); err != nil { - collector.Collect(fmt.Errorf("error in PostCanAbuseWeakCertBinding: unable to fetch %v property for node ID %v: %v", ad.StrongCertificateBindingEnforcementRaw.String(), dcForNode.ID, err)) + log.Warnf("error in PostCanAbuseWeakCertBinding: unable to fetch %v property for node ID %v: %v", ad.StrongCertificateBindingEnforcementRaw.String(), dcForNode.ID, err) continue } else if strongCertBindingEnforcement == 0 || strongCertBindingEnforcement == 1 { if !channels.Submit(ctx, outC, analysis.CreatePostRelationshipJob{ diff --git a/packages/go/analysis/ad/post.go b/packages/go/analysis/ad/post.go index 40a32db130..d25b48f695 100644 --- a/packages/go/analysis/ad/post.go +++ b/packages/go/analysis/ad/post.go @@ -55,6 +55,9 @@ func PostProcessedRelationships() []graph.Kind { ad.ADCSESC5, ad.ADCSESC6a, ad.ADCSESC7, + ad.ADCSESC10a, + ad.ADCSESC10b, + ad.ADCSESC9a, ad.EnrollOnBehalfOf, } } diff --git a/packages/go/analysis/ad/queries.go b/packages/go/analysis/ad/queries.go index 7c3dc6bbf2..ad6a6dc740 100644 --- a/packages/go/analysis/ad/queries.go +++ b/packages/go/analysis/ad/queries.go @@ -1439,6 +1439,20 @@ func FetchCanAbuseWeakCertBindingRels(tx graph.Transaction, node *graph.Node) ([ } } +func FetchCanAbuseUPNCertMappingRels(tx graph.Transaction, node *graph.Node) ([]*graph.Relationship, error) { + if rels, err := ops.FetchRelationships(tx.Relationships().Filterf(func() graph.Criteria { + return query.And( + query.Equals(query.StartID(), node.ID), + query.Kind(query.Relationship(), ad.CanAbuseUPNCertMapping), + query.Kind(query.End(), ad.Entity), + ) + })); err != nil { + return nil, err + } else { + return rels, nil + } +} + func FetchEnterpriseCAsCertChainPathToDomain(tx graph.Transaction, enterpriseCA, domain *graph.Node) (graph.PathSet, error) { return ops.TraversePaths(tx, ops.TraversalPlan{ Root: enterpriseCA, diff --git a/packages/go/dawgs/traversal/traversal.go b/packages/go/dawgs/traversal/traversal.go index 3ec0b72b44..dcfe83f106 100644 --- a/packages/go/dawgs/traversal/traversal.go +++ b/packages/go/dawgs/traversal/traversal.go @@ -49,7 +49,9 @@ type PatternMatchDelegate = func(terminal *graph.PathSegment) error // The return value of the Do(...) function may be passed directly to a Traversal via a Plan as the Plan.Driver field. type PatternContinuation interface { Outbound(criteria ...graph.Criteria) PatternContinuation + OutboundWithDepth(min, max int, criteria ...graph.Criteria) PatternContinuation Inbound(criteria ...graph.Criteria) PatternContinuation + InboundWithDepth(min, max int, criteria ...graph.Criteria) PatternContinuation Do(delegate PatternMatchDelegate) Driver } @@ -57,6 +59,8 @@ type PatternContinuation interface { type expansion struct { criteria []graph.Criteria direction graph.Direction + minDepth int + maxDepth int } func (s expansion) PrepareCriteria(segment *graph.PathSegment) (graph.Criteria, error) { @@ -84,6 +88,7 @@ func (s expansion) PrepareCriteria(segment *graph.PathSegment) (graph.Criteria, type patternTag struct { patternIdx int + depth int } func popSegmentPatternTag(segment *graph.PathSegment) *patternTag { @@ -95,6 +100,7 @@ func popSegmentPatternTag(segment *graph.PathSegment) *patternTag { } else { tag = &patternTag{ patternIdx: 0, + depth: 0, } } @@ -112,26 +118,62 @@ func (s *pattern) Do(delegate PatternMatchDelegate) Driver { return s.Driver } -// Outbound specifies the next outbound expansion step for this pattern. -func (s *pattern) Outbound(criteria ...graph.Criteria) PatternContinuation { +// OutboundWithDepth specifies the next outbound expansion step for this pattern with depth parameters. +func (s *pattern) OutboundWithDepth(min, max int, criteria ...graph.Criteria) PatternContinuation { + if min < 0 { + min = 1 + log.Warnf("Negative mindepth not allowed. Setting min depth for expansion to 1") + } + + if max < 0 { + max = 0 + log.Warnf("Negative maxdepth not allowed. Setting max depth for expansion to 0") + } + s.expansions = append(s.expansions, expansion{ criteria: criteria, direction: graph.DirectionOutbound, + minDepth: min, + maxDepth: max, }) return s } -// Inbound specifies the next inbound expansion step for this pattern. -func (s *pattern) Inbound(criteria ...graph.Criteria) PatternContinuation { +// Outbound specifies the next outbound expansion step for this pattern. By default, this expansion will use a minimum +// depth of 1 to make the expansion required and a maximum depth of 0 to expand indefinitely. +func (s *pattern) Outbound(criteria ...graph.Criteria) PatternContinuation { + return s.OutboundWithDepth(1, 0, criteria...) +} + +// InboundWithDepth specifies the next inbound expansion step for this pattern with depth parameters. +func (s *pattern) InboundWithDepth(min, max int, criteria ...graph.Criteria) PatternContinuation { + if min < 0 { + min = 1 + log.Warnf("Negative mindepth not allowed. Setting min depth for expansion to 1") + } + + if max < 0 { + max = 0 + log.Warnf("Negative maxdepth not allowed. Setting max depth for expansion to 0") + } + s.expansions = append(s.expansions, expansion{ criteria: criteria, direction: graph.DirectionInbound, + minDepth: min, + maxDepth: max, }) return s } +// Inbound specifies the next inbound expansion step for this pattern. By default, this expansion will use a minimum +// depth of 1 to make the expansion required and a maximum depth of 0 to expand indefinitely. +func (s *pattern) Inbound(criteria ...graph.Criteria) PatternContinuation { + return s.InboundWithDepth(1, 0, criteria...) +} + // NewPattern returns a new PatternContinuation for building a new pattern. func NewPattern() PatternContinuation { return &pattern{} @@ -152,9 +194,9 @@ func (s *pattern) Driver(ctx context.Context, tx graph.Transaction, segment *gra for next := range cursor.Chan() { nextSegment := segment.Descend(next.Node, next.Relationship) nextSegment.Tag = &patternTag{ - // Use the tag's patternIdx here since this is the reference that will see the increment when - // the current expansion is exhausted + // Use the tag's patternIdx and depth since this is a continuation of the expansions patternIdx: tag.patternIdx, + depth: tag.depth + 1, } nextSegments = append(nextSegments, nextSegment) @@ -168,34 +210,51 @@ func (s *pattern) Driver(ctx context.Context, tx graph.Transaction, segment *gra if fetchDirection, err := currentExpansion.direction.Reverse(); err != nil { return nil, err } else { - // Perform the current expansion. - if criteria, err := currentExpansion.PrepareCriteria(segment); err != nil { - return nil, err - } else if err := tx.Relationships().Filter(criteria).FetchDirection(fetchDirection, fetchFunc); err != nil { - return nil, err - } - - // No further expansions means this pattern segment is complete. Increment the pattern index to select the - // next pattern expansion. - tag.patternIdx++ - - // Perform the next expansion if there is one. - if tag.patternIdx < len(s.expansions) { - nextExpansion := s.expansions[tag.patternIdx] - - // Expand the next segments - if criteria, err := nextExpansion.PrepareCriteria(segment); err != nil { + // If no max depth was set or if a max depth was set expand the current step further + if currentExpansion.maxDepth == 0 || tag.depth < currentExpansion.maxDepth { + // Perform the current expansion. + if criteria, err := currentExpansion.PrepareCriteria(segment); err != nil { return nil, err } else if err := tx.Relationships().Filter(criteria).FetchDirection(fetchDirection, fetchFunc); err != nil { return nil, err } - } else if len(nextSegments) == 0 { - // If there are no expanded segments and there are no remaining expansions, this is a terminal segment. - // Hand it off to the delegate and handle any returned error. - if err := s.delegate(segment); err != nil { - return nil, err + } + + // Check first if this current segment was fetched using the current expansion (i.e. non-optional) + if tag.depth > 0 && currentExpansion.minDepth == 0 || tag.depth >= currentExpansion.minDepth { + // No further expansions means this pattern segment is complete. Increment the pattern index to select the + // next pattern expansion. Additionally, set the depth back to zero for the tag since we are leaving the + // current expansion. + tag.patternIdx++ + tag.depth = 0 + + // Perform the next expansion if there is one. + if tag.patternIdx < len(s.expansions) { + nextExpansion := s.expansions[tag.patternIdx] + + // Expand the next segments + if criteria, err := nextExpansion.PrepareCriteria(segment); err != nil { + return nil, err + } else if err := tx.Relationships().Filter(criteria).FetchDirection(fetchDirection, fetchFunc); err != nil { + return nil, err + } + + // If the next expansion is optional, make sure to preserve the current traversal branch + if nextExpansion.minDepth == 0 { + // Reattach the tag to the segment before adding it to the returned segments for the next expansion + segment.Tag = tag + nextSegments = append(nextSegments, segment) + } + } else if len(nextSegments) == 0 { + // If there are no expanded segments and there are no remaining expansions, this is a terminal segment. + // Hand it off to the delegate and handle any returned error. + if err := s.delegate(segment); err != nil { + return nil, err + } } } + + // If the above condition does not match then this current expansion is non-terminal and non-continuable } // Return any collected segments diff --git a/packages/go/graphschema/ad/ad.go b/packages/go/graphschema/ad/ad.go index 1d02df59bb..5877ce4728 100644 --- a/packages/go/graphschema/ad/ad.go +++ b/packages/go/graphschema/ad/ad.go @@ -103,6 +103,8 @@ var ( ADCSESC7 = graph.StringKind("ADCSESC7") ADCSESC9a = graph.StringKind("ADCSESC9a") ADCSESC9b = graph.StringKind("ADCSESC9b") + ADCSESC10a = graph.StringKind("ADCSESC10a") + ADCSESC10b = graph.StringKind("ADCSESC10b") ) type Property string @@ -640,13 +642,13 @@ func Nodes() []graph.Kind { return []graph.Kind{Entity, User, Computer, Group, GPO, OU, Container, Domain, LocalGroup, LocalUser, AIACA, RootCA, EnterpriseCA, NTAuthStore, CertTemplate} } func Relationships() []graph.Kind { - return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, Contains, GPLink, AllowedToDelegate, GetChanges, GetChangesAll, GetChangesInFilteredSet, TrustedBy, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, LocalToComputer, MemberOfLocalGroup, RemoteInteractiveLogonPrivilege, SyncLAPSPassword, WriteAccountRestrictions, RootCAFor, DCFor, PublishedTo, ManageCertificates, ManageCA, DelegatedEnrollmentAgent, Enroll, HostsCAService, WritePKIEnrollmentFlag, WritePKINameFlag, NTAuthStoreFor, TrustedForNTAuth, EnterpriseCAFor, CanAbuseUPNCertMapping, CanAbuseWeakCertBinding, IssuedSignedBy, GoldenCert, EnrollOnBehalfOf, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC5, ADCSESC6a, ADCSESC7, ADCSESC9a, ADCSESC9b} + return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, Contains, GPLink, AllowedToDelegate, GetChanges, GetChangesAll, GetChangesInFilteredSet, TrustedBy, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, LocalToComputer, MemberOfLocalGroup, RemoteInteractiveLogonPrivilege, SyncLAPSPassword, WriteAccountRestrictions, RootCAFor, DCFor, PublishedTo, ManageCertificates, ManageCA, DelegatedEnrollmentAgent, Enroll, HostsCAService, WritePKIEnrollmentFlag, WritePKINameFlag, NTAuthStoreFor, TrustedForNTAuth, EnterpriseCAFor, CanAbuseUPNCertMapping, CanAbuseWeakCertBinding, IssuedSignedBy, GoldenCert, EnrollOnBehalfOf, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC5, ADCSESC6a, ADCSESC7, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b} } func ACLRelationships() []graph.Kind { return []graph.Kind{AllExtendedRights, ForceChangePassword, AddMember, AddAllowedToAct, GenericAll, WriteDACL, WriteOwner, GenericWrite, ReadLAPSPassword, ReadGMSAPassword, Owns, AddSelf, WriteSPN, AddKeyCredentialLink, GetChanges, GetChangesAll, GetChangesInFilteredSet, WriteAccountRestrictions, SyncLAPSPassword, DCSync, ManageCertificates, ManageCA, Enroll, WritePKIEnrollmentFlag, WritePKINameFlag} } func PathfindingRelationships() []graph.Kind { - return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, Contains, GPLink, AllowedToDelegate, TrustedBy, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, SyncLAPSPassword, WriteAccountRestrictions, GoldenCert, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC5, ADCSESC6a, ADCSESC7, ADCSESC9a, ADCSESC9b, DCFor} + return []graph.Kind{Owns, GenericAll, GenericWrite, WriteOwner, WriteDACL, MemberOf, ForceChangePassword, AllExtendedRights, AddMember, HasSession, Contains, GPLink, AllowedToDelegate, TrustedBy, AllowedToAct, AdminTo, CanPSRemote, CanRDP, ExecuteDCOM, HasSIDHistory, AddSelf, DCSync, ReadLAPSPassword, ReadGMSAPassword, DumpSMSAPassword, SQLAdmin, AddAllowedToAct, WriteSPN, AddKeyCredentialLink, SyncLAPSPassword, WriteAccountRestrictions, GoldenCert, ADCSESC1, ADCSESC3, ADCSESC4, ADCSESC5, ADCSESC6a, ADCSESC7, ADCSESC9a, ADCSESC9b, ADCSESC10a, ADCSESC10b, DCFor} } func IsACLKind(s graph.Kind) bool { for _, acl := range ACLRelationships() { diff --git a/packages/javascript/bh-shared-ui/src/commonSearches.tsx b/packages/javascript/bh-shared-ui/src/commonSearches.tsx index e3b7dc7d72..cf7de1eaee 100644 --- a/packages/javascript/bh-shared-ui/src/commonSearches.tsx +++ b/packages/javascript/bh-shared-ui/src/commonSearches.tsx @@ -155,6 +155,52 @@ export const CommonSearches: CommonSearchType[] = [ }, ], }, + { + subheader: 'Active Directory Certificate Services', + category: categoryAD, + queries: [ + { + description: 'PKI hierarchy', + cypher: `MATCH p=()-[:HostsCAService|IssuedSignedBy|EnterpriseCAFor|RootCAFor|TrustedForNTAuth|NTAuthStoreFor*..]->()\nRETURN p`, + }, + { + description: 'Public Key Services container', + cypher: `MATCH p = (c:Container)-[:Contains*..]->()\nWHERE c.distinguishedname starts with "CN=PUBLIC KEY SERVICES,CN=SERVICES,CN=CONFIGURATION,DC="\nRETURN p`, + }, + { + description: 'Enrollment rights on published certificate templates', + cypher: `MATCH p = ()-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nRETURN p`, + }, + { + description: 'Enrollment rights on published ESC1 certificate templates', + cypher: `MATCH p = ()-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE ct.enrolleesuppliessubject = True\nAND ct.authenticationenabled = True\nAND ct.requiresmanagerapproval = False\nRETURN p`, + }, + { + description: 'Enrollment rights on published enrollment agent certificate templates', + cypher: `MATCH p = ()-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nWHERE ct.effectiveekus CONTAINS "1.3.6.1.4.1.311.20.2.1"\nOR ct.effectiveekus CONTAINS "2.5.29.37.0"\nOR SIZE(ct.effectiveekus) = 0\nRETURN p`, + }, + { + description: 'Enrollment rights on published certificate templates with no security extension', + cypher: `MATCH p = ()-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(:EnterpriseCA)\nnWHERE ct.nosecurityextension = true\nRETURN p`, + }, + { + description: 'Enrollment rights on certificate templates published to Enterprise CA with User Specified SAN enabled', + cypher: `MATCH p = ()-[:Enroll|GenericAll|AllExtendedRights]->(ct:CertTemplate)-[:PublishedTo]->(eca:EnterpriseCA)\nWHERE eca.isuserspecifiessanenabled = True\nRETURN p`, + }, + { + description: 'CA administrators and CA managers', + cypher: `MATCH p = ()-[:ManageCertificates|ManageCA]->(:EnterpriseCA)\nRETURN p`, + }, + { + description: 'Domain controllers with weak certificate binding enabled', + cypher: `MATCH p = (dc:Computer)-[:DCFor]->(d)\nWHERE dc.strongcertificatebindingenforcementraw = 0 OR dc.strongcertificatebindingenforcementraw = 1\nRETURN p`, + }, + { + description: 'Domain controllers with UPN certificate mapping enabled', + cypher: `MATCH p = (dc:Computer)-[:DCFor]->(d)\nWHERE dc.certificatemappingmethodsraw IN [4, 5, 6, 7, 12, 13, 14, 15, 20, 21, 22, 23, 28, 29, 30, 31]\nRETURN p`, + } + ], + }, { subheader: 'General', category: categoryAzure, diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/General.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/General.tsx index 25356805b2..73d474ef4c 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/General.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/General.tsx @@ -27,15 +27,14 @@ const General: FC = ({ sourceName, sourceType, targetName }) => { domain {targetName}. - The principal has permission to enroll on a certificate template with the Certificate Request Agent EKU - (OID 1.3.6.1.4.1.311.20.2.1), allowing them to obtain an enrollment agent certificate. They also have - permission to enroll for a certificate template that permits enrollment by enrollment agents and can be - used for authentication. Additionally, they also have enrollment permissions for an enterprise CA with - the necessary templates published. This enterprise CA is trusted for NT authentication in the forest, - along with the CA certificate chain up to the root CA certificate. This setup lets the principal enroll - certificates for any AD forest user or computer, enabling authentication and impersonation of any AD - forest user or computer without their credentials, unless the target user or computer is protected by - enrollment agent restrictions on the enterprise CA. + The principal has permission to enroll on a certificate allowing them to obtain an enrollment agent + certificate. They also have permission to enroll for a certificate template that permits enrollment by + enrollment agents and can be used for authentication. Additionally, they also have enrollment + permissions for an enterprise CA with the necessary templates published. This enterprise CA is trusted + for NT authentication in the forest, along with the CA certificate chain up to the root CA certificate. + This setup lets the principal enroll certificates for any AD forest user or computer, enabling + authentication and impersonation of any AD forest user or computer without their credentials, unless the + target user or computer is protected by enrollment agent restrictions on the enterprise CA. ); diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/LinuxAbuse.tsx index 5b14384420..5a555d739b 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/LinuxAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/LinuxAbuse.tsx @@ -32,6 +32,15 @@ const LinuxAbuse: FC = () => { "certipy req -u 'user@corp.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'vulnerable template'" } + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and + cannot be added to the Subject or Subject Alternate name, then it is because the enrollee principal does + not have their 'mail' or 'dNSHostName' attribute set, which is required by the certificate template. The + 'mail' attribute can be set on both user and computer objects but the 'dNSHostName' attribute can only + be set on computer objects. Computers have validated write permission to their own 'dNSHostName' + attribute by default, but neither users nor computers can write to their own 'mail' attribute by + default. + Step 2: @@ -44,6 +53,12 @@ const LinuxAbuse: FC = () => { "certipy req -u 'user@corp.local' -p 'password' -dc-ip 'DC_IP' -target 'ca_host' -ca 'ca_name' -template 'User' -on-behalf-of 'contoso\\administrator' -pfx 'user.pfx'" } + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and + cannot be added to the Subject or Subject Alternate name, then it is because the target principal does + not have their 'mail' or 'dNSHostName' attribute set, which is required by the certificate template. + Choose another target with the given attribute set. + Step 3: diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/WindowsAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/WindowsAbuse.tsx index fc9c11c595..e6bd090402 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/WindowsAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC3/WindowsAbuse.tsx @@ -30,6 +30,15 @@ const WindowsAbuse: FC = () => { {'Certify.exe request /ca:CORPDC01.CORP.LOCAL\\CORP-CORPDC01-CA /template:Vuln-EnrollmentAgent'} + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and + cannot be added to the Subject or Subject Alternate name, then it is because the enrollee principal does + not have their 'mail' or 'dNSHostName' attribute set, which is required by the certificate template. The + 'mail' attribute can be set on both user and computer objects but the 'dNSHostName' attribute can only + be set on computer objects. Computers have validated write permission to their own 'dNSHostName' + attribute by default, but neither users nor computers can write to their own 'mail' attribute by + default. + Step 2: @@ -55,6 +64,12 @@ const WindowsAbuse: FC = () => { Save the certificate as itadminenrollment.pem and the private key as{' '} itadminenrollment.key. + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and + cannot be added to the Subject or Subject Alternate name, then it is because the target principal does + not have their 'mail' or 'dNSHostName' attribute set, which is required by the certificate template. + Choose another target with the given attribute set. + Step 4: diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/ADCSESC6a.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/ADCSESC6a.tsx index 35d0f7205c..a2013b7bc9 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/ADCSESC6a.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/ADCSESC6a.tsx @@ -19,6 +19,7 @@ import WindowsAbuse from './WindowsAbuse'; import LinuxAbuse from './LinuxAbuse'; import Opsec from './Opsec'; import References from './References'; +import Composition from './Composition'; const ADCSESC6a = { general: General, @@ -26,6 +27,7 @@ const ADCSESC6a = { linuxAbuse: LinuxAbuse, opsec: Opsec, references: References, + composition: Composition, }; export default ADCSESC6a; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/Composition.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/Composition.tsx new file mode 100644 index 0000000000..4a24ff5eab --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/Composition.tsx @@ -0,0 +1,54 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { FC } from 'react'; +import { Alert, Box, Skeleton, Typography } from '@mui/material'; +import { apiClient } from '../../../utils/api'; +import { EdgeInfoProps } from '..'; +import { useQuery } from 'react-query'; +import VirtualizedNodeList, { VirtualizedNodeListItem } from '../../VirtualizedNodeList'; + +const Composition: FC = ({ sourceDBId, targetDBId, edgeName }) => { + const { data, isLoading, isError } = useQuery(['edgeComposition', sourceDBId, targetDBId, edgeName], ({ signal }) => + apiClient.getEdgeComposition(sourceDBId!, targetDBId!, edgeName!).then((result) => result.data) + ); + + const nodesArray: VirtualizedNodeListItem[] = Object.values(data?.data.nodes || {}).map((node) => ({ + name: node.label, + objectId: node.objectId, + kind: node.kind, + })); + + return ( + <> + + The relationship represents the effective outcome of the configuration and relationships between several + different objects. All objects involved in the creation of this relationship are listed here: + + + {isLoading ? ( + + ) : isError ? ( + Couldn't load edge composition + ) : ( + + )} + + + ); +}; + +export default Composition; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/LinuxAbuse.tsx index 874627307e..83ce889f64 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/LinuxAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/LinuxAbuse.tsx @@ -30,6 +30,15 @@ const LinuxAbuse: FC = () => { 'certipy req -u john@corp.local -p Passw0rd -ca corp-DC-CA -target ca.corp.local -template ESC6 -upn administrator@corp.local' } + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and + cannot be added to the Subject or Subject Alternate name, then it is because the enrollee principal does + not have their 'mail' or 'dNSHostName' attribute set, which is required by the certificate template. The + 'mail' attribute can be set on both user and computer objects but the 'dNSHostName' attribute can only + be set on computer objects. Computers have validated write permission to their own 'dNSHostName' + attribute by default, but neither users nor computers can write to their own 'mail' attribute by + default. + Step 2: Request a ticket granting ticket (TGT) from the domain, specifying the certificate created in Step 1 and the IP of a domain controller: diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/WindowsAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/WindowsAbuse.tsx index 12e36054d6..296249a25f 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/WindowsAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6a/WindowsAbuse.tsx @@ -30,6 +30,15 @@ const WindowsAbuse: FC = () => { '.\\Certify.exe request /ca:rootdomaindc.forestroot.com\\forestroot-RootDomainDC-CA /template:ESC6 /altname:forestroot\\ForestRootDA' } + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and + cannot be added to the Subject or Subject Alternate name, then it is because the enrollee principal does + not have their 'mail' or 'dNSHostName' attribute set, which is required by the certificate template. The + 'mail' attribute can be set on both user and computer objects but the 'dNSHostName' attribute can only + be set on computer objects. Computers have validated write permission to their own 'dNSHostName' + attribute by default, but neither users nor computers can write to their own 'mail' attribute by + default. + Step 2: Convert the emitted certificate to PFX format: diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6b/LinuxAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6b/LinuxAbuse.tsx index 275e0334c2..2c91fa3bf5 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6b/LinuxAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6b/LinuxAbuse.tsx @@ -33,6 +33,15 @@ const LinuxAbuse: FC = () => { 'certipy req -u john@corp.local -p Passw0rd -ca corp-DC-CA -target ca.corp.local -template ESC6 -upn administrator@corp.local' } + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and + cannot be added to the Subject or Subject Alternate name, then it is because the enrollee principal does + not have their 'mail' or 'dNSHostName' attribute set, which is required by the certificate template. The + 'mail' attribute can be set on both user and computer objects but the 'dNSHostName' attribute can only + be set on computer objects. Computers have validated write permission to their own 'dNSHostName' + attribute by default, but neither users nor computers can write to their own 'mail' attribute by + default. + Step 2: diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6b/WindowsAbuse.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6b/WindowsAbuse.tsx index a9b6f1bef5..0d81e85a18 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6b/WindowsAbuse.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC6b/WindowsAbuse.tsx @@ -33,6 +33,15 @@ const WindowsAbuse: FC = () => { '.\\Certify.exe request /ca:rootdomaindc.forestroot.com\\forestroot-RootDomainDC-CA /template:ESC6 /altname:forestroot\\ForestRootDA' } + + If the enrollment fails with an error message stating that the Email or DNS name is unavailable and + cannot be added to the Subject or Subject Alternate name, then it is because the enrollee principal does + not have their 'mail' or 'dNSHostName' attribute set, which is required by the certificate template. The + 'mail' attribute can be set on both user and computer objects but the 'dNSHostName' attribute can only + be set on computer objects. Computers have validated write permission to their own 'dNSHostName' + attribute by default, but neither users nor computers can write to their own 'mail' attribute by + default. + Step 2: diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC9a/ADCSESC9a.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC9a/ADCSESC9a.tsx index 9de2b72c5f..7dcde37b44 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC9a/ADCSESC9a.tsx +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC9a/ADCSESC9a.tsx @@ -19,6 +19,7 @@ import WindowsAbuse from './WindowsAbuse'; import LinuxAbuse from './LinuxAbuse'; import Opsec from './Opsec'; import References from './References'; +import Composition from "./Composition"; const ADCSESC9a = { general: General, @@ -26,6 +27,7 @@ const ADCSESC9a = { linuxAbuse: LinuxAbuse, opsec: Opsec, references: References, + composition: Composition }; export default ADCSESC9a; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC9a/Composition.tsx b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC9a/Composition.tsx new file mode 100644 index 0000000000..4a24ff5eab --- /dev/null +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/ADCSESC9a/Composition.tsx @@ -0,0 +1,54 @@ +// Copyright 2024 Specter Ops, Inc. +// +// Licensed under the Apache License, Version 2.0 +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// SPDX-License-Identifier: Apache-2.0 + +import { FC } from 'react'; +import { Alert, Box, Skeleton, Typography } from '@mui/material'; +import { apiClient } from '../../../utils/api'; +import { EdgeInfoProps } from '..'; +import { useQuery } from 'react-query'; +import VirtualizedNodeList, { VirtualizedNodeListItem } from '../../VirtualizedNodeList'; + +const Composition: FC = ({ sourceDBId, targetDBId, edgeName }) => { + const { data, isLoading, isError } = useQuery(['edgeComposition', sourceDBId, targetDBId, edgeName], ({ signal }) => + apiClient.getEdgeComposition(sourceDBId!, targetDBId!, edgeName!).then((result) => result.data) + ); + + const nodesArray: VirtualizedNodeListItem[] = Object.values(data?.data.nodes || {}).map((node) => ({ + name: node.label, + objectId: node.objectId, + kind: node.kind, + })); + + return ( + <> + + The relationship represents the effective outcome of the configuration and relationships between several + different objects. All objects involved in the creation of this relationship are listed here: + + + {isLoading ? ( + + ) : isError ? ( + Couldn't load edge composition + ) : ( + + )} + + + ); +}; + +export default Composition; diff --git a/packages/javascript/bh-shared-ui/src/components/HelpTexts/utils.ts b/packages/javascript/bh-shared-ui/src/components/HelpTexts/utils.ts index 7a6caf3abe..afa0cf9ccd 100644 --- a/packages/javascript/bh-shared-ui/src/components/HelpTexts/utils.ts +++ b/packages/javascript/bh-shared-ui/src/components/HelpTexts/utils.ts @@ -14,7 +14,7 @@ // // SPDX-License-Identifier: Apache-2.0 -import { makeStyles } from "@mui/styles"; +import { makeStyles } from '@mui/styles'; export const groupSpecialFormat = (sourceType: string | undefined, sourceName: string | undefined) => { if (!sourceType || !sourceName) return 'This entity has'; @@ -44,7 +44,6 @@ export const typeFormat = (type: string | undefined): string => { } }; - export const useHelpTextStyles = makeStyles((theme) => ({ containsCodeEl: { '& code': { @@ -59,4 +58,4 @@ export const useHelpTextStyles = makeStyles((theme) => ({ whiteSpace: 'pre-wrap', }, }, -})); \ No newline at end of file +})); diff --git a/packages/javascript/bh-shared-ui/src/graphSchema.ts b/packages/javascript/bh-shared-ui/src/graphSchema.ts index 3759576a63..f475267d94 100644 --- a/packages/javascript/bh-shared-ui/src/graphSchema.ts +++ b/packages/javascript/bh-shared-ui/src/graphSchema.ts @@ -131,6 +131,8 @@ export enum ActiveDirectoryRelationshipKind { ADCSESC7 = 'ADCSESC7', ADCSESC9a = 'ADCSESC9a', ADCSESC9b = 'ADCSESC9b', + ADCSESC10a = 'ADCSESC10a', + ADCSESC10b = 'ADCSESC10b', } export function ActiveDirectoryRelationshipKindToDisplay(value: ActiveDirectoryRelationshipKind): string | undefined { switch (value) { @@ -260,6 +262,10 @@ export function ActiveDirectoryRelationshipKindToDisplay(value: ActiveDirectoryR return 'ADCSESC9a'; case ActiveDirectoryRelationshipKind.ADCSESC9b: return 'ADCSESC9b'; + case ActiveDirectoryRelationshipKind.ADCSESC10a: + return 'ADCSESC10a'; + case ActiveDirectoryRelationshipKind.ADCSESC10b: + return 'ADCSESC10b'; default: return undefined; } @@ -528,6 +534,8 @@ export function ActiveDirectoryPathfindingEdges(): ActiveDirectoryRelationshipKi ActiveDirectoryRelationshipKind.ADCSESC7, ActiveDirectoryRelationshipKind.ADCSESC9a, ActiveDirectoryRelationshipKind.ADCSESC9b, + ActiveDirectoryRelationshipKind.ADCSESC10a, + ActiveDirectoryRelationshipKind.ADCSESC10b, ActiveDirectoryRelationshipKind.DCFor, ]; }