


















How to detect and respond to OAuth consent attacks in Google Workspace
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.
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:
developer@example.com identity actually mean to use this application? Was the user somehow coerced into this consent?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"
]
}
]
}
]
}
This corresponds to the actor that performed the action.
“developer@example.com (ID: 123456789012345678901)”
| 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. |
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”
| 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. |
This corresponds to the time in which the action occurred.
“At 2025-10-29T01:08:13.274Z”
| Operation | Field | Value | Description |
authorize | id.time | 2025-10-29T01:08:13.274Z | The date and time that this event occurred. |
This corresponds to the environment in which the action occurred.
“within Google Workspace account C01wnsypx”
| Operation | Field | Value | Description |
authorize | id.customerId | C01wnsypx | The account in which the action took place. |
From where did the action originate?
“This action was performed from the following IP address: 136.226.68.203.”
| Operation | Field | Value | Description |
authorize | ipAddress | 136.226.68.203 | The IP address from which the actor performed the action. |
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.
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 todriveThis strategy focuses on two key criteria:
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.readonlyThis 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. |
This strategy focuses on two key criteria:
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. |
If it has been determined that an OAuth consent grant was malicious, the following immediate actions can be performed:
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.comUsing 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.
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:
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.
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。