Skip to content

Commit

Permalink
chore: wrap ADCS ingest and post-processing by feature flag
Browse files Browse the repository at this point in the history
  • Loading branch information
juggernot325 committed Nov 15, 2023
1 parent 7124fcf commit 1f5284a
Show file tree
Hide file tree
Showing 5 changed files with 106 additions and 79 deletions.
4 changes: 2 additions & 2 deletions cmd/api/src/analysis/ad/post.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func PostLocalGroups(ctx context.Context, db graph.Database, localGroupExpansion
}
}

func Post(ctx context.Context, db graph.Database) (*analysis.AtomicPostProcessingStats, error) {
func Post(ctx context.Context, db graph.Database, adcsEnabled bool) (*analysis.AtomicPostProcessingStats, error) {
aggregateStats := analysis.NewAtomicPostProcessingStats()
if stats, err := analysis.DeleteTransitEdges(ctx, db, ad.Entity, ad.Entity, adAnalysis.PostProcessedRelationships()...); err != nil {
return &aggregateStats, err
Expand All @@ -157,7 +157,7 @@ func Post(ctx context.Context, db graph.Database) (*analysis.AtomicPostProcessin
return &aggregateStats, err
} else if localGroupStats, err := PostLocalGroups(ctx, db, groupExpansions); err != nil {
return &aggregateStats, err
} else if adcsStats, err := adAnalysis.PostADCS(ctx, db, groupExpansions); err != nil {
} else if adcsStats, err := adAnalysis.PostADCS(ctx, db, groupExpansions, adcsEnabled); err != nil {
return &aggregateStats, err
} else {
aggregateStats.Merge(stats)
Expand Down
5 changes: 4 additions & 1 deletion cmd/api/src/daemons/datapipe/analysis.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/specterops/bloodhound/src/analysis/azure"
"github.com/specterops/bloodhound/src/config"
"github.com/specterops/bloodhound/src/database"
"github.com/specterops/bloodhound/src/model/appcfg"
"github.com/specterops/bloodhound/src/services/agi"
"github.com/specterops/bloodhound/src/services/dataquality"
)
Expand Down Expand Up @@ -61,7 +62,9 @@ func RunAnalysisOperations(ctx context.Context, db database.Database, graphDB gr
collector.Collect(fmt.Errorf("azure tier zero tagging failed: %w", err))
}

if stats, err := ad.Post(ctx, graphDB); err != nil {
if adcsFlag, err := db.GetFlagByKey(appcfg.FeatureAdcs); err != nil {
collector.Collect(fmt.Errorf("error retrieving ADCS feature flag: %w", err))
} else if stats, err := ad.Post(ctx, graphDB, adcsFlag.Enabled); err != nil {
collector.Collect(fmt.Errorf("error during ad post: %w", err))
} else {
stats.LogStats()
Expand Down
6 changes: 4 additions & 2 deletions cmd/api/src/daemons/datapipe/convertors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/specterops/bloodhound/graphschema/ad"
)

func convertComputerData(data []ein.Computer) ConvertedData {
func convertComputerData(data []ein.Computer, adcsEnabled bool) ConvertedData {
converted := ConvertedData{}

for _, computer := range data {
Expand Down Expand Up @@ -57,7 +57,9 @@ func convertComputerData(data []ein.Computer) ConvertedData {
}
}

converted.NodeProps = append(converted.NodeProps, ein.ParseDCRegistryData(computer))
if adcsEnabled {
converted.NodeProps = append(converted.NodeProps, ein.ParseDCRegistryData(computer))
}

converted.NodeProps = append(converted.NodeProps, baseNodeProp)
}
Expand Down
80 changes: 49 additions & 31 deletions cmd/api/src/daemons/datapipe/ingest.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package datapipe

import (
"encoding/json"
"fmt"
"io"
"strings"
"time"
Expand All @@ -28,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/appcfg"
)

func (s *Daemon) ReadWrapper(batch graph.Batch, reader io.Reader) error {
Expand Down Expand Up @@ -58,6 +60,11 @@ func (s *Daemon) IngestAzureData(batch graph.Batch, converted ConvertedAzureData
}

func (s *Daemon) IngestWrapper(batch graph.Batch, wrapper DataWrapper) error {
adcsFlag, err := s.db.GetFlagByKey(appcfg.FeatureAdcs)
if err != nil {
return fmt.Errorf("error getting ADCS feature flag: %w", err)
}

switch wrapper.Metadata.Type {
case DataTypeComputer:
// We should not be getting anything with Version < 5 at this point, and we don't want to ingest it if we do as post-processing will blow it away anyways
Expand All @@ -67,10 +74,11 @@ func (s *Daemon) IngestWrapper(batch graph.Batch, wrapper DataWrapper) error {
if err := json.Unmarshal(wrapper.Payload, &computerData); err != nil {
return err
} else {
converted := convertComputerData(computerData)
converted := convertComputerData(computerData, adcsFlag.Enabled)
s.IngestBasicData(batch, converted)
}
}

case DataTypeUser:
var userData []ein.User
if err := json.Unmarshal(wrapper.Payload, &userData); err != nil {
Expand Down Expand Up @@ -134,48 +142,58 @@ func (s *Daemon) IngestWrapper(batch graph.Batch, wrapper DataWrapper) error {
}

case DataTypeAIACA:
var aiacaData []ein.AIACA
if err := json.Unmarshal(wrapper.Payload, &aiacaData); err != nil {
return err
} else {
converted := convertAIACAData(aiacaData)
s.IngestBasicData(batch, converted)
if adcsFlag.Enabled {
var aiacaData []ein.AIACA
if err := json.Unmarshal(wrapper.Payload, &aiacaData); err != nil {
return err
} else {
converted := convertAIACAData(aiacaData)
s.IngestBasicData(batch, converted)
}
}

case DataTypeRootCA:
var rootcaData []ein.RootCA
if err := json.Unmarshal(wrapper.Payload, &rootcaData); err != nil {
return err
} else {
converted := convertRootCAData(rootcaData)
s.IngestBasicData(batch, converted)
if adcsFlag.Enabled {
var rootcaData []ein.RootCA
if err := json.Unmarshal(wrapper.Payload, &rootcaData); err != nil {
return err
} else {
converted := convertRootCAData(rootcaData)
s.IngestBasicData(batch, converted)
}
}

case DataTypeEnterpriseCA:
var enterprisecaData []ein.EnterpriseCA
if err := json.Unmarshal(wrapper.Payload, &enterprisecaData); err != nil {
return err
} else {
converted := convertEnterpriseCAData(enterprisecaData)
s.IngestBasicData(batch, converted)
if adcsFlag.Enabled {
var enterprisecaData []ein.EnterpriseCA
if err := json.Unmarshal(wrapper.Payload, &enterprisecaData); err != nil {
return err
} else {
converted := convertEnterpriseCAData(enterprisecaData)
s.IngestBasicData(batch, converted)
}
}

case DataTypeNTAuthStore:
var ntauthstoreData []ein.NTAuthStore
if err := json.Unmarshal(wrapper.Payload, &ntauthstoreData); err != nil {
return err
} else {
converted := convertNTAuthStoreData(ntauthstoreData)
s.IngestBasicData(batch, converted)
if adcsFlag.Enabled {
var ntauthstoreData []ein.NTAuthStore
if err := json.Unmarshal(wrapper.Payload, &ntauthstoreData); err != nil {
return err
} else {
converted := convertNTAuthStoreData(ntauthstoreData)
s.IngestBasicData(batch, converted)
}
}

case DataTypeCertTemplate:
var certtemplateData []ein.CertTemplate
if err := json.Unmarshal(wrapper.Payload, &certtemplateData); err != nil {
return err
} else {
converted := convertCertTemplateData(certtemplateData)
s.IngestBasicData(batch, converted)
if adcsFlag.Enabled {
var certtemplateData []ein.CertTemplate
if err := json.Unmarshal(wrapper.Payload, &certtemplateData); err != nil {
return err
} else {
converted := convertCertTemplateData(certtemplateData)
s.IngestBasicData(batch, converted)
}
}

case DataTypeAzure:
Expand Down
90 changes: 47 additions & 43 deletions packages/go/analysis/ad/adcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,54 +376,58 @@ func certTemplateHasEku(certTemplate *graph.Node, targetEkus ...string) (bool, e
}
}

func PostADCS(ctx context.Context, db graph.Database, groupExpansions impact.PathAggregator) (*analysis.AtomicPostProcessingStats, error) {
operation := analysis.NewPostRelationshipOperation(ctx, db, "ADCS Post Processing")

if enterpriseCertAuthorities, err := FetchNodesByKind(ctx, db, ad.EnterpriseCA); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching enterpriseCA nodes: %w", err)
} else if rootCertAuthorities, err := FetchNodesByKind(ctx, db, ad.RootCA); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching rootCA nodes: %w", err)
} else if certTemplates, err := FetchNodesByKind(ctx, db, ad.CertTemplate); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching cert template nodes: %w", err)
} else if domains, err := FetchNodesByKind(ctx, db, ad.Domain); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching domain nodes: %w", err)
} else if step1Stats, err := postADCSPreProcessStep1(ctx, db, enterpriseCertAuthorities, rootCertAuthorities); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed adcs pre-processing step 1: %w", err)
} else if step2Stats, err := postADCSPreProcessStep2(ctx, db, certTemplates); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed adcs pre-processing step 2: %w", err)
} else {
operation.Stats.Merge(step1Stats)
operation.Stats.Merge(step2Stats)

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

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {

if enterpriseCAs, err := FetchEnterpriseCAsTrustedForNTAuthPathToDomain(tx, innerDomain); err != nil {
return err
} else {
for _, enterpriseCA := range enterpriseCAs {
if validPaths, err := FetchEnterpriseCAsCertChainPathToDomain(tx, enterpriseCA, innerDomain); err != nil {
log.Errorf("error fetching paths from enterprise ca %d to domain %d: %w", enterpriseCA.ID, innerDomain.ID, err)
} else if validPaths.Len() == 0 {
continue
} else {
if err := PostGoldenCert(ctx, tx, outC, innerDomain, enterpriseCA); err != nil {
log.Errorf("failed post processing for %s: %w", ad.GoldenCert.String(), err)
} else if err := PostADCSESC1(ctx, tx, outC, db, groupExpansions, enterpriseCertAuthorities, certTemplates, enterpriseCA, innerDomain); err != nil {
log.Errorf("failed post processing for %s: %w", ad.ADCSESC1.String(), err)
func PostADCS(ctx context.Context, db graph.Database, groupExpansions impact.PathAggregator, adcsEnabled bool) (*analysis.AtomicPostProcessingStats, error) {
if adcsEnabled {
operation := analysis.NewPostRelationshipOperation(ctx, db, "ADCS Post Processing")

if enterpriseCertAuthorities, err := FetchNodesByKind(ctx, db, ad.EnterpriseCA); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching enterpriseCA nodes: %w", err)
} else if rootCertAuthorities, err := FetchNodesByKind(ctx, db, ad.RootCA); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching rootCA nodes: %w", err)
} else if certTemplates, err := FetchNodesByKind(ctx, db, ad.CertTemplate); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching cert template nodes: %w", err)
} else if domains, err := FetchNodesByKind(ctx, db, ad.Domain); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed fetching domain nodes: %w", err)
} else if step1Stats, err := postADCSPreProcessStep1(ctx, db, enterpriseCertAuthorities, rootCertAuthorities); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed adcs pre-processing step 1: %w", err)
} else if step2Stats, err := postADCSPreProcessStep2(ctx, db, certTemplates); err != nil {
return &analysis.AtomicPostProcessingStats{}, fmt.Errorf("failed adcs pre-processing step 2: %w", err)
} else {
operation.Stats.Merge(step1Stats)
operation.Stats.Merge(step2Stats)

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

operation.Operation.SubmitReader(func(ctx context.Context, tx graph.Transaction, outC chan<- analysis.CreatePostRelationshipJob) error {

if enterpriseCAs, err := FetchEnterpriseCAsTrustedForNTAuthPathToDomain(tx, innerDomain); err != nil {
return err
} else {
for _, enterpriseCA := range enterpriseCAs {
if validPaths, err := FetchEnterpriseCAsCertChainPathToDomain(tx, enterpriseCA, innerDomain); err != nil {
log.Errorf("error fetching paths from enterprise ca %d to domain %d: %w", enterpriseCA.ID, innerDomain.ID, err)
} else if validPaths.Len() == 0 {
continue
} else {
return nil
if err := PostGoldenCert(ctx, tx, outC, innerDomain, enterpriseCA); err != nil {
log.Errorf("failed post processing for %s: %w", ad.GoldenCert.String(), err)
} else if err := PostADCSESC1(ctx, tx, outC, db, groupExpansions, enterpriseCertAuthorities, certTemplates, enterpriseCA, innerDomain); err != nil {
log.Errorf("failed post processing for %s: %w", ad.ADCSESC1.String(), err)
} else {
return nil
}
}
}
}
}
return nil
})
}
return nil
})
}

return &operation.Stats, operation.Done()
return &operation.Stats, operation.Done()
}
} else {
return &analysis.AtomicPostProcessingStats{}, nil
}
}

Expand Down

0 comments on commit 1f5284a

Please sign in to comment.