> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/gcp/gws_add_group_member.md).

# GWS\_ADD\_GROUP\_MEMBER

## Summary

|                                |                                                                                                                           |
| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------- |
| **FSProtect 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

```bash
# Find all GCP-bound groups across the organization
gcloud asset search-all-iam-policies \
    --scope='organizations/ORG_ID' \
    --query='memberTypes:group' \
    --format="table(resource, policy.bindings[].role, policy.bindings[].members)"
```

```bash
# Identify who currently has OWNER or MANAGER roles on a group (can add members)
GROUP_EMAIL="target-group@example.com"
gcloud identity groups memberships list \
  --group-email=$GROUP_EMAIL \
  --format="table(preferredMemberKey.id, roles[0].name)" \
  | grep -E "OWNER|MANAGER"
```

```bash
# 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 API
PROJECT_ID="my-project"
gcloud projects get-iam-policy $PROJECT_ID --format=json | \
  jq -r '.bindings[] | .members[] | select(startswith("group:"))' | sort -u
```

### Google Admin Console

1. Open **Google Admin Console** (`admin.google.com`) → **Directory** → **Groups**.
2. Click the target group → **Members**.
3. Identities with **Role: Owner** or **Role: Manager** can add new members.
4. Check **Group settings** → **Who can join the group** — if set to `Anyone in the organization`, any user can self-join without admin intervention.
5. Check **Allow members outside your organization** — if enabled, external identities can be added and will bypass DRS policies.

## Exploitation

```bash
# Add attacker-controlled identity as MEMBER via Directory API
GROUP_EMAIL="privileged-group@example.com"
curl -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" \
  -d '{"email": "attacker@example.com", "role": "MEMBER"}'

# Wait 1-5 minutes for GCP IAM propagation, then verify inherited access
gcloud projects list
gcloud storage ls
```

## Mitigation

1. **Convert GCP-bound groups to security groups** — security groups restrict membership modification to Super Admins only, eliminating open-join policies:

   ```bash
   gcloud identity groups update GROUP_EMAIL \
     --labels=cloudidentity.googleapis.com/groups.security=''
   ```
2. **Audit and remediate open group join policies** — groups created via GCP IAM console may inherit permissive join policies. Set to `Only invited users` via Google Admin Console → **Directory** → **Groups** → group → **Group settings** → **Who can join**.
3. **Disable external membership** for sensitive groups via Google Admin Console → group settings → disable **Allow members outside your organization**.
4. **Minimize owners and managers** on GCP-bound groups to reduce the number of principals who can exercise this permission.
5. **Enable GWS audit log export to Cloud Logging** — without this, `groups_enterprise` events (including self-join events) are invisible to GCP-based monitoring.
6. **Audit GCP IAM group bindings** regularly:

   ```bash
   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

1. Open **Google Admin Console** (`admin.google.com`) → **Reporting** → **Audit and investigation** → **Groups Enterprise log events**.
2. Filter by **Event Name: Add member** (API-based) or **Event Name: join** (self-join via Groups UI).
3. Cross-reference the **Target group** against groups with GCP IAM bindings.

### Cloud Logging (GCP)

```bash
# Monitor API-based group member additions
gcloud 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>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/gcp/gws_add_group_member.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
