
Riproduzione benigna e autonoma di CVE-2026-77521 (esecuzione di comandi con prompt injection in MaxKB)
Un laboratorio autonomo e innocuo che riproduce
GHSA-f36j-f34j-h3rx
/ CVE-2026-77521: in MaxKB (una
piattaforma open-source per assistenti AI aziendali / RAG), qualsiasi assistente dotato di un tool,
di un tool MCP, di una skill o di una sotto-applicazione riceve un tool shell execute
iniettato automaticamente che non è protetto da un meccanismo di approvazione umana — quindi il prompt injection (dalla chat
o da un documento ingerito) raggiunge una shell. Una sandbox avrebbe dovuto contenerlo,
ma una stringa con shell=True ha permesso ai metacaratteri di evaderla.
| Advisory | GHSA-f36j-f34j-h3rx |
| CVE | CVE-2026-77521 |
| Progetto | 1Panel-dev/MaxKB |
| Affected | < 2.10.5-lts |
| Patched | 2.10.5-lts (commit 594f50f) — parziale, vedi sotto |
| CVSS | 10.0 CRITICAL (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H) |
| Weakness | CWE-78 (OS command injection) · CWE-250 (unnecessary privileges) · CWE-749 (exposed dangerous method) |
Questo riproduce una vulnerabilità già corretta e divulgata pubblicamente a scopo didattico e difensivo. Viene eseguito interamente in locale e in modo innocuo:
id, la scrittura di un file marcatore
che il PoC elimina). Nessun vero gosu/sandbox/root è coinvolto — il wrapper di
privilege-drop è sostituito da un marcatore env inerte, così la demo funziona ovunque.Un assistente MaxKB con qualsiasi capacità collegata è costruito su deepagents con un
SandboxShellBackend. deepagents inietta automaticamente un tool shell execute (più
read_file/write_file/…), e MaxKB non usa alcun excluded_tools, quindi execute è
sempre presente. La configurazione interrupt_on — che decide cosa richiede l'approvazione
umana — è {"write_file": False, "read_file": False, "edit_file": False}:
execute non è presente, quindi viene eseguito senza alcuna approvazione. Qualsiasi testo in grado di orientare
il modello (un messaggio in chat, o un documento avvelenato che la pipeline RAG ingerisce) può
quindi far eseguire all'agente comandi shell. Nelle installazioni da sorgente SANDBOX
è disattivato per impostazione predefinita, quindi i comandi vengono eseguiti come utente dell'app; nel container ufficiale (root)
il wrapper della sandbox era una stringa con shell=True, quindi ;/>/$() venivano eseguiti al di fuori
di gosu, come root.
interrupt_on di MaxKB, una chiamata execute iniettata viene eseguita senza approvazione; aggiungendo
"execute": True la si sottopone a gate. → poc/01_missing_hitl_gate.py;+redirect evade il
prefisso gosu a stringa singola; il re-quoting di _build_sandbox_command() in 2.10.5-lts
lo neutralizza. → poc/02_sandbox_escape.pyexecute con il comando dell'attaccante (e non lo fa con un
documento pulito). → poc/03_real_llm.pyLa patch 2.10.5-lts distribuita (commit 594f50f)
rafforza solo l'evasione dalla sandbox — aggiunge _build_sandbox_command(), che
esegue lo split sugli operatori di shell, ri-quota ogni token con shlex.quote e avvolge
ogni comando semplice nel prefisso gosu. Verificato rispetto al
sorgente v2.10.5-lts,
il core dell'agency è invariato: interrupt_on omette ancora execute, il
backend è ancora SandboxShellBackend, e SANDBOX ha ancora come default 0. Quindi
dopo la correzione, un execute iniettato tramite prompt viene ancora eseguito senza approvazione umana —
il muro interno è stato rinforzato mentre il cancello è rimasto aperto. Il PoC 1 dimostra la
mitigazione consigliata (porre un gate su execute), che il vendor non ha distribuito.
Nessuna dipendenza per i 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
Lo stai presentando?
DEMO.mdè un runbook edemo.shlo guida un tasto alla volta.
Qualsiasi endpoint compatibile con OpenAI (self-hosted o open-model ospitato). Esempio (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
Dietro un proxy di ispezione TLS riprova senza verifica con un avviso
(INSECURE_SSL=1 per forzarlo). Salta in modo pulito se non è impostato alcun endpoint.
| Percorso | Di cosa si tratta |
|---|---|
sandbox_shell.py | Modello benigno della costruzione del comando execute() di MaxKB — stringa vulnerabile vs _build_sandbox_command() corretta |
agent_sim.py | Modello minimale del gate di approvazione umana interrupt_on di deepagents |
payloads/poisoned-document.md | Documento KB avvelenato dall'attaccante (iniezione indiretta) |
poc/01_missing_hitl_gate.py | L'execute iniettato viene eseguito senza approvazione; il gating lo blocca |
poc/02_sandbox_escape.py | I metacaratteri evadono il prefisso gosu; il parser 2.10.5 li contiene |
poc/03_real_llm.py | Un modello reale chiama execute dal documento avvelenato (pulito vs avvelenato) |
DEMO.md / demo.sh | Runbook del presentatore e driver demo cadenzato |
assets/attack-flow.{svg,png} | Diagramma del flusso di attacco (hero del blog) |
assets/poc3-groq-sample.txt | Output registrato del PoC 3 (fallback demo) |