diff --git a/.gitignore b/.gitignore index 50d6977..f6fbc56 100644 --- a/.gitignore +++ b/.gitignore @@ -689,3 +689,4 @@ settings.json main.tf builds/** cinqctl +.scratch \ No newline at end of file diff --git a/cloud-inquisitor/aws_cloudfront_resource.go b/cloud-inquisitor/aws_cloudfront_resource.go index 59fb227..278b277 100644 --- a/cloud-inquisitor/aws_cloudfront_resource.go +++ b/cloud-inquisitor/aws_cloudfront_resource.go @@ -181,6 +181,58 @@ func (cf *AWSCloudFrontDistributionResource) createDistributionEntries() error { return nil } +func (cf *AWSCloudFrontDistributionResource) deleteDistributionEntries() error { + db, err := database.NewDBConnection() + defer db.Close() + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + + // get account + account := model.Account{AccountID: cf.AccountID} + err = db.FirstOrCreate(&account, account).Error + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + cf.logger.WithFields(cf.GetMetadata()).Debugf("account: %#v", account) + + distro := model.Distribution{DistributionID: cf.DistributionID, Domain: cf.DomainName, AccountID: account.ID} + err = db.Delete(&distro).Error + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + + // delete origins + for _, cfOrigin := range cf.Origins { + origin := model.Origin{ + OriginID: cfOrigin.ID, + Domain: cfOrigin.Domain, + DistributionID: distro.ID, + } + err = db.Delete(&origin).Error + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + + // delete origin groups + for _, cfGroup := range cf.OriginGroups { + group := model.OriginGroup{ + GroupID: cfGroup.ID, + DistributionID: distro.ID, + } + err = db.Delete(&group).Error + if err != nil { + cf.logger.WithFields(cf.GetMetadata()).Error(err.Error()) + return err + } + + return nil +} + func (cf *AWSCloudFrontDistributionResource) updateDistributionEntries() error { db, err := database.NewDBConnection() defer db.Close() @@ -496,6 +548,8 @@ func (cf *AWSCloudFrontDistributionHijackableResource) PublishState() error { switch cf.EventName { case "CreateDistribution": return cf.createDistributionEntries() + case "DeleteDistribution": + return cf.deleteDistributionEntries() case "UpdateDistribution": return cf.updateDistributionEntries() default: @@ -505,6 +559,50 @@ func (cf *AWSCloudFrontDistributionHijackableResource) PublishState() error { return nil } -func (cf *AWSCloudFrontDistributionHijackableResource) AnalyzeForHijack() (*model.HijackableResourceChain, error) { - return &model.HijackableResourceChain{}, nil +func (cf *AWSCloudFrontDistributionHijackableResource) AnalyzeForHijack() (*model.HijackableResourceRoot, error) { + switch cf.EventName { + case "CreateDistribution": + return cf.analyzeCreateDistributionEntries() + case "DeleteDistribution": + return cf.analyzeDeleteDistributionEntries() + default: + return &model.HijackableResourceRoot{}, nil + } +} + +func (cf *AWSCloudFrontDistributionHijackableResource) analyzeDeleteDistributionEntries(*model.HijackableResourceRoot, error) { + return cf.analyzeDeleteDistributionEntries() } + +func (cf *AWSCloudFrontDistributionHijackableResource) analyzeDeleteDistributionEntries(*model.HijackableResourceRoot, error) { + resolver, err := graph.NewResolver() + if err != nil { + cf.GetLogger().Errorf("error creating a new resolver to evaluate cloudfront hijacks: %v", err.Error()) + return &model.HijackableResourceRoot{}, err + } + + domains := make([]string, len(cf.Origins)) + for i, ori := range cf.Origins { + domain[i] = ori.Domain + } + + ctx := context.Background() + root, err := resolver.Query().GetHijackMapWithResourceIDAndDomainsAndTypeAndDirectionThenFlattened( + ctx, + fmt.Sprintf( + "cloudfront-%s-%s-%s", + cf.AccountID, + cf.DistributionID, + cf.DomainName, + ), + cf.DistributionID, + domains, + model.TypeDistribution, + model.DirectionDownstream, + ) + if err != nil { + eb.GetLogger().Errorf("error querying graph for cloudfront hijack analysis: %v", err.Error()) + } + + return root, err +} \ No newline at end of file diff --git a/cloud-inquisitor/graph/model/models_gen.go b/cloud-inquisitor/graph/model/models_gen.go index d810173..51341c3 100644 --- a/cloud-inquisitor/graph/model/models_gen.go +++ b/cloud-inquisitor/graph/model/models_gen.go @@ -22,10 +22,10 @@ type HijackableResourceMap struct { } type HijackableResourceRoot struct { - ID string `json:"id"` - RootResourceID string `json:"rootResourceID"` - Direction Direction `json:"direction"` - Maps []*HijackableResourceMap `json:"maps"` + ID string `json:"id"` + RootResource *HijackableResource `json:"rootResourceID"` + Direction Direction `json:"direction"` + Maps []*HijackableResourceMap `json:"maps"` } type Direction string diff --git a/cloud-inquisitor/graph/schema.graphqls b/cloud-inquisitor/graph/schema.graphqls index 6e84867..a9d9f3c 100644 --- a/cloud-inquisitor/graph/schema.graphqls +++ b/cloud-inquisitor/graph/schema.graphqls @@ -84,7 +84,7 @@ type HijackableResource { type HijackableResourceRoot { id: ID! - rootResourceID: ID! + rootResource: HijackableResource! direction: Direction! maps: [HijackableResourceMap!]! } diff --git a/cloud-inquisitor/notification/templates.go b/cloud-inquisitor/notification/templates.go index 9279038..065775b 100644 --- a/cloud-inquisitor/notification/templates.go +++ b/cloud-inquisitor/notification/templates.go @@ -15,87 +15,39 @@ func init() { } type HijackChainElement struct { - AccountId string - Resource string - ResourceType string - ResourceReferenced string - ResourceReferencedType string + AccountId string + Resource string + ResourceType string } type HijackNotificationContent struct { PrimaryResource string PrimaryResourceType string PrimaryAccountId string - HijackChain []HijackChainElement + HijackChains [][]HijackChainElement } -func GenerateContent(rawChain *model.HijackableResourceChain) HijackNotificationContent { +func GenerateContent(root *model.HijackableResourceRoot) HijackNotificationContent { content := HijackNotificationContent{ - PrimaryResource: rawChain.Resource.ID, - PrimaryAccountId: rawChain.Resource.Account, - PrimaryResourceType: rawChain.Resource.Type.String(), + PrimaryResource: root.RootResource.ID, + PrimaryAccountId: root.RootResource.Account, + PrimaryResourceType: root.RootResource.Type.String(), } - chain := []HijackChainElement{} - for idx, resource := range rawChain.Upstream { - if idx == len(rawChain.Upstream)-1 { - chain = append(chain, HijackChainElement{ - AccountId: resource.Account, - Resource: resource.ID, - ResourceType: resource.Type.String(), - ResourceReferenced: rawChain.Resource.ID, - ResourceReferencedType: rawChain.Resource.Type.String(), - }) - } else { - chain = append(chain, HijackChainElement{ - AccountId: resource.Account, - Resource: resource.ID, - ResourceType: resource.Type.String(), - ResourceReferenced: rawChain.Upstream[idx+1].ID, - ResourceReferencedType: rawChain.Upstream[idx+1].Type.String(), - }) - } - } - - if len(rawChain.Downstream) == 0 { - chain = append(chain, HijackChainElement{ - AccountId: rawChain.Resource.Account, - Resource: rawChain.Resource.ID, - ResourceType: rawChain.Resource.Type.String(), - ResourceReferenced: "not applicable", - ResourceReferencedType: "not applicable", - }) - } else { - chain = append(chain, HijackChainElement{ - AccountId: rawChain.Resource.Account, - Resource: rawChain.Resource.ID, - ResourceType: rawChain.Resource.Type.String(), - ResourceReferenced: rawChain.Downstream[0].ID, - ResourceReferencedType: rawChain.Downstream[0].Type.String(), - }) - } - - for idx, resource := range rawChain.Downstream { - if idx == len(rawChain.Downstream)-1 { - chain = append(chain, HijackChainElement{ - AccountId: resource.Account, - Resource: resource.ID, - ResourceType: resource.Type.String(), - ResourceReferenced: "not applicable", - ResourceReferencedType: "not applicable", - }) - } else { - chain = append(chain, HijackChainElement{ - AccountId: resource.Account, - Resource: resource.ID, - ResourceType: resource.Type.String(), - ResourceReferenced: rawChain.Downstream[idx+1].ID, - ResourceReferencedType: rawChain.Downstream[idx+1].Type.String(), + var hijackChains [][]HijackChainElement + for _, chain := range root.Maps { + hijackChain := []HijackChainElement{} + for _, element := range chain.Contains { + hijackChain = append(hijackChain, HijackChainElement{ + AccountId: element.Resource.Account, + Resource: element.Resource.ID, + ResourceType: element.Resource.Type.String(), }) } + hijackChains = append(hijackChains, hijackChain) } - content.HijackChain = chain + content.HijackChains = hijackChains return content } diff --git a/cloud-inquisitor/notification/templates/hijack_template.html b/cloud-inquisitor/notification/templates/hijack_template.html index 59ac00f..2a09b97 100644 --- a/cloud-inquisitor/notification/templates/hijack_template.html +++ b/cloud-inquisitor/notification/templates/hijack_template.html @@ -41,53 +41,46 @@

Potential Domain Hijack Found

Deleting resource {{ .PrimaryResourceType }}:{{ .PrimaryResource }} in AWS account {{ .PrimaryAccountId }} has resulted in a potential domain hijack; there is a DNS record pointing to a resource not owned or no longer owned by us.

-

The following table includes all resources in the chain that would lead to a potential domain hijack.

+

The following tables includes all resources in the chain that would lead to a potential domain hijack.

- - - - - - - - - - - - {{- with .HijackChain }} - {{- range $i, $elem := . }} - {{ if isEven $i }}{{- else }}{{- end }} - - - - - + {{- with .HijackChains }} + {{- range $j, $singlechain := . }} +

Chain {{ $j }}

+
- Account ID - - Resource - - Resource Type - - Resource Referenced - - Resource Referenced Type -
- {{ $elem.AccountId }} - - {{ $elem.Resource }} - - {{ $elem.ResourceType }} - - {{ $elem.ResourceReferenced }} - - {{ $elem.ResourceReferencedType }} -
+ + + + + - {{- end }} - {{- end }} - -
+ Account ID + + Resource + + Resource Type +
+ + + {{- with $singlechain }} + {{- range $i, $elem := . }} + {{ if isEven $i }}{{- else }}{{- end }} + + {{ $elem.AccountId }} + + + {{ $elem.Resource }} + + + {{ $elem.ResourceType }} + + + {{- end }} + {{- end }} + + + {{- end }} + {{- end }}
\ No newline at end of file diff --git a/cloud-inquisitor/notification/templates/hijack_template.txt b/cloud-inquisitor/notification/templates/hijack_template.txt index 50bb55f..fe10062 100644 --- a/cloud-inquisitor/notification/templates/hijack_template.txt +++ b/cloud-inquisitor/notification/templates/hijack_template.txt @@ -1,17 +1,22 @@ Potential Domain Hijack Found -Deleting resource {{ .PrimaryResourceType }}:{{ .PrimaryResource }} in AWS account {{ .PrimaryAccountId }} has resulted in a potential domain hijack; - there is a DNS record pointing to a resource not owned or no longer owned by us. +Deleting resource {{ .PrimaryResourceType }}:{{ .PrimaryResource }} in AWS account {{ .PrimaryAccountId }} has resulted in a potential security risk. +By claiming the corresponding resource an attacker will be able to make Players and Rioters believe they represent part of Riot and do harm to them. -The following table includes all resources in the chain that would lead to a potential domain hijack. +The following table includes all resources in the chain that would lead to a potential hijack. -{{ with .HijackChain }} -{{ range $i, $elem := . }} +{{ with .HijackChains }} +{{ range $j, $singlechain := . }} +============== +Hijack Chain {{ $j }} +============== + {{ with $singlechain }} + {{ range $i, $elem := . }} Resource {{ $i }}: Account ID: {{ $elem.AccountId }} Resource: {{ $elem.Resource }} Resource Type: {{ $elem.ResourceType }} - Resource Referenced: {{ $elem.ResourceReferenced }} - Resource Referenced Type: {{ $elem.ResourceReferencedType }} + {{ end }} + {{ end }} +{{ end }} {{ end }} -{{ end }} \ No newline at end of file diff --git a/cloud-inquisitor/notification/templates_func_test.go b/cloud-inquisitor/notification/templates_func_test.go index 06646ce..4cb84d8 100644 --- a/cloud-inquisitor/notification/templates_func_test.go +++ b/cloud-inquisitor/notification/templates_func_test.go @@ -21,13 +21,13 @@ func TestAWSSES(t *testing.T) { PrimaryResource: "test.bucket.com", PrimaryResourceType: "S3 Bucket", PrimaryAccountId: "123456789012", - HijackChain: []HijackChainElement{ - HijackChainElement{ - AccountId: "123456789012", - Resource: "public.test.bucket.com", - ResourceType: "route53", - ResourceReferenced: "test.bucket.com", - ResourceReferencedType: "S3 Bucket", + HijackChains: [][]HijackChainElement{ + []HijackChainElement{ + HijackChainElement{ + AccountId: "123456789012", + Resource: "public.test.bucket.com", + ResourceType: "route53", + }, }, }, } diff --git a/cloud-inquisitor/notification/templates_test.go b/cloud-inquisitor/notification/templates_test.go index 8e8941a..868f96e 100644 --- a/cloud-inquisitor/notification/templates_test.go +++ b/cloud-inquisitor/notification/templates_test.go @@ -25,7 +25,7 @@ func TestHijackHTMLNoChain(t *testing.T) { PrimaryResource: "test.bucket.com", PrimaryResourceType: "S3 Bucket", PrimaryAccountId: "123456789012", - HijackChain: []HijackChainElement{}, + HijackChains: [][]HijackChainElement{}, } html, err := NewHijackHTML(content) @@ -54,20 +54,18 @@ func TestHijackHTMLWithChain(t *testing.T) { PrimaryResource: "test.bucket.com", PrimaryResourceType: "S3 Bucket", PrimaryAccountId: "123456789012", - HijackChain: []HijackChainElement{ - HijackChainElement{ - AccountId: "123456789012", - Resource: "public.test.bucket.com", - ResourceType: "route53", - ResourceReferenced: "test.bucket.com", - ResourceReferencedType: "S3 Bucket", - }, - HijackChainElement{ - AccountId: "123456789012", - Resource: "public.test.bucket.com", - ResourceType: "route53", - ResourceReferenced: "test.bucket.com", - ResourceReferencedType: "S3 Bucket", + HijackChains: [][]HijackChainElement{ + []HijackChainElement{ + HijackChainElement{ + AccountId: "123456789012", + Resource: "public.test.bucket.com", + ResourceType: "route53", + }, + HijackChainElement{ + AccountId: "123456789012", + Resource: "public.test.bucket.com", + ResourceType: "route53", + }, }, }, } @@ -98,7 +96,7 @@ func TestHijackTextNoChain(t *testing.T) { PrimaryResource: "test.bucket.com", PrimaryResourceType: "S3 Bucket", PrimaryAccountId: "123456789012", - HijackChain: []HijackChainElement{}, + HijackChains: [][]HijackChainElement{}, } text, err := NewHijackText(content) @@ -127,20 +125,18 @@ func TestHijackTextWithChain(t *testing.T) { PrimaryResource: "test.bucket.com", PrimaryResourceType: "S3 Bucket", PrimaryAccountId: "123456789012", - HijackChain: []HijackChainElement{ - HijackChainElement{ - AccountId: "123456789012", - Resource: "public.test.bucket.com", - ResourceType: "route53", - ResourceReferenced: "test.bucket.com", - ResourceReferencedType: "S3 Bucket", - }, - HijackChainElement{ - AccountId: "123456789012", - Resource: "public.test.bucket.com", - ResourceType: "route53", - ResourceReferenced: "test.bucket.com", - ResourceReferencedType: "S3 Bucket", + HijackChains: [][]HijackChainElement{ + []HijackChainElement{ + HijackChainElement{ + AccountId: "123456789012", + Resource: "public.test.bucket.com", + ResourceType: "route53", + }, + HijackChainElement{ + AccountId: "123456789012", + Resource: "public.test.bucket.com", + ResourceType: "route53", + }, }, }, } @@ -174,25 +170,23 @@ AccountId: rawChain.Resource.Account, ResourceReferencedType: "", */ func TestGenerateContent(t *testing.T) { - chain := &model.HijackableResourceChain{ + chain := &model.HijackableResourceRoot{ ID: "testID", - Resource: &model.HijackableResource{ + RootResource: &model.HijackableResource{ ID: "test root resource", Account: "test root account", Type: model.TypeElasticbeanstalk, }, - Upstream: []*model.HijackableResource{ - &model.HijackableResource{ - ID: "test upstream ", - Account: "test upstream account", - Type: model.TypeElasticbeanstalk, - }, - }, - Downstream: []*model.HijackableResource{ - &model.HijackableResource{ - ID: "test downstream", - Account: "test downstream account", - Type: model.TypeElasticbeanstalk, + Direction: model.DirectionDownstream, + Maps: []*model.HijackableResourceMap{ + &model.HijackableResourceMap{ + Resource: &model.HijackableResource{ + ID: "test downstream", + Account: "test downstream account", + Type: model.TypeElasticbeanstalk, + }, + Direction: model.DirectionDownstream, + Contains: []*model.HijackableResourceMap{}, }, }, } diff --git a/cloud-inquisitor/notification/test_html/hijack_test_no_chain.html b/cloud-inquisitor/notification/test_html/hijack_test_no_chain.html index 593409f..874a723 100755 --- a/cloud-inquisitor/notification/test_html/hijack_test_no_chain.html +++ b/cloud-inquisitor/notification/test_html/hijack_test_no_chain.html @@ -41,9 +41,10 @@

Potential Domain Hijack Found

Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has resulted in a potential domain hijack; there is a DNS record pointing to a resource not owned or no longer owned by us.

-

The following table includes all resources in the chain that would lead to a potential domain hijack.

+

The following tables includes all resources in the chain that would lead to a potential domain hijack.

+

Chain 0

@@ -56,12 +57,6 @@

Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has

- - diff --git a/cloud-inquisitor/notification/test_html/hijack_test_with_chain.html b/cloud-inquisitor/notification/test_html/hijack_test_with_chain.html index fd8d0c0..8db7fba 100755 --- a/cloud-inquisitor/notification/test_html/hijack_test_with_chain.html +++ b/cloud-inquisitor/notification/test_html/hijack_test_with_chain.html @@ -41,9 +41,10 @@

Potential Domain Hijack Found

Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has resulted in a potential domain hijack; there is a DNS record pointing to a resource not owned or no longer owned by us.

-

The following table includes all resources in the chain that would lead to a potential domain hijack.

+

The following tables includes all resources in the chain that would lead to a potential domain hijack.

+

Chain 0

Resource Type - Resource Referenced - - Resource Referenced Type -
@@ -56,12 +57,6 @@

Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has

- - @@ -75,12 +70,6 @@

Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has

- - - -
Resource Type - Resource Referenced - - Resource Referenced Type -
route53 - test.bucket.com - - S3 Bucket -
@@ -92,12 +81,6 @@

Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has

route53 - test.bucket.com - - S3 Bucket -
diff --git a/cloud-inquisitor/notification/test_text/hijack_test_no_chain.txt b/cloud-inquisitor/notification/test_text/hijack_test_no_chain.txt index b018db8..2072c24 100755 --- a/cloud-inquisitor/notification/test_text/hijack_test_no_chain.txt +++ b/cloud-inquisitor/notification/test_text/hijack_test_no_chain.txt @@ -1,8 +1,8 @@ Potential Domain Hijack Found -Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has resulted in a potential domain hijack; - there is a DNS record pointing to a resource not owned or no longer owned by us. +Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has resulted in a potential security risk. +By claiming the corresponding resource an attacker will be able to make Players and Rioters believe they represent part of Riot and do harm to them. -The following table includes all resources in the chain that would lead to a potential domain hijack. +The following table includes all resources in the chain that would lead to a potential hijack. \ No newline at end of file diff --git a/cloud-inquisitor/notification/test_text/hijack_test_with_chain.txt b/cloud-inquisitor/notification/test_text/hijack_test_with_chain.txt index d605588..2b3644e 100755 --- a/cloud-inquisitor/notification/test_text/hijack_test_with_chain.txt +++ b/cloud-inquisitor/notification/test_text/hijack_test_with_chain.txt @@ -1,24 +1,24 @@ Potential Domain Hijack Found -Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has resulted in a potential domain hijack; - there is a DNS record pointing to a resource not owned or no longer owned by us. +Deleting resource S3 Bucket:test.bucket.com in AWS account 123456789012 has resulted in a potential security risk. +By claiming the corresponding resource an attacker will be able to make Players and Rioters believe they represent part of Riot and do harm to them. -The following table includes all resources in the chain that would lead to a potential domain hijack. +The following table includes all resources in the chain that would lead to a potential hijack. +============== +Hijack Chain 0 +============== + Resource 0: Account ID: 123456789012 Resource: public.test.bucket.com Resource Type: route53 - Resource Referenced: test.bucket.com - Resource Referenced Type: S3 Bucket Resource 1: Account ID: 123456789012 Resource: public.test.bucket.com Resource Type: route53 - Resource Referenced: test.bucket.com - Resource Referenced Type: S3 Bucket \ No newline at end of file diff --git a/terraform_modules/event_rule/patterns/cloudfront_dns_hijacks.json b/terraform_modules/event_rule/patterns/cloudfront_dns_hijacks.json index d69ef29..88e7094 100644 --- a/terraform_modules/event_rule/patterns/cloudfront_dns_hijacks.json +++ b/terraform_modules/event_rule/patterns/cloudfront_dns_hijacks.json @@ -11,6 +11,7 @@ ], "eventName": [ "CreateDistribution", + "DeleteDistribution", "UpdateDistribution" ] }