This walkthrough assumes you have CloudGoat setup on your Kali Linux. You can use our Working with CloudGoat: The “vulnerable by design” AWS environment post as a guide in deploying it.
Scenario summary
The scenario starts with the IAM user Chris, where the attacker discovers that they can assume a role that has full Lambda access and pass role permissions. The attacker can then perform privilege escalation to obtain full admin access. The goal of the scenario is to download the confidential files from the S3 bucket.
Walkthrough
To deploy the resources for each scenario on AWS: ./cloudgoat.py create lambda_privesc
Deploying the resources gives us the access key and secret key for Chris:
Save the credential to a profile – Chris: ./cloudgoat.py create lambda_privesc
Enumerate the policies and permissions attached to the user “Chris” and see what privileges the user has. Running the below revealed nothing. aws iam list-user-policies –-user-name
–profile list-user-policies: Lists the names of inline policies embedded in the specified IAM user. aws iam list-attached-user-policies –-user-name –profile list-attached-user-policies: Lists all managed policies that are attached to the specified IAM user. Get more information on the managed policy attached to the IAM user – Chris: aws iam get-policy -–policy-arn
–profile
get-policy: Retrieves information about the specified managed policy, including the policy’s default version and the total number of IAM users, groups and roles to which the policy is attached.
We can see that this policy version is v1.
5. Review details of the policy attached to this IAM user – Chris:
aws iam get-policy-version -–policy-arn
We noticed that this policy has sts:AssumeRole allowed. A bad actor with the sts:AssumeRole would be able to change the assume role policy document of any existing role to allow them to assume that role. It returns a set of temporary security credentials that you can use to access AWS resources that you might not normally have access to.
6. Review details of the policy attached to this IAM user – Chris:
aws iam list-roles –profile
We noticed two roles assigned to the user: “cg-debug-role-cgidpqw7rhl92u” and “cg-lambdaManager-role- cgidpqw7rhl92u”.
7. Get more information about the roles:
aws iam list-attached-user-policies –-role-name
The “cg-debug-role-cgidpqw7rhl92u” role has a AdministratorAccess policy attached to it.
8. Get more information on the managed policy attached to the IAM role:
aws iam get-policy -–policy-arn
- Review details of the policy attached to this IAM role:
aws iam get-policy-version -–policy-arn
–profile –version-id
We noticed that this policy has iam:PassRole allowed. A user with the iam:PassRole, lambda:CreateFunction and lambda:InvokeFunction permissions can escalate privileges by passing an existing IAM role to new Lambda function that includes code to import the relevant AWS library to their programming language of choice, then using it perform actions of their choice. The code could then be run by invoking the function through the AWS API. This would give a user access to the privileges associated with any Lambda service role that exists in the account, which could range from no privilege escalation to full administrator access to the account.
aws sts assume role –role-arn
We try to assume the debug role, but access is denied because Chris is not authorized to assume the role. We try to assume the Lambda manager role:
The action was successful because Chris is authorized to assume the role. We are granted the temporary credential of the role (i.e., access key ID, secret access key and session token).
10. Configure the IAM credential on AWS CLI:
aws configure –profile
Using vi, edit the credential file to include the AWS session token:
vi ~/.aws/credentials
11. Create a Lambda function which will attach the administrator policy to the IAM user – Chris:
import boto3
def lambda_handler(event, context):
client = boto3.client(‘iam’)
response = client.attach_user_policy(UserName =
Create-function: This creates a Lambda function. To create a function, you need a deployment package and an execution role. The deployment package contains your function code.
13. Invoke the Lambda function created in Step 12 above. If successful, it will return a status code of 200.
aws lambda invoke –function-name <insert function name created in Step 12 above>
Invoke: Invokes the function created in Step 13 directly.
14. Confirm if the IAM user – Chris has the new role attached to his profile:
aws iam list-attached-user-policies –-user-name
- To destroy the resources created during this lab: ./cloudgoat.py destroy lambda_privesc
Summary
The user was granted the sts:AssumeRole which allowed the IAM user – Chris to assume a role (cg-lambdaManager-role- cgidpqw7rhl92u). The IAM role itself also had iam:PassRole, which allowed it to pass another higher privileged role (cg-debug-role-cgidpqw7rhl92u) via the Lambda function.
Sources
Versioning IAM Policies, AWS AWS CLI Command Reference – IAM, AWS Defeating a Cloud Breach Part 1, AttackIQ The Capital One Breach & “cloud_breach_s3” CloudGoat Scenario, Rhino Security Labs Well, that escalated quickly, Bishop Fox AWS IAM Privilege Escalation Methods, Rhino Security Labs Capital One Data Breach – Step by step analysis, Roostify