One of the most important steps in an infrastructure attack is persisting in the target’s environment. Persistence often needs to be established multiple times, depending on the level of access they gain. Security systems have become smart enough to detect many forms of persistence mechanisms. This has led attackers to continue to find new and creative ways to persist in a target’s environment.
Google Workspace Apps Script is a feature that allows any user with a Gmail account to automate business applications and enables the applications to interact with each other. Underneath, when an Apps Script app is deployed, a GCP Project is created on the GCP Organization the account is part of. Aside from the format of the Project ID, these projects are not very different from a normal GCP project. This means, an attacker can choose to use one of these projects to host resources and persist on the target. They can also create a GCP project with the same name format as an Apps Script project to impersonate a legitimate Apps Script Project and evade detection.
This blog will go through how Apps Script projects work and how an attacker can utilize the Apps Script projects to persist in a target’s environment. Then, we will look into how these techniques can be detected and prevented, so they will not be able to be maliciously utilized by attackers.
The ins and outs of Apps Script
Google Workspace Apps Script, associated with the endpoint script.google.com
, are a low code solution, allowing anyone with a Gmail account to automate business applications that integrate with Google Workspace. It offers a scripting interface using JavaScript to integrate Google services and build lightweight automations.

Apps Script is highly flexible. With it, you can:
- Create custom menus, dialogs, and sidebars in Google Docs, Sheets, and Forms
- Develop custom functions and macros for Google Sheets
- Publish web apps, either as standalone applications or embedded within Google Sites
- Connect with other Google services such as AdSense, Analytics, Calendar, Drive, Gmail, and Maps
- Build lightweight add-ons and share them through the Google Workspace Marketplace

When an App Script is created, a project with a prefix sys-
is automatically created. These projects are not visible in the organization’s projects list on the console, which makes sense, since they are not considered Organization Projects.

However, the projects are visible through the terminal tool (gcloud
) by identities with access to execute resourcemanager.projects.list
.

When an App Script project is created, GCP will create a Resource Manager folder and subfolder by default in the organization, with the names system-gsuite/apps-script
. Here again, there seem to be no projects inside these folders when viewed in the console.
Console view of the Apps Script subfolder with no projects visible

Using the CLI, however, we see the App Script projects inside the apps-script
subfolder. This is where the App Script projects reside after creation.

Abusing Apps Script impersonation on a GCP Organization
Cryptomining Instance
App Script projects follow an ID format of sys-<26 numbers>
. In GCP, we can create a project and store it in any folder or subfolder we have access to and we can set the project name to anything as long as it contains ASCII letters, digits, and hyphens, and is between 6-30 characters. The combination of sys-<26 numbers>
is exactly 30 characters long, containing numbers, letters, and a hyphen.

One difference we found was how the projects looked based on the location where they were stored. If a project is stored at the organization level, the project, though having an ID format of sys-<26 numbers>
, will show in the console (project sys-00000000000000000000000000). However, when created inside the apps-script
folder, the app does not show as a project on the console (project sys-11111111111111111111111111
).

The projects are still listed when resourcemanager.projects.list
is executed on the terminal.

An attacker with permissions to resourcemanager.projects.create
can utilize the fact App Script projects do not show as other projects do to create a project in the target’s organization and store resources there. Each project can also have a name, which can be provided by the creator. An attacker can also look at other projects in the target’s organziation to find a convincing name for the project.

For example, a bad actor could use this hidden project to create a large instance and use it as a cryptomining harvester. To do that, we need to:
- Enable billing for the project
- Enable the compute API
- Create an instance

The attacker now controls a high performance instance they can use as a cryptomining harvester.
Persist on the Organization using a Service Account inside a hidden project
Persistence allows an attacker to return to the target’s infrastructure, ideally as a highly privileged identity. There are different ways to persist in a GCP organization, including user creation service account creation, creating permanent credentials, and creating resources with highly privileged identities assigned to them. If a persistence mechanism can be created inside a project, it can be created into an App Script impersonated project. For example, we can create a service account, create a key for it, assign a highly privileged role on the organization and other projects, and keep it for later use.

To make matters worse, the identity will only be listed if the project name is known. We can even put a policy on the project that prevents anybody from accessing the service account. This isn’t an “unbreakable” prevention, but it might prevent some attempts to clean up the service account, especially since these projects look like they are created and managed by Google.
name: organizations/ORG_ID/denyPolicies/deny-service-account-all
displayName: "Restrict all SA usage"
rules:
- denyRule:
deniedPrincipals:
- principalSet://goog/public:all
deniedPermissions:
- iam.serviceAccounts.*
Why even impersonate an Apps Script project?
An Apps Script project underneath is a normal GCP project. What differs from a normal project is that by default, no identity, except for one identity controlled by Google, will have the right access to it. The service account appsdev-apps-dev-script-auth@system.gserviceaccount.com
is the universal identity that creates the Apps Script projects and is only managed by Google. It is the only identity that, by default, can manage the Apps Script project and its resources.

An attacker with the right permissions can modify the project’s IAM policy to allow itself to host any resource on any service it wants on this project.

For example, a bad actor could:
- Creating a service account and assigning an organization policy to it to persist in the target organization
- Link the project to a Billing Account and create large resources on it for cryptomining
Detecting the abuse of Apps Script projects
Finding project impersonation by looking at the billing information of the project
GCP has different billing types for resources, with some free without limits. IAM identities are a good example of such resources. Other examples of free resources include IAM resource manager organizations, artifact registry, VPC basic networking, and more.
To be able to use a specific service and its resources, an organization owner needs to link a project to a Billing Account. Linking a project to a Billing Account is a fancy way of saying “put a payment method on the project”. The Billing Account links a payment method with the project, and each month, based on the usage of resources, the owner of the project pays, while the Billing Account provides them with an invoice for the costs.
Being able to create a large compute instance means a Billing Account needs to be linked to the project. One way to detect if an App Script project is created by an attacker and used as a host for large resources is to see if the project is linked to a Billing Account. In the example below, project sys-22222222222222222222222222
, which is an attacker created project with a large compute instance, will have a Billing Account linked to it (as seen by the fields billingAccountName
and billingEnabled
), while a legitimate App Script project (project sys-14600875379148140018929136
) does not require a link to a billing account.

Blending the project in by modifying billing information
One bypass to this detection is to unlink the project from the Billing Account. The project will need to be part of the billing account if the attacker wants to create an instance (or any paid service). So, any resource or service not offered in the free-tier will need to be deployed into a project with a linked Billing Account.
Unlinking the project from the Billing Account will result in the resource being deleted.

IAM service accounts do not fall into the paid resources scope, so even a project not linked to a billing account can contain a persistent service account, breaking the previous detection.

Finding occurrences of an App Script project impersonation through enabled API Services
To be able to use a specific service on a project through the API (which gcloud
uses), the service API needs to be enabled. By default, a new project will have several services enabled, with more services being able to be enabled or disabled using serviceusage.services.enable
or serviceusage.services.disable
.
Apps Script projects, on the other hand, by default will have no services enabled, but they can be enabled based on demands. One way to detect if a project is not actually an Apps Script project is to look at the enabled services.

Blending in by disabling API endpoints
One problem with this detection method is that an Apps Script project might need some services enabled, based on the task they will execute. An attacker can choose to disable all the services they are not using and make the project seem a bit more legitimate.

For example, to be able to create a compute instance in the project, compute
and oslogin
services need to be enabled. If disabled while a resource exists, the execution will fail, indicating current project usage. Other than that, the rest of the services can be disabled and then be temporarily re-enabled by the attacker each time a task executes on the target’s environment.

Another problem is that some service APIs will work in a project without the service API being enabled on the project. So, services like IAM or some basic storage commands will be allowed, as well as the service usage API (so that the service APIs can be enabled or disabled on the project) and Resource Manager.
- IAM
- Cloud Resource Manager
- Service Usage API
- Cloud Storage (base functionality is always available; enabling billing gives extra control, billing, and metrics).
This means, a project showing no services enabled will still be able to host a service account, which can be used as a persistence mechanism by the attacker.
Finding occurrences of an App Script project impersonation in the Logs
Another detection approach is looking at the logs for the apps-script
folder. In the example below, the projects created by the attacker (sys-11111111111111111111111111
and sys-22222222222222222222222222
) have the principalEmail
field set to the creator’s user email, while the ones created by Google have an email of appsdev-apps-dev-script-auth@system.gserviceaccount.com
. This is tricky because the events can only be found under the folder’s events, not the Organization’s.

Limiting Project Impersonation using organization policies
Apps Script projects, when created, will have an ID format of sys-<26 random digits>
. One way to prevent project creation of an Apps Script impersonator would be to deny the creation and update of projects with an ID format of sys-<26 random digits>
using the following policy.
name: >-
organizations/012345678912/customConstraints/custom.denyAppsScriptProjectImpersonation
resource_types: cloudresourcemanager.googleapis.com/Project
method_types:
- CREATE
- UPDATE
condition: 'resource.projectId.matches(''sys-[0-9]{26}'')'
action_type: DENY
display_name: Deny Apps Script Project Impersonation
description: ''
Whenever an attacker tries to create a project with that format, the organization policy will deny the execution.

The Organization Policy will deny the creation of any project with an ID format of sys-<26 random digits>
, including legitimate ones created by Google through the endpoint script.google.com
.

Detecting Apps Script project abuse with Exaforce
Exaforce provides layered coverage to detect and stop malicious usage of Apps Script projects, whether through impersonation or misuse. Our approach leverages advanced anomaly detection to understand the baseline behavior of entities in your environment to spot abnormal behavior. We’ve enabled some new detections to protect our customers as a result of this research.
- Exaforce detects Google Apps Script projects that use unusual API services, labels, or policy bindings, so they are flagged during onboarding.
- Exaforce detects project impersonation attempts wherein an attacker attempts to create projects that look like an Apps Script project.
Below is an example scenario of an App Script Project Impersonation attempt.

The detection overview provides a summary and conclusion from our Exabot’s (AI agent) automated triage of the alert. It outlines critical information such as the principal in question.

We map related events for this principal into a session, so it’s easy to understand the context in which this project was created and other activities performed by this principal for comprehensive impact analysis.

All related events and resources impacted are mapped visually into a graph for easy investigation.
Preventative controls
Exaforce detections will ensure you have visibility into this potential issue. We also recommend the enforcement of the organization policy mentioned above that blocks creation of projects with IDs matching sys-[0-9]{26}
if Apps Script is not in use at the organization. This will lower your attack surface and greatly improve the security posture of your organization.
Stealthy persistence and defense
Apps Script projects can serve as stealthy persistence mechanisms if left unmonitored. Attackers can impersonate them to hide cryptomining, privileged service accounts, or other malicious resources inside your environment.
Defenders need to understand how Apps Script projects work under the hood and how to detect and block potential abuse. Leveraging organization policies and strong detections like those provided by Exaforce provides comprehensive coverage for this persistence vector.