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