Open question

The hook on file descriptor five

A missing permission check and an in-process execution model let code inside a Google Vertex AI sandbox rewrite the channel it used to return results, so a security-validator agent approved code with explicit violations. Google closed the report as working as intended.

Logged 5 Oct 2026

The orchestrator and the user code share one process

Google's Gemini Enterprise Agent Platform, formerly Vertex AI Agent Engine, runs user-submitted Python inside a gVisor sandbox. Inside that sandbox one process runs at PID 1: an orchestrator that receives execution requests, runs the submitted code, serializes the result as a protobuf message, and writes it back to the API server. User-submitted code runs through exec() inside that same process, sharing its address space, its memory, and its file descriptors.

Two file descriptors, inherited at startup, carry the conversation: fd=6 for inbound SandboxIn requests and fd=5 for outbound SandboxOut responses. The frames carry no signature, so any well-formed message on fd=5 is read by the API server as a legitimate reply from the sandbox.

Two bugs, one channel the attacker keeps

The first bug is in gVisor's Sentry: it omits the ptrace-equivalent permission check that standard Linux applies when a process opens /proc/[pid]/mem for writing. Because the user's code is PID 1, /proc/1/mem is its own memory, and it can pwrite() into pages marked read-only executable, an operation standard Linux typically rejects. gVisor returns errno=0.

That bug is not required for the main path. The Global Offset Table of libpython3.12.so.1.0 is mapped read-write, and the ordinary ctypes module can write to it. The authors locate the table through the library's PT_DYNAMIC segment, then hook all nine of its relevant entries in a single execute_code() call. The write() hook on fd=5 discards the sandbox's real response and writes a crafted protobuf in its place; the read() hook on fd=6 rewrites submitted code in memory after the API-layer content policy has approved it and before exec() runs it. The hook holds for the lifetime of the sandbox.

SAFE — approve for deployment

In the authors' end-to-end demonstration, a code-security-validator agent was asked to review code carrying explicit security violations. The agent replied "SAFE — Approve for deployment". Its underlying analysis ran correctly and found every issue; the response it was shown had been replaced before it saw it. The authors note that agents sharing a sandbox, or a pool of sandboxes, let one user poison the channel for every user who can reach the agent.

Google's Vulnerability Reward Program assessed the finding as working as intended and closed the ticket as Won't Fix. The report is published under a 90-day coordinated disclosure. The authors' proposed fixes are to fork a child process for each execute_code() call, so fd=5 and fd=6 are inherited only by the parent, and to sign every SandboxOut message with a key provisioned outside the sandbox. Their closing observation: the sandbox contains the attacker relative to the outside world; it does not contain them relative to the output channel.

Sources

Research