A voice server that logs 'please do not hack me' right before it crashes is basically putting a 'kick me' sign on its own back
CVE-2026-4390 is a use-after-free in TeamSpeak 3 Server's process_resend_queue function within the Connection State Management component. An attacker sends a duplicate clientinit handshake message in rapid succession over UDP port 9987, triggering a race condition. The error handler removes the client from one internal lookup table but leaves a stale reference in a second (client-ID-based) table. When the resend-queue timer fires and the attacker later acknowledges the error packet, the server dereferences a freed ResendingPacket structure — classic dangling pointer. Affected versions are 3.13.0 through 3.13.7; the TeamSpeak SDK server-side library through 3.3.1 is also vulnerable. The fix ships in TeamSpeak 3 Server 3.13.8 and SDK 3.5.0.
The third-party NVD/VulDB score of 5.4 MEDIUM (using A:L) materially understates the risk. TeamSpeak's own advisory — TS-SA-2026-001 — scores this 7.1 HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H), correctly recognizing that a reliable, repeatable server crash is A:H, not A:L. The modzero research blog further reveals the attack fires during the handshake *before* full client authentication completes, meaning the PR:L rating is generous — most TeamSpeak 3 servers require zero credentials to initiate the handshake. RCE remains unproven due to per-thread tcache segregation and the three-layer pointer chain, but the DoS is reliable and the public PoC lowers the bar to script-kiddie level.
5 steps from start to impact.
Identify exposed TeamSpeak 3 server
- Target runs TeamSpeak 3 Server ≤ 3.13.7
- UDP 9987 reachable from attacker network
- Server must actually be running and reachable; some deployments sit behind VPN or IP allowlists
product:TeamSpeak will enumerate exposed instances.Initiate handshake — send first clientinit
clientinit packet over UDP to begin the connection handshake. No credentials, tokens, or server passwords are required at this stage; this is the raw protocol negotiation. The server allocates a connection-state object and places it in both an endpoint-keyed hashmap and a client-ID-keyed hashmap.- Ability to send crafted UDP packets to port 9987
- Trivial — any UDP sender can do this; no auth or password needed
Race the handshake — send duplicate clientinit
clientinit before the server finishes processing the first. Both packets bypass the initial state guard concurrently. The second packet lands in an unexpected handshake state, triggering the error handler that logs the string 'please do not hack me.' The born0monday/teamspeak3-vulnerabilities GitHub PoC automates this timing.- Precise but consistent timing of two UDP packets
- The modzero blog notes the race is 'reportedly consistent in practice,' so timing is not a significant barrier
clientinit within a tight window, but no public Snort/Suricata signature exists yet.State desync creates dangling pointer
ResendingPacket structure is freed by the resend-queue timer, but pointers to it persist. This is a textbook CWE-416 use-after-free.- Steps 2 and 3 completed successfully
- None — this is deterministic once the race wins
Trigger crash via stale ACK
ts3server process crashes and must be restarted manually or by a watchdog. Repeated exploitation trivially keeps the server offline.- Freed memory has not been reclaimed by another allocation
- Memory reclamation patterns make this highly reliable in practice per modzero's analysis
ts3server.sqlitedb and your config first. Per the noisgate mitigation SLA for HIGH, deploy within 30 days; full remediation within 180 days across all instances.Restart=on-failure with RestartSec=5s and StartLimitBurst=10 (or equivalent supervisor) to auto-restart the TS3 process after a crash. This does not prevent the crash but limits the DoS window to seconds rather than until manual intervention. Log restarts to your SIEM for detection.clientinit packets from the same source IP within a sub-second window on UDP 9987. This provides detection-in-depth but does not block the attack unless deployed inline (e.g., on a UDP-aware NGFW or iptables rate-limit rule).- WAF / reverse proxy — TeamSpeak voice traffic is UDP-based and proprietary; no HTTP-layer WAF can inspect or filter it.
- DDoS mitigation services (Cloudflare, Akamai) — Generic volumetric DDoS scrubbing won't help because this is a protocol-level logic bug triggered by two well-formed packets, not a flood.
- TeamSpeak server passwords — The attack fires during the pre-auth handshake *before* any server password check occurs. Setting a server password does not gate the vulnerable code path.
- Rate limiting by connection count — The attack requires only two packets total; rate limits tuned for volumetric abuse will not trigger.
The supporting signals.
| In-the-wild exploitation | None observed. TeamSpeak confirms no known active exploitation in customer environments as of advisory date. Not listed in CISA KEV. |
|---|---|
| Proof-of-concept | Public. GitHub repo born0monday/teamspeak3-vulnerabilities contains PoC code covering CVE-2026-4390, CVE-2026-4391, and CVE-2026-4392. Detailed technical write-up on born0monday's blog. Not yet indexed on pocindex.io or ExploitDB. |
| EPSS | 0.00415 (~0.4 percentile) — very low modeled exploitation probability, consistent with a niche product. Do not let this override the structural assessment. |
| KEV status | Not listed. No CISA Known Exploited Vulnerabilities entry as of 2026-09-27. |
| CVSS vector | User-provided: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:L → 5.4 MEDIUM. Vendor (TeamSpeak TS-SA-2026-001): CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:H → 7.1 HIGH. The A:H is correct — the crash is reliable and repeatable. Modzero's research indicates PR:N may be more accurate since the attack fires during the pre-auth handshake. |
| Affected versions | TeamSpeak 3 Server 3.13.0 – 3.13.7; TeamSpeak SDK (server-side) ≤ 3.3.1 |
| Fixed versions | TeamSpeak 3 Server 3.13.8; TeamSpeak SDK 3.5.0. No distro backports expected — TeamSpeak distributes its own binaries. |
| Scanning / exposure | No GreyNoise tags observed for CVE-2026-4390 scanning activity. TeamSpeak voice servers on UDP 9987 are internet-facing by design; Shodan fingerprints TS3 banners. Tens of thousands of instances are typically indexed. |
| Disclosure date | 2026-05-27 via modzero advisory MZ-26-01 and TeamSpeak advisory TS-SA-2026-001. |
| Researcher / org | Michael Imfeld at modzero AG. PoC published by born0monday on GitHub and Infosec Exchange. |
Sources.
Why this verdict
- Availability impact is A:H, not A:L. The server crashes deterministically — a reliable, repeatable DoS. The third-party 5.4 score used
A:Lwhich is factually wrong; TeamSpeak's own advisory correctly scoresA:H, yielding 7.1. - Authentication barrier is near-zero. The vendor says
PR:L, but modzero's research demonstrates the attack fires during the UDP handshake before any credential exchange. Initiating aclientinitrequires no password, token, or server membership — any UDP sender can trigger the race. - Internet-facing by design. TeamSpeak voice servers *must* accept inbound UDP on port 9987 to function. There is no 'put it behind a WAF' option for UDP voice traffic without breaking the service. Attack surface is inherently exposed.
- Public PoC removes exploitation complexity. The born0monday GitHub repository provides working exploit code. Combined with the consistent race window documented by modzero, the practical attack complexity is LOW.
- Role multiplier: TeamSpeak 3 Server is a voice communication platform. It does not occupy any high-value deployment role (not an identity provider, hypervisor, CI/CD system, backup, PKI, PAM, edge appliance, SIEM, or OT controller). Blast radius is bounded to voice-service disruption — no lateral movement, no data exfiltration, no domain/fleet/supply-chain escalation. No high-value-role floor applies. This caps the ceiling below CRITICAL despite the low-friction attack path.
Why not higher?
Impact is limited to denial of service; remote code execution remains unproven despite modzero's investigation into vtable manipulation paths. TeamSpeak 3 Server is not a high-value-role component — crashing it disrupts voice comms but does not expose credentials, enable lateral movement, or compromise fleet integrity. No high-value-role floor forces CRITICAL.
Why not lower?
A reliable, repeatable remote DoS against an internet-facing service with a public PoC and near-zero authentication barrier is textbook HIGH. The vendor's own corrected assessment agrees at 7.1. Downgrading to MEDIUM would require either a meaningful authentication gate (there isn't one) or the service to be non-internet-facing (it can't be). The EPSS score of 0.4% reflects low *current* exploitation probability, not low exploitability — the PoC works.
Crowdsourced verification payload.
Run this script on each host running TeamSpeak 3 Server as any user who can execute the ts3server binary. Pass the binary path as an argument: bash check_cve_2026_4390.sh /opt/teamspeak3-server_linux_amd64/ts3server. No elevated privileges required.
#!/bin/bash
# CVE-2026-4390 Vulnerability Check — TeamSpeak 3 Server
# Checks installed ts3server version against the fixed release.
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -uo pipefail
FIXED="3.13.8"
TS3="${1:-}"
if [ -z "$TS3" ]; then
for candidate in \
/opt/teamspeak3-server_linux_amd64/ts3server \
/opt/teamspeak/ts3server \
/home/teamspeak/ts3server \
/usr/local/bin/ts3server; do
if [ -x "$candidate" ]; then
TS3="$candidate"
break
fi
done
fi
if [ -z "$TS3" ] || [ ! -f "$TS3" ]; then
echo "UNKNOWN — ts3server binary not found. Pass path as argument."
exit 2
fi
# Extract version from ts3server version output
VER=$("$TS3" version 2>/dev/null | grep -oP '[0-9]+\.[0-9]+\.[0-9]+' | head -1) || true
# Fallback: check CHANGELOG in same directory
if [ -z "$VER" ]; then
DIR=$(dirname "$TS3")
if [ -f "$DIR/CHANGELOG" ]; then
VER=$(grep -oP '[0-9]+\.[0-9]+\.[0-9]+' "$DIR/CHANGELOG" | head -1) || true
fi
fi
if [ -z "$VER" ]; then
echo "UNKNOWN — could not determine TeamSpeak 3 Server version."
exit 2
fi
# Compare: if FIXED sorts first or equal, version is patched
if printf '%s\n' "$FIXED" "$VER" | sort -V | head -1 | grep -qx "$FIXED"; then
echo "PATCHED — TeamSpeak 3 Server $VER (>= $FIXED)"
exit 0
else
echo "VULNERABLE — TeamSpeak 3 Server $VER (needs >= $FIXED)"
exit 1
fi