Vulnerable AWS Lambda function – Initial access in cloud attacks
content:
Table of contents Lambda functions Shared responsibility model Real attack scenarios Mitigate this vector attack ConclusionContent
Our security research team will explain a real attack scenario from the black box and white box perspective on how a vulnerable AWS Lambda function could be used by attackers as initial access into your cloud environment. Finally, we show the best practices to mitigate this vector of attack. Serverless is becoming mainstream in business applications to achieve scalability, performance, and cost efficiency without managing the underlying infrastructure. These workloads are able to scale to thousands of concurrent requests per second. One of the most used Serverless functions in cloud environments is the AWS Lambda function. One essential element of production raising an application is security. An error in code or a lack of user input validation may cause the function to be compromised and could lead the attackers to get access to your cloud account.
About AWS Lambda function
AWS Lambda is an event-driven, serverless compute service which permits the execution of code written in different programming languages and automates actions inside a cloud environment. One of the main benefits of this approach is that Lambda runs our code in a highly available compute infrastructure directly managed by AWS. The cloud provider takes care of all the administrative activities related to the infrastructure underneath, including server and operating system maintenance, automatic scaling, patching, and logging. The user can just use the service implementing their code and the function is ready to go.Security, a shared pain
From a security perspective, due to its nature to be managed by the cloud provider but still configurable by the user, even the security concerns and risks are shared between the two actors.Attack Scenarios
We are going through two attack scenarios using two different testing approaches: black box and white box testing, which are two of the main testing approaches used in penetration testing to assess the security posture of a specific infrastructure, application, or function. Looking at the Lambda function from a different perspective would help to create a better overall picture of the security posture of our function, and help us better understand the possible attacks and the related risks.Black box vs white box
In Black box testing, whoever is attacking the environment doesn’t have any information about the environment itself and the internal workings of the software system. In this approach, the attacker needs to make assumptions about what might be behind the logic of a specific feature and keep testing those assumptions to find a way in. For our scenario, the attacker doesn’t have any access to the cloud environment and doesn’t have any internal information about the cloud environment or the functions and roles available in the account. In White box testing, the attacker already has internal information which can be used during the attack to achieve their goals. In this case, the attacker has all the information needed to find the possible vulnerabilities and security issues. For this reason, white box testing is considered the most exhaustive way of testing. In our scenario, the attacker has read-only initial access in the cloud environment and this information can be used by the attacker to assess what is already deployed and better target the attack.#1 Black Box Scenario
aws s3 ls prod-file-bucket-eu
aws s3api get-object-tagging --bucket prod-file-bucket-eu --key config161.zip
aws s3 cp config.zip s3://prod-file-bucket-eu/ curl -X PUT -T config.zip \ -H "Host: prod-file-bucket-eu.s3.amazonaws.com" \ -H "Date: Thu, 02 Dec 2021 15:47:04 +0100" \ -H "Content-Type: ${contentType}" \ http://prod-file-bucket-eu.s3.amazonaws.com/config.zipOnce the file is uploaded, we can check the file tag and see that the tag has been added automatically.
aws s3api get-object-tagging --bucket prod-file-bucket-eu --key config161.zip
aws sts get-caller-identity { "UserId": "AROA2PVZZYWS7MCERGTMS:corpFuncEasy", "Account": "AccountID", "Arn": "arn:aws:sts::AccountID:assumed-role/corpFuncEasy/corpFuncEasy" }Once in there, the attacker can start the enumeration process to assess the privileges obtained and see if there are paths to further escalate the privileges inside the cloud account.
#1 White Box Scenario
aws sts get-caller-identity { "UserId": "AIDA2PVZZYWS3MXZKDH66", "Account": "AccountID", "Arn": "arn:aws:iam::AccountID:user/operator" }
aws iam list-attached-user-policies --user-name operator
aws lambda list-functions
ws lambda get-function --function-name corpFuncEasy
{ "Configuration": { "FunctionName": "corpFuncEasy", "FunctionArn": "arn:aws:lambda:us-east-1:AccountID:function:corpFuncEasy", ... }, "Code": { "RepositoryType": "S3", "Location": "https://prod-04-2014-tasks.s3.us-east-1.amazonaws.com/snapshots/AccountID/corpFuncEasy-9c1924b0-501a-..." }, "Tags": { "lambda-console:blueprint": "s3-get-object-python" } }With knowledge of the code, we can better assess the function and see if the security best practices have been applied.
How to mitigate this vector attack
We have seen the attack scenario from the black box and white box perspectives, but what can we do to mitigate this scenario? In the proposed scenario, we covered different AWS components, like S3 buckets and AWS lambda, in which some security aspects have been neglected. In order to successfully mitigate this scenario, we can act on different levels and different features. In particular, we could:- Disable the public access for the S3 bucket, so that it will be accessible just from inside and to the users who are authenticated into the cloud account.
- Check the code used inside the lambda function, to be sure there aren’t any security bugs inside it and all the user inputs are correctly sanitized following the security guidelines for writing code securely.
- Apply the least privileges concept in all the AWS IAM Roles applied to cloud features to avoid unwanted actions or possible privilege escalation paths inside the account.
Disable the public access for the S3 bucket
An S3 bucket is one of the key components in AWS used as storage. S3 buckets are often used by attackers who want to break into cloud accounts. It’s critical to keep S3 buckets as secure as possible, applying all the security settings available and avoiding unwanted access to our data or files. For this specific scenario, the bucket was publicly open and all the unauthorized users were able to read and write objects into the bucket. To avoid this behavior, we need to make sure that the bucket is available, privately applying the following security settings to restrict the access.{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": [ "s3:GetObject", "s3:PutObject" ], "Resource": "arn:aws:s3:::3bucket********/www/html/word/*" }, { "Sid": "PublicReadListObject", "Effect": "Allow", "Principal": "*", "Action": "s3:List*", "Resource": "arn:aws:s3:::s3bucket********/*" } ] }
Check the code used inside the lambda function
As any other web application, making sure the code is implemented following the security best practice is fundamental to avoid security issues. The code in the Lambda function isn’t an exception and we need to be sure the code is secure and bulletproof. In the case, the following is shown before the vulnerable piece of code an attacker could use to attack the function:file_count_KB = subprocess.check_output( "stat -c %s " + file_download_path, shell=True, stderr=subprocess.STDOUT ).decode().rstrip()The variable file_download_path contains the full file path including the file name. The path is concatenated directly into the command line to execute the command. However, the file name is decided and controlled by the user, so for this reason, we need to apply the proper user input validation and sanitisation based on the chars allowed before appending the path into a command. To make sure to apply the right and effective validation, we need to have a clear idea on which chars are allowed and which we want to have inside our string, and apply the best practices on input validation. Using regex, it’s possible to allow just the chars that we want inside our file path and block the execution in case an attacker tries to submit bad chars. In the example reported below, we can see an example of regex to validate linux file path.
pattern = "^\/$|(\/[a-zA-Z_0-9-]+)+$" if re.match(pattern, file_download_path): file_count_KB = subprocess.check_output( "stat -c %s " + file_download_path, shell=True, stderr=subprocess.STDOUT ).decode().rstrip()It is worth mentioning that the regex is specific to the field or input we want to validate. OWASP provides great best practices you can follow when you need to handle user input of any kind.