✓ Human-authored analysis; AI used for formatting and proofreading.
In October 2024, a researcher named @zolaer9527 deployed the CloudFront Extensions Console from the awslabs GitHub repository. An AWS-published reference application for managing CloudFront configurations and read the IAM roles it created.
The Lambda functions it provisioned shared a role with these permissions:
{
"Effect": "Allow",
"Action": [
"iam:CreateRole",
"iam:GetRole",
"iam:CreatePolicy",
"iam:AttachRolePolicy"
],
"Resource": "*"
}
iam:AttachRolePolicy on Resource: *. Any user who can invoke those Lambda functions can attach the AWS-managed AdministratorAccess policy to any role they already have access to. Full account administrator. One Lambda invocation away.
AWS VDP resolved the report (#2805173) as fixed in November 2024.
The Application and Its Roles
The CloudFront Extensions Console is a reference implementation for CloudFront configuration versioning and extension deployment. When deployed, it creates six Lambda functions across two shared roles.
Role 1: cloudFrontExtensionsConso-CloudFrontConfigVersionCo-*
Shared by four functions:
cf_config_version_exportercf_config_version_managercf_config_version_manager_graphqlcloudFrontExtensionsConso-SingletonLambda-*
Permissions include: iam:CreateRole, iam:GetRole, iam:CreatePolicy, iam:AttachRolePolicy for Resource: *.
Role 2: cloudFrontExtensionsConso-RepoConstructExtDeployerR-*
Shared by two functions:
cloudFrontExtensionsConso-RepoConstructSyncExtensi-*cloudFrontExtensionsConso-RepoConstructExtDeployer-*
Permissions: iam:* for Resource: *. Unrestricted IAM. Everything.
The Privilege Escalation Path
The attack requires a user who has permission to invoke one of these Lambda functions but does not yet have administrative access. This is a realistic scenario. A developer with CloudFront management access, a CI/CD pipeline role, or any principal in the account that was granted access to the Extensions Console.
1. User has: lambda:InvokeFunction on cf_config_version_manager
2. User invokes the function with a crafted payload
3. Function's role has: iam:AttachRolePolicy on Resource: *
4. Function attaches AdministratorAccess to the user's role
5. User now has: full administrative access to the AWS account
The second role is worse. iam:* on * means the function can do anything IAM allows such as create users, create access keys, set passwords, modify trust policies, delete permission boundaries. It is not privilege escalation to administrator. It is unrestricted identity control.
Why Reference Applications Are a Supply Chain Problem
The CloudFront Extensions Console is published by AWS under the awslabs organization. The same GitHub organization that hosts AWS CDK constructs, AWS samples, and AWS solution implementations. Organizations deploy it because it is AWS-published and appears authoritative.
The trust model is: "AWS published it, so it follows AWS security best practices." The reality is that awslabs repositories are community contributions and reference implementations, not production-hardened services. The security review bar is different.
When an organization deploys a CloudFront Extensions Console following AWS documentation, they inherit these roles without reading the IAM policies. The application deploys successfully. CloudFront configuration versioning works. The over-permissioned roles sit in the account, waiting.
This is the supply chain trust problem applied to infrastructure tooling: the vulnerability is not in application code you write. It is in the roles created by infrastructure you deploy from a trusted source.
The Security Invariant
The invariant this report violated:
Lambda function roles must not have IAM write permissions (
iam:Create*,iam:Attach*,iam:Put*) onResource: *.
When this invariant is false, any principal that can invoke the Lambda function can use the function's role as a privilege escalation vehicle.
What becomes possible when it is false:
A user with Lambda invocation access can escalate to full administrator in one API call, using the function role as a proxy for IAM operations they are not directly permitted to perform.
This is the confused deputy class: the Lambda function is trusted to perform IAM operations for legitimate purposes, but nothing restricts how those operations are invoked or what they target.
What Stave Detects
CTL.LAMBDA.OVERPRIVILEGED.001 CRITICAL privilege_escalation
"Lambda Function Roles Must Not Have IAM Write Permissions on *"
Fires when: a Lambda function's execution role has any of
iam:CreateRole, iam:CreatePolicy, iam:AttachRolePolicy,
iam:PutRolePolicy, or iam:* as an Allow action on
Resource: * or Resource: "arn:aws:iam::*:*"
The detection evaluates the normalized IAM role permissions in the snapshot. The raw policy JSON is preserved for audit evidence but the predicate evaluates the normalized permission set has_iam_write_star: true derived from policy analysis at snapshot generation time.
Running the CloudFront Extensions Console scenario:
VERDICT: VIOLATION
CTL.LAMBDA.OVERPRIVILEGED.001 CRITICAL privilege_escalation
Role: cloudFrontExtensionsConso-CloudFrontConfigVersionCo-*
Functions: cf_config_version_exporter, cf_config_version_manager,
cf_config_version_manager_graphql, Singleton
Permissions: iam:CreateRole, iam:CreatePolicy, iam:AttachRolePolicy
Resource: *
CTL.LAMBDA.OVERPRIVILEGED.001 CRITICAL privilege_escalation
Role: cloudFrontExtensionsConso-RepoConstructExtDeployerR-*
Functions: SyncExtensi, ExtDeployer
Permissions: iam:*
Resource: *
Compound chains:
lambda_total_compromise confidence: 1.00
shadow_admin_by_accumulation confidence: 0.87
Both roles fire the same control. The second role iam:* on * also activates shadow_admin_by_accumulation because unrestricted IAM is sufficient to construct any privilege path in the account.
The E2E Test
This report is part of Stave's end-to-end test suite. The test reconstructs the CloudFront Extensions Console role configuration as a snapshot. Two Lambda function groups, each with their respective over-permissioned roles runs stave apply, and compares output byte-for-byte against a golden file.
The test proves that the control predicate detects the specific permission pattern documented in the report, not just "Lambda with broad IAM permissions" in general.
Remediation
AWS's resolution involved scoping the permissions to specific resources and removing IAM permissions that were not required for the application's core functionality.
The principle of least privilege applied to Lambda execution roles:
{
"Effect": "Allow",
"Action": [
"iam:GetRole"
],
"Resource": "arn:aws:iam::ACCOUNT_ID:role/cloudfront-extensions-*"
}
If IAM role creation is required, scope it to a specific path and add a permissions boundary condition:
{
"Effect": "Allow",
"Action": ["iam:CreateRole", "iam:AttachRolePolicy"],
"Resource": "arn:aws:iam::ACCOUNT_ID:role/cloudfront-extensions-*",
"Condition": {
"StringEquals": {
"iam:PermissionsBoundary": "arn:aws:iam::ACCOUNT_ID:policy/CloudFrontExtensionsBoundary"
}
}
}
The permissions boundary prevents the Lambda from creating roles that exceed the boundary's permissions even if the role creation succeeds, the created role cannot escalate beyond what the boundary allows.
For organizations that already deployed the CloudFront Extensions Console:
# Audit the roles created by the application
aws iam list-roles --query \
'Roles[?contains(RoleName, `cloudFrontExtensionsConso`)].RoleName'
# Review attached policies for each role
aws iam list-attached-role-policies \
--role-name cloudFrontExtensionsConso-CloudFrontConfigVersionCo-XXXXX
# Review inline policies
aws iam list-role-policies \
--role-name cloudFrontExtensionsConso-CloudFrontConfigVersionCo-XXXXX
Checklist
For any Lambda function that handles infrastructure management tasks:
- Execution role has no
iam:*oriam:Attach*onResource: * - IAM permissions scoped to specific resource ARN patterns
- Permissions boundaries applied when IAM creation is genuinely required
-
CTL.LAMBDA.OVERPRIVILEGED.001runs in CI on every role change - Third-party and AWS reference application deployments audited for over-permissioned roles on first deploy
The application deployed correctly. CloudFront versioning worked. The roles sat in the account with iam:* on * until someone read them.
Reported by @zolaer9527 to the AWS VDP on October 26, 2024. Resolved November 2024. HackerOne #2805173. Stave detects over-permissioned Lambda execution roles via CTL.LAMBDA.OVERPRIVILEGED.001, evaluated from local IAM snapshots without AWS credentials.
Top comments (0)