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 = <