Skip to content

Commit

Permalink
New attack technique: Persistence AWS Lambda Layer Extension (#427)
Browse files Browse the repository at this point in the history
* Add lambda-layer-extension technique

* build documentation

* Minor docs editing

* Minor code changes

---------

Co-authored-by: Christophe Tafani-Dereeper <christophe.tafanidereeper@datadoghq.com>
  • Loading branch information
adanalvarez and christophetd committed Nov 9, 2023
1 parent 7a96eb0 commit d151fe9
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Establishes persistence by creating an access key on an existing IAM user.
- Create an IAM access key on the user.

References:

- https://sysdig.com/blog/scarleteel-2-0/


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Establishes persistence by creating a new IAM user with administrative permissio
References:

- https://permiso.io/blog/s/approach-to-detection-androxgh0st-greenbot-persistence/
- https://permiso.io/blog/s/unmasking-guivil-new-cloud-threat-actor/
- https://blog.darklab.hk/2021/07/06/trouble-in-paradise/
- https://expel.com/blog/incident-report-from-cli-to-console-chasing-an-attacker-in-aws/

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ user intended to be used programmatically through the AWS console usual login pr
- Create an IAM Login Profile on the user

References:

- https://permiso.io/blog/s/approach-to-detection-androxgh0st-greenbot-persistence/
- https://permiso.io/blog/s/unmasking-guivil-new-cloud-threat-actor/
- https://blog.darklab.hk/2021/07/06/trouble-in-paradise/
- https://expel.com/blog/incident-report-from-cli-to-console-chasing-an-attacker-in-aws/

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: Add a Malicious Lambda Extension
---

# Add a Malicious Lambda Extension


<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


- Persistence
- Privilege Escalation

## Description


Establishes persistence by adding a malicious lambda extension.

<span style="font-variant: small-caps;">Warm-up</span>:

- Create a Lambda function and a lambda extension (layer).

<span style="font-variant: small-caps;">Detonation</span>:

- Add the extension as a layer to the Lambda function.

References:

- https://www.clearvector.com/blog/lambda-spy/


## Instructions

```bash title="Detonate with Stratus Red Team"
stratus detonate aws.persistence.lambda-layer-extension
```
## Detection


Through CloudTrail's <code>UpdateFunctionConfiguration20150331v2</code> event.

While matching this event may be impractical and prone to false positives in most environments, the following can help to craft more precise detections:

- Identify calls to <code>UpdateFunctionConfiguration20150331v2</code> where the <code>responseElements</code> field contains <code>layer</code>, indicating that the function's layers were modified.
- Identify calls to <code>UpdateFunctionConfiguration20150331v2</code> where <code>responseElements.layers</code> includes a layer that's from a different AWS account.'


Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ A further, more advanced, use-case could be updating the code to exfiltrate the
- Update the Lambda function code.

References:

- https://research.splunk.com/cloud/aws_lambda_updatefunctioncode/
- Expel's AWS security mindmap

Expand Down
4 changes: 4 additions & 0 deletions docs/attack-techniques/AWS/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ Note that some Stratus attack techniques may correspond to more than a single AT

- [Create an IAM Roles Anywhere trust anchor](./aws.persistence.rolesanywhere-create-trust-anchor.md)

- [Add a Malicious Lambda Extension](./aws.persistence.lambda-layer-extension.md)


## Privilege Escalation

Expand All @@ -100,3 +102,5 @@ Note that some Stratus attack techniques may correspond to more than a single AT

- [Create an IAM Roles Anywhere trust anchor](./aws.persistence.rolesanywhere-create-trust-anchor.md)

- [Add a Malicious Lambda Extension](./aws.persistence.lambda-layer-extension.md)

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Establishes persistence by creating a new service account and assigning it
- Update the current GCP project's IAM policy to bind the service account to the <code>owner</code> role'

References:

- https://about.gitlab.com/blog/2020/02/12/plundering-gcp-escalating-privileges-in-google-cloud-platform/


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 @@ -39,6 +39,7 @@ This page contains the list of all Stratus Attack Techniques.
| [Backdoor Lambda Function Through Resource-Based Policy](./AWS/aws.persistence.lambda-backdoor-function.md) | [AWS](./AWS/index.md) | Persistence |
| [Overwrite Lambda Function Code](./AWS/aws.persistence.lambda-overwrite-code.md) | [AWS](./AWS/index.md) | Persistence |
| [Create an IAM Roles Anywhere trust anchor](./AWS/aws.persistence.rolesanywhere-create-trust-anchor.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation |
| [Add a Malicious Lambda Extension](./AWS/aws.persistence.lambda-layer-extension.md) | [AWS](./AWS/index.md) | Persistence, Privilege Escalation |
| [Execute Command on Virtual Machine using Custom Script Extension](./azure/azure.execution.vm-custom-script-extension.md) | [Azure](./azure/index.md) | Execution |
| [Execute Commands on Virtual Machine using Run Command](./azure/azure.execution.vm-run-command.md) | [Azure](./azure/index.md) | Execution |
| [Export Disk Through SAS URL](./azure/azure.exfiltration.disk-export.md) | [Azure](./azure/index.md) | Exfiltration |
Expand Down
16 changes: 16 additions & 0 deletions docs/index.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,14 @@ AWS:
- Privilege Escalation
platform: AWS
isIdempotent: false
- id: aws.persistence.lambda-layer-extension
name: Add a Malicious Lambda Extension
isSlow: false
mitreAttackTactics:
- Persistence
- Privilege Escalation
platform: AWS
isIdempotent: true
Privilege Escalation:
- id: aws.execution.ec2-user-data
name: Execute Commands on EC2 Instance via User Data
Expand Down Expand Up @@ -263,6 +271,14 @@ AWS:
- Privilege Escalation
platform: AWS
isIdempotent: false
- id: aws.persistence.lambda-layer-extension
name: Add a Malicious Lambda Extension
isSlow: false
mitreAttackTactics:
- Persistence
- Privilege Escalation
platform: AWS
isIdempotent: true
GCP:
Exfiltration:
- id: gcp.exfiltration.share-compute-disk
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package aws

import (
"context"
_ "embed"
"errors"
"log"

"github.com/aws/aws-sdk-go-v2/service/lambda"
"github.com/datadog/stratus-red-team/v2/pkg/stratus"
"github.com/datadog/stratus-red-team/v2/pkg/stratus/mitreattack"
)

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

func init() {
stratus.GetRegistry().RegisterAttackTechnique(&stratus.AttackTechnique{
ID: "aws.persistence.lambda-layer-extension",
FriendlyName: "Add a Malicious Lambda Extension",
Description: `
Establishes persistence by adding a malicious lambda extension.
Warm-up:
- Create a Lambda function and a lambda extension (layer).
Detonation:
- Add the extension as a layer to the Lambda function.
References:
- https://www.clearvector.com/blog/lambda-spy/
`,
Detection: `
Through CloudTrail's <code>UpdateFunctionConfiguration20150331v2</code> event.
While matching this event may be impractical and prone to false positives in most environments, the following can help to craft more precise detections:
- Identify calls to <code>UpdateFunctionConfiguration20150331v2</code> where the <code>responseElements</code> field contains <code>layer</code>, indicating that the function's layers were modified.
- Identify calls to <code>UpdateFunctionConfiguration20150331v2</code> where <code>responseElements.layers</code> includes a layer that's from a different AWS account.'
`,
Platform: stratus.AWS,
PrerequisitesTerraformCode: tf,
IsIdempotent: true,
MitreAttackTactics: []mitreattack.Tactic{mitreattack.Persistence, mitreattack.PrivilegeEscalation},
Detonate: detonate,
Revert: revert,
})
}

func detonate(params map[string]string, providers stratus.CloudProviders) error {
ctx := context.Background()
lambdaClient := lambda.NewFromConfig(providers.AWS().GetConnection())
lambdaExtensionLayerArn := params["lambda_extension_layer_arn"]
lambdaArn := params["lambda_arn"]

// Update the function configuration with our layer
_, err := lambdaClient.UpdateFunctionConfiguration(ctx, &lambda.UpdateFunctionConfigurationInput{
FunctionName: &lambdaArn,
Layers: []string{lambdaExtensionLayerArn},
})

if err != nil {
return errors.New("unable to update function configuration: " + err.Error())
}

log.Println("Added simulated malicious layer to Lambda function ", lambdaArn)
return nil
}

func revert(params map[string]string, providers stratus.CloudProviders) error {
ctx := context.Background()
lambdaArn := params["lambda_arn"]
lambdaClient := lambda.NewFromConfig(providers.AWS().GetConnection())

// Update the function configuration with an empty list of layers
_, err := lambdaClient.UpdateFunctionConfiguration(ctx, &lambda.UpdateFunctionConfigurationInput{
FunctionName: &lambdaArn,
Layers: []string{},
})

if err != nil {
return errors.New("unable to update function configuration: " + err.Error())
}
log.Printf("Layers have been removed from the Lambda function %s\n", lambdaArn)
return nil
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 4.0"
}
}
}
provider "aws" {
skip_region_validation = true
skip_credentials_validation = true
skip_get_ec2_platforms = true
default_tags {
tags = {
StratusRedTeam = true
}
}
}

locals {
resource_prefix = "stratus-red-team-lambda-layer"
}

resource "aws_iam_role" "lambda_role" {
name = "${local.resource_prefix}-lambda-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
"Service" : [
"lambda.amazonaws.com"
]
}
},
]
})
}

resource "aws_iam_policy" "lambda_logs" {
name = "${local.resource_prefix}-lambda-logs"
description = "Allows Lambda function to write logs to CloudWatch"

policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
EOF
}

resource "aws_iam_role_policy_attachment" "lambda_logs_attach" {
role = aws_iam_role.lambda_role.name
policy_arn = aws_iam_policy.lambda_logs.arn
}

resource "aws_s3_bucket" "bucket" {
bucket = "${local.resource_prefix}-bucket"
force_destroy = true
}
resource "aws_s3_bucket_object" "code" {
bucket = aws_s3_bucket.bucket.id
key = "simpleLambda.zip"
content_base64 = "UEsDBBQACAAIAAAAIYoAAAAAAAAAAAAAAAAPAAAAc2ltcGxlTGFtYmRhLnB5RIuxCgIxEAX7+4pntQpXHJZpbSws/IWcu8cJya4ke2IQ/11M43TDMCwL1qicpOzlKeojbqYuLz+EAQCK+FYU7y4/qHr0rZ6MhQKO0zT+02zcKIDOkpJhKZaRG+o9P5Lg2nw1xSXmmeOO+vUZvgEAAP//UEsHCCE/h3lyAAAAgQAAAFBLAQIUAxQACAAIAAAAIYohP4d5cgAAAIEAAAAPAAAAAAAAAAAAAACkgQAAAABzaW1wbGVMYW1iZGEucHlQSwUGAAAAAAEAAQA9AAAArwAAAAAA"
}

resource "aws_lambda_function" "lambda" {
s3_bucket = aws_s3_bucket.bucket.id
s3_key = aws_s3_bucket_object.code.key
function_name = "${local.resource_prefix}-simpleLambda"
role = aws_iam_role.lambda_role.arn
handler = "${local.resource_prefix}-simpleLambda.handler"
timeout = 20

runtime = "python3.10"

publish = true
}

resource "aws_s3_bucket_object" "code_layer" {
bucket = aws_s3_bucket.bucket.id
key = "simpleLayer.zip"
content_base64 = "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"
}

resource "aws_lambda_layer_version" "lambda_extension_layer" {
s3_bucket = aws_s3_bucket.bucket.id
s3_key = aws_s3_bucket_object.code_layer.key
layer_name = "${local.resource_prefix}-my-lambda-extension"

compatible_runtimes = ["python3.10"]
}

output "lambda_extension_layer_arn" {
value = aws_lambda_layer_version.lambda_extension_layer.arn
}

output "lambda_arn" {
value = aws_lambda_function.lambda.arn
}
1 change: 1 addition & 0 deletions v2/internal/attacktechniques/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/lambda-backdoor-function"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/lambda-overwrite-code"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/rolesanywhere-create-trust-anchor"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/aws/persistence/lambda-layer-extension"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-custom-script-extension"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/execution/vm-run-command"
_ "github.com/datadog/stratus-red-team/v2/internal/attacktechniques/azure/exfiltration/disk-export"
Expand Down

0 comments on commit d151fe9

Please sign in to comment.