diff --git a/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md
new file mode 100755
index 00000000..4a39977e
--- /dev/null
+++ b/docs/attack-techniques/AWS/aws.execution.ssm-start-session.md
@@ -0,0 +1,61 @@
+---
+title: Usage of ssm:StartSession on multiple instances
+---
+
+# Usage of ssm:StartSession on multiple instances
+
+ slow
+ idempotent
+
+Platform: AWS
+
+## MITRE ATT&CK Tactics
+
+
+- Execution
+
+## 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://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
+
+
+## Instructions
+
+```bash title="Detonate with Stratus Red Team"
+stratus detonate aws.execution.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..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
diff --git a/docs/attack-techniques/list.md b/docs/attack-techniques/list.md
index 2203c1d9..6a6bdfd5 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) | 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 cbd16188..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
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
new file mode 100644
index 00000000..810ee5fd
--- /dev/null
+++ b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go
@@ -0,0 +1,105 @@
+package aws
+
+import (
+ "context"
+ _ "embed"
+ "fmt"
+ "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"
+)
+
+//go:embed main.tf
+var tf []byte
+
+func init() {
+ const codeBlock = "```"
+ stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
+ 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.
+
+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://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
+`,
+ 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.Execution},
+ Detonate: detonate,
+ })
+}
+
+func detonate(params map[string]string, providers stratus.CloudProviders) error {
+ ssmClient := ssm.NewFromConfig(providers.AWS().GetConnection())
+ 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 {
+ 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)
+ }
+ 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{
+ 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/attacktechniques/aws/execution/ssm-start-session/main.tf b/v2/internal/attacktechniques/aws/execution/ssm-start-session/main.tf
new file mode 100644
index 00000000..23f19248
--- /dev/null
+++ b/v2/internal/attacktechniques/aws/execution/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 = <