Back to  vulnerability database

CVE-2026-97693: Sandbox isolation failure allows cross-tenant access and internal service compromise

Sub A critical vulnerability in MaxKB may allow remote attackers to execute system commands on affected servers.

CVSS Score
9.9
Severity
Critical
Type
Sandbox isolation failure
Attack type
Sandbox isolation failure
Vendor/tech
xerrors/Yuxi
Know exploit
No
Impact Assessment
Confidentiality
High
Integrity
High
Availability
High

Overview

Every Yuxi chat agent is built with the DeepAgents filesystem middleware on top of an execution-capable sandbox backend, so the agent always has an execute shell tool. DeepAgents refuses human-in-the-loop permissions on an execution-capable backend, so that tool cannot be gated. Untrusted chat, or indirect prompt injection via content the agent ingests (knowledge-base documents, uploads, OCR output), can therefore drive the model to run commands in the user's sandbox.

The sandbox was meant to be the security boundary, but in Docker deployments it does not isolate tenants. The sandbox provisioner attaches every sandbox to the same flat Docker network as the application services (PostgreSQL, Neo4j, MinIO, Redis and the provisioner itself). The provisioner's management API has no authentication and lists every tenant's sandbox URL, and the sandbox API itself needs no token. A low-privileged user who can run commands in their own sandbox can therefore reach other tenants' sandboxes, read their secrets and files, and reach the shared datastores, without knowing any password and without a container escape.

‍

Because any low-privileged Yuxi user, or a poisoned document the agent reads, can reach beyond their own sandbox into other tenants' secrets and the shared datastores without a password or container escape, affected deployments should be treated as high priority.

Affected Product

xerrors/Yuxi

Affected Files

  • docker/sandbox_provisioner/app.py
  • docker-compose.yml
  • docker-compose.prod.yml
  • backend/package/yuxi/agents/backends/sandbox/backend.py
  • backend/package/yuxi/agents/backends/composite.py
  • backend/package/yuxi/agents/buildin/chatbot/graph.py

Root Cause

Affected configuration (default usage)

1. A Docker Compose deployment that uses tha sandbox provisioner with execution-capable sandboxes (the shipped compose files)

2. A low-privileged authenticated user who can run commands in their own sandbox, either directly through chat or through indirect prompt injection in content the agent ingests

Details

1. Every chat agent gets an execute tool that cannot be gated.

graph.py always installs the DeepAgents filesystem middleware, and composite.py backs it with ProvisionerSandboxBackend, which supports execution. DeepAgents therefore registers execute, and sandbox/backend.py forwards the command string to the sandbox unchanged. Attaching HITL permissions to an execution-capable backend raises NotImplementedError, so execute cannot be gated in this stack.

2. Sandboxes share one flat network with the application services.

The provisioner attaches every sandbox to DOCKER_NETWORK, which defaults to yuxi-know_app-network (app.py:428). That is the same app-network as PostgreSQL, Neo4j, MinIO, Redis and the provisioner (docker-compose.prod.yml:126, 352-354). There is no per-sandbox network and no egress policy.

3. The provisioner management API is unauthenticated.

The /api/sandboxes routes (create, get, touch, list, delete) have no authentication dependency, token or header check (app.py:984-1075). The list route returns the sandbox_url of every sandbox (app.py:1038-1054).

4. The sandbox API needs no token.

The sandbox client is built from a base URL alone (sandbox/backend.py:219). Anyone who knows a sandbox URL can run commands in it and read the environment the provisioner injected, including the user's agent secrets.

5. Shipped default service credentials.

The compose files ship fallback credentials for PostgreSQL (postgres/postgres), Neo4j (neo4j/0123456789) and MinIO (minioadmin/minioadmin). The PostgreSQL account is a superuser, so COPY ... FROM PROGRAM runs commands on the database container.

‍

PoC

All tests ran on our own local deployment of the shipped compose files at commit 2c8ff10dc6bc, with a benign id command and canary strings only. No live or third-party Yuxi instance was touched.

Entry point: indirect prompt injection reaches execute

1. Build an agent with Yuxi's tool binding (DeepAgents filesystem middleware on an execution-capable backend).

2. Confirm execute is in the agent's tool set, and that attaching HITL permissions to it raises NotImplementedError.

3. ,Place a poisoned "knowledge-base document" in the agent's context.

4. pRun the agent with a real model. The model calls execute("id") on its own and pastes the output into its reply.

Credential-free cross-tenant access

1. Start the sandbox provisioner (Docker backend) on yuxi-know_app-network, with the compose-default all-in-one-sandbox image.

2. Provision two tenants: Bob (victim) with secret environment variables and a workspace canary file, and Alice (attacker).

3. From Alice's network position, with no credentials, call GET /api/sandboxes on the provisioner. It returns every tenant's sandbox_url, including Bob's.

4. Connect to Bob's sandbox URL with no token, using the same agent-sandbox client Yuxi's backend uses.

5. Run id in Bob's sandbox: uid=1000(gem).

6. Read Bob's injected secrets (ANTHROPIC_API_KEY=sk-bob-SECRET-BBBB, STRIPE_KEY=bob-stripe-SECRET) and his workspace canary file.

Shared datastores and database command execution

1. Bring up PostgreSQL, Neo4j and MinIO from the vendor's docker-compose.prod.yml under the project name yuxi-know, so the network is yuxi-know_app-network.

2. Seed the agent_envs table (the per-user secret store) with two tenants, Alice and Bob.

3. From a container on that network (the same position as an injected sandbox), log in to PostgreSQL with postgres/postgres and read agent_envs. Both tenants' secrets are returned.

4. As the default superuser, run COPY pwn FROM PROGRAM 'id'. The command runs on the PostgreSQL container: uid=999(postgres).

5. Log in to Neo4j (neo4j/0123456789) and MinIO (minioadmin/minioadmin) with the shipped defaults.

Unauthenticated provisioner management

1. Run the sandbox provisioner (memory backend, without docker.sock).

2. With no credentials, call create, list and delete on /api/sandboxes.

3. All three return HTTP 200, so any caller on the network can enumerate and delete other users' sandboxes.

Impact

  • Cross-tenant access: reach other tenants' sandbox APIs and read their environment variables, mounted workspace files, uploads and outputs.
  • Cross-tenant command execution: run commands in another tenant's active sandbox.
  • Internal service compromise: reach PostgreSQL, Neo4j, MinIO and other services on the shared application network, using the shipped default credentials where they were not changed.
  • Database command execution: run commands in the PostgreSQL container when the exposed account is a superuser.
  • Denial of service: enumerate, create or delete sandboxes through the unauthenticated provisioner API.
  • Indirect prompt injection: an attacker who controls only ingested content can drive execute in a victim's sandbox without direct chat access.

No container escape or kernel vulnerability is required.

‍

Suggested Remediation

1. Upgrade to Yuxi 0.7.1 or later. The patch gives every sandbox its own Docker network that only the provisioner can join, blocks sandbox-to-sandbox and sandbox-to-service traffic, and stops publishing sandbox ports directly.

2. Require a deployment-specific SANDBOX_PROVISIONER_TOKEN on all sandbox management and proxy requests (part of 0.7.1).

3. Rotate credentials. Upgrading the compose files does not rotate credentials already stored in PostgreSQL, Neo4j or MinIO data volumes, so replace every shipped or historical default with a unique random value.

4. If you cannot upgrade immediately: disable sandbox execution or restrict Yuxi to fully trusted administrators, isolate each sandbox from other sandboxes and the application network, block sandbox access to the datastores and the provisioner, and remove those services' ports from untrusted interfaces.

5. Review and rotate secrets stored in user agent environments if an untrusted user may already have had sandbox execution.

6. Note: changing service passwords alone is not enough. The cross-tenant sandbox API and the unauthenticated provisioner paths do not depend on those credentials.

Additional Resources

Automated AI Red Teaming

Test your applications against adversarial conditions.
Book a Demo