Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New technique: Usage of ssm:SendCommand on multiple instances #482

Merged
merged 3 commits into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions docs/attack-techniques/AWS/aws.execution.ssm-send-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
title: Usage of ssm:SendCommand on multiple instances
---

# Usage of ssm:SendCommand 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) to execute commands through SendCommand on 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>:

- Runs <code>ssm:SendCommand</code> on several EC2 instances, to execute the command <code>echo "id=$(id), hostname=$(hostname)"</code> on each of them.

References:

- https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/#send-command
- https://www.chrisfarris.com/post/aws-ir/
- https://www.invictus-ir.com/news/aws-cloudtrail-cheat-sheet
- https://securitycafe.ro/2023/01/17/aws-post-explitation-with-ssm-sendcommand/


## Instructions

```bash title="Detonate with Stratus Red Team"
stratus detonate aws.execution.ssm-send-command
```
## Detection


Identify, through CloudTrail's <code>SendCommand</code> event, especially when <code>requestParameters.instanceIds</code> contains several instances. Sample event:

```json
{
"eventSource": "ssm.amazonaws.com",
"eventName": "SendCommand",
"requestParameters": {
"instanceIds": [
"i-0f364762ca43f9661",
"i-0a86d1f61db2b9b5d",
"i-08a69bfbe21c67e70"
],
"documentName": "AWS-RunShellScript",
"parameters": "HIDDEN_DUE_TO_SECURITY_REASONS",
"interactive": false
}
}
```

While this technique uses a single call to <code>ssm:SendCommand</code> on several instances, an attacker may use one call per instance to execute commands on. In that case, the <code>SendCommand</code> event will be emitted for each call.


2 changes: 2 additions & 0 deletions docs/attack-techniques/AWS/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:SendCommand on multiple instances](./aws.execution.ssm-send-command.md)

- [Usage of ssm:StartSession on multiple instances](./aws.execution.ssm-start-session.md)


Expand Down
1 change: 1 addition & 0 deletions docs/attack-techniques/list.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:SendCommand on multiple instances](./AWS/aws.execution.ssm-send-command.md) | [AWS](./AWS/index.md) | Execution |
| [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 |
Expand Down
7 changes: 7 additions & 0 deletions docs/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,13 @@ AWS:
- Privilege Escalation
platform: AWS
isIdempotent: true
- id: aws.execution.ssm-send-command
name: Usage of ssm:SendCommand on multiple instances
isSlow: true
mitreAttackTactics:
- Execution
platform: AWS
isIdempotent: true
- id: aws.execution.ssm-start-session
name: Usage of ssm:StartSession on multiple instances
isSlow: true
Expand Down
123 changes: 123 additions & 0 deletions v2/internal/attacktechniques/aws/execution/ssm-send-command/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package aws

import (
"context"
_ "embed"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"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"
"time"
)

//go:embed main.tf
var tf []byte

const commandToExecute = `echo "id=$(id), hostname=$(hostname)"`

func init() {
const codeBlock = "```"
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: "aws.execution.ssm-send-command",
FriendlyName: "Usage of ssm:SendCommand on multiple instances",
IsSlow: true,
Description: `
Simulates an attacker utilizing AWS Systems Manager (SSM) to execute commands through SendCommand on multiple EC2 instances.

Warm-up:

- Create multiple EC2 instances and a VPC (takes a few minutes).

Detonation:

- Runs <code>ssm:SendCommand</code> on several EC2 instances, to execute the command <code>` + commandToExecute + `</code> on each of them.

References:

- https://hackingthe.cloud/aws/post_exploitation/run_shell_commands_on_ec2/#send-command
- https://www.chrisfarris.com/post/aws-ir/
- https://www.invictus-ir.com/news/aws-cloudtrail-cheat-sheet
- https://securitycafe.ro/2023/01/17/aws-post-explitation-with-ssm-sendcommand/
`,
Detection: `
Identify, through CloudTrail's <code>SendCommand</code> event, especially when <code>requestParameters.instanceIds</code> contains several instances. Sample event:

` + codeBlock + `json
{
"eventSource": "ssm.amazonaws.com",
"eventName": "SendCommand",
"requestParameters": {
"instanceIds": [
"i-0f364762ca43f9661",
"i-0a86d1f61db2b9b5d",
"i-08a69bfbe21c67e70"
],
"documentName": "AWS-RunShellScript",
"parameters": "HIDDEN_DUE_TO_SECURITY_REASONS",
"interactive": false
}
}
` + codeBlock + `

While this technique uses a single call to <code>ssm:SendCommand</code> on several instances, an attacker may use one call per instance to execute commands on. In that case, the <code>SendCommand</code> event will be emitted for each call.
`,
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("Executing command '" + commandToExecute + "' through ssm:SendCommand on all instances...")

result, err := ssmClient.SendCommand(context.Background(), &ssm.SendCommandInput{
InstanceIds: instanceIDs,
DocumentName: aws.String("AWS-RunShellScript"),
Parameters: map[string][]string{
"commands": {commandToExecute},
},
})
if err != nil {
return fmt.Errorf("failed to send command to instances: %v", err)
}

commandId := result.Command.CommandId
log.Println("Command sent successfully. Command ID: " + *commandId)
log.Println("Waiting for command outputs")

for _, instanceID := range instanceIDs {
result, err := ssm.NewCommandExecutedWaiter(ssmClient).WaitForOutput(context.Background(), &ssm.GetCommandInvocationInput{
InstanceId: &instanceID,
CommandId: commandId,
}, 2*time.Minute)
if err != nil {
return fmt.Errorf("failed to execute command on instance %s: %v", instanceID, err)
}
log.Print(fmt.Sprintf("Successfully executed on instance %s. Output: %s", instanceID, *result.StandardOutputContent))
}

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 v2/internal/attacktechniques/aws/execution/ssm-send-command/main.tf
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-send-command-execution"
}

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
}
1 change: 1 addition & 0 deletions v2/internal/attacktechniques/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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-send-command"
_ "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"
Expand Down
Loading