Yaamlabs
Vulnerabilities

Tailworm: Tailscale SSH forwarding gave users root

A researcher showed Tailscale SSH socket forwarding running as root. Tailscale fixed it in 1.104.4. What broke, how to patch or switch it off, and what to check.

By Yaali. October 28, 2026, 6 min read, Vulnerabilities, Identity, Patching.

Cover illustration of a server rack with a glowing tunnel leading into a bright inner chamber, with the Yaamlabs logo and the text: Tailscale SSH forwarding handed out root, fixed in 1.104.4

On October 25, Erica Windisch of Cyberdione Labs published Tailworm, a report on eleven findings across Tailscale and the open source control server headscale. The serious one is in Tailscale SSH, the SSH server built into the tailscaled daemon. Anyone your policy allows to SSH into a Linux machine, even as an ordinary user, could use Unix socket forwarding to make the root-owned daemon open sockets that only root should reach. In the lab that meant a root shell and a read of /etc/shadow.

No fix had shipped when the report went out, so the first headlines called the flaws unpatched. Tailscale has since published bulletin TS-2026-014 and released client 1.104.4 with the fix. If you use Tailscale SSH (you will see tailscale up --ssh or tailscale set --ssh on your servers, and ssh rules in your tailnet policy), this affects you. Plain OpenSSH reached over a Tailscale connection does not use this code and is not affected.

Four steps of the Tailworm attack: an approved SSH session asks for socket forwarding, root tailscaled checks only the path name, then dials, replaces or races into a root-only socket, giving root through Docker, systemd or the local API

How it works

Tailscale SSH lets a policy rule decide who may log in to which machine as which user. There is no password or key exchange: the WireGuard tunnel identifies the device, and the rule in the tailnet policy file does the rest. Like OpenSSH, it supports forwarding. With ssh -L the server connects to something on the target on your behalf; with ssh -R it listens on the target and sends connections back to you. Both can point at a Unix socket, the file-based endpoint that services such as Docker, systemd and tailscaled itself use for local control.

OpenSSH does this work in a process running as the logged-in user, so the kernel's file permissions decide which sockets that user can touch. Before 1.104.4, Tailscale did it inside tailscaled, which runs as root. It checked the requested socket path by name against an allowlist and then opened, removed or bound the socket with root's rights.

The report shows three ways to turn that into root access:

  • A local forward (-L) to a root-only socket. The daemon dials it as root. A socket inside an allowed directory falls straight away; reaching /var/run/docker.sock, and so a privileged container, needed the race below.
  • A remote forward (-R) whose path is a root-owned socket. The daemon unlinks the real socket and binds the attacker's listener in its place, so clients of that service now talk to the attacker. The researcher replaced a systemd socket this way.
  • A race on the allowlist. A loop renames directories so the path points somewhere harmless when it is checked and somewhere privileged when it is used. The report measured about 170,000 flips per second, a time-of-check to time-of-use (TOCTOU) bug.

The tailscaled local API socket was another target. Through it the researcher captured an auth key and, in the lab, joined a rogue node to the tailnet under a victim's identity.

In June, TS-2026-004 fixed forwarding that followed symlinks into protected sockets, in version 1.98.9. Tailworm's race gets around that fix, so 1.98.9 does not protect you here; 1.104.4 does.

Exploitation and disputed claims

No attacks in the wild had been reported as of October 25, and the researcher has not published the full reproduction kits. The attacker needs a first foothold: a device or account that a policy rule already lets SSH into the target. The "worm" in the name comes from a lab where broad rules let one compromised node reach the next. Commenters have questioned that word: it spreads only as far as your SSH rules allow.

How easy the escalation is also varies. Konfirmity's review of the report says one route succeeded between 0 and 4 percent of the time. Windisch called the disclosure "reported but not coordinated", though she told The Hacker News she sent the report and proof of concept to Tailscale before publishing. Tailscale's vice president of engineering replied on Reddit that the company had received it and was fixing what it found.

One date is unclear. The bulletins carry October 26, the Tailworm site says 1.104.4 was tagged on October 27, and the GitHub release page shows October 28. Tailscale lists no CVE or CVSS score for TS-2026-014.

What to do

Action list: upgrade every Tailscale SSH node to 1.104.4, disable forwarding with TS_SSH_DISABLE_FORWARDING=1 if you cannot, narrow SSH rules, and check socket changes and new devices

  1. Upgrade every node that runs Tailscale SSH. Run tailscale version and update to 1.104.4 or newer, with sudo tailscale update or your package manager. The same release fixes TS-2026-015 (memory exhaustion from SSH window-change and environment messages) and TS-2026-016 and TS-2026-017 in tailscale serve. Restarting tailscaled drops open Tailscale SSH sessions, so make sure you have another way in first.
  2. If you cannot upgrade today, switch forwarding off. Tailscale's bulletin gives TS_SSH_DISABLE_FORWARDING=1 in the tailscaled service environment, then a restart. On Debian and Ubuntu packages that is a line in /etc/default/tailscaled; elsewhere use systemctl edit tailscaled and add Environment=TS_SSH_DISABLE_FORWARDING=1. This breaks tools that tunnel over SSH, such as VS Code Remote SSH and database tunnels. Where Tailscale SSH is not needed at all, sudo tailscale set --ssh=false removes it.
  3. Treat every SSH grant as root until you have patched. Replace rules that let whole tags or autogroup:member reach whole tags with one source group to one destination. Restricting rules to non-root users does not help here, because the attack starts from an ordinary account.
  4. Do not rely on check mode. action: "check" asks for a fresh sign-in before a session starts, by default every 12 hours, but it does nothing inside a session that has already been approved. The report adds that an unexpected re-authentication prompt should be handled as an incident.
  5. On headscale, assume exposure. The report says headscale 0.29.5 grants forwarding on every accepted SSH session with no policy switch to disable it, and Konfirmity notes 0.29.6 does not change that. Upgrade the clients to 1.104.4 and use the environment variable on any node you cannot update.

Checking for compromise

Tailscale does not log individual forwarding requests, and session recordings capture terminal output, not forwarded traffic. Look on the hosts instead. Auditd rules on unlink, bind and chmod under /tmp, home directories and /run/user will show socket swaps, and a burst of directory renames from one SSH session points to the race. A socket at mode 0755 where you expect 0600 can mean a race was won. Review docker ps -a and Docker events for containers nobody started.

In the admin console, open the configuration audit log (kept 90 days on every plan) and look for unexpected new devices, auth key creation or use, and policy edits. If a machine looks compromised, delete the device in the console rather than only reimaging it, because node keys copied from its state file stay valid until then. Then rotate auth keys and any credentials the host could reach, and rotate Tailnet Lock signing keys if a signing node was involved.

Why the design mattered

Both TS-2026-004 and TS-2026-014 came from one decision: a privileged daemon doing filesystem work for an unprivileged user after checking only a path string. The 1.104.4 fix makes forwarding run as the authenticated user, which is how OpenSSH has always done it. When you review any agent that runs as root and acts for remote users (SSH servers, remote management tools, CI runners), check whether each action actually runs with the user's privileges or only checks a name first.

Tailnet policies and SSH rules are part of the access reviews in our network penetration testing and safeguarding and hardening work. If you want a second look at your rules before or after patching, open the chat and Yaali, our AI agent, will pass your question to an engineer.


Sources: Tailscale security bulletins (TS-2026-004, TS-2026-014 to TS-2026-017), Tailworm report, Cyberdione Labs, Konfirmity advisory, The Hacker News, RuntimeWire, Tailscale releases on GitHub.

Read next

Back to the blog, or tell us about your system in the chat. Yaali, our AI agent, answers first and brings in an engineer.