← Back to Feed CACHED · 2026-09-26 22:59:31 · CACHE_KEY CVE-2026-4390
CVE-2026-4390 · CWE-119 · Disclosed 2026-05-27

TeamSpeak 3 Server Use-After-Free in Connection State Management (process_resend_queue)

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

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.

"Pre-auth-adjacent DoS with public PoC on an internet-facing voice server warrants HIGH, not MEDIUM"
02 · The Attack Path

5 steps from start to impact.

STEP 01

Identify exposed TeamSpeak 3 server

The attacker scans for UDP port 9987 (default voice port). TeamSpeak servers are internet-facing by design — they must accept inbound UDP from arbitrary clients. Shodan, Censys, and FOFA all fingerprint TS3 banners.
Conditions required:
  • Target runs TeamSpeak 3 Server ≤ 3.13.7
  • UDP 9987 reachable from attacker network
Where this breaks in practice:
  • Server must actually be running and reachable; some deployments sit behind VPN or IP allowlists
Detection/coverage: Shodan/Censys queries for product:TeamSpeak will enumerate exposed instances.
STEP 02

Initiate handshake — send first clientinit

The attacker sends a standard 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.
Conditions required:
  • Ability to send crafted UDP packets to port 9987
Where this breaks in practice:
  • Trivial — any UDP sender can do this; no auth or password needed
STEP 03

Race the handshake — send duplicate clientinit

The attacker immediately sends a second 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.
Conditions required:
  • Precise but consistent timing of two UDP packets
Where this breaks in practice:
  • The modzero blog notes the race is 'reportedly consistent in practice,' so timing is not a significant barrier
Detection/coverage: IDS rules could flag duplicate clientinit within a tight window, but no public Snort/Suricata signature exists yet.
STEP 04

State desync creates dangling pointer

The error handler removes the client entry from the endpoint-keyed hashmap but leaves the stale reference in the client-ID-keyed hashmap. The ResendingPacket structure is freed by the resend-queue timer, but pointers to it persist. This is a textbook CWE-416 use-after-free.
Conditions required:
  • Steps 2 and 3 completed successfully
Where this breaks in practice:
  • None — this is deterministic once the race wins
STEP 05

Trigger crash via stale ACK

When the attacker sends an ACK for the error message, the server retrieves the freed pointer from the ack window and dereferences it, causing a segfault. The ts3server process crashes and must be restarted manually or by a watchdog. Repeated exploitation trivially keeps the server offline.
Conditions required:
  • Freed memory has not been reclaimed by another allocation
Where this breaks in practice:
  • Memory reclamation patterns make this highly reliable in practice per modzero's analysis
Detection/coverage: Process crash monitoring / watchdog restart logs will surface the event post-facto.
03 · Compensating Control

1
HIGH 7.1→IGNORE 0.0
SEVERITY REDUCED
Upgrade to TeamSpeak 3 Server 3.13.8 immediately — This is the definitive fix. TeamSpeak distributes standalone binaries — download from the vendor site, stop the service, replace the binary, and restart. Back up 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.
2
HIGH 7.1→MEDIUM 5.0
SEVERITY REDUCED
Restrict UDP 9987 to known client IP ranges via firewall ACL — If patching is delayed, limit inbound UDP 9987 to corporate VPN egress IPs or known player/user subnets. This eliminates opportunistic external attackers. The residual risk is that an attacker on an allowed subnet can still trigger the crash.
3
HIGH 7.1→MEDIUM 5.5
SEVERITY REDUCED
Deploy a process watchdog with restart throttling — Configure systemd 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.
4
HIGH 7.1→HIGH 6.5
Monitor for duplicate clientinit race pattern — Write a custom IDS rule to flag two 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).
What doesn't work
  • 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.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNone observed. TeamSpeak confirms no known active exploitation in customer environments as of advisory date. Not listed in CISA KEV.
Proof-of-conceptPublic. 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.
EPSS0.00415 (~0.4 percentile) — very low modeled exploitation probability, consistent with a niche product. Do not let this override the structural assessment.
KEV statusNot listed. No CISA Known Exploited Vulnerabilities entry as of 2026-09-27.
CVSS vectorUser-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 versionsTeamSpeak 3 Server 3.13.0 – 3.13.7; TeamSpeak SDK (server-side) ≤ 3.3.1
Fixed versionsTeamSpeak 3 Server 3.13.8; TeamSpeak SDK 3.5.0. No distro backports expected — TeamSpeak distributes its own binaries.
Scanning / exposureNo 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 date2026-05-27 via modzero advisory MZ-26-01 and TeamSpeak advisory TS-SA-2026-001.
Researcher / orgMichael Imfeld at modzero AG. PoC published by born0monday on GitHub and Infosec Exchange.

Sources.

  1. TeamSpeak Security Advisory TS-SA-2026-001
  2. modzero Advisory MZ-26-01
  3. modzero Blog — Please Do Not Hack Me
  4. born0monday PoC Repository (GitHub)
  5. SentinelOne Vulnerability Database — CVE-2026-4390
  6. OpenCVE — CVE-2026-4390
  7. ESAGAMES — TeamSpeak 3 Vulnerability Guide
05 · The Call

Final Verdict
↑ UPGRADED to HIGH (7.1/10)

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:L which is factually wrong; TeamSpeak's own advisory correctly scores A: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 a clientinit requires 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.

06 · Verification

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.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/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