From 2b6d40767aced1fdf4f2d95a73525d025ed23ac9 Mon Sep 17 00:00:00 2001 From: adanalvarez Date: Sat, 3 Feb 2024 00:31:58 +0100 Subject: [PATCH 1/8] add aws ssm-start-session attack technique --- .../aws.lateral-movement.ssm-start-session.md | 60 ++++++++ docs/attack-techniques/AWS/index.md | 2 + docs/attack-techniques/list.md | 1 + docs/index.yaml | 7 + .../ssm-start-session/main.go | 81 +++++++++++ .../ssm-start-session/main.tf | 129 ++++++++++++++++++ v2/internal/attacktechniques/main.go | 1 + 7 files changed, 281 insertions(+) create mode 100755 docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md create mode 100644 v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go create mode 100644 v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.tf diff --git a/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md b/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md new file mode 100755 index 00000000..aa460233 --- /dev/null +++ b/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md @@ -0,0 +1,60 @@ +--- +title: Usage of AWS Systems Manager console to start a session on multiple instances +--- + +# Usage of AWS Systems Manager console to start a session on multiple instances + + slow + idempotent + +Platform: AWS + +## MITRE ATT&CK Tactics + + +- Lateral Movement + +## Description + + +Simulates an attacker utilizing AWS Systems Manager (SSM) StartSession to gain unauthorized interactive access to multiple EC2 instances. + +Warm-up: + +- Create multiple EC2 instances and a VPC (takes a few minutes). + +Detonation: + +- Initiates a connection to the EC2 for a Session Manager session. + +References: + +- https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/#session-manager + + +## Instructions + +```bash title="Detonate with Stratus Red Team" +stratus detonate aws.lateral-movement.ssm-start-session +``` +## Detection + + +Identify, through CloudTrail's StartSession event, when a user is starting an interactive session to multiple EC2 instances. Sample event: + +``` +{ + "eventSource": "ssm.amazonaws.com", + "eventName": "StartSession", + "requestParameters": { + "target": "i-123456" + }, + "responseElements": { + "sessionId": "...", + "tokenValue": "Value hidden due to security reasons.", + "streamUrl": "wss://ssmmessages.eu-west-1.amazonaws.com/v1/data-channel/..." + }, +} +``` + + diff --git a/docs/attack-techniques/AWS/index.md b/docs/attack-techniques/AWS/index.md index 4fcddb7c..5fac98e3 100755 --- a/docs/attack-techniques/AWS/index.md +++ b/docs/attack-techniques/AWS/index.md @@ -79,6 +79,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Usage of EC2 Instance Connect on multiple instances](./aws.lateral-movement.ec2-instance-connect.md) +- [Usage of AWS Systems Manager console to start a session on multiple instances](./aws.lateral-movement.ssm-start-session.md) + ## Persistence diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 2203c1d9..0840e185 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -35,6 +35,7 @@ This page contains the list of all Stratus Attack Techniques. | [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 | +| [Usage of AWS Systems Manager console to start a session on multiple instances](./AWS/aws.lateral-movement.ssm-start-session.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 cbd16188..05fbd8e9 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -190,6 +190,13 @@ AWS: - Lateral Movement platform: AWS isIdempotent: true + - id: aws.lateral-movement.ssm-start-session + name: Usage of AWS Systems Manager console to start a session 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/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go b/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go new file mode 100644 index 00000000..bebadfe2 --- /dev/null +++ b/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go @@ -0,0 +1,81 @@ +package aws + +import ( + "context" + _ "embed" + "fmt" + "github.com/aws/aws-sdk-go-v2/service/ssm" + "github.com/datadog/stratus-red-team/v2/pkg/stratus" + "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" + "strings" +) + +//go:embed main.tf +var tf []byte + +func init() { + const codeBlock = "```" + stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ + ID: "aws.lateral-movement.ssm-start-session", + FriendlyName: "Usage of AWS Systems Manager console to start a session on multiple instances", + IsSlow: true, + Description: ` +Simulates an attacker utilizing AWS Systems Manager (SSM) StartSession to gain unauthorized interactive access to multiple EC2 instances. + +Warm-up: + +- Create multiple EC2 instances and a VPC (takes a few minutes). + +Detonation: + +- Initiates a connection to the EC2 for a Session Manager session. + +References: + +- https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/#session-manager +`, + Detection: ` +Identify, through CloudTrail's StartSession event, when a user is starting an interactive session to multiple EC2 instances. Sample event: + +` + codeBlock + ` +{ + "eventSource": "ssm.amazonaws.com", + "eventName": "StartSession", + "requestParameters": { + "target": "i-123456" + }, + "responseElements": { + "sessionId": "...", + "tokenValue": "Value hidden due to security reasons.", + "streamUrl": "wss://ssmmessages.eu-west-1.amazonaws.com/v1/data-channel/..." + }, +} +` + 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 { + ssmClient := ssm.NewFromConfig(providers.AWS().GetConnection()) + instanceIDs := strings.Split(params["instance_ids"], ",") + + for _, instanceID := range instanceIDs { + cleanInstanceID := strings.Trim(instanceID, " \"\n\r") + _, err := ssmClient.StartSession(context.TODO(), &ssm.StartSessionInput{ + Target: &cleanInstanceID, + }) + + if err != nil { + return fmt.Errorf("failed to start session with instance %s: %v", cleanInstanceID, err) + } + + fmt.Printf("Session started with instance %s\n", cleanInstanceID) + } + + return nil +} \ No newline at end of file diff --git a/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.tf b/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.tf new file mode 100644 index 00000000..23f19248 --- /dev/null +++ b/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/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-ssm-start-session-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 = < Date: Wed, 7 Feb 2024 08:12:20 +0100 Subject: [PATCH 2/8] Add reference for real-world StartSession ussage --- .../AWS/aws.lateral-movement.ssm-start-session.md | 1 + .../aws/lateral-movement/ssm-start-session/main.go | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md b/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md index aa460233..db92588a 100755 --- a/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md +++ b/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md @@ -30,6 +30,7 @@ Simulates an attacker utilizing AWS Systems Manager (SSM) StartSession to gain u References: - https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/#session-manager +- https://awstip.com/responding-to-an-attack-in-aws-9048a1a551ac ## Instructions diff --git a/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go b/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go index bebadfe2..7d1a863e 100644 --- a/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go +++ b/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go @@ -33,6 +33,7 @@ Detonation: References: - https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/#session-manager +- https://awstip.com/responding-to-an-attack-in-aws-9048a1a551ac `, Detection: ` Identify, through CloudTrail's StartSession event, when a user is starting an interactive session to multiple EC2 instances. Sample event: From 928ceb2b39bd9cab777c85c78b94de6891e29b45 Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Wed, 7 Feb 2024 09:53:35 +0100 Subject: [PATCH 3/8] Map to exfiltration instead of lateral movement --- ...tart-session.md => aws.execution.ssm-start-session.md} | 6 +++--- docs/attack-techniques/AWS/index.md | 4 ++-- docs/attack-techniques/list.md | 2 +- docs/index.yaml | 8 ++++---- .../ssm-start-session/main.go | 8 ++++---- .../ssm-start-session/main.tf | 0 v2/internal/attacktechniques/main.go | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) rename docs/attack-techniques/AWS/{aws.lateral-movement.ssm-start-session.md => aws.execution.ssm-start-session.md} (87%) rename v2/internal/attacktechniques/aws/{lateral-movement => execution}/ssm-start-session/main.go (90%) rename v2/internal/attacktechniques/aws/{lateral-movement => execution}/ssm-start-session/main.tf (100%) diff --git a/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md similarity index 87% rename from docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md rename to docs/attack-techniques/AWS/aws.execution.ssm-start-session.md index db92588a..cde965be 100755 --- a/docs/attack-techniques/AWS/aws.lateral-movement.ssm-start-session.md +++ b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md @@ -1,8 +1,8 @@ --- -title: Usage of AWS Systems Manager console to start a session on multiple instances +title: Usage of ssm:StartSession on multiple instances --- -# Usage of AWS Systems Manager console to start a session on multiple instances +# Usage of ssm:StartSession on multiple instances slow idempotent @@ -36,7 +36,7 @@ References: ## Instructions ```bash title="Detonate with Stratus Red Team" -stratus detonate aws.lateral-movement.ssm-start-session +stratus detonate aws.execution.ssm-start-session ``` ## Detection diff --git a/docs/attack-techniques/AWS/index.md b/docs/attack-techniques/AWS/index.md index 5fac98e3..cb37f91d 100755 --- a/docs/attack-techniques/AWS/index.md +++ b/docs/attack-techniques/AWS/index.md @@ -77,9 +77,9 @@ Note that some Stratus attack techniques may correspond to more than a single AT ## Lateral Movement -- [Usage of EC2 Instance Connect on multiple instances](./aws.lateral-movement.ec2-instance-connect.md) +- [Usage of ssm:StartSession on multiple instances](./aws.execution.ssm-start-session.md) -- [Usage of AWS Systems Manager console to start a session on multiple instances](./aws.lateral-movement.ssm-start-session.md) +- [Usage of EC2 Instance Connect on multiple instances](./aws.lateral-movement.ec2-instance-connect.md) ## Persistence diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 0840e185..94637380 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -25,6 +25,7 @@ This page contains the list of all Stratus Attack Techniques. | [Download EC2 Instance User Data](./AWS/aws.discovery.ec2-download-user-data.md) | [AWS](./AWS/index.md) | Discovery | | [Launch Unusual EC2 instances](./AWS/aws.execution.ec2-launch-unusual-instances.md) | [AWS](./AWS/index.md) | Execution | | [Execute Commands on EC2 Instance via User Data](./AWS/aws.execution.ec2-user-data.md) | [AWS](./AWS/index.md) | Execution, Privilege Escalation | +| [Usage of ssm:StartSession on multiple instances](./AWS/aws.execution.ssm-start-session.md) | [AWS](./AWS/index.md) | Lateral Movement | | [Open Ingress Port 22 on a Security Group](./AWS/aws.exfiltration.ec2-security-group-open-port-22-ingress.md) | [AWS](./AWS/index.md) | Exfiltration | | [Exfiltrate an AMI by Sharing It](./AWS/aws.exfiltration.ec2-share-ami.md) | [AWS](./AWS/index.md) | Exfiltration | | [Exfiltrate EBS Snapshot by Sharing It](./AWS/aws.exfiltration.ec2-share-ebs-snapshot.md) | [AWS](./AWS/index.md) | Exfiltration | @@ -35,7 +36,6 @@ This page contains the list of all Stratus Attack Techniques. | [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 | -| [Usage of AWS Systems Manager console to start a session on multiple instances](./AWS/aws.lateral-movement.ssm-start-session.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 05fbd8e9..d8cbb09f 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -183,15 +183,15 @@ AWS: platform: AWS isIdempotent: true Lateral Movement: - - id: aws.lateral-movement.ec2-instance-connect - name: Usage of EC2 Instance Connect on multiple instances + - id: aws.execution.ssm-start-session + name: Usage of ssm:StartSession on multiple instances isSlow: true mitreAttackTactics: - Lateral Movement platform: AWS isIdempotent: true - - id: aws.lateral-movement.ssm-start-session - name: Usage of AWS Systems Manager console to start a session on multiple instances + - id: aws.lateral-movement.ec2-instance-connect + name: Usage of EC2 Instance Connect on multiple instances isSlow: true mitreAttackTactics: - Lateral Movement diff --git a/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go similarity index 90% rename from v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go rename to v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go index 7d1a863e..4a3ae111 100644 --- a/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.go +++ b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go @@ -16,8 +16,8 @@ var tf []byte func init() { const codeBlock = "```" stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{ - ID: "aws.lateral-movement.ssm-start-session", - FriendlyName: "Usage of AWS Systems Manager console to start a session on multiple instances", + ID: "aws.execution.ssm-start-session", + FriendlyName: "Usage of ssm:StartSession on multiple instances", IsSlow: true, Description: ` Simulates an attacker utilizing AWS Systems Manager (SSM) StartSession to gain unauthorized interactive access to multiple EC2 instances. @@ -56,7 +56,7 @@ Identify, through CloudTrail's StartSession event, when a user is s Platform: stratus.AWS, PrerequisitesTerraformCode: tf, IsIdempotent: true, - MitreAttackTactics: []mitreattack.Tactic{mitreattack.LateralMovement}, + MitreAttackTactics: []mitreattack.Tactic{mitreattack.Execution}, Detonate: detonate, }) } @@ -79,4 +79,4 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error } return nil -} \ No newline at end of file +} diff --git a/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.tf b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.tf similarity index 100% rename from v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session/main.tf rename to v2/internal/attacktechniques/aws/execution/ssm-start-session/main.tf diff --git a/v2/internal/attacktechniques/main.go b/v2/internal/attacktechniques/main.go index b794ea3f..4458c982 100644 --- a/v2/internal/attacktechniques/main.go +++ b/v2/internal/attacktechniques/main.go @@ -17,6 +17,7 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/discovery/ec2-get-user-data" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/execution/ec2-launch-unusual-instances" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/execution/ec2-user-data" + _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/execution/ssm-start-session" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/exfiltration/ec2-security-group-open-port-22-ingress" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/exfiltration/ec2-share-ami" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/exfiltration/ec2-share-ebs-snapshot" @@ -27,7 +28,6 @@ import ( _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/impact/s3-ransomware-individual-deletion" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/initial-access/console-login-without-mfa" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/lateral-movement/ec2-send-ssh-public-key" - _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/lateral-movement/ssm-start-session" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/iam-backdoor-role" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/iam-backdoor-user" _ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/iam-create-admin-user" From 81df1b73613a51215ce0221d3fc6389d38929473 Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Wed, 7 Feb 2024 09:59:09 +0100 Subject: [PATCH 4/8] Minor code change --- .../attacktechniques/aws/execution/ssm-start-session/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go index 4a3ae111..52a4dfaf 100644 --- a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go +++ b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go @@ -32,8 +32,8 @@ Detonation: References: +- https://awstip.com/responding-to-an-attack-in-aws-9048a1a551ac (evidence of usage in the wild) - https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/#session-manager -- https://awstip.com/responding-to-an-attack-in-aws-9048a1a551ac `, Detection: ` Identify, through CloudTrail's StartSession event, when a user is starting an interactive session to multiple EC2 instances. Sample event: @@ -67,7 +67,7 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error for _, instanceID := range instanceIDs { cleanInstanceID := strings.Trim(instanceID, " \"\n\r") - _, err := ssmClient.StartSession(context.TODO(), &ssm.StartSessionInput{ + _, err := ssmClient.StartSession(context.Background(), &ssm.StartSessionInput{ Target: &cleanInstanceID, }) From 44a2992ff753ad4570ee174c4adf40f16b3d2b95 Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Wed, 7 Feb 2024 17:15:43 +0100 Subject: [PATCH 5/8] Regenerate docs and small code change to terminate sessions --- .../AWS/aws.execution.ssm-start-session.md | 4 ++-- docs/attack-techniques/AWS/index.md | 4 ++-- docs/attack-techniques/list.md | 2 +- docs/index.yaml | 14 +++++++------- .../aws/execution/ssm-start-session/main.go | 12 +++++++++--- 5 files changed, 21 insertions(+), 15 deletions(-) diff --git a/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md index cde965be..4a39977e 100755 --- a/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md +++ b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md @@ -12,7 +12,7 @@ Platform: AWS ## MITRE ATT&CK Tactics -- Lateral Movement +- Execution ## Description @@ -29,8 +29,8 @@ Simulates an attacker utilizing AWS Systems Manager (SSM) StartSession to gain u References: +- https://awstip.com/responding-to-an-attack-in-aws-9048a1a551ac (evidence of usage in the wild) - https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/#session-manager -- https://awstip.com/responding-to-an-attack-in-aws-9048a1a551ac ## Instructions diff --git a/docs/attack-techniques/AWS/index.md b/docs/attack-techniques/AWS/index.md index cb37f91d..195e94ad 100755 --- a/docs/attack-techniques/AWS/index.md +++ b/docs/attack-techniques/AWS/index.md @@ -47,6 +47,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT - [Execute Commands on EC2 Instance via User Data](./aws.execution.ec2-user-data.md) +- [Usage of ssm:StartSession on multiple instances](./aws.execution.ssm-start-session.md) + ## Exfiltration @@ -77,8 +79,6 @@ Note that some Stratus attack techniques may correspond to more than a single AT ## Lateral Movement -- [Usage of ssm:StartSession on multiple instances](./aws.execution.ssm-start-session.md) - - [Usage of EC2 Instance Connect on multiple instances](./aws.lateral-movement.ec2-instance-connect.md) diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md index 94637380..6a6bdfd5 100755 --- a/docs/attack-techniques/list.md +++ b/docs/attack-techniques/list.md @@ -25,7 +25,7 @@ This page contains the list of all Stratus Attack Techniques. | [Download EC2 Instance User Data](./AWS/aws.discovery.ec2-download-user-data.md) | [AWS](./AWS/index.md) | Discovery | | [Launch Unusual EC2 instances](./AWS/aws.execution.ec2-launch-unusual-instances.md) | [AWS](./AWS/index.md) | Execution | | [Execute Commands on EC2 Instance via User Data](./AWS/aws.execution.ec2-user-data.md) | [AWS](./AWS/index.md) | Execution, Privilege Escalation | -| [Usage of ssm:StartSession on multiple instances](./AWS/aws.execution.ssm-start-session.md) | [AWS](./AWS/index.md) | Lateral Movement | +| [Usage of ssm:StartSession on multiple instances](./AWS/aws.execution.ssm-start-session.md) | [AWS](./AWS/index.md) | Execution | | [Open Ingress Port 22 on a Security Group](./AWS/aws.exfiltration.ec2-security-group-open-port-22-ingress.md) | [AWS](./AWS/index.md) | Exfiltration | | [Exfiltrate an AMI by Sharing It](./AWS/aws.exfiltration.ec2-share-ami.md) | [AWS](./AWS/index.md) | Exfiltration | | [Exfiltrate EBS Snapshot by Sharing It](./AWS/aws.exfiltration.ec2-share-ebs-snapshot.md) | [AWS](./AWS/index.md) | Exfiltration | diff --git a/docs/index.yaml b/docs/index.yaml index d8cbb09f..3ced0577 100644 --- a/docs/index.yaml +++ b/docs/index.yaml @@ -116,6 +116,13 @@ AWS: - Privilege Escalation platform: AWS isIdempotent: true + - id: aws.execution.ssm-start-session + name: Usage of ssm:StartSession on multiple instances + isSlow: true + mitreAttackTactics: + - Execution + platform: AWS + isIdempotent: true Exfiltration: - id: aws.exfiltration.ec2-security-group-open-port-22-ingress name: Open Ingress Port 22 on a Security Group @@ -183,13 +190,6 @@ AWS: platform: AWS isIdempotent: true Lateral Movement: - - id: aws.execution.ssm-start-session - name: Usage of ssm:StartSession on multiple instances - isSlow: true - mitreAttackTactics: - - Lateral Movement - platform: AWS - isIdempotent: true - id: aws.lateral-movement.ec2-instance-connect name: Usage of EC2 Instance Connect on multiple instances isSlow: true diff --git a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go index 52a4dfaf..6df82a88 100644 --- a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go +++ b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go @@ -67,15 +67,21 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error for _, instanceID := range instanceIDs { cleanInstanceID := strings.Trim(instanceID, " \"\n\r") - _, err := ssmClient.StartSession(context.Background(), &ssm.StartSessionInput{ + session, err := ssmClient.StartSession(context.Background(), &ssm.StartSessionInput{ Target: &cleanInstanceID, }) - if err != nil { return fmt.Errorf("failed to start session with instance %s: %v", cleanInstanceID, err) } - fmt.Printf("Session started with instance %s\n", cleanInstanceID) + + // Terminate the session to not leave it hanging + _, err = ssmClient.TerminateSession(context.Background(), &ssm.TerminateSessionInput{ + SessionId: session.SessionId, + }) + if err != nil { + return fmt.Errorf("failed to terminate SSM session with instance %s: %v", cleanInstanceID, err) + } } return nil From 6a7b370611f4a6df80777b8e5fe8c4de338f8832 Mon Sep 17 00:00:00 2001 From: adanalvarez Date: Wed, 7 Feb 2024 22:17:28 +0100 Subject: [PATCH 6/8] Fix error 400 TargetNotConnected --- .../aws/execution/ssm-start-session/main.go | 43 +++++++++++++------ 1 file changed, 29 insertions(+), 14 deletions(-) diff --git a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go index 6df82a88..f4bd9ee2 100644 --- a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go +++ b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go @@ -4,6 +4,7 @@ import ( "context" _ "embed" "fmt" + "time" "github.com/aws/aws-sdk-go-v2/service/ssm" "github.com/datadog/stratus-red-team/v2/pkg/stratus" "github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack" @@ -64,25 +65,39 @@ Identify, through CloudTrail's StartSession event, when a user is s func detonate(params map[string]string, providers stratus.CloudProviders) error { ssmClient := ssm.NewFromConfig(providers.AWS().GetConnection()) instanceIDs := strings.Split(params["instance_ids"], ",") + const maxRetries = 5 // Maximum number of retries + const retryDelay = 10 * time.Second // Delay between retries for _, instanceID := range instanceIDs { + success := false cleanInstanceID := strings.Trim(instanceID, " \"\n\r") - session, err := ssmClient.StartSession(context.Background(), &ssm.StartSessionInput{ - Target: &cleanInstanceID, - }) - if err != nil { - return fmt.Errorf("failed to start session with instance %s: %v", cleanInstanceID, err) + + for attempt := 0; attempt < maxRetries; attempt++ { + session, err := ssmClient.StartSession(context.Background(), &ssm.StartSessionInput{ + Target: &cleanInstanceID, + }) + if err != nil { + fmt.Printf("Attempt %d: StartSession failed for instance %s, retrying in %v...\n", attempt+1, cleanInstanceID, retryDelay) + time.Sleep(retryDelay) + continue + } + + fmt.Printf("Session started with instance %s\n", cleanInstanceID) + success = true + + // Attempt to terminate the session to not leave it hanging + _, err = ssmClient.TerminateSession(context.Background(), &ssm.TerminateSessionInput{ + SessionId: session.SessionId, + }) + if err != nil { + return fmt.Errorf("failed to terminate SSM session with instance %s: %v", cleanInstanceID, err) + } + break } - fmt.Printf("Session started with instance %s\n", cleanInstanceID) - - // Terminate the session to not leave it hanging - _, err = ssmClient.TerminateSession(context.Background(), &ssm.TerminateSessionInput{ - SessionId: session.SessionId, - }) - if err != nil { - return fmt.Errorf("failed to terminate SSM session with instance %s: %v", cleanInstanceID, err) + + if !success { + return fmt.Errorf("failed to start session with instance %s after %d retries", cleanInstanceID, maxRetries) } } - return nil } From 1a2c8718bfa76527d852193820a351194293f8f1 Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Fri, 9 Feb 2024 11:54:12 +0100 Subject: [PATCH 7/8] Refactor to reuse existing code waiting for instances to be available in SSM --- .../ec2-steal-instance-credentials/main.go | 31 +-------- .../aws/execution/ssm-start-session/main.go | 64 ++++++++++--------- v2/internal/utils/aws_utils.go | 64 +++++++++++++++++-- 3 files changed, 94 insertions(+), 65 deletions(-) diff --git a/v2/internal/attacktechniques/aws/credential-access/ec2-steal-instance-credentials/main.go b/v2/internal/attacktechniques/aws/credential-access/ec2-steal-instance-credentials/main.go index f0f7bc09..20628321 100644 --- a/v2/internal/attacktechniques/aws/credential-access/ec2-steal-instance-credentials/main.go +++ b/v2/internal/attacktechniques/aws/credential-access/ec2-steal-instance-credentials/main.go @@ -8,7 +8,6 @@ import ( "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/service/ec2" "github.com/aws/aws-sdk-go-v2/service/ssm" - "github.com/aws/aws-sdk-go-v2/service/ssm/types" "github.com/aws/aws-sdk-go-v2/service/sts" "github.com/datadog/stratus-red-team/v2/internal/utils" "github.com/datadog/stratus-red-team/v2/pkg/stratus" @@ -61,7 +60,7 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error instanceId := params["instance_id"] instanceRoleName := params["instance_role_name"] - if err := waitForInstanceToRegisterInSSM(ssmClient, instanceId); err != nil { + if err := utils.WaitForInstanceToRegisterInSSM(ssmClient, instanceId); err != nil { return err } @@ -117,31 +116,3 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error } return nil } - -// waitForInstanceToRegisterInSSM waits for an instance to be registered in SSM -// may be slow (60+ seconds) -func waitForInstanceToRegisterInSSM(ssmClient *ssm.Client, instanceId string) error { - log.Println("Waiting for instance " + instanceId + " to show up in AWS SSM") - for { - result, err := ssmClient.DescribeInstanceInformation(context.Background(), &ssm.DescribeInstanceInformationInput{ - Filters: []types.InstanceInformationStringFilter{ - {Key: aws.String("InstanceIds"), Values: []string{instanceId}}, - }, - }) - - if err != nil { - return err - } - - // When the instance isn't registered in SSM yet, it returns an empty array - // If the result we get back contains 1 instance and it has the right status, - // we're good to go! - instances := result.InstanceInformationList - if len(instances) == 1 && instances[0].PingStatus == types.PingStatusOnline { - log.Println("Instance " + instanceId + " is ready to go in SSM") - return nil - } - - time.Sleep(1 * time.Second) - } -} diff --git a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go index f4bd9ee2..b7a4d0a0 100644 --- a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go +++ b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go @@ -4,10 +4,11 @@ import ( "context" _ "embed" "fmt" - "time" "github.com/aws/aws-sdk-go-v2/service/ssm" + "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" + "log" "strings" ) @@ -64,40 +65,41 @@ Identify, through CloudTrail's StartSession event, when a user is s func detonate(params map[string]string, providers stratus.CloudProviders) error { ssmClient := ssm.NewFromConfig(providers.AWS().GetConnection()) - instanceIDs := strings.Split(params["instance_ids"], ",") - const maxRetries = 5 // Maximum number of retries - const retryDelay = 10 * time.Second // Delay between retries + instanceIDs := getInstanceIds(params) + + if err := utils.WaitForInstancesToRegisterInSSM(ssmClient, instanceIDs); err != nil { + return fmt.Errorf("failed to wait for instances to register in SSM: %v", err) + } + + log.Println("Instances are ready and registered in SSM!") + log.Println("Starting SSM sessions on each instance...") for _, instanceID := range instanceIDs { - success := false - cleanInstanceID := strings.Trim(instanceID, " \"\n\r") - - for attempt := 0; attempt < maxRetries; attempt++ { - session, err := ssmClient.StartSession(context.Background(), &ssm.StartSessionInput{ - Target: &cleanInstanceID, - }) - if err != nil { - fmt.Printf("Attempt %d: StartSession failed for instance %s, retrying in %v...\n", attempt+1, cleanInstanceID, retryDelay) - time.Sleep(retryDelay) - continue - } - - fmt.Printf("Session started with instance %s\n", cleanInstanceID) - success = true - - // Attempt to terminate the session to not leave it hanging - _, err = ssmClient.TerminateSession(context.Background(), &ssm.TerminateSessionInput{ - SessionId: session.SessionId, - }) - if err != nil { - return fmt.Errorf("failed to terminate SSM session with instance %s: %v", cleanInstanceID, err) - } - break + session, err := ssmClient.StartSession(context.Background(), &ssm.StartSessionInput{ + Target: &instanceID, + }) + if err != nil { + return fmt.Errorf("failed to start session with instance %s: %v", instanceID, err) } - - if !success { - return fmt.Errorf("failed to start session with instance %s after %d retries", cleanInstanceID, maxRetries) + fmt.Printf("Session started on instance %s\n", instanceID) + + // Attempt to terminate the session to not leave it hanging + _, err = ssmClient.TerminateSession(context.Background(), &ssm.TerminateSessionInput{ + SessionId: session.SessionId, + }) + if err != nil { + return fmt.Errorf("failed to terminate SSM session with instance %s: %v", instanceID, err) } } + return nil } + +func getInstanceIds(params map[string]string) []string { + instanceIds := strings.Split(params["instance_ids"], ",") + // iterate over instanceIds and remove \n, \r, spaces and " from each instanceId + for i, instanceId := range instanceIds { + instanceIds[i] = strings.Trim(instanceId, " \"\n\r") + } + return instanceIds +} diff --git a/v2/internal/utils/aws_utils.go b/v2/internal/utils/aws_utils.go index ab1d3ccd..ece1d277 100644 --- a/v2/internal/utils/aws_utils.go +++ b/v2/internal/utils/aws_utils.go @@ -9,11 +9,15 @@ import ( "github.com/aws/aws-sdk-go-v2/credentials/stscreds" "github.com/aws/aws-sdk-go-v2/feature/s3/manager" "github.com/aws/aws-sdk-go-v2/service/s3" - "github.com/aws/aws-sdk-go-v2/service/s3/types" + s3types "github.com/aws/aws-sdk-go-v2/service/s3/types" + "github.com/aws/aws-sdk-go-v2/service/ssm" + ssmtypes "github.com/aws/aws-sdk-go-v2/service/ssm/types" + "github.com/aws/aws-sdk-go-v2/service/sts" backoff "github.com/cenkalti/backoff/v4" "io" "log" + "strconv" "strings" "time" ) @@ -83,15 +87,15 @@ func IsErrorDueToEBSEncryptionByDefault(err error) bool { // S3 utils -func ListAllObjectVersions(s3Client *s3.Client, bucketName string) ([]types.ObjectIdentifier, error) { +func ListAllObjectVersions(s3Client *s3.Client, bucketName string) ([]s3types.ObjectIdentifier, error) { log.Println("Listing objects in bucket " + bucketName) - var result []types.ObjectIdentifier + var result []s3types.ObjectIdentifier objectVersions, err := s3Client.ListObjectVersions(context.Background(), &s3.ListObjectVersionsInput{Bucket: &bucketName}) if err != nil { return nil, fmt.Errorf("unable to list bucket objects: %w", err) } for _, objectVersion := range objectVersions.Versions { - result = append(result, types.ObjectIdentifier{Key: objectVersion.Key, VersionId: objectVersion.VersionId}) + result = append(result, s3types.ObjectIdentifier{Key: objectVersion.Key, VersionId: objectVersion.VersionId}) } return result, nil } @@ -130,3 +134,55 @@ func UploadFile(s3Client *s3.Client, bucketName string, filename string, content }) return err } + +// ec2 utils + +// WaitForInstancesToRegisterInSSM waits for a set of instances to be registered in SSM +// may be slow (60+ seconds) +func WaitForInstancesToRegisterInSSM(ssmClient *ssm.Client, instanceIds []string) error { + if len(instanceIds) == 1 { + log.Println("Waiting for instance" + instanceIds[0] + " to show up in AWS SSM") + } else { + log.Println("Waiting for " + strconv.Itoa(len(instanceIds)) + " instances to show up in AWS SSM. This can take a few minutes.") + } + + for { + time.Sleep(1 * time.Second) + result, err := ssmClient.DescribeInstanceInformation(context.Background(), &ssm.DescribeInstanceInformationInput{ + Filters: []ssmtypes.InstanceInformationStringFilter{ + {Key: aws.String("InstanceIds"), Values: instanceIds}, + }, + }) + + if err != nil { + return err + } + + instances := result.InstanceInformationList + if len(instances) < len(instanceIds) { + // Not enough instances registered yet, continue waiting + continue + } + + // Checked that all instances are ready in SSM + allInstancesOnline := true + for _, instance := range instances { + if instance.PingStatus != ssmtypes.PingStatusOnline { + allInstancesOnline = false + break + } + } + + if allInstancesOnline { + // If that's the case, great! + return nil + } + + // Otherwise, keep waiting + } +} + +// utility function for a single instance +func WaitForInstanceToRegisterInSSM(ssmClient *ssm.Client, instanceId string) error { + return WaitForInstancesToRegisterInSSM(ssmClient, []string{instanceId}) +} From f8bfb4de53ef3b03782925c2ff5020b4d5d3ab59 Mon Sep 17 00:00:00 2001 From: Christophe Tafani-Dereeper Date: Fri, 9 Feb 2024 12:08:06 +0100 Subject: [PATCH 8/8] formatting --- .../attacktechniques/aws/execution/ssm-start-session/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go index b7a4d0a0..810ee5fd 100644 --- a/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go +++ b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go @@ -81,7 +81,7 @@ func detonate(params map[string]string, providers stratus.CloudProviders) error if err != nil { return fmt.Errorf("failed to start session with instance %s: %v", instanceID, err) } - fmt.Printf("Session started on instance %s\n", instanceID) + fmt.Printf("\tSession started on instance %s\n", instanceID) // Attempt to terminate the session to not leave it hanging _, err = ssmClient.TerminateSession(context.Background(), &ssm.TerminateSessionInput{