- An AWS account
In a previous post I described how to complete the iam privesc by rollback scenario in Cloudgoat manually, as well as showing how Pacu can be used to automate the process. In this post I will going over another Cloudgoat scenario, lambda privesc. In this scenario, the user is given access keys to a low privileged user that has access to iam:Get*, iam:List*, and sts:AssumeRole. Using theses permissions the user assumes a role with full access to lambda and escalates privileges by creating a new lambda function to attach the AdministratorAccess managed policy to the starting user.
Chris To Lambda Manager
In the previous Cloudgoat walkthrough, I discussed how the process of configuring Cloudgoat and creating scenarios. To avoid going over something I already discussed I will not go over that process again. Instead if a refresher is required, review that post here.
After creating the lambda_privesc scenario we are provided with a set of access keys for the Chris user.
Output of Create Command
The scenario name, lambda_privesc, hints that the AWS Lambda service will be of use in escalating privileges. However, the user we start off with does not have access to any Lambda calls. This tells me that I have to find a way to gain access to lambda by moving laterally within the AWS environment. Permissions can be determined in a number of ways, luckily the Chris account has access to the IAM List and IAM Get calls which allows us to view the policy associated with the user as well as its permission set. In future posts I will touch on how permissions can be enumerated when the IAM service cannot be used. There is also something else I wanted to touch on quickly. AWS has many services which each service having many, many potential calls. Luckily the AWS CLI provides a built man page that makes finding the necessary calls easy. If you cannot remember, or are searching for a call for a specific service, issue the following and a helpful man page is brought up.
aws <service> help
AWS IAM Man Page
To first step to enumerating the permissions for the Chris user is to get the username. This can be done with the
aws sts get-caller-identity command or the
aws iam get-user command. Either way, once the username is known, get the attached policy by running the following.
aws iam list-attached-user-policies --user-name <username>
Next, get the policy versions and list the permissions for that version by running the following two commands.
aws iam list-policy-versions --policy-arn <policy-arn> aws iam get-policy-version --policy-arn <policy arn> --version-id v1
The output shows that we have access to all IAM List and all IAM Get commands as well as the AssumeRole command. Our current level of access presents us with the possibility of escalating our privileges within the account via the AssumeRole command. List the available roles using the
aws iam list-roles command. After analyzing the output the cg-lambdamanager role should stick out. This is because the AssumeRolePolicyDocument associated with that role lists that the Chris user has the ability to assume that role. Lets analyze the permissions associated with the cg-lambdamanager role to see what can be gained by assuming it. Run the following to enumerate the cg-lambdamangers permissions.
aws iam list-attached-role-policies --role-name <role name> aws iam list-policy-versions --policy-arn <policy arn> aws iam get-policy-version --policy-arn <policy arn> --version-id <version id>
The output shows that the cg-lambdamanager role has the ability to run all lambda commands as well as the iam:PassRole command. The purpose of pairing the iam:PassRole command with a service, is to allow the service to use other services. For example, if I needed a lambda function to interface with the S3 service, I would need to first create a role that lambda can assume to make calls to the S3 service. This functionality can be abused by an attacker to potentially escalate privileges within an AWS environment. For example, if there was a lambda role that had an elevated level of permissions, a lambda function can be created that used that role. The code within the lambda function will then use that role to accomplish some purpose. This will be shown in the following steps. First, assume the cg-lambdamanager role by issuing the following.
aws sts assume-role --role-arn <role arn> --role-session-name lambdamanager
This command outputs an AccessKey, a SecretKey, and a SessionToken. The SessionToken has to manually be added to the ~/.aws/credentials file under the name aws_session_token.
Lambdamanager to Admin
Now that the cg-lambdamanager role has been assumed lets begin to plot a path to gain administrator privileges in the account. Looking at the output of
aws iam list-roles again will show another interesting role, cg-debug-role, that is assumable by the Lambda service as shown in the following.
Note that this role can only be assumed by the Lambda service as stated in the roles AssumeRolePolicyDocument. Lets use the Chris user to see what this role has access to since the cg-lambdamanager role does not have access to IAM. Please note that all calls made with the lambdamanager role will have
--profile lambdamanager appended to it, all other calls will be performed by the Chris user. Listing the policies attached to the cg-debug role using
aws iam list-attached-role-policies --role-name <role name> reveal that the only attached policy is the AdministratorAccess managed policy.
We now know that it is possible to gain admin privileges on this account. Let us begin the process of performing privilege escalation. It is worth noting that, thanks to some fantastic research done by Rhino Security Labs, the methods of performing privilege escalation along with examples can be found on their blog here and here. Based on the method described in escalation method 15, passing a role to a new lambda function then invoking it, we can use the following python code to add the AdministratorAccess policy to the Chris account.
import boto3 def lambda_handler(event, context): client = boto3.client("iam") response = client.attach_user_policy( UserName="<username>", PolicyArn="arn:aws:iam::aws:policy/AdministratorAccess" ) return response
Save the python code to a file titled privesc.py and zip it into a folder titled code.zip with the command
zip -r code.zip privesc.py. Once the code is zipped, privilege escalation can be performed by issuing the following commands.
aws lambda create-function --function-name lambda-privesc --runtime python3.6 --role <cg-debug-role arn> --handle privesc.lambda_handler --zip-file fileb://code.zip --profile lambdamanager aws lambda invoke --function-name lambda-privesc out.txt --profile lambdamanager
Upon completion, the policies attached to the Chris account can be reexamined. If the lambda function ran successfully, the Chris account will now have the AdministratorAccess managed policy attached to it.