Skip to content

Lambda Function Enumeration

7 min read Created May 24, 2026 Last updated May 24, 2026

Lambda functions frequently store credentials in environment variables and run with overpermissioned execution roles. Function code itself often contains hardcoded secrets or internal API endpoints. Start enumeration here before moving to IAM policies. You need at minimum lambda:ListFunctions to see functions in the account. Without it, the call returns an access denied error and you get no output. In shared or misconfigured accounts, some users have this permission without realizing it. The runtime and last-modified date tell you which functions are active and which runtimes to target for known vulnerabilities.

  • Environment variables with plaintext credentials (DB URLs, API keys, tokens)
  • Execution roles with iam:*, s3:*, or sts:AssumeRole on broad resources
  • Function URLs with AuthType: NONE (publicly invocable, no authentication)
  • Downloadable deployment packages containing hardcoded secrets in source code
  • Shared layers with connection strings or internal SDK credentials

List Functions

┌──(kali㉿kali)-[~]
└─$ aws lambda list-functions --profile $PROFILE --region $REGION --query 'Functions[*].{Name:FunctionName,Runtime:Runtime,Role:Role,Modified:LastModified}' --output table

Get Function Configuration

Includes environment variables, execution role, timeout, memory, and VPC config. The VPC section matters when the function has access to private subnets. A function inside a VPC can reach internal resources not exposed externally. Timeout and memory values indicate what the function does. A 15-minute timeout with high memory suggests batch processing or a heavyweight job. A short timeout points to a lightweight trigger or webhook handler.

┌──(kali㉿kali)-[~]
└─$ aws lambda get-function-configuration --function-name $FUNCTION_NAME --profile $PROFILE --region $REGION

Environment variables are returned in plaintext if you have lambda:GetFunction or lambda:GetFunctionConfiguration. This is one of the highest-yield checks in Lambda enumeration. Developers treat environment variables as secret storage because they are not visible in the console to casual users. The API returns them without any additional decryption step unless the function uses KMS to encrypt them at the application level. If the output shows a KMSKeyArn, the values may appear as ciphertext. Most environments skip that extra layer.

┌──(kali㉿kali)-[~]
└─$ aws lambda get-function-configuration --function-name $FUNCTION_NAME --profile $PROFILE --region $REGION | jq '.Environment.Variables'

Common findings: DATABASE_URL, API_KEY, SECRET_KEY, AWS_ACCESS_KEY_ID, SLACK_TOKEN. Any AWS_ACCESS_KEY_ID value represents a separate set of AWS credentials. Check whether that key belongs to a user or a role. User keys have long-term validity unless rotated. Add any found credentials to your profile list and enumerate their permissions separately. Third-party API keys are useful for expanding scope or demonstrating impact beyond the AWS environment.

Download Function Code

Get a pre-signed URL to download the deployment package. The URL is temporary and expires quickly. Download the zip and inspect the source before it expires. For Node.js functions, check node_modules for pinned dependency versions with known CVEs. For Python functions, look at requirements.txt and any inline imports. The code often reveals internal API routes, other Lambda function names, and the data structures the function processes. Functions that call other services frequently hardcode resource names or account IDs in the code rather than using environment variables.

┌──(kali㉿kali)-[~]
└─$ aws lambda get-function --function-name $FUNCTION_NAME --profile $PROFILE --region $REGION | jq -r '.Code.Location'
┌──(kali㉿kali)-[~]
└─$ URL=$(aws lambda get-function --function-name $FUNCTION_NAME --profile $PROFILE --region $REGION | jq -r '.Code.Location')
┌──(kali㉿kali)-[~]
└─$ curl -s "$URL" -o function.zip
┌──(kali㉿kali)-[~]
└─$ unzip function.zip -d function_code/
┌──(kali㉿kali)-[~]
└─$ grep -r "key\|secret\|password\|token\|credential" function_code/ --include="*.py" --include="*.js" -i

Execution Role

The role ARN is in the function configuration under Role. Extract the role name from the ARN: it is the segment after role/. Then enumerate both attached and inline policies. Inline policies are often missed and can grant broader access than attached managed policies. Use list-role-policies to see inline policies alongside list-attached-role-policies.

┌──(kali㉿kali)-[~]
└─$ aws iam get-role --role-name $ROLE_NAME --profile $PROFILE
┌──(kali㉿kali)-[~]
└─$ aws iam list-attached-role-policies --role-name $ROLE_NAME --profile $PROFILE

A Lambda execution role with iam:* or s3:* on * is a direct privilege escalation path. See IAM PassRole Lambda Escalation. Also check for sts:AssumeRole on other roles. A function that can assume a more privileged role is a stepping stone. Permissions like ec2:* or secretsmanager:GetSecretValue on * are high-value. Even a limited permission like lambda:UpdateFunctionCode can be an escalation path if you can modify the function to run arbitrary code under its own role.

Layers

┌──(kali㉿kali)-[~]
└─$ aws lambda list-layers --profile $PROFILE --region $REGION
┌──(kali㉿kali)-[~]
└─$ aws lambda get-layer-version --layer-name $LAYER_NAME --version-number 1 --profile $PROFILE --region $REGION

Layers can contain shared libraries and configuration, and they are worth downloading if accessible. A layer might hold shared authentication logic, internal SDKs, or configuration files with connection strings. To download, extract the Content.Location URL from the layer version response and fetch it the same way as function code. Layers shared across multiple functions are especially valuable: credentials stored in a layer apply to every function that uses it.

Event Source Mappings

Shows what triggers each function: SQS queues, DynamoDB streams, Kinesis streams, and similar sources. The mapping reveals internal queue and stream names. Use those names to understand the application's data flow. If you have write access to a trigger source such as an SQS queue, you control the input the function receives. This is relevant for input validation bypasses or injecting payloads into the function's processing logic.

┌──(kali㉿kali)-[~]
└─$ aws lambda list-event-source-mappings --function-name $FUNCTION_NAME --profile $PROFILE --region $REGION

Function URLs

Lambda function URLs expose functions directly over HTTPS without API Gateway. They are less commonly audited than API Gateway endpoints. The URL is stable and does not rotate unless explicitly deleted and recreated. Check the AuthType field in the response. AWS_IAM requires signed requests, which limits access to callers with valid AWS credentials and the correct permissions.

┌──(kali㉿kali)-[~]
└─$ aws lambda list-function-url-configs --function-name $FUNCTION_NAME --profile $PROFILE --region $REGION

Auth type NONE means the function is publicly invocable without any authentication. Anyone with the URL can call the function. The URL appears under FunctionUrl in the response. Test whether the function accepts arbitrary payloads or enforces input validation. Functions with no auth and no input validation are common injection targets. Even if the function performs limited actions, it may trigger other services or pass untrusted input downstream.

Invoke a Function

If you have lambda:InvokeFunction, call the function directly with a controlled payload. Use an empty JSON object as the initial payload to observe the default behavior. The response writes to output.json. A 200 status means the function ran synchronously. A 202 means it was queued asynchronously. Error output also writes to output.json and often reveals internal stack traces, framework versions, and dependency paths.

┌──(kali㉿kali)-[~]
└─$ aws lambda invoke --function-name $FUNCTION_NAME --payload '{}' output.json --profile $PROFILE --region $REGION
┌──(kali㉿kali)-[~]
└─$ cat output.json

Useful for testing RCE paths or triggering functions that perform privileged actions. If the function interacts with other AWS services, invoking it with controlled input may expose those service calls in error output. Functions that write to S3, send emails, or update DynamoDB records can be abused when you control their input. After invoking, check CloudWatch Logs if you have access: log output often contains debug information, internal hostnames, and data the function processed during the call.