
Reprodução benigna e autocontida da CVE-2026-77521 (execução de comandos com injeção de prompt no MaxKB)
Um laboratório autocontido e benigno que reproduz
GHSA-f36j-f34j-h3rx
/ CVE-2026-77521: no MaxKB (uma
plataforma open-source de assistente de IA corporativo / RAG), qualquer assistente com uma ferramenta,
ferramenta MCP, skill ou sub-aplicação recebe uma ferramenta execute de shell injetada automaticamente
que não está atrás de um gate de aprovação humana — portanto, a injeção de prompt (a partir do chat
ou de um documento ingerido) alcança um shell. Um sandbox deveria contê-la,
mas uma string com shell=True permitia que metacaracteres escapassem dele.
| Advisory | GHSA-f36j-f34j-h3rx |
| CVE | CVE-2026-77521 |
| Projeto | 1Panel-dev/MaxKB |
| Afetado | < 2.10.5-lts |
| Corrigido | 2.10.5-lts (commit 594f50f) — parcial, veja abaixo |
| CVSS | 10.0 CRITICAL (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H) |
| Fraqueza | CWE-78 (injeção de comando do SO) · CWE-250 (privilégios desnecessários) · CWE-749 (método perigoso exposto) |
Isto reproduz uma vulnerabilidade corrigida e divulgada publicamente para fins educacionais e defensivos. Ele é executado inteiramente localmente e de forma benigna:
id, gravação de um arquivo marcador
que o PoC exclui). Nenhum gosu/sandbox/root real está envolvido — o
wrapper de redução de privilégios é substituído por um marcador env inerte para que a demonstração rode em qualquer lugar.Um assistente do MaxKB com qualquer capacidade anexada é construído sobre deepagents com um
SandboxShellBackend. O deepagents injeta automaticamente uma ferramenta execute de shell (além de
read_file/write_file/…), e o MaxKB não usa excluded_tools, então execute está
sempre presente. A configuração interrupt_on — que decide o que precisa de aprovação
humana — é {"write_file": False, "read_file": False, "edit_file": False}:
execute não está nela, então é executada sem nenhuma aprovação. Qualquer texto que possa direcionar
o modelo (uma mensagem de chat, ou um documento envenenado que o pipeline RAG ingere) pode,
portanto, fazer o agente executar comandos de shell. Em instalações a partir do código-fonte, SANDBOX
vem desativado por padrão, então os comandos rodam como o usuário da aplicação; no contêiner oficial (root)
o wrapper do sandbox era uma string com shell=True, então ;/>/$() eram executados fora
do gosu, como root.
interrupt_on real do MaxKB,
uma chamada execute injetada é executada sem aprovação; adicionar
"execute": True a coloca atrás de um gate. → poc/01_missing_hitl_gate.py;+redirecionamento escapa do
prefixo gosu de string única; a re-citação do _build_sandbox_command() do 2.10.5-lts
neutraliza isso. → poc/02_sandbox_escape.pyexecute com o comando do atacante (e não o faz em um
documento limpo). → poc/03_real_llm.pyO patch 2.10.5-lts entregue (commit 594f50f)
reforça apenas a fuga do sandbox — adiciona _build_sandbox_command(), que
divide pelos operadores de shell, recoloca aspas em cada token com shlex.quote e envolve
cada comando simples no prefixo gosu. Verificado contra o
código-fonte v2.10.5-lts,
o núcleo do agente está inalterado: interrupt_on ainda omite execute, o
backend continua a ser SandboxShellBackend, e SANDBOX ainda tem como padrão 0. Portanto,
após a correção, um execute injetado por prompt ainda é executado sem aprovação humana —
a parede interior foi reforçada enquanto o portão permaneceu aberto. O PoC 1 demonstra a
mitigação recomendada (portão em execute), que o fornecedor não entregou.
Sem dependências para o PoC 1/2 (Python 3.9+):
./run.sh # all three (PoC 3 skips without an endpoint)
python3 poc/01_missing_hitl_gate.py # missing approval gate
python3 poc/02_sandbox_escape.py # metacharacter sandbox escape
Apresentando isto?
DEMO.mdé um runbook edemo.sho conduz uma tecla de cada vez.
Qualquer endpoint compatível com OpenAI (auto-hospedado ou modelo aberto hospedado). Exemplo (Groq):
export OPENAI_BASE_URL=https://api.groq.com/openai/v1
export OPENAI_API_KEY=$GROQ_API_KEY # read from env only; never logged
MODEL="openai/gpt-oss-20b" python3 poc/03_real_llm.py
Atrás de um proxy de inspeção TLS, ele tenta novamente sem verificação com um aviso
(INSECURE_SSL=1 para forçar). Ele é ignorado de forma limpa se nenhum endpoint estiver definido.
| Caminho | O que é |
|---|---|
sandbox_shell.py | Modelo benigno da construção do comando execute() do MaxKB — string vulnerável vs _build_sandbox_command() corrigido |
agent_sim.py | Modelo mínimo do gate de aprovação humana interrupt_on do deepagents |
payloads/poisoned-document.md | Documento de KB envenenado pelo atacante (injeção indireta) |
poc/01_missing_hitl_gate.py | execute injetado é executado sem aprovação; o gating o bloqueia |
poc/02_sandbox_escape.py | Metacaracteres escapam do prefixo gosu; o parser 2.10.5 os contém |
poc/03_real_llm.py | Um modelo real chama execute a partir do documento envenenado (limpo vs envenenado) |
DEMO.md / demo.sh | Runbook do apresentador e driver de demonstração com ritmo definido |
assets/attack-flow.{svg,png} | Diagrama do fluxo de ataque (destaque do blog) |
assets/poc3-groq-sample.txt | Saída gravada do PoC 3 (fallback da demonstração) |