CloudGoat is Rhino Security Labs’s AWS pentest training tool, deploying “vulnerable by design” AWS infrastructure to exploit it safely (and legally) in your own environment. This blog post will walk through the new vulnerable_lambda scenario, where you will learn to discover and exploit a vulnerability during the implementation of an AWS Lambda function.
In this post, we will explain some of the ways in which Lambdas can be exploited, walk through the most recent CloudGoat scenario at a high level, and share some general tips for pentesting cloud environments.
We have seen a number of Lambda implementation mistakes over the years in penetration tests, which has inspired the most recent CloudGoat scenario. As with all CloudGoat scenarios, vulnerable_lambda does not reflect our experiences with one specific environment but is rather an example of many misconfigurations that can appear in an IaaS environment.
In the past we’ve written about leveraging misconfigured IAM policies to escalate privileges in a vulnerable Lambda. To summarize that post, an attacker is either updating a function’s source code or passing a role to a function and invoking it.
These privilege escalation paths are pretty simple to find using a tool such as NCC Group’s pmapper, and have the required permissions. This post, however, is focused on identifying and exploiting flaws in the Lambda code itself, overlapping AWS pentesting with traditional application attacks.
SPOILER WARNING:
The rest of this post is a walkthrough of the vulnerable_lambda scenario. If you want to work through the scenario on your own first, click here to find CloudGoat on Github.
This scenario is categorized as “Easy”, and can be a place to start if you’re never tried CloudGoat, but does require some knowledge in secure code review and app pentesting as well.
The following graphic is a visual overview of how to solve this scenario. At the highest level, this scenario involves discovering an “access delegation” lambda that is intended to apply policies to users, reading over the source to identify a SQL injection flaw, and exploiting it to escalate privileges.
If it’s not immediately apparent why having access delegation via lambda functions is a bad idea, remember that there is already a very powerful and granular solution for access control in AWS, and that’s IAM (and sometimes resource policies). There are also a few AWS-native solutions to the issue of granting users temporary access to a variety of roles. We suggest setting up AWS SSO and granting users access to roles upon successful login, if needed.
The diagram above shows the steps that need to be taken in order to complete this scenario.
The first step as an attacker, after obtaining credentials, is to understand the permissions associated with those credentials.
This can be done with the following AWS CLI commands:
sts get-caller-identity (whoami?) iam list-groups-for-user (what groups am I a part of?) iam list-group-policies (what policies are attached to those groups?) iam get-policy (what statements compose these policies?) iam get-policy-version (what statements compose these policies?) iam list-user-policies (what are the inline policies for this user?) iam get-user-policy (what statements compose these policies?) iam list-attached-user-policies (what policies are attached to this user?)
Enumeration in Hardened AWS Environments
Many AWS environments often restrict many of these IAM permissions, limiting user enumeration. While not required for this scenario, the ‘plan B’ would be to brute-force API calls to identify what access is available. The effectiveness of this technique can vary, and is very ‘loud’ in CloudTrail and other logging systems – use only as necessary.
Also it should be noted these commands are for IAM User credentials. For Roles, there are separate commands that perform similar actions.
After the permission enumeration process, you’ll see that the ‘bilbo’ user has “iam:Get*” and “iam:List*” for ““Resource”: “*””. You can also see that bilbo has “sts:AssumeRole” for ““Resource”: “arn:aws:iam::940877411605:role/cg-lambda-invoker*””, and so you can check out which permissions the “cg-lambda-invoker” role has with the techniques described above.
The name of the “cg-lambda-invoker” role and the associated permissions both point toward the lambda service as a possible target. We don’t have the “iam:PassRole” permission, which is required for common privilege escalations involving the lambda service. However, this does not mean that the lambda service should be ignored. Improperly configured lambdas can still be exploited.
The “aws lambda get-function” CLI call returns a very important field — “{“code”:{“location”:”url/to/download/deployment/package”}}
This presigned URL is used to download the deployment package for the associated Lambda, giving us source code access (and more information to leverage in an attack).
When visiting the “location” URL, your browser should download a zip of the deployment package for the associated lambda.
When extracted, you will have access to all the source code (including dependencies) for the lambda.
The “main.py” file is where the function handler exists for this particular lambda, and if ever you’re unsure of where the function Handler is, the same API call that returns the URL for the source code also returns the “Handler” field.
In that file you can see the code being used to apply policies. We can see there’s no filtering of user input to the SQL statement passed to the database, leading to a SQL injection vulnerability. Also included is the database structure, giving some direction on creating a SQL query.
After exploitation, you’ll be an admin, and you can easily read the secret from SecretsManager.
Because of the way AWS lambdas are deployed, you’ll have access to all of the dependencies for a lambda when you download the deployment zip. Outdated or vulnerable dependencies provide another potential attack path if you find that the source code for the lambda itself is not directly vulnerable.
Sidenote: Often, Systems Manager Parameter Store or Secrets Manager are good places to look after you get admin in an account. Cross-Account access is also a possible next step after gaining admin but is not a factor in this scenario.
As a final step, you will need to run “aws –profile bilbo–region us-east-1 secretsmanager list-secrets”, which will read out all secrets in SecretsManager. Then you can run “aws –profile bilbo –region us-east-1 secretsmanager get-secret-value –secret-id [ARN_OF_TARGET_SECRET]” to read out this scenario’s secret and complete this scenario.
This walkthrough demonstrates general cloud pentesting techniques that can be used to assess lambda functions, and explores some details of the CloudGoat scenario “vulnerable_lambda”.
We hope that CloudGoat is a valuable educational tool for you in your cloud security journey. If you want to contribute to CloudGoat, we welcome issues and pull requests.
For future Rhino blog updates and research, follow us on Twitter, Linkedin, or via RSS feed.