From 8947d725a77dd238ee410fda8b286855ad20666d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adan=20=C3=81lvarez?= Date: Tue, 30 Jan 2024 12:22:53 +0100 Subject: [PATCH] New attack technique: Usage of ec2instanceconnect:SendSSHPublicKey on multiple instances (#467) * add aws send-ssh-public-key attack technique * delete replace * terraform fmt * Formatting and small code enhancements --------- Co-authored-by: Christophe Tafani-Dereeper --- ...s.lateral-movement.ec2-instance-connect.md | 59 ++++++++ ...ateral-movement.ec2-send-ssh-public-key.md | 46 +++++++ docs/attack-techniques/AWS/index.md | 5 + docs/attack-techniques/list.md | 1 + docs/index.yaml | 8 ++ v2/go.mod | 9 +- v2/go.sum | 10 ++ .../ec2-send-ssh-public-key/main.go | 91 ++++++++++++ .../ec2-send-ssh-public-key/main.tf | 129 ++++++++++++++++++ .../ec2-send-ssh-public-key/my_key.pub | 1 + v2/internal/attacktechniques/main.go | 1 + 11 files changed, 356 insertions(+), 4 deletions(-) create mode 100755 docs/attack-techniques/AWS/aws.lateral-movement.ec2-instance-connect.md create mode 100755 docs/attack-techniques/AWS/aws.lateral-movement.ec2-send-ssh-public-key.md create mode 100644 v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key/main.go create mode 100644 v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key/main.tf create mode 100644 v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key/my_key.pub diff --git a/docs/attack-techniques/AWS/aws.lateral-movement.ec2-instance-connect.md b/docs/attack-techniques/AWS/aws.lateral-movement.ec2-instance-connect.md new file mode 100755 index 00000000..f04eb98b --- /dev/null +++ b/docs/attack-techniques/AWS/aws.lateral-movement.ec2-instance-connect.md @@ -0,0 +1,59 @@ +--- +title: Usage of EC2 Instance Connect on multiple instances +--- + +# Usage of EC2 Instance Connect on multiple instances + + slow + idempotent + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Lateral Movement + +## Description + + +Simulates an attacker pushing an SSH public key to multiple EC2 instances, which then will allow anyone with the corresponding private key to +connect directly to the systems via SSH. + +Warm-up: + +- Create multiple EC2 instances and a VPC (takes a few minutes). + +Detonation: + +- Adds a public SSH key to the EC2 for 60 seconds. + +References: + +- https://securitylabs.datadoghq.com/articles/tales-from-the-cloud-trenches-ecs-crypto-mining/#hands-on-keyboard-activity-begins +- https://sysdig.com/blog/2023-global-cloud-threat-report/ + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.lateral-movement.ec2-instance-connect +``` +## Detection + + +Identify, through CloudTrail's SendSSHPublicKey event, when a user is adding an SSH key to multiple EC2 instances. Sample event: + +``` +{ + "eventSource": "ec2-instance-connect.amazonaws.com", + "eventName": "SendSSHPublicKey", + "requestParameters": { + "instanceId": "i-123456", + "instanceOSUser": "ec2-user", + "sSHPublicKey": "ssh-ed25519 ..." + } +} +``` + + diff --git a/docs/attack-techniques/AWS/aws.lateral-movement.ec2-send-ssh-public-key.md b/docs/attack-techniques/AWS/aws.lateral-movement.ec2-send-ssh-public-key.md new file mode 100755 index 00000000..e910efa5 --- /dev/null +++ b/docs/attack-techniques/AWS/aws.lateral-movement.ec2-send-ssh-public-key.md @@ -0,0 +1,46 @@ +--- +title: Usage of ec2instanceconnect:SendSSHPublicKey on multiple instances +--- + +# Usage of ec2instanceconnect:SendSSHPublicKey on multiple instances + + slow + idempotent + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Lateral Movement + +## Description + + +Simulates an attacker pushing a Secure Shell (SSH) public key to multiple EC2 instances, which then will allow anyone with the corresponding private key to +connect directly to the systems via SSH. + +Warm-up: + +- Create multiple EC2s instances and VPC (takes a few minutes). + +Detonation: + +- Adds a public SSH key to the EC2 for 60 seconds. + +References: + +- https://sysdig.com/blog/2023-global-cloud-threat-report/ + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.lateral-movement.ec2-send-ssh-public-key +``` +## Detection + + +Identify, through CloudTrail's SendSSHPublicKey event, when a user is adding an SSH key to multiple EC2s. + + diff --git a/docs/attack-techniques/AWS/index.md b/docs/attack-techniques/AWS/index.md index 8249cd7f..bbaf2330 100755 --- a/docs/attack-techniques/AWS/index.md +++ b/docs/attack-techniques/AWS/index.md @@ -73,6 +73,11 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Console Login without MFA](./aws.initial-access.console-login-without-mfa.md) +## Lateral Movement + +- [Usage of EC2 Instance Connect on multiple instances](./aws.lateral-movement.ec2-instance-connect.md) + + ## Persistence - [Backdoor an IAM Role](./aws.persistence.iam-backdoor-role.md) diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 44009bf8..32d19d8c 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -33,6 +33,7 @@ This page contains the list of all Stratus Attack Techniques. | [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 | +| [Usage of EC2 Instance Connect on multiple instances](./AWS/aws.lateral-movement.ec2-instance-connect.md) | [AWS](./AWS/index.md) | Lateral Movement | | [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 | | [Create an administrative IAM User](./AWS/aws.persistence.iam-create-admin-user.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation | diff --git a/docs/index.yaml b/docs/index.yaml index 41e62f6e..b4d150d2 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -175,6 +175,14 @@ AWS: - Initial Access platform: AWS isIdempotent: true + Lateral Movement: + - id: aws.lateral-movement.ec2-instance-connect + name: Usage of EC2 Instance Connect on multiple instances + isSlow: true + mitreAttackTactics: + - Lateral Movement + platform: AWS + isIdempotent: true Persistence: - id: aws.persistence.iam-backdoor-role name: Backdoor an IAM Role diff --git a/v2/go.mod b/v2/go.mod index 23d0d7cd..4fa162f7 100644 --- a/v2/go.mod +++ b/v2/go.mod @@ -8,7 +8,7 @@ require ( 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.23.5 + github.com/aws/aws-sdk-go-v2 v1.24.1 github.com/aws/aws-sdk-go-v2/config v1.25.11 github.com/aws/aws-sdk-go-v2/credentials v1.16.9 github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4 @@ -23,7 +23,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.25.2 github.com/aws/aws-sdk-go-v2/service/ssm v1.44.2 github.com/aws/aws-sdk-go-v2/service/sts v1.26.2 - github.com/aws/smithy-go v1.18.1 + github.com/aws/smithy-go v1.19.0 github.com/cenkalti/backoff/v4 v4.2.1 github.com/fatih/color v1.13.0 github.com/golang-jwt/jwt v3.2.2+incompatible @@ -47,10 +47,11 @@ require ( github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.8 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.8 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 // indirect + github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.20.6 // indirect github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 // indirect github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.8 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.8 // indirect diff --git a/v2/go.sum b/v2/go.sum index 73b47830..12321869 100644 --- a/v2/go.sum +++ b/v2/go.sum @@ -36,6 +36,8 @@ github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPd github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go-v2 v1.23.5 h1:xK6C4udTyDMd82RFvNkDQxtAd00xlzFUtX4fF2nMZyg= github.com/aws/aws-sdk-go-v2 v1.23.5/go.mod h1:t3szzKfP0NeRU27uBFczDivYJjsmSnqI8kIvKyWb9ds= +github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= +github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3 h1:Zx9+31KyB8wQna6SXFWOewlgoY5uGdDAu6PTOEU3OQI= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.3/go.mod h1:zxbEJhRdKTH1nqS2qu6UJ7zGe25xaHxZXaC2CvuQFnA= github.com/aws/aws-sdk-go-v2/config v1.25.11 h1:RWzp7jhPRliIcACefGkKp03L0Yofmd2p8M25kbiyvno= @@ -48,8 +50,12 @@ github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4 h1:TUCNKBd4/JEefsZDxo5de github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.4/go.mod h1:egDkcl+zsgFqS6VO142bKboip5Pe1sNMwN55Xy38QsM= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.8 h1:8GVZIR0y6JRIUNSYI1xAMF4HDfV8H/bOsZ/8AD/uY5Q= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.8/go.mod h1:rwBfu0SoUkBUZndVgPZKAD9Y2JigaZtRP68unRiYToQ= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.8 h1:ZE2ds/qeBkhk3yqYvS3CDCFNvd9ir5hMjlVStLZWrvM= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.8/go.mod h1:/lAPPymDYL023+TS6DJmjuL42nxix2AvEvfjqOBRODk= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1 h1:uR9lXYjdPX0xY+NhvaJ4dD8rpSRz5VY81ccIIoNG+lw= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.1/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.8 h1:abKT+RuM1sdCNZIGIfZpLkvxEX3Rpsto019XG/rkYG8= @@ -58,6 +64,8 @@ github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.35.2 h1:GHPyHyMEk/HV6vuwl9wnw github.com/aws/aws-sdk-go-v2/service/cloudtrail v1.35.2/go.mod h1:4W2MRbqyH3vsAbiLhV2I5K9UCKXjpoPeyYhBcuHvE6o= github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2 h1:e3Imv1oXz+W3Tfclflkh72t5TUPUwWdkHP7ctQGk8Dc= github.com/aws/aws-sdk-go-v2/service/ec2 v1.138.2/go.mod h1:d1hAqgLDOPaSO1Piy/0bBmj6oAplFwv6p0cquHntNHM= +github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.20.6 h1:Y0pqdpafA8TdG6AalCMFbbQ5SlO99MAybU0BDPLHbwo= +github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect v1.20.6/go.mod h1:y6fUhf01cjz+VUz+zrmJh3KfIXhefV7dS4STCxgHx7g= github.com/aws/aws-sdk-go-v2/service/iam v1.28.2 h1:lax3msAOJ99KL6k9YHehZPZqfxJ7+uFXvtpFWNBgPEo= github.com/aws/aws-sdk-go-v2/service/iam v1.28.2/go.mod h1:KJbw+8r7gZfjF+OewOVhyEQKiJXJ/OM1F1r3aAvKS9M= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.10.3 h1:e3PCNeEaev/ZF01cQyNZgmYE9oYYePIMJs2mWSKG514= @@ -90,6 +98,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.26.2 h1:fFrLsy08wEbAisqW3KDl/cPHrF43 github.com/aws/aws-sdk-go-v2/service/sts v1.26.2/go.mod h1:7Ld9eTqocTvJqqJ5K/orbSDwmGcpRdlDiLjz2DO+SL8= github.com/aws/smithy-go v1.18.1 h1:pOdBTUfXNazOlxLrgeYalVnuTpKreACHtc62xLwIB3c= github.com/aws/smithy-go v1.18.1/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= +github.com/aws/smithy-go v1.19.0 h1:KWFKQV80DpP3vJrrA9sVAHQ5gc2z8i4EzrLhLlWXcBM= +github.com/aws/smithy-go v1.19.0/go.mod h1:NukqUGpCZIILqqiV0NIjeFh24kd/FAa4beRb6nbIUPE= 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/lateral-movement/ec2-send-ssh-public-key/main.go b/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key/main.go new file mode 100644 index 00000000..b91de2a8 --- /dev/null +++ b/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key/main.go @@ -0,0 +1,91 @@ +package aws + +import ( + "context" + _ "embed" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/ec2instanceconnect" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "log" + "strings" +) + +//go:embed my_key.pub +var publicSSHKey string + +//go:embed main.tf +var tf []byte + +func init() { + const codeBlock = "```" + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "aws.lateral-movement.ec2-instance-connect", + FriendlyName: "Usage of EC2 Instance Connect on multiple instances", + IsSlow: true, + Description: ` +Simulates an attacker pushing an SSH public key to multiple EC2 instances, which then will allow anyone with the corresponding private key to +connect directly to the systems via SSH. + +Warm-up: + +- Create multiple EC2 instances and a VPC (takes a few minutes). + +Detonation: + +- Adds a public SSH key to the EC2 for 60 seconds. + +References: + +- https://securitylabs.datadoghq.com/articles/tales-from-the-cloud-trenches-ecs-crypto-mining/#hands-on-keyboard-activity-begins +- https://sysdig.com/blog/2023-global-cloud-threat-report/ +`, + Detection: ` +Identify, through CloudTrail's SendSSHPublicKey event, when a user is adding an SSH key to multiple EC2 instances. Sample event: + +` + codeBlock + ` +{ + "eventSource": "ec2-instance-connect.amazonaws.com", + "eventName": "SendSSHPublicKey", + "requestParameters": { + "instanceId": "i-123456", + "instanceOSUser": "ec2-user", + "sSHPublicKey": "ssh-ed25519 ..." + } +} +` + codeBlock + ` +`, + Platform: stratus.AWS, + PrerequisitesTerraformCode: tf, + IsIdempotent: true, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.LateralMovement}, + Detonate: detonate, + }) +} + +func detonate(params map[string]string, providers stratus.CloudProviders) error { + ec2instanceconnectClient := ec2instanceconnect.NewFromConfig(providers.AWS().GetConnection()) + instanceIDs := strings.Split(params["instance_ids"], ",") + + for _, instanceID := range instanceIDs { + cleanInstanceID := strings.Trim(instanceID, " \"\n\r") + err := sendSSHPublicKey(ec2instanceconnectClient, cleanInstanceID, "ec2-user", publicSSHKey) + if err != nil { + return fmt.Errorf("failed to send SSH public key to instance %s: %v", cleanInstanceID, err) + } + + log.Printf("SSH public key successfully added to instance %s", cleanInstanceID) + } + + return nil +} + +func sendSSHPublicKey(ec2instanceconnectClient *ec2instanceconnect.Client, instanceId, instanceOSUser, sshPublicKey string) error { + _, err := ec2instanceconnectClient.SendSSHPublicKey(context.Background(), &ec2instanceconnect.SendSSHPublicKeyInput{ + InstanceId: &instanceId, + InstanceOSUser: &instanceOSUser, + SSHPublicKey: &sshPublicKey, + }) + + return err +} diff --git a/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key/main.tf b/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key/main.tf new file mode 100644 index 00000000..d9eb7908 --- /dev/null +++ b/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key/main.tf @@ -0,0 +1,129 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 4.0" + } + } +} + +provider "aws" { + skip_region_validation = true + skip_credentials_validation = true + default_tags { + tags = { + StratusRedTeam = true + } + } +} + +locals { + resource_prefix = "stratus-red-team-ec2-sshpublickey-lateral-movement" +} + +variable "instance_count" { + description = "Number of instances to create" + default = 3 +} + +data "aws_availability_zones" "available" { + state = "available" +} + +module "vpc" { + source = "terraform-aws-modules/vpc/aws" + version = "~> 3.0" + + name = "${local.resource_prefix}-vpc" + cidr = "10.0.0.0/16" + + azs = [data.aws_availability_zones.available.names[0]] + private_subnets = ["10.0.1.0/24"] + public_subnets = ["10.0.128.0/24"] + + map_public_ip_on_launch = false + enable_nat_gateway = true + + tags = { + StratusRedTeam = true + } +} + +data "aws_ami" "amazon-2" { + most_recent = true + + filter { + name = "name" + values = ["amzn2-ami-hvm-*-x86_64-ebs"] + } + owners = ["amazon"] +} + +resource "aws_network_interface" "iface" { + count = var.instance_count + subnet_id = module.vpc.private_subnets[0] + + private_ips = [format("10.0.1.%d", count.index + 10)] +} + +resource "aws_iam_role" "instance-role" { + name = "${local.resource_prefix}-role" + path = "/" + + assume_role_policy = <