Skip to content

Commit

Permalink
merge w main
Browse files Browse the repository at this point in the history
  • Loading branch information
brandonshearin committed Jan 24, 2024
2 parents 4deeba4 + be626ae commit ecfbbd4
Show file tree
Hide file tree
Showing 58 changed files with 4,776 additions and 179 deletions.
15 changes: 13 additions & 2 deletions .golangci.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"unused"
],
"enable": [
"gosimple"
"gosimple",
"stylecheck"
]
},
"issues": {
Expand Down Expand Up @@ -49,11 +50,21 @@
"serial_integration"
]
},
"severity": {
"default-severity": "error",
"rules": [
{
"text": "(ST\\d{4}|S\\d{4})",
"severity": "warning"
}
]
},
"linters-settings": {
"stylecheck": {
"checks": [
"all",
"-ST1000"
"-ST1000",
"-ST1003"
]
}
}
Expand Down
243 changes: 241 additions & 2 deletions cmd/api/src/analysis/ad/adcs_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package ad_test

import (
"context"

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

Expand All @@ -33,6 +34,7 @@ import (

"github.com/specterops/bloodhound/dawgs/graph"
"github.com/specterops/bloodhound/graphschema/ad"
"github.com/specterops/bloodhound/graphschema/common"
"github.com/specterops/bloodhound/src/test/integration"

"github.com/stretchr/testify/assert"
Expand All @@ -55,6 +57,7 @@ func TestADCSESC1(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 @@ -427,14 +430,16 @@ func TestEnrollOnBehalfOf(t *testing.T) {
return nil
}, func(harness integration.HarnessDetails, db graph.Database) {
certTemplates, err := ad2.FetchNodesByKind(context.Background(), db, ad.CertTemplate)
v1Templates := make([]*graph.Node, 0)
// TODO: v1Templates are never used in any assertions and should either have assertions added or be removed from the test entirely
//v1Templates := make([]*graph.Node, 0)
v2Templates := make([]*graph.Node, 0)

for _, template := range certTemplates {
if version, err := template.Properties.Get(ad.SchemaVersion.String()).Float64(); err != nil {
continue
} else if version == 1 {
v1Templates = append(v1Templates, template)
continue
//v1Templates = append(v1Templates, template)
} else if version >= 2 {
v2Templates = append(v2Templates, template)
}
Expand Down Expand Up @@ -473,6 +478,7 @@ func TestADCSESC3(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 @@ -529,6 +535,7 @@ func TestADCSESC3(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 @@ -580,3 +587,235 @@ func TestADCSESC3(t *testing.T) {
})
})
}

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

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

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)

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

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.PostADCSESC6a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC6a.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.ADCSESC6a)
})); err != nil {
t.Fatalf("error fetching esc6a edges in integration test; %v", err)
} else {
require.Equal(t, 2, len(results))

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

}
return nil
})
})

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

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)

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

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.PostADCSESC6a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC6a.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.ADCSESC6a)
})); err != nil {
t.Fatalf("error fetching esc6a edges in integration test; %v", err)
} else {
require.Equal(t, 1, len(results))
name, _ := results.Pick().Properties.Get(common.Name.String()).String()
require.Equal(t, name, "Group0")
}

return nil
})
})

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

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)

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

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.PostADCSESC6a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC6a.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.ADCSESC6a)
})); err != nil {
t.Fatalf("error fetching esc6a edges in integration test; %v", err)
} else {
require.Equal(t, 2, len(results))
names := []string{}
for _, result := range results.Slice() {
name, _ := result.Properties.Get(common.Name.String()).String()
names = append(names, name)
}
require.Contains(t, names, "Group1", "Group2")
}

return nil
})
})

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

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)

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

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.PostADCSESC6a(ctx, tx, outC, groupExpansions, innerEnterpriseCA, innerDomain, cache); err != nil {
t.Logf("failed post processing for %s: %v", ad.ADCSESC6a.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.ADCSESC6a)
})); err != nil {
t.Fatalf("error fetching esc6a edges in integration test; %v", err)
} else {
names := []string{}
for _, result := range results.Slice() {
name, _ := result.Properties.Get(common.Name.String()).String()
names = append(names, name)
}
require.Equal(t, 14, len(results))
require.NotContains(t, names, "User2")
require.NotContains(t, names, "User3")
require.NotContains(t, names, "User5")
require.NotContains(t, names, "User7")
require.NotContains(t, names, "Computer6")
}
return nil
})
})
}
2 changes: 1 addition & 1 deletion cmd/api/src/api/middleware/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func AuthMiddleware(authenticator api.Authenticator) mux.MiddlewareFunc {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, "Token ID is malformed.", request), response)
return
} else if userAuth, responseCode, err := authenticator.ValidateRequestSignature(tokenID, request, time.Now()); err != nil {
msg := fmt.Errorf("Unable to validate request signature for client: %w.", err).Error()
msg := fmt.Errorf("unable to validate request signature for client: %w", err).Error()
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(responseCode, msg, request), response)
return
} else {
Expand Down
3 changes: 2 additions & 1 deletion cmd/api/src/api/v2/apiclient/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ package apiclient

import (
"fmt"
"net/http"

"github.com/specterops/bloodhound/src/api"
"github.com/specterops/bloodhound/src/model/appcfg"
"net/http"
)

func (s Client) GetFeatureFlags() ([]appcfg.FeatureFlag, error) {
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/src/api/v2/file_uploads.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ func (s Resources) ProcessFileUpload(response http.ResponseWriter, request *http
} else if fileUploadJob, err := fileupload.GetFileUploadJobByID(s.DB, int64(fileUploadJobID)); err != nil {
api.HandleDatabaseError(request, response, err)
} else if fileName, err := fileupload.SaveIngestFile(s.Config.TempDirectory(), request.Body); err != nil {
if errors.Is(err, fileupload.InvalidIngestFileType) {
if errors.Is(err, fileupload.ErrInvalidIngestFileType) {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, fmt.Sprintf("Error saving ingest file: %v", err), request), response)
} else {
api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusInternalServerError, fmt.Sprintf("Error saving ingest file: %v", err), request), response)
Expand Down
2 changes: 1 addition & 1 deletion cmd/api/src/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ func SetValuesFromEnv(varPrefix string, target any, env []string) error {
if formattedPrefix := formatEnvironmentVariablePrefix(varPrefix); strings.HasPrefix(key, formattedPrefix) {
cfgKeyPath := strings.TrimPrefix(key, formattedPrefix)

if err := SetValue(target, cfgKeyPath, valueStr); errors.Is(err, InvalidConfigurationPathError) {
if err := SetValue(target, cfgKeyPath, valueStr); errors.Is(err, ErrInvalidConfigurationPath) {
log.Warnf("%s", err)
} else if err != nil {
return err
Expand Down
4 changes: 2 additions & 2 deletions cmd/api/src/config/reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import (
)

var structTagRegex = regexp.MustCompile(`(\w+):"([^"]+)"`)
var InvalidConfigurationPathError = errors.New("Unable to find a configuration element by path")
var ErrInvalidConfigurationPath = errors.New("unable to find a configuration element by path")

// taggedField represents a struct field by its index and a parsed representation of any tags associated with the
// struct field.
Expand Down Expand Up @@ -293,7 +293,7 @@ func SetValue(target any, path, value string) error {
}

if !found {
return fmt.Errorf("%w: %s", InvalidConfigurationPathError, path)
return fmt.Errorf("%w: %s", ErrInvalidConfigurationPath, path)
}
}

Expand Down
Loading

0 comments on commit ecfbbd4

Please sign in to comment.