Skip to content

Commit

Permalink
Merge branch 'main' of github.com:SpecterOps/BloodHound into esc-9b-f…
Browse files Browse the repository at this point in the history
…inding-panel
  • Loading branch information
benwaples committed Jan 26, 2024
2 parents f6e82f3 + f099e37 commit 107949d
Show file tree
Hide file tree
Showing 17 changed files with 1,069 additions and 65 deletions.
50 changes: 50 additions & 0 deletions .github/workflows/static-code-analysis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# 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

name: Static Code Analysis

on:
push:
branches: [main]
pull_request:
types: [opened, synchronize]


jobs:
run-analysis:
runs-on: ubuntu-latest

steps:
- name: Checkout source code for this repository
uses: actions/checkout@v3

- name: Install Go
uses: actions/setup-go@v4
with:
go-version: '^1.21.0'

- name: Install Node
uses: actions/setup-node@v4
with:
node-version: 18

- name: Install Yarn
run: |
npm install --global yarn
- name: Run Analysis
run: |
go run github.com/specterops/bloodhound/packages/go/stbernard analysis
28 changes: 16 additions & 12 deletions .golangci.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,18 @@
"max-same-issues": 0,
"exclude-rules": [
{
"path": "hyperloglog_bench_test\\.go",
"text": "SA6002:"
"path": ".go",
"text": "((neo4j(.+)(NewDriver|Result))|Id|database.Database|(.+)Deprecated) is deprecated"
},
{
"path": "cache_test\\.go",
"text": "SA1026:"
"text": "SA1026:",
"severity": "warning"
},
{
"path": "foldr_test\\.go",
"text": "SA4000:"
},
{
"path": ".go",
"text": "((neo4j(.+)(NewDriver|Result))|Id|database.Database|(.+)Deprecated) is deprecated"
},
{
"path": "expected_ingest.go",
"text": "ST1022:"
"text": "SA4000:",
"severity": "warning"
}
]
},
Expand All @@ -50,6 +44,16 @@
{
"text": "(ST\\d{4}|S\\d{4}|SA1019)",
"severity": "warning"
},
{
"path": "hyperloglog_bench_test\\.go",
"text": "SA6002:",
"severity": "warning"
},
{
"path": "expected_ingest.go",
"text": "ST1022:",
"severity": "warning"
}
]
},
Expand Down
2 changes: 2 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"go.testEnvVars": {
"INTEGRATION_CONFIG_PATH": "${workspaceFolder}/local-harnesses/integration.config.json"
},
"go.formatTool": "goimports",
"go.lintTool": "golangci-lint",
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter"
},
Expand Down
67 changes: 65 additions & 2 deletions cmd/api/src/analysis/ad/adcs_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -489,7 +489,7 @@ func TestADCSESC3(t *testing.T) {
for _, enterpriseCA := range enterpriseCAs {
if cache.DoesCAChainProperlyToDomain(enterpriseCA, innerDomain) {
if err := ad2.PostADCSESC3(ctx, tx, outC, groupExpansions, enterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC1.String(), err)
t.Logf("failed post processing for %s: %v", ad.ADCSESC3.String(), err)
} else {
return nil
}
Expand Down Expand Up @@ -546,7 +546,7 @@ func TestADCSESC3(t *testing.T) {
for _, enterpriseCA := range enterpriseCAs {
if cache.DoesCAChainProperlyToDomain(enterpriseCA, innerDomain) {
if err := ad2.PostADCSESC3(ctx, tx, outC, groupExpansions, enterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC1.String(), err)
t.Logf("failed post processing for %s: %v", ad.ADCSESC3.String(), err)
} else {
return nil
}
Expand Down Expand Up @@ -584,6 +584,65 @@ func TestADCSESC3(t *testing.T) {
})
}

func TestADCSESC9a(t *testing.T) {
testContext := integration.NewGraphTestContext(t, graphschema.DefaultGraphSchema())
testContext.DatabaseTestWithSetup(func(harness *integration.HarnessDetails) error {
harness.ESC9AHarness.Setup(testContext)
return nil
}, func(harness integration.HarnessDetails, db graph.Database) {
operation := analysis.NewPostRelationshipOperation(context.Background(), db, "ADCS Post Process Test - ESC9a")

groupExpansions, err := ad2.ExpandAllRDPLocalGroups(context.Background(), db)
require.Nil(t, err)
enterpriseCertAuthorities, err := ad2.FetchNodesByKind(context.Background(), db, ad.EnterpriseCA)
require.Nil(t, err)
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
require.Nil(t, err)
domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain)
require.Nil(t, err)

cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, enterpriseCertAuthorities, certTemplates)

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

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

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.ADCSESC9a)
})); err != nil {
t.Fatalf("error fetching esc9a edges in integration test; %v", err)
} else {
assert.Equal(t, 1, len(results))

require.True(t, results.Contains(harness.ESC9AHarness.Attacker))

}
return nil
})
})

}

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

Expand All @@ -600,6 +659,7 @@ func TestADCSESC6a(t *testing.T) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
require.Nil(t, err)
domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain)
require.Nil(t, err)

cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, enterpriseCertAuthorities, certTemplates)
Expand Down Expand Up @@ -655,6 +715,7 @@ func TestADCSESC6a(t *testing.T) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
require.Nil(t, err)
domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain)
require.Nil(t, err)

cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, enterpriseCertAuthorities, certTemplates)
Expand Down Expand Up @@ -709,6 +770,7 @@ func TestADCSESC6a(t *testing.T) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
require.Nil(t, err)
domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain)
require.Nil(t, err)

cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, enterpriseCertAuthorities, certTemplates)
Expand Down Expand Up @@ -767,6 +829,7 @@ func TestADCSESC6a(t *testing.T) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
require.Nil(t, err)
domains, err := ad2.FetchNodesByKind(context.Background(), db, ad.Domain)
require.Nil(t, err)

cache := ad2.NewADCSCache()
cache.BuildCache(context.Background(), db, enterpriseCertAuthorities, certTemplates)
Expand Down
44 changes: 31 additions & 13 deletions cmd/api/src/test/integration/graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,23 +414,41 @@ func (s *GraphTestContext) NewActiveDirectoryRootCAWithThumbprint(name, domainSI
}), ad.Entity, ad.RootCA)
}

func (s *GraphTestContext) NewActiveDirectoryCertTemplate(name, domainSID string, requiresManagerApproval, authenticationEnabled, enrolleeSupplieSubject, subjectAltRequireUpn, noSecurityExtension bool, schemaVersion, authorizedSignatures int, ekus, applicationPolicies []string) *graph.Node {
func (s *GraphTestContext) NewActiveDirectoryCertTemplate(name, domainSID string, data CertTemplateData) *graph.Node {
return s.NewNode(graph.AsProperties(graph.PropertyMap{
common.Name: name,
common.ObjectID: must.NewUUIDv4().String(),
ad.DomainSID: domainSID,
ad.RequiresManagerApproval: requiresManagerApproval,
ad.AuthenticationEnabled: authenticationEnabled,
ad.EnrolleeSuppliesSubject: enrolleeSupplieSubject,
ad.NoSecurityExtension: noSecurityExtension,
ad.SchemaVersion: float64(schemaVersion),
ad.AuthorizedSignatures: float64(authorizedSignatures),
ad.EKUs: ekus,
ad.ApplicationPolicies: applicationPolicies,
ad.SubjectAltRequireUPN: subjectAltRequireUpn,
common.Name: name,
common.ObjectID: must.NewUUIDv4().String(),
ad.DomainSID: domainSID,
ad.RequiresManagerApproval: data.RequiresManagerApproval,
ad.AuthenticationEnabled: data.AuthenticationEnabled,
ad.EnrolleeSuppliesSubject: data.EnrolleeSuppliesSubject,
ad.NoSecurityExtension: data.NoSecurityExtension,
ad.SchemaVersion: data.SchemaVersion,
ad.AuthorizedSignatures: data.AuthorizedSignatures,
ad.EKUs: data.EKUS,
ad.ApplicationPolicies: data.ApplicationPolicies,
ad.SubjectAltRequireUPN: data.SubjectAltRequireUPN,
ad.SubjectAltRequireSPN: data.SubjectAltRequireSPN,
ad.SubjectAltRequireDNS: data.SubjectAltRequireDNS,
ad.SubjectAltRequireDomainDNS: data.SubjectAltRequireDomainDNS,
}), ad.Entity, ad.CertTemplate)
}

type CertTemplateData struct {
RequiresManagerApproval bool
AuthenticationEnabled bool
EnrolleeSuppliesSubject bool
SubjectAltRequireUPN bool
SubjectAltRequireSPN bool
SubjectAltRequireDNS bool
SubjectAltRequireDomainDNS bool
NoSecurityExtension bool
SchemaVersion float64
AuthorizedSignatures float64
EKUS []string
ApplicationPolicies []string
}

func (s *GraphTestContext) setupAzure() {
s.Harness.AZBaseHarness.Setup(s)
s.Harness.AZGroupMembership.Setup(s)
Expand Down
Loading

0 comments on commit 107949d

Please sign in to comment.