Summary
| Forestall ACL Alias | GWS_ADD_GROUP_MEMBER |
| GWS Alias | Add Group Member |
| Affected Object Types | GWS Group |
| Exploitation Certainty | Certain |
| Granting Roles / Positions | Google Workspace Super Admin, Groups Admin (directory role), existing Group Owner, existing Group Manager |
Description
GWS_ADD_GROUP_MEMBER represents the ability to add new members to a Google Workspace Group. When a controlled identity is added as a member of a group, it immediately inherits all GCP IAM roles bound to that group at any resource scope (organization, folder, project, or resource level).
Group membership changes are recorded exclusively in Google Workspace audit logs (admin.googleapis.com or groups_enterprise application). The GCP IAM audit log event setIamPolicy is never triggered by group membership changes, so defenders monitoring only GCP Cloud Audit Logs will not detect this escalation. Additionally, member additions via the Groups UI route to the groups_enterprise stream while Admin API additions route to the admin stream — monitoring only one stream misses the other.
When an identity is added, it immediately gains access to the group’s entire email conversation history and archive. This historical access generates no audit event.
Groups created via the GCP IAM console (not the Google Admin Console) inherit the organization-wide default that allows any org member to self-join via the Groups UI without any admin action. This behavior was classified by Google as “Won’t Fix (Intended Behavior)” (NetSPI VRP disclosure, July 2024). If a group has allowExternalMembers=true, external identities can also be added, bypassing Domain-Restricted Sharing policies.
Identification
gcloud CLI
# Find all GCP-bound groups across the organizationgcloud asset search-all-iam-policies \ --scope='organizations/ORG_ID' \ --query='memberTypes:group' \ --format="table(resource, policy.bindings[].role, policy.bindings[].members)"# Identify who currently has OWNER or MANAGER roles on a group (can add members)gcloud identity groups memberships list \ --group-email=$GROUP_EMAIL \ --format="table(preferredMemberKey.id, roles[0].name)" \ | grep -E "OWNER|MANAGER"# Find open-join groups (any org member can self-join without admin action)# Check join policy for each GCP-bound group via the Groups Settings APIPROJECT_ID="my-project"gcloud projects get-iam-policy $PROJECT_ID --format=json | \ jq -r '.bindings[] | .members[] | select(startswith("group:"))' | sort -uGoogle Admin Console
- Open Google Admin Console (
admin.google.com) → Directory → Groups. - Click the target group → Members.
- Identities with Role: Owner or Role: Manager can add new members.
- Check Group settings → Who can join the group — if set to
Anyone in the organization, any user can self-join without admin intervention. - Check Allow members outside your organization — if enabled, external identities can be added and will bypass DRS policies.
Exploitation
# Add attacker-controlled identity as MEMBER via Directory APIcurl -X POST \ "https://admin.googleapis.com/admin/directory/v1/groups/$GROUP_EMAIL/members" \ -H "Authorization: Bearer $(gcloud auth print-access-token)" \ -H "Content-Type: application/json" \
# Wait 1-5 minutes for GCP IAM propagation, then verify inherited accessgcloud projects listgcloud storage lsMitigation
- Convert GCP-bound groups to security groups — security groups restrict membership modification to Super Admins only, eliminating open-join policies:
gcloud identity groups update GROUP_EMAIL \--labels=cloudidentity.googleapis.com/groups.security=''
- Audit and remediate open group join policies — groups created via GCP IAM console may inherit permissive join policies. Set to
Only invited usersvia Google Admin Console → Directory → Groups → group → Group settings → Who can join. - Disable external membership for sensitive groups via Google Admin Console → group settings → disable Allow members outside your organization.
- Minimize owners and managers on GCP-bound groups to reduce the number of principals who can exercise this permission.
- Enable GWS audit log export to Cloud Logging — without this,
groups_enterpriseevents (including self-join events) are invisible to GCP-based monitoring. - Audit GCP IAM group bindings regularly:
gcloud asset search-all-iam-policies \--scope='organizations/ORG_ID' \--query='memberTypes:group'
Detection
Group membership additions appear only in Google Workspace Audit Logs, not in GCP IAM audit logs. Monitor both the admin and groups_enterprise streams — each captures a different addition method.
Google Admin Console
- Open Google Admin Console (
admin.google.com) → Reporting → Audit and investigation → Groups Enterprise log events. - Filter by Event Name: Add member (API-based) or Event Name: join (self-join via Groups UI).
- Cross-reference the Target group against groups with GCP IAM bindings.
Cloud Logging (GCP)
# Monitor API-based group member additionsgcloud logging read \ 'logName="organizations/ORG_ID/logs/cloudaudit.googleapis.com%2Factivity" AND protoPayload.serviceName="admin.googleapis.com" AND protoPayload.methodName="google.admin.AdminService.addGroupMember"' \ --format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.request.groupKey, protoPayload.request.memberKey)"References
- https://www.netspi.com/blog/technical-blog/cloud-pentesting/escalating-privileges-in-google-cloud-via-open-groups/
- https://hackersvanguard.com/the-hidden-google-groups-security-risks/
- https://cloud.google.com/iam/docs/groups-in-cloud-console
- https://developers.google.com/workspace/admin/reports/v1/appendix/activity/groups-enterprise
- https://cloud.google.com/security-command-center/docs/concepts-event-threat-detection-overview
- https://developers.google.com/admin-sdk/directory/reference/rest/v1/members/insert
- https://cloud.hacktricks.wiki/en/pentesting-cloud/workspace-security/gws-post-exploitation.html