
Overview
Any MaxKB assistant that has a tool, MCP tool, skill, or sub-application attached routes chat through a deepagents agent built with a host-shell backend (SandboxShellBackend). deepagents auto-exposes an execute shell tool whenever such a backend is passed;
MaxKB does not remove it (excluded_tools is not used) and does not gate it (interrupt_on covers only write_file/read_file/edit_file, never execute). Untrusted chat - or indirect prompt injection via content the assistant ingests (RAG / uploaded documents) - can therefore drive the model to run shell commands.
Containment depends entirely on MAXKB_SANDBOX. On a source / bare-metal deployment MAXKB_SANDBOX defaults off - SANDBOX is absent from Config.defaults in apps/maxkb/conf.py, and sandbox_shell.py reads CONFIG.get("SANDBOX", 0) - so execute runs commands directly on the host as the application user (remote code execution). On the official image MAXKB_SANDBOX=1 is set in the base image (installer/Dockerfile-base:48); the app image (installer/Dockerfile:16) compiles sandbox.so and inherits MAXKB_SANDBOX_HOME but does not itself set MAXKB_SANDBOX. Even with the sandbox on, the wrapper is bypassable (see "Container sandbox is bypassable"); and because neither Dockerfile has a USER directive, the container runs as root.
Because exploitation can occur remotely and does not require authentication or user interaction, affected deployments should be treated as high priority.
Affected Product
Affected Files
- apps/application/flow/step_node/ai_chat_step_node/impl/base_chat_node.py
- apps/application/flow/tools.py
- apps/application/flow/backend/sandbox_shell.py
- apps/maxkb/conf.py
- installer/Dockerfile-base
- installer/Dockerfile
Root Cause
Affected configuration (default usage)
1. An assistant/agent with any tool / MCP / skill / sub-app attached (the normal way MaxKB is used as an agent platform; a capability-less chat is not affected - it does not enter the agent path).
2. Reachable untrusted input: multi-tenant chat, a public/embedded bot (unauthenticated), or ingested content carrying indirect prompt injection.
Details
1. Tool/MCP/skill-attached chat routes to the shell agent - base_chat_node.py:409 (dispatches to mcp_response_generator(...) at :427):
if len(mcp_servers_config) > 0 or len(tools) > 0: # :409 -> mcp_response_generator(...) at :4272. The agent is built with a host-shell backend; execute is neither excluded nor gated - tools.py:467-477:
agent = create_deep_agent(model=chat_model, backend=SandboxShellBackend(root_dir=temp_dir, virtual_mode=True),
skills=["/skills"],tools=tools,
system_prompt=system_prompt,
interrupt_on={"write_file": False, "read_file": False, "edit_file": False}, #
checkpointer=checkpointer)
response = agent.astream({"messages": message_list}, config=...)
3. The shell backend - sandbox_shell.py:
_enable_sandbox = bool(int(CONFIG.get("SANDBOX", 0)))_run_user = "sandbox" if _enable_sandbox else getpass.getuser()
def execute(self, command, *, timeout=None): ...
if _enable_sandbox:
command = f'env -i LD_PRELOAD=/opt/maxkb-app/sandbox/lib/sandbox.so ... gosu {_run_user} {command}'
return super().execute(command=command, timeout=timeout) SANDBOX is not in Config.defaults (apps/maxkb/conf.py), so on any deployment that does not explicitly set MAXKB_SANDBOX the wrapper is skipped and the command runs via subprocess.run(cmd, shell=True) directly on the host as the app user.
PoC
Host / source deployment (host RCE)
1. Run MaxKB's real code from source on a host with MAXKB_SANDBOX unset.
2. Create an assistant with one tool attached.
3. Send a single chat message asking the assistant to run system-info commands. No jailbreak is needed; a benign, operations-flavored request is enough.
4. The execute tool runs on the host and writes a file.
5. The file contains the host's own uname / id / sw_vers output (host OS and host user). We confirmed this output is absent inside any container.
Note: the model refused a blatant "proof" filename. Benign phrasing still executes, and so would indirect injection or a less-aligned model. Model alignment is a partial speed-bump, not a control.
Official container
1. Deploy the official 1panel/maxkb:v2.10.3-lts image.
2. Repeat the same chat flow against an assistant with a tool attached.
3. The execute tool runs commands inside the running container and writes a file (see "Container sandbox is bypassable" below).
Container sandbox is bypassable
1. On the official image, MAXKB_SANDBOX=1 is set in the base image (installer/Dockerfile-base:48), so execute wraps every command as env -i LD_PRELOAD=.../sandbox.so ... gosu sandbox <cmd>.
2. The whole command still runs under shell=True in a root shell. The image has no USER directive, and we confirmed UID 0 at runtime.
3. The wrapper applies gosu sandbox only to the bare command token.
4. The outer root shell evaluates shell metacharacters and redirections (>, ;, |, $(...)) outside gosu.
5. Result: in our test, sandbox.so blocked id as the inner token, but the redirect wrote a root-owned file.
6. Conclusion: shell syntax escapes the container "sandbox" to the app/root user, so it does not contain command execution.
Impact
- Source/bare-metal deploy: arbitrary command execution on the host as the app user (root if the service runs as root) - read secrets/config, reach internal services, pivot, persist. Multi-tenant -> cross-tenant. Public bot -> unauthenticated.
- Official container: command execution constrained to the sandbox user / sandbox.so, but bypassable to the app/root user via shell metacharacters.
- Indirect prompt injection: an attacker who controls only ingested content can drive execute without direct chat access.
Suggested Remediation
1. Do not apply SandboxShellBackend by default. Build the agent (create_deep_agent, tools.py:467) with a non-execution backend (e.g. StateBackend) by default, so the execute shell tool and the filesystem tools are never auto-added. Use SandboxShellBackend only for assistants where an operator has explicitly and knowingly enabled a code-execution capability (with a clear warning).
2. Gate execute with human-in-the-loop: add "execute": True to interrupt_on in tools.py:473 (the deepagents docs explicitly recommend HITL for shell).
3. Fail closed on non-container deploys: default MAXKB_SANDBOX on, or refuse to start the execute-capable flow when the sandbox runtime (sandbox.so, sandbox user) is unavailable. Do not present bare-metal as safe without a host-level sandbox.
4. Fix the wrapper: never build the sandboxed command as a shell=True string in a root shell; execute argv as a list under the unprivileged user so shell metacharacters cannot escape gosu.
5. Note: deepagents' excluded_tools alone will not fix this - it removes the tool from what the model is told, not from the executor.
Additional Resources
- Lasso MaxKB Blog - https://www.lasso.security/blog/cve-2026-77521-maxkbypass---from-prompt-injection-to-bypassing-maxkb-agents-sandbox
- GitHub Security Advisory - MaxKB: Prompt-injectable agent can lead to command execution
github.com/1Panel-dev/MaxKB/security/advisories/GHSA-f36j-f34j-h3rx - MaxKB 2.10.5-lts Release
github.com/1Panel-dev/MaxKB/releases/tag/v2.10.5-lts - Security Fix Commit - 594f50f
github.com/1Panel-dev/MaxKB/commit/594f50f2ea80a502d1c955371ba0438b277c30ea