← Research / CVE-2026-19743
TeamViewer for Linux

Local privilege escalation to root in TeamViewer for Linux

CVSS 7.8 · HIGH Patched · 15.82 CWE-22 · Path Traversal

The root teamviewerd daemon on Linux accepts commands from any local user over 127.0.0.1:5939 with no per-caller authorization. From there a short chain turns an ordinary account into root, with no sudo, no password, and no TeamViewer account required.

Identifier
CVE-2026-19743
Weakness
CWE-22 · Path Traversal
CVSS 3.1
7.8 (High)
Vulnerable
< 15.82 (tested 15.79.4)
Fixed in
15.82
Advisory
TV-2026-1010
Disclosed
2026-09-29
Research
Timo De Clercq
// Demo

Unprivileged → root in one command

The proof of concept runs entirely as a normal user. It hands any shell command to the root daemon, which executes it. Below is a faithful replay of a real run. uid=1001 goes in, and uid=0 comes out.

user@lab:~/poc

Replay of poc.sh. Timings and counters are from a lab run on 15.79.4.

// The chain

Five steps, all from an unprivileged socket

Every attacker action is an ordinary TCP write to 127.0.0.1:5939. The only privileged actor anywhere in the chain is teamviewerd itself, acting on attacker-supplied input.

unpriv

Speak IPC

Complete the 5939 handshake with a hardcoded key. No account, no authz.

unpriv

Write root config

0309 SetValue points the CustomConfiguration URL at a loopback server.

unpriv

Crash to reload

A malformed frame aborts the daemon; systemd restarts it and re-reads the config.

root

Fetch + extract

The daemon downloads the attacker's zip and extracts it with no path check.

root

Write anywhere

Zip Slip ../ traversal drops a file into /etc/cron.d → root command.

// Background

The trust model: there isn't one

On Linux, TeamViewer ships a system service, teamviewerd, that runs as root and listens on the loopback interface, TCP port 5939. The GUI client talks to it over this socket using a binary IPC protocol. The daemon does privileged work on the client's behalf, such as editing the system configuration, fetching "custom configuration" bundles from the network, and managing the device identity.

The protocol has a handshake, which looks like authentication but is not. The client proves nothing about who it is; it only proves it knows a constant compiled into the binary:

# response the daemon accepts, for anyone who can read the binary
response = md5(server_nonce + K)
K = 40c289053be8c1697d74d836fc1d2f6e   # hardcoded in teamviewerd

Because K is the same everywhere and the daemon applies no per-caller authorization after the handshake, any local user who can open a socket to 127.0.0.1:5939 can issue privileged commands. That is the root of everything below.

// Finding 1 · CVE-2026-19743

Local privilege escalation to root

The escalation composes four primitives. None is exotic on its own; chained, they walk an unprivileged socket all the way to root code execution.

1 · Arbitrary write to the root config

Command 0309 (SetValue) writes any key/value into the root-owned /etc/teamviewer/global.conf. The daemon never checks whether the caller should be allowed to. We set the two keys that make the daemon fetch from us:

0309 CustomConfigurationUrl         = http://127.0.0.1:8899/
0309 CustomConfigurationRevisionUrl = http://127.0.0.1:8899/

Setting RevisionUrl forces a synchronous write-through of both keys to disk. The config file is mode 600 (unreadable to us), but its directory is world-executable, so the unprivileged process can stat() the file and poll its mtime:size signature to prove the flush landed before relying on it. That turns a race into a confirmed state transition.

2 · A crash that doubles as a reload

The daemon re-reads global.conf on startup, so the planted URLs take effect after a restart. An unprivileged user cannot call systemctl restart, but it can make the daemon restart itself. One command frame with ID 0x0887 carrying a body of the wrong type throws an uncaught C++ exception:

std::terminate  ->  SIGABRT  ->  systemd Restart=on-abort  ->  fresh daemon re-reads our config

3 · The Zip Slip path traversal (the core of the vulnerability)

With our URLs live, firing the CustomConfiguration 'f' trigger makes the root daemon fetch a zip from our loopback server and extract it. This is where we plant a classic Zip Slip.

What is Zip Slip? Zip Slip is a directory-traversal flaw in how archives are unpacked. An archive stores a name for every entry, and a safe extractor treats that name as relative to the output directory. A vulnerable one joins the name onto the output path and writes it as-is, so an entry named ../../etc/cron.d/tvlpe walks up out of that directory and lands wherever the attacker points it. It is the archive equivalent of the ../ path-traversal bug (CWE-22), and because teamviewerd extracts as root, the write happens as root.

TeamViewer's extractor builds each output path as dest_dir + "/" + entry_name with no ../ sanitization, so a crafted entry name escapes the temp directory entirely:

# zip entry name, stored verbatim
../../../../../../../../../../../../etc/cron.d/tvlpe
# root resolves it to ->  /etc/cron.d/tvlpe

Extra ../ above / are harmless, so we over-supply depth and land the write wherever we want. Here that target is a cron file that runs our command as root within the minute.

4 · Winning the cleanup race with a symlink

The daemon unlinks its extraction scratch space afterward. We plant a symlink at the expected scratch path pointing at the real target, so the root write follows the link to /etc/cron.d/tvlpe and the subsequent unlink only removes the (now harmless) link, so the swap beats the cleanup. The PoC verifies success out-of-band: the attacker's HTTP server logs the root fetch, so each attempt is confirmed, not hoped for.

Net effect A user with uid=1001 and no sudo writes an arbitrary root-owned file and executes an arbitrary command as root, repeatably and reliably.
// Severity

Scoring & scope

CVE-2026-19743 is scored 7.8 High for the privilege escalation. Vector CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H:

Attack vector
Local
Complexity
Low
Privileges
Low
User interact.
None
Confidentiality
High
Integrity
High
Availability
High

The weakness is cross-platform. The same IPC path traversal reaches NT AUTHORITY\SYSTEM on Windows and root on macOS, but this writeup covers the Linux teamviewerd chain. It is most dangerous on shared and multi-user hosts: kiosks, lab machines, call-center workstations, and any box where an unprivileged service account could be subverted.

BranchAffectedFixedStatus
15.x< 15.8215.82patch
15.64 LTS (Win 7/8)< 15.64.815.64.8patch
14.7< 14.7.4885514.7.48855patch
13.2 (Linux)< 13.2.15399513.2.153995patch
// Remediation

Fixing & detecting it

  • Update to 15.82 (or the patched build for your branch, above). This is the only real fix, and it is bundled in advisory TV-2026-1010.
  • If you can't patch immediately, stop and disable teamviewerd on hosts where untrusted local users have shell access. The socket is the attack surface.
  • Detection: repeated teamviewerd SIGABRT / restart cycles in journalctl -u teamviewerd; unexpected CustomConfiguration* keys in global.conf; and new or modified files under /etc/cron.d correlated with daemon restarts.
// Disclosure

Timeline

  • 2026-07-17Reported to TeamViewer through YesWeHack: unauthenticated local IPC to root, with a working proof-of-concept.
  • 2026-09-07TeamViewer accepts the report.
  • 2026-09-29TeamViewer publishes advisory TV-2026-1010 and releases 15.82.

Coordinated disclosure through the vendor. Details are released only after a fixed build was available.

Published for defensive and educational purposes against patched software. Any security testing should target only systems you own or are authorized to assess.