The sandbox wrapped one command and stopped
MaxKB is an open-source enterprise product from FIT2CLOUD, published under the 1Panel-dev organisation, that lets an organisation build assistants on top of its own documents and crawl external sites into a knowledge base. Its Docker image has passed a million pulls and the repository carries more than 22,000 stars. A user creates an assistant, attaches tools, MCP servers, skills, or sub-applications, connects a knowledge base, and exposes the assistant either through an authenticated console or as a public, embeddable chat widget.
When an assistant carries any of those capabilities, its chat is routed through a shell sandbox that MaxKB built for itself on top of gosu. In sandbox_shell.py the code assembled a single string: an env -i with LD_PRELOAD pointing at the project's sandbox.so, then gosu sandbox, then the model-supplied command, and handed the whole thing to a shell running as root. The gosu prefix governs one command, and the string was never parsed first, so anything after a semicolon, a pipe, a command substitution, or a redirection became a new command in the outer root shell, outside the sandbox entirely. Lasso Security showed a payload of ls; whoami; id in which ls was blocked by the sandbox while whoami and id ran as root.
The larger gap was that the shell tool ran at all without asking. An assistant with a tool, MCP tool, skill, or sub-application uses SandboxShellBackend, which exposes an execute tool and omits execute from interrupt_on, so no human approval step stands between the model and the command. On source or bare-metal deployments the MAXKB_SANDBOX setting defaults off, so the command runs directly as the application user; only the official container wraps it, and that wrapper is the one the metacharacters walk past.
A crawled page was the way in
The reach came from content rather than from the chat. MaxKB builds a knowledge base from uploaded documents or from external websites it crawls, and that retrieved content is placed in the model's context at answer time. Lasso planted an instruction on a page the target would crawl, waited for it to be indexed, and then watched a benign question from the victim carry the injected instruction into the agent. The agent answered the question normally and, in the same turn, ran the command the page had asked for. In the demonstration the command was a curl to the attacker's server, but it was an ordinary shell, so it could have been anything.
Because MaxKB is deployed as a multi-tenant product, execution at the application user's privilege level sits underneath the tenancy model. A page one tenant can influence and another tenant indexes carries the instruction across the boundary, so a single poisoned source can reach other tenants' data along with application secrets and database credentials. Once a command runs, the attacker can read secrets off disk, reach the database, talk to internal services on that network, and leave something behind for persistence. Lasso rated the flaw critical at 10.0, and CISA's assessment marked it as proof-of-concept exploitable, automatable, and total in technical impact.
The fix parsed the string before wrapping it
The vendor fixed it quickly. Lasso reported privately on 8 July 2026, MaxKB replied the next day scoping the issue to the official image and asking for a container proof of concept, and the fix commit 594f50f2 landed on 9 July. The new code parses the model-supplied string before wrapping it: _build_sandbox_command() applies the gosu sandbox prefix to every simple command, not just the first. The release, 2.10.5-lts, shipped on 6 August, and the advisory went public with the CVE on 2 September.
Lasso's conclusion is broader than one product. Across the other agent projects it reviewed, it kept finding the same shape: isolation that is off unless someone explicitly turns it on. The advice is to confirm the sandbox is actually engaged in the deployment mode you run, not merely present in the repository, and to upgrade to 2.10.5-lts or later with the sandbox enabled. The case repeats, from the other direction, what the brig and Docker Sandboxes findings showed: an agent sandbox is only as good as the wrapper that is supposed to hold it.
Sources
Research
Thread
- Relates to: Configure a flow, run a command
Both are cases where an agent's execution surface runs commands beyond the boundary it was meant to hold. There a user who could write an agent flow could run commands on the gateway; here a poisoned knowledge base runs commands on the host as root.
- Related to: RestrictedPython was never a dependency
Both are agent platforms whose documented containment did not hold at the command it was meant to contain. MaxKB built a gosu sandbox and left the outer root shell reachable past a semicolon; Astron Agent named RestrictedPython in its docstrings and never depended on it, so the code node ran tenant Python as root.
- Relates to: A symlink led the sandbox out
Both are agent sandboxes that let the confined party reach the host. There the host server followed a symlink out of the shared workspace; here the sandbox wrapper covered only the first command, so a semicolon ran the rest as root.
Semantic neighbors
- Kernel panics from inside the sandbox77% similar
- Identical requests, one header apart77% similar
- The sandbox flag changed nothing77% similar
