Google’s Vertex AI Agent Engine makes it easy to deploy and manage AI agents at scale. What it does not do by default is apply the principle of least privilege to the service account those agents run under.
Unit 42 published research in April 2026 — the “Double Agents” paper — documenting exactly how far the default service agent permissions extend. A user with Viewer-level access to a GCP project can extract service agent credentials from the instance metadata service and obtain project-wide access to Cloud Storage, BigQuery, Pub/Sub, and Artifact Registry. In some configurations, those scopes extend into the organisation’s Google Workspace: Gmail, Calendar, Drive.
Google classified both reported vulnerabilities as working as intended and recommended that customers use Bring Your Own Service Account (BYOSA) to replace the default service agent. This guide covers what the attack path looks like, why the defaults are dangerous, and how to remediate.
What the Default Service Agent Looks Like
When you create an Agent Engine resource, GCP automatically creates or reuses a service agent with the format:
[email protected]
This service agent is granted the roles/aiplatform.serviceAgent role. The problem is what that role includes. The Vertex AI service agent role has broad permissions across GCP services — it is designed to support the full range of things Vertex AI might need to do, including:
- Reading from and writing to Cloud Storage
- Querying BigQuery datasets
- Publishing to Pub/Sub topics
- Pulling from Artifact Registry
In the Agent Engine context, most deployments do not need all of these. A simple conversational agent that queries an external API does not need BigQuery access. But it gets it.
The Attack Path
The Unit 42 researchers documented this as a privilege escalation scenario accessible from a low-privileged starting point.
Step 1: Low-privileged access. An attacker with Viewer-level project access (or any principal that can interact with Agent Engine resources) can request the service agent’s access token from the GCP metadata server accessible from within Agent Engine’s execution environment.
Step 2: Token retrieval. From inside an Agent Engine session, the metadata service is reachable at http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token. This returns a short-lived OAuth2 token for the service agent.
Step 3: Lateral movement. The token is usable against any GCP API the service agent has permission to call. The attacker now has project-wide Cloud Storage access, can query BigQuery datasets, and can access any resource within the service agent’s IAM scope.
Step 4: Google Workspace access. In organisations where the default service agent has been granted Google Workspace delegation (which can happen automatically depending on how the project was configured), the token can be used to access Gmail, Google Calendar, and Google Drive for any user in the Workspace tenant.
This is not a bug in the metadata service — that service is functioning as designed. The problem is the combination of over-broad default permissions and the accessibility of the metadata endpoint from within the execution environment.
Remediation: BYOSA (Bring Your Own Service Account)
The recommended fix is to create a dedicated service account with only the permissions your agent actually needs, and configure Agent Engine to use that account instead of the default service agent.
Step 1: Create a minimal service account
# Create a new service account for your agent
gcloud iam service-accounts create vertex-agent-sa \
--description="Minimal SA for Vertex AI Agent Engine" \
--display-name="Vertex AI Agent Engine SA"
Step 2: Grant only the permissions your agent needs
PROJECT_ID="your-project-id"
SA_EMAIL="vertex-agent-sa@${PROJECT_ID}.iam.gserviceaccount.com"
# Example: Agent only needs to read from one specific GCS bucket
gcloud storage buckets add-iam-policy-binding gs://your-agent-data-bucket \
--member="serviceAccount:${SA_EMAIL}" \
--role="roles/storage.objectViewer"
# Do NOT grant roles/storage.admin, roles/bigquery.dataViewer at project level,
# or any other permission the agent doesn't directly require
Step 3: Configure Agent Engine to use the custom SA
When creating or updating your Agent Engine resource, specify the service account:
from google.cloud import aiplatform
# Create agent with custom service account
agent = aiplatform.Agent.create(
display_name="my-agent",
service_account=f"vertex-agent-sa@{project_id}.iam.gserviceaccount.com",
# ... other configuration
)
Or via Terraform:
resource "google_vertex_ai_agent_engine" "agent" {
display_name = "my-agent"
project = var.project_id
location = "us-central1"
service_account = google_service_account.agent_sa.email
# ... other configuration
}
resource "google_service_account" "agent_sa" {
account_id = "vertex-agent-sa"
display_name = "Vertex AI Agent Engine SA"
project = var.project_id
}
Step 4: Prevent metadata service abuse
Even with a minimal custom service account, restrict what can call the metadata service from within your agent’s execution context. In Agent Engine, you can constrain outbound network calls using VPC Service Controls:
# Create a service perimeter that restricts metadata access patterns
# (requires VPC Service Controls to be configured for your org)
gcloud access-context-manager perimeters create vertex-agent-perimeter \
--policy=POLICY_ID \
--title="Vertex AI Agent Perimeter" \
--resources=projects/PROJECT_NUMBER \
--restricted-services="aiplatform.googleapis.com"
Auditing Existing Deployments
To identify over-privileged service agents in your current GCP projects:
# List all service accounts with aiplatform service agent roles
gcloud projects get-iam-policy PROJECT_ID \
--flatten="bindings[].members" \
--format="table(bindings.role,bindings.members)" \
--filter="bindings.members:gcp-sa-aiplatform.iam.gserviceaccount.com"
To check the effective permissions of the default service agent:
# Get the service agent email
SERVICE_AGENT=$(gcloud iam service-accounts list \
--filter="email:gcp-sa-aiplatform.iam.gserviceaccount.com" \
--format="value(email)" \
--project=PROJECT_ID)
# List all IAM bindings for this service account
gcloud projects get-iam-policy PROJECT_ID \
--flatten="bindings[].members" \
--format="table(bindings.role)" \
--filter="bindings.members:${SERVICE_AGENT}"
Monitoring for Metadata Service Abuse
Cloud Logging captures authentication events including service account token generation. Alert on unusual patterns:
# Cloud Logging query: service account token requests from unexpected principals
resource.type="service_account"
protoPayload.methodName="google.iam.credentials.v1.IAMCredentials.GenerateAccessToken"
protoPayload.authenticationInfo.principalEmail=~"gcp-sa-aiplatform"
protoPayload.requestMetadata.callerIp!="35.190.0.0/16"
Adjust the IP range filter to your Agent Engine execution environment’s known IP ranges.
The Broader Pattern
The Vertex AI Agent Engine case is an instance of a broader pattern in AI cloud services: the platforms are built for capability and developer experience, and security hardening is left to the operator. Default service agents in Vertex AI, default IAM roles in AWS Bedrock, and default execution contexts in Azure AI services are all configured for maximum compatibility.
For new Vertex AI deployments, BYOSA should be the default posture. The effort to create a minimal service account is low; the blast radius reduction in the event of a compromised agent session is significant.
For existing deployments, audit service agent permissions and narrow them to what the agent actually calls. The permissions granted to roles/aiplatform.serviceAgent exceed what most agents require, and there is no operational cost to restricting them through a custom SA.