In late 2024, a significant supply chain attack targeted Chrome extension developers, ultimately affecting more than 2.6 million users. The campaign began with developers receiving a deceptive email containing a link. This link redirected them to a legitimate Google login page, which then fraudulently requested authorization for a malicious Google OAuth application named “Privacy Policy Extension.” Crucially, this application sought the https://www.googleapis.com/auth/chromewebstore scope.
Once a developer or user granted permissions to this adversary-controlled app, the threat actor immediately gained the ability to modify and publish a malicious version of the developers Chrome extensions to the Google Chrome Web Store. The compromised extension, upon installation, was designed to collect and exfiltrate session cookies and authentication tokens, specifically targeting Facebook Ads accounts.
Using Red Canary’s minimal viable story framework for analyzing detection data, this blog will detail how to detect and remediate this type of attack within Google Workspace. We’ll also share detection and remediation opportunities modeled after this supply chain attack.
What happened?
In this scenario, the following event occurred:
At 2025-10-29T01:08:13.274Z, within Google Workspace account
C01wnsypx,developer@example.com(ID:123456789012345678901) authorized an application,Privacy Policy Extension(Client ID:123456789012-abc123.apps.googleusercontent.com) and consented to the following Chrome Web Store API OAuth permission:https://www.googleapis.com/auth/chromewebstore. This action was performed from the following IP address:136.226.68.203.
Summarized at a higher level, this means that the Privacy Policy Extension app has access to see, edit, update, or publish any Chrome Web Store extensions, themes, apps, and licenses that developer@example.com has access to. The following questions may arise in the course of an investigation:
- Did the legitimate user behind the
developer@example.comidentity actually mean to use this application? Was the user somehow coerced into this consent? - Is it authorized for this app to interact with the user’s extensions, themes, apps, and licenses?
- Is this app actually sanctioned within this organization?
In order to answer these questions, we need data, specifically, the events.name: authorize activity from the Admin Report OAuth Token Audit Activity.
Here is the mocked data that was used to model this incident:
{
"kind": "admin#reports#activity",
"id": {
"time": "2025-10-29T01:08:13.274Z",
"applicationName": "token",
"customerId": "C01wnsypx"
},
"actor": {
"email": "developer@example.com",
"profileId": "123456789012345678901"
},
"ipAddress": "136.226.68.203",
"networkInfo": {
"ipAsn": [
22616
],
"regionCode": "US",
"subdivisionCode": "US-VA"
},
"events": [
{
"type": "auth",
"name": "authorize",
"parameters": [
{
"name": "client_id",
"value": "123456789012-abc123.apps.googleusercontent.com"
},
{
"name": "app_name",
"value": "Privacy Policy Extension"
},
{
"name": "client_type",
"value": "WEB"
},
{
"name": "scope_data",
"multiMessageValue": [
{
"parameter": [
{
"name": "scope_name",
"value": "https://www.googleapis.com/auth/chromewebstore"
},
{
"name": "product_bucket",
"multiValue": [
"OTHER"
]
}
]
}
]
},
{
"name": "scope",
"multiValue": [
"https://www.googleapis.com/auth/chromewebstore"
]
}
]
}
]
}
Who
This corresponds to the actor that performed the action.
“developer@example.com (ID: 123456789012345678901)”
Field derivation
| Operation | Field | Value | Description |
authorize | actor.email | developer@example.com | The human-readable name for the identity. |
authorize | actor.profileId | 123456789012345678901 | The user’s unique Google Workspace profile ID. |
What
This corresponds to the resource that was affected and the specifics of the action that was performed on it.
“authorized an application, Privacy Policy Extension (Client ID: 123456789012-abc123.apps.googleusercontent.com) and consented to the following Chrome Web Store API OAuth permission: https://www.googleapis.com/auth/chromewebstore”
Field derivation
| Operation | Field | Value | Description |
authorize | events.name | “authorized” | The action performed by the actor. |
authorize | events[0]['parameters']['app_name'].value | Privacy Policy Extension | The name of the app. |
authorize | events[0]['parameters']['client_id'].value | 123456789012-abc123.apps.googleusercontent.com | The globally unique identifier of the application. |
authorize | events[0]['parameters']['scope'].multiValue | ["https://www.googleapis.com/auth/chromewebstore"] | This array indicates the specific OAuth scopes that were consented to. |
When
This corresponds to the time in which the action occurred.
“At 2025-10-29T01:08:13.274Z”
Field derivation
| Operation | Field | Value | Description |
authorize | id.time | 2025-10-29T01:08:13.274Z | The date and time that this event occurred. |
Where
This corresponds to the environment in which the action occurred.
“within Google Workspace account C01wnsypx”
Field derivation
| Operation | Field | Value | Description |
authorize | id.customerId | C01wnsypx | The account in which the action took place. |
Whence
From where did the action originate?
“This action was performed from the following IP address: 136.226.68.203.”
Field derivation
| Operation | Field | Value | Description |
authorize | ipAddress | 136.226.68.203 | The IP address from which the actor performed the action. |
Detection
If you are concerned about users being phished and consenting to unsanctioned applications that have requested rare or high-risk scopes, your detection strategy must be aligned with clearly defined objectives.
Detection objectives
- Do you aim to detect all applications with high-risk scopes, or only a subset?
- What is your tolerance for high detection volume and false positives?
- Can you assess the prevalence of existing applications in your environment?
The combination of your scope and objectives will dictate the most effective detection strategy.
As a foundational step to minimize detection volume, we recommend first identifying the existing applications in your environment. You can leverage the following GAM command to create this initial list:
gam all users print tokens todrive1. Detect an initial authorization of a high-risk application, defined as an app never before seen in the organization that requests high-risk scopes.
This strategy focuses on two key criteria:
- New application: The application doesn’t already exist in the organization, as an existing app is more likely to be sanctioned.
- High-risk scopes: The application requests scopes that Google defines as high-risk, such as those related to sending mail or deleting files in Drive. As of this writing, Google allows you to restrict access to high-risk scopes for Gmail, Google Drive, and Google Chat, including the following:
https://mail.google.com/,https://www.googleapis.com/auth/gmail.compose,https://www.googleapis.com/auth/gmail.insert,https://www.googleapis.com/auth/gmail.metadata,https://www.googleapis.com/auth/gmail.modify,https://www.googleapis.com/auth/gmail.readonly,https://www.googleapis.com/auth/gmail.send,https://www.googleapis.com/auth/gmail.settings.basic,https://www.googleapis.com/auth/gmail.settings.sharing,https://www.googleapis.com/auth/documents,https://www.googleapis.com/auth/documents.readonly,https://www.googleapis.com/auth/drive,https://www.googleapis.com/auth/drive.activity,https://www.googleapis.com/auth/drive.activity.readonly,https://www.googleapis.com/auth/drive.admin,https://www.googleapis.com/auth/drive.admin.labels,https://www.googleapis.com/auth/drive.admin.labels.readonly,https://www.googleapis.com/auth/drive.admin.readonly,https://www.googleapis.com/auth/drive.admin.shareddrive,https://www.googleapis.com/auth/drive.admin.shareddrive.readonly,https://www.googleapis.com/auth/drive.apps,https://www.googleapis.com/auth/drive.apps.readonly,https://www.googleapis.com/auth/drive.categories.readonly,https://www.googleapis.com/auth/drive.labels.readonly, https://www.googleapis.com/auth/drive.meet.readonly, https://www.googleapis.com/auth/drive.metadata,https://www.googleapis.com/auth/drive.metadata.readonly,https://www.googleapis.com/auth/drive.photos.readonly,https://www.googleapis.com/auth/drive.readonly,https://www.googleapis.com/auth/drive.scripts,https://www.googleapis.com/auth/drive.teams,https://www.googleapis.com/auth/forms.body,https://www.googleapis.com/auth/forms.body.readonly,https://www.googleapis.com/auth/forms.currentonly,https://www.googleapis.com/auth/forms.responses.readonly,https://www.googleapis.com/auth/presentations,https://www.googleapis.com/auth/presentations.readonly,https://www.googleapis.com/auth/script.addons.curation,https://www.googleapis.com/auth/script.projects,https://www.googleapis.com/auth/sites,https://www.googleapis.com/auth/sites.readonly,https://www.googleapis.com/auth/spreadsheets,https://www.googleapis.com/auth/spreadsheets.readonly,https://www.googleapis.com/auth/chat.delete,https://www.googleapis.com/auth/chat.import,https://www.googleapis.com/auth/chat.messages,https://www.googleapis.com/auth/chat.messages.readonly
This detection strategy can be implemented with the following pseudo-logic:
| Operation | Field | Condition | Value | Description |
authorize | events[0]['parameters']['client_id'].value | Not in | A list of approved client IDs | The application’s Client ID is not in a pre-approved list. |
authorize | events[0]['parameters']['client_id'].value | Not in | A list of client IDs observed in the last X amount of days (e.g., 90 days). | The application’s Client ID has not been seen in the environment in the last X amount of days (e.g., 90 days). |
authorize | events[0]['parameters']['scope'].multiValue | Contains one or more of the following | See the list of risky scopes above, e.g., https://www.googleapis.com/auth/gmail.readonly | One or more of the known high-risk scopes. |
2. Detect an initial authorization of a potentially malicious application, defined as an app never before seen in the organization that requests rare or unapproved scopes.
This strategy focuses on two key criteria:
- New application: The application has never been authorized by any user in the organization before.
- Suspicious scopes: The application is requesting permissions (scopes) that are rarely requested by other applications or are considered high-risk/unapproved by the organization’s policy.
This detection strategy can be implemented with the following pseudo-logic:
| Operation | Field | Condition | Value | Description |
authorize | events[0]['parameters']['client_id'].value | Not in | A list of approved client IDs | The application’s Client ID is not in a pre-approved list. |
authorize | events[0]['parameters']['client_id'].value | Not in | A list of client IDs observed in the last X amount of days (e.g., 90 days). | The application’s Client ID has not been seen in the environment in the last X amount of days. |
authorize | events[0]['parameters']['scopes'] | Not in | A list of common or approved scopes | The scopes requested by the application are not in a list of common or approved scopes. |
Remediation
If it has been determined that an OAuth consent grant was malicious, the following immediate actions can be performed:
1. Revoke tokens for the flagged client ID.
Using the client ID value found in events[0]['parameters']['client_id'].value field, the following GAM command can be executed:
gam all users delete tokens clientId 1234567890-abc123.apps.googleusercontent.com2. Block the flagged client ID.
Using the client ID value found in events[0][‘parameters’][‘client_id’].value field, an administrator can revoke an application’s access to any Google data.
Mitigations
Fortunately, Google offers several opportunities to mitigate this technique. As discussed, this attack relies on a victim having permission to consent to an application without prior administrative approval.
As Google states, by default, users can sign in with Google to any third-party app, and accessed apps can request unrestricted Google data for that user.
To prevent users from introducing unvetted applications and over-provisioning OAuth permissions, Google provides two primary mitigation options:
- Restrict all third-party app access (safest, highest administrative burden): This option involves not allowing users to access any third-party apps by default, but still allowing them to request access to unconfigured apps. This requires an administrator with the Security settings administrator privilege to configure the app’s access settings (e.g., Trusted, Limited, Specific Google data, or Blocked).
- Allow limited access (balances security and usability): This option grants users the ability to consent to apps that request only basic information necessary for “Sign in with Google,” such as a user’s name, email, and profile picture. This eases the administrative burden while maintaining a stronger security posture than the default.
Threat hunting
Proactive hunting methods include querying for external apps that have been consented to by fewer than a set minimum number of users, helping administrators zero in on unique and potentially dangerous installations. Threat hunting for malicious OAuth applications should then continue with a thorough audit of all authorized apps in your environment. Review each app’s name, publisher, permissions, and unique application ID.
One of the strongest indicators of a custom-built malicious app is “rare” community use meaning the app is not commonly found across other organization and may be tailored for a specific target. Malicious apps often request excessive, high-risk permissions, such as full read/write access to files or the ability to read and send mail, far beyond what’s needed for normal operation. Unlike trusted enterprise apps, suspicious applications are usually authorized by only one or a handful of users, often without elevated privileges. Analysts should regularly review activity and audit logs for behavioral anomalies, such as dormant apps suddenly using rare or risky permissions, which can signal the start of malicious actions like internal phishing or data theft.