Skip to content

Commit

Permalink
New Technique: T1651 (#3031)
Browse files Browse the repository at this point in the history
Co-authored-by: Carrie Roberts <[email protected]>
Co-authored-by: Bhavin Patel <[email protected]>
  • Loading branch information
3 people authored Jan 17, 2025
1 parent 16d1709 commit fd82e0a
Show file tree
Hide file tree
Showing 8 changed files with 322 additions and 0 deletions.
57 changes: 57 additions & 0 deletions atomics/T1651/T1651.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
attack_technique: T1651
display_name: Cloud Administration Command
atomic_tests:
- name: AWS Run Command (and Control)
description: |
This test simulates an adversary using the AWS Run Command service to execute commands on EC2 instances.
supported_platforms:
- iaas:aws
input_arguments:
access_key:
description: AWS Access Key
type: string
default: ""
secret_key:
description: AWS Secret Key
type: string
default: ""
session_token:
description: AWS Session Token
type: string
default: ""
profile:
description: AWS profile
type: string
default: ""
region:
description: AWS region to deploy the EC2 instance
type: string
default: us-east-2
dependency_executor_name: powershell
dependencies:
- description: |
The AWS PowerShell module must be installed.
prereq_command: |
try {if (Get-InstalledModule -Name AWSPowerShell -ErrorAction SilentlyContinue) {exit 0} else {exit 1}} catch {exit 1}
get_prereq_command: |
Install-Module -Name AWSPowerShell -Force
- description: |
Terraform must be installed.
prereq_command: |
terraform --version
get_prereq_command: |
Write-Host "Terraform is required. Download it from https://www.terraform.io/downloads.html"
executor:
command: |
Import-Module "PathToAtomicsFolder/T1651/src/T1651-1/AWSSSMAttack.ps1" -Force
$access_key = "#{access_key}"
$secret_key = "#{secret_key}"
$session_token = "#{session_token}"
$aws_profile = "#{profile}"
$region = "#{region}"
Set-AWSAuthentication -AccessKey $access_key -SecretKey $secret_key -SessionToken $session_token -AWSProfile $aws_profile -AWSRegion $region
Invoke-Terraform -TerraformCommand init -TerraformDirectory "PathToAtomicsFolder/T1651/src/T1651-1"
Invoke-Terraform -TerraformCommand apply -TerraformDirectory "PathToAtomicsFolder/T1651/src/T1651-1" -TerraformVariables @("profile=T1651-1", "region=$region")
Invoke-SSMAttack -AWSProfile "T1651-1" -TerraformDirectory "PathToAtomicsFolder/T1651/src/T1651-1"
Invoke-Terraform -TerraformCommand destroy -TerraformDirectory "PathToAtomicsFolder/T1651/src/T1651-1" -TerraformVariables @("profile=T1651-1", "region=$region")
name: powershell
126 changes: 126 additions & 0 deletions atomics/T1651/src/T1651-1/AWSSSMAttack.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
Import-Module AWSPowerShell

function Set-AWSAuthentication {
param (
[string]$AccessKey,
[string]$SecretKey,
[string]$SessionToken,
[string]$AWSProfile,
[string]$AWSRegion
)
if ($SessionToken -eq "" -and $AWSProfile -eq "") {
Set-AWSCredential -AccessKey $AccessKey -SecretKey $SecretKey -StoreAs "T1651-1"
}
elseif ($SessionToken -ne "" -and $AWSProfile -ne "") {
Set-AWSCredential -AccessKey $AccessKey -SecretKey $SecretKey -SessionToken $SessionToken -StoreAs "T1651-1"
}
elseif ($AWSProfile -ne "") {
Set-AWSCredential -ProfileName $AWSProfile -StoreAs "T1651-1"
}

try {
Get-STSCallerIdentity -ProfileName "T1651-1" | Out-Null
}
catch {
Write-Host "ERROR: Failed to authenticate to AWS. Please check your credentials and try again."
exit 1
}
Set-DefaultAWSRegion -Region $AWSRegion
}

function Invoke-Terraform {
param (
[string]$TerraformCommand,
[string]$TerraformDirectory,
[string[]]$TerraformVariables
)

$currentPath = Resolve-Path .

if (-not (Test-Path $TerraformDirectory)) {
Write-Host "ERROR: Terraform directory not found. Please check the path and try again."
exit 1
}

if (-not (Get-ChildItem $TerraformDirectory -Filter "*.tf")) {
Write-Host "ERROR: No Terraform files found in the directory. Please check the path and try again."
exit 1
}

foreach($variable in $TerraformVariables) {
$varName = $variable.Split("=")[0]
$varValue = $variable.Split("=")[1]
[Environment]::SetEnvironmentVariable("TF_VAR_$varName", $varValue, "Process")
}

Set-Location $TerraformDirectory

if ($TerraformCommand -eq "init") {
try {
terraform init | Out-Null
}
catch {
Write-Host "ERROR: Failed to initialize Terraform. Please check the error message and try again."
exit 1
}
} elseif ($TerraformCommand -eq "apply") {
try {
terraform apply -auto-approve | Out-Null
}
catch {
Write-Host "ERROR: Failed to apply Terraform. Please check the error message and try again."
exit 1
}
} elseif ($TerraformCommand -eq "destroy") {
try {
terraform destroy -auto-approve | Out-Null
}
catch {
Write-Host "ERROR: Failed to destroy Terraform. Please check the error message and try again."
exit 1
}
} else {
Write-Host "ERROR: Invalid Terraform command. Please use 'init', 'apply', or 'destroy'."
exit 1
}

Set-Location $currentPath
}

function Invoke-SSMAttack {
param (
[string]$AWSProfile,
[string]$TerraformDirectory
)
$currentPath = Resolve-Path .
Set-Location $TerraformDirectory
$instanceId = (terraform output -json | ConvertFrom-Json | Select-Object -ExpandProperty aws_ec2_instance_id).value
Set-Location $currentPath
foreach($i in 1..50) {
$instanceStatus = (Get-SSMInstanceAssociationsStatus -InstanceId $instanceId -ProfileName $AWSProfile).Status
if ($instanceStatus -eq "Success") {
break
}
Start-Sleep -Seconds 6
}
$commandId = (Send-SSMCommand -DocumentName "AWS-RunShellScript" -Target @{Key="tag:AtomicTest";Values=@("T1651-1")} -Comment "Atomic Test T1651-1" -Parameters @{commands = @("cat /etc/shadow")}).CommandId
foreach ($i in 1..50) {
$commandStatus = (Get-SSMCommandInvocation -CommandId $commandId -ProfileName $AWSProfile).Status
if ($commandStatus -eq "Success") {
$instanceId = (Get-SSMCommandInvocation -CommandId $commandId)[0].InstanceId
$output = (Get-SSMCommandInvocationDetail -CommandId $commandId -InstanceId $instanceId).StandardOutputContent
break
}
elseif ($commandStatus -eq "Failed") {
Write-Host "ERROR: Failed to execute the SSM command. Please check the error message and try again."
exit 1
}
Start-Sleep -Seconds 6
}
if ($output -eq "") {
Write-Host "ERROR: No output received from the SSM command. Please check the error message and try again."
exit 1
}
Write-Host "SSM Command Output:"
Write-Host $output
}
30 changes: 30 additions & 0 deletions atomics/T1651/src/T1651-1/compute.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
data "aws_ami" "ami" {
most_recent = true

filter {
name = "name"
values = ["ubuntu/images/hvm-ssd/ubuntu-jammy-22.04-amd64-server-*"]
}

filter {
name = "virtualization-type"
values = ["hvm"]
}

owners = ["099720109477"]
}

resource "aws_instance" "instance" {
ami = data.aws_ami.ami.id
instance_type = var.instance_type
subnet_id = aws_subnet.subnet.id
iam_instance_profile = aws_iam_instance_profile.profile.name
vpc_security_group_ids = [aws_security_group.sg.id]
user_data = <<EOF
systemctl enable amazon-ssm-agent
systemctl start amazon-ssm-agent
EOF
tags = {
Name = "T1651-1"
}
}
23 changes: 23 additions & 0 deletions atomics/T1651/src/T1651-1/main.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "= 5.82.2"
}
}
}

provider "aws" {
profile = var.profile
region = var.region

default_tags {
tags = {
AtomicTest = "T1651-1"
}
}
}

locals {
cloud = length(regexall("-gov-", var.region)) > 0 ? "aws-us-gov" : length(regexall("-cn-", var.region)) > 0 ? "aws-cn" : "aws"
}
38 changes: 38 additions & 0 deletions atomics/T1651/src/T1651-1/network.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
resource "aws_vpc" "vpc" {
cidr_block = "10.0.0.0/16"
}

resource "aws_subnet" "subnet" {
vpc_id = aws_vpc.vpc.id
cidr_block = "10.0.0.0/24"
map_public_ip_on_launch = true
}

resource "aws_internet_gateway" "igw" {
vpc_id = aws_vpc.vpc.id
}

resource "aws_route_table" "rt" {
vpc_id = aws_vpc.vpc.id

route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.igw.id
}
}

resource "aws_route_table_association" "rta" {
subnet_id = aws_subnet.subnet.id
route_table_id = aws_route_table.rt.id
}

resource "aws_security_group" "sg" {
vpc_id = aws_vpc.vpc.id

egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
3 changes: 3 additions & 0 deletions atomics/T1651/src/T1651-1/output.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
output "aws_ec2_instance_id" {
value = aws_instance.instance.id
}
31 changes: 31 additions & 0 deletions atomics/T1651/src/T1651-1/security.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
data "aws_iam_policy" "ssm" {
arn = "arn:${local.cloud}:iam::aws:policy/AmazonSSMManagedInstanceCore"
}

resource "aws_iam_role" "role" {
name = "T1651-1-Role"
assume_role_policy = jsonencode(
{
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
}
)
}

resource "aws_iam_role_policy_attachment" "attachment" {
role = aws_iam_role.role.name
policy_arn = data.aws_iam_policy.ssm.arn
}

resource "aws_iam_instance_profile" "profile" {
name = "T1651-1-Profile"
role = aws_iam_role.role.name
}
14 changes: 14 additions & 0 deletions atomics/T1651/src/T1651-1/variables.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
variable "profile" {
description = "The AWS profile to use"
default = "default"
}

variable "region" {
description = "The AWS region to deploy to"
default = "us-east-2"
}

variable "instance_type" {
description = "The instance type to use for the EC2 instance"
default = "t2.micro"
}

0 comments on commit fd82e0a

Please sign in to comment.