-
Notifications
You must be signed in to change notification settings - Fork 220
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New attack technique: Usage of SSM StartSession on multiple instances (…
…#477) * add aws ssm-start-session attack technique * Add reference for real-world StartSession ussage * Map to exfiltration instead of lateral movement * Minor code change * Regenerate docs and small code change to terminate sessions * Fix error 400 TargetNotConnected * Refactor to reuse existing code waiting for instances to be available in SSM * formatting --------- Co-authored-by: Christophe Tafani-Dereeper <[email protected]>
- Loading branch information
1 parent
c86741b
commit 904c352
Showing
9 changed files
with
367 additions
and
34 deletions.
There are no files selected for viewing
61 changes: 61 additions & 0 deletions
61
docs/attack-techniques/AWS/aws.execution.ssm-start-session.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
--- | ||
title: Usage of ssm:StartSession on multiple instances | ||
--- | ||
|
||
# Usage of ssm:StartSession on multiple instances | ||
|
||
<span class="smallcaps w3-badge w3-orange w3-round w3-text-sand" title="This attack technique might be slow to warm up or detonate">slow</span> | ||
<span class="smallcaps w3-badge w3-blue w3-round w3-text-white" title="This attack technique can be detonated multiple times">idempotent</span> | ||
|
||
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. | ||
|
||
<span style="font-variant: small-caps;">Warm-up</span>: | ||
|
||
- Create multiple EC2 instances and a VPC (takes a few minutes). | ||
|
||
<span style="font-variant: small-caps;">Detonation</span>: | ||
|
||
- 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 <code>StartSession</code> 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/..." | ||
}, | ||
} | ||
``` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
105 changes: 105 additions & 0 deletions
105
v2/internal/attacktechniques/aws/execution/ssm-start-session/main.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <code>StartSession</code> 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 | ||
} |
129 changes: 129 additions & 0 deletions
129
v2/internal/attacktechniques/aws/execution/ssm-start-session/main.tf
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 = <<EOF | ||
{ | ||
"Version": "2012-10-17", | ||
"Statement": [ | ||
{ | ||
"Action": "sts:AssumeRole", | ||
"Principal": { | ||
"Service": "ec2.amazonaws.com" | ||
}, | ||
"Effect": "Allow", | ||
"Sid": "" | ||
} | ||
] | ||
} | ||
EOF | ||
} | ||
|
||
resource "aws_iam_role_policy_attachment" "rolepolicy" { | ||
role = aws_iam_role.instance-role.name | ||
policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore" | ||
} | ||
|
||
resource "aws_iam_instance_profile" "instance" { | ||
name = "${local.resource_prefix}-instance" | ||
role = aws_iam_role.instance-role.name | ||
} | ||
|
||
resource "aws_instance" "instance" { | ||
count = var.instance_count | ||
ami = data.aws_ami.amazon-2.id | ||
instance_type = "t3.micro" | ||
iam_instance_profile = aws_iam_instance_profile.instance.name | ||
|
||
network_interface { | ||
device_index = 0 | ||
network_interface_id = aws_network_interface.iface[count.index].id | ||
} | ||
|
||
tags = { | ||
Name = "${local.resource_prefix}-instance-${count.index}" | ||
} | ||
} | ||
|
||
output "instance_ids" { | ||
value = aws_instance.instance[*].id | ||
} | ||
|
||
output "display" { | ||
value = format("Instances ready: \n%s", join("\n", [ | ||
for i in aws_instance.instance : | ||
format(" %s in %s", i.id, data.aws_availability_zones.available.names[0]) | ||
])) | ||
} | ||
|
||
output "instance_role_name" { | ||
value = aws_iam_role.instance-role.name | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.