aws-samples / cloudfront-authorization-at-edge

Protect downloads of your content hosted on CloudFront with Cognito authentication using cookies and Lambda@Edge
https://aws.amazon.com/blogs/networking-and-content-delivery/authorizationedge-using-cookies-protect-your-amazon-cloudfront-content-from-being-downloaded-by-unauthenticated-users/
MIT No Attribution
461 stars 157 forks source link

A potential risk in cloudfront-authorization-at-edge which can be used to upload malicious code. #253

Closed zolaer9527 closed 8 months ago

zolaer9527 commented 8 months ago

Hello! I found a potential risk in the cloudfront-authorization-at-edge when I deployed it in the AWS Serverless Application Repository. It can leverage "lambda:UpdateFunctionCode" to upload a carefully crafted malicious code zip which will replace the code of the specified function. Then the malicious can leverage the malicious code to do whatever he/she likes, such as stealing sensitive information, causing DOS attacks, and other more serious consequences.

Detail Analysis

The arn of the cloudfront-authorization-at-edge application is "arn:aws:serverlessrepo:us-east-1:520945424137:applications/cloudfront-authorization-at-edge". After the application deployed, there was a role named serverlessrepo-cloudfront-LambdaCodeUpdateHandlerR-MTRLVJ4XJCPJ used by a serverlessrepo-cloudfront--LambdaCodeUpdateHandler-r390kIqpUdsD function. This role has lambda:UpdateFunctionCode permission for some functions with specific characters in their names. These characters are "-CheckAuthHandler-", "-ParseAuthHandler-", "-RefreshAuthHandler-", "-SignOutHandler-", "-HttpHeadersHandler-", and "-TrailingSlashHandler-". There were four functions created by the application with the characters in their names. Thus, a malicious can leverage the lambda:UpdateFunctionCode permission to replace these four function codes with some malicious codes, resulting in information leak, permission escalation, etc.

To Reproduce

I deployed the cloudfront-authorization-at-edge application in the console. After that, I executed the following command in Cloudshell.

  1. aws sts assume-role --role-arn "arn:aws:iam::747955086145:role/serverlessrepo-cloudfront-LambdaCodeUpdateHandlerR-MTRLVJ4XJCPJ" --role-session-name CLISession
  2. Then I exported AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN from the echo result into the environment variable and switched users.
  3. Finally, I verified my current identity and uploaded a pre-prepared code zip to replace the original code of the function.

Screenshots piture-1 piture-2

Mitigation Discussion

  1. If code signing is enabled for the function, the code package must be signed by a trusted publisher. So these four functions' code signing should be enabled.
  2. The permission "lambda:UpdateFunctionCode" should be removed if it will not affect the normal functionality.

Question

  1. Is it a real issue in cloudfront-authorization-at-edge?
  2. If it's a real issue, can any of my suggestions be used to solve this problem?
  3. If my suggestions can be used to solve the problem, can you give me a CVE?
ottokruse commented 8 months ago

Hi @zolaer9527

Thanks for the report. Here's our findings:

You mention as first step executing this from CloudShell:

aws sts assume-role --role-arn "arn:aws:iam::747955086145:role/serverlessrepo-cloudfront-LambdaCodeUpdateHandlerR-MTRLVJ4XJCPJ" --role-session-name CLISession

But that is impossible, because that role can only be assumed by the AWS Lambda service (via the trust relationships of the role), so you must have already altered the trust relationship of the role to allow the role to be assumed by your AWS console user?

Here is the definition of the Lambda function: https://github.com/aws-samples/cloudfront-authorization-at-edge/blob/1adb1ede0ed73741b8f36c6a3ee8ce769fd697a7/template.yaml#L1144 Note that the associated role is created automatically by AWS SAM, and should only have a trust relationship to the AWS Lambda service (or there would be a bug in AWS SAM), and I checked a deployment in my own account where this is indeed the case.

You can indeed log the credentials from a Lambda function, and then use those credentials to do what the Lambda function is allowed to do. Normally you wouldn't do this, but as developer with admin privileges you could if you wanted to. There's no surprise there though? Note that this requires you to alter the Lambda function code, to add that logging. Threat actors would not be able to do that.

I believe your scenario is not a plausible threat, unless your AWS account is compromised (in which case you have more pressing concerns):

Back to the role itself: the permissions it has are necessary and required for deployment of the solution with CloudFormation. It needs "lambda:UpdateFunctionCode" the permission.

You are right that Lambda function code signing is an option to ensure with great confidence that only code you trust is deployed to Lambda––if you require that level of assurance. This however is beyond the scope of this sample solution. If you want to add it, you can take this sample solution and tweak it to suit your needs.

I will close this issue, let us know if the above didn't address your concern satisfactorily.

zolaer9527 commented 7 months ago

Hello @ottokruse, in the trust relationships of the role, I find the principal is "Service": "lambda.amazonaws.com". Does this mean that the role can be assumed by all AWS Lambda functions? If not, how does the AWS implement one-on-one binding between a function and a role?

ottokruse commented 7 months ago

Does this mean that the role can be assumed by all AWS Lambda functions

Yes, but only by Lambda functions within the account.

zolaer9527 commented 7 months ago

OK, thank you!