Tor Deep Dive Part 1: How Over 300 Tor Exit Relays Were Hijacked to Steal SSH Passwords

On 2 October 2026 a researcher who publishes as kaba tried to SSH to one of their own servers over Tor and got a host key warning. The warning was real. Their connection had been redirected at the exit relay to a server that was not theirs. Within days the Tor Project’s network health team had flagged hundreds of exit relays, and the directory authorities had voted them out as exits.

This is the first post in a series on Tor. I want the incident written down properly before the rest of the series builds on it, because the early coverage mixes confirmed facts, single-source claims, and guesses. Where the sources disagree I say so.

What happened

Three separate accounts cover the incident, and each sees a different part of it.

  • The victim and discoverer. kaba’s write-up, published on 20 October, describes the redirect, a scanner built on exitmap, and the cleanup.
  • A relay operator. An operator who signs as Zimmer posted a detailed timeline to the tor-relays mailing list for two of his own relays that were taken over.
  • Directory data. An analysis of public relay descriptors counted relays that changed their exit policy in the same window.

Pulling them together:

  • Attackers gained access to Linux hosts running Tor relays. In the Zimmer case the first login was on 28 August, more than a month before anything visible happened.
  • They installed Ebury 1.8.3, an OpenSSH backdoor and credential stealer.
  • On 1 and 2 October they edited the relays’ torrc files. Relays that had been non-exits became exits, and relays that had blocked port 22 now allowed it.
  • On the compromised relays, SSH traffic on port 22 was redirected to one of 12 interception servers.
  • The Tor Project’s network health team flagged the changes on 4 October. Directory authorities voted the BadExit and MiddleOnly flags from 18:00 UTC that day, and the flags were in the consensus about three hours later.
  • On 5 October the attackers restarted many relays. They could only serve as middle relays by then, because of the flags.

The Tor Project’s own advisory to relay operators went to the tor-relays list on 5 October. It describes the same pattern: compromised Linux hosts, a torrc edited so non-exit relays became exits, and SSH interception.

How big was it

The numbers differ by source, and the differences matter.

SourceFigureWhat it measures
kaba333 of roughly 3,000 exitsExits that the scanner saw redirecting SSH to the same interception servers
Public directory analysis, as reported by The Hacker News377 relays, 59 operators, peak 7.46% of exit capacity on 4 OctoberRelays that switched to the attacker’s exit policy. The analysis itself says this is a policy match, not proof of compromise
Zimmer168 relays on 57 IPs, listed by Onionoo on 6 OctoberOne operator family only

The Hacker News also quotes 273 affected relays for the Zimmer family, which does not match his own post. I have used his own figures. kaba’s account says about a dozen relays served a fake host key and that more than 300 sent traffic through the same interception servers. Other write-ups break that down differently, and I could not reconcile them from the primary material. Treat “over 300” as the safe summary.

How the attack worked

Tor exit relays are where your traffic leaves the Tor network and reaches the open internet. An exit sees the destination, and it sees the payload unless the protocol on top is encrypted. That makes the exit policy a trust boundary. An operator who controls the torrc of an exit controls which traffic it carries and where that traffic goes. Part 2 covers that model in full.

The attackers did three things with that.

They changed what exits would carry. In Zimmer’s case the live policy rejected only ports 25, 465, 587, 993 and 995, then accepted everything else. That closed the encrypted mail ports and left plaintext mail, telnet, IRC, RDP and SSH open. Why they closed the TLS mail ports is not established. kaba suggests the goal was to collect more unencrypted mail traffic. That is a guess, and I would keep it labelled as one.

They redirected SSH. Traffic to port 22 was sent to interception servers. When kaba ignored the warning and logged in with a password, the interception server captured it, opened a second connection to the real server to watch commands, and a third connection to install Ebury. A honeypot showed the attacker used paramiko, checked the user ID and sudo rights, then ran a base64-encoded Perl payload through sudo bash -c. Part 5 rebuilds the redirect in an isolated lab.

They hid the changes. In the Zimmer case the implant lowered sshd’s log level, so accepted logins stopped being logged. It also ran hidden processes named guard, failover and snf. The first torrc edit on 1 October went into a file the relays never used, because Debian runs relays as tor@ instances. The edit that took effect was on 2 October at 09:34 UTC.

One caveat needs stating plainly. Zimmer’s post says he does not know whether port 22 interception was running on his own two relays. His open question is whether the newly opened port 22 was used to intercept SSH or whether a WireGuard tunnel was built. The interception evidence comes from kaba’s scan across the wider network, not from his hosts.

How it was found

Two things found it, and neither was an automated alarm.

  1. A user who read the warning. kaba had a known_hosts entry for the server, so OpenSSH printed the changed-host-key warning and stopped. Someone connecting for the first time from a fresh machine would have seen nothing at all. SSH trusts the first key it is shown.
  2. A relay-side change check. The network health team spotted the exit policy changes. Zimmer’s relays had run the open policy for about 52 hours before they were flagged on 4 October.

After the warning, kaba built a scanner on exitmap that pins each exit, connects out through it to a test server, and compares the host fingerprint seen with the real one. That scan found the 333 relays and the 12 interception servers, and the interception servers’ ED25519 host key fingerprint is public: SHA256:6baCZCPsl+AfQuujUS81fMEE/C7UeJ+AwjrImqmDB3E. Part 7 builds a small scanner of the same kind.

What is still unknown

  • How the hosts were first compromised. For Zimmer it was a reused password. His operator password was used for SSH, sudo and a backup passphrase, and it was stored in cleartext in a helper script. Where the attacker obtained it is unknown. For the other hosts the answer is open. kaba found that 26 of 27 compromised servers outside Zimmer’s family were at one hosting provider, FranTech/BuyVM, and says that does not prove the provider was compromised.
  • Who is behind it. Attribution is unresolved. Ebury has been in use since 2009, and ESET estimates around 400,000 servers compromised over that time, so Ebury on a host does not name an actor.
  • Whether other relays are still affected. A clean host check does not rule out Ebury on a relay.

What to do if you used SSH over Tor

If you connected to a server over Tor between 1 and 5 October and accepted a host key you did not recognise, or connected for the first time with a password, treat the password as stolen.

  1. Change the password on that server and on anything else that shared it.
  2. Check the server for signs of Ebury (see the commands below, and Part 6).
  3. Move that server to key-only SSH.

Key-based login is not relayable by this attack. The client’s signature covers a session identifier that is different on each leg of a man-in-the-middle connection, so the real server rejects it. Part 5 shows that result on a test lab. The weak spot for key users is agent forwarding to a host you have not verified, so leave ForwardAgent off by default.

What to do if you run a relay

The Tor Project’s advisory to operators gives this guidance:

  1. Take the relay offline.
  2. Preserve the disk and logs before wiping.
  3. Reinstall rather than clean up.
  4. Generate a new relay identity and treat the old keys as stolen.
  5. Rotate every credential on the host.
  6. Report to bad-relays@lists.torproject.org.

The same advisory lists checks for a relay that may have been touched: the Exit flag or exit policy on Tor Metrics, torrc and any %include paths, NAT rules redirecting port 22, unknown listeners, new users, authorized_keys entries, cron jobs and systemd units. Part 8 runs through them.

Zimmer’s post adds checks that I would run on any Debian host I care about, with the caveat that a userland rootkit can hide things, so run them from a clean shell or an offline image:

find / -xdev -name 'libkeyutils*' -perm -4000 2>/dev/null
dpkg -S "$(readlink -f /usr/lib/x86_64-linux-gnu/libkeyutils.so.1)"
grep -E '@event-|@/dev/(event|stats)-' /proc/net/unix
systemctl status ssh | sed -n '/CGroup/,$p'

A setuid libkeyutils.so.1.10.2 that no package owns is the main indicator. His post says dpkg -V and debsums will not catch it, and in Part 6 I confirmed the dpkg -V half on a test host. His post also says ESET’s public Ebury detection script predates version 1.8.3. When I checked ESET’s repository in Part 6, the script did list the 1.8.3 socket name, so I would run it, but it only checks sockets and is silent on the library layout. Other indicators in his write-up include an abstract UNIX socket named @event-0YsOG9WSOE, a fake /sbin/rsyslogd inside the ssh.service cgroup, and logins from 185.225.226.62.

The lessons he draws apply to any server, not only relays:

  • Use key-only SSH on management and jump hosts, and keep them off the public internet where you can.
  • Never reuse one password across SSH, sudo and backups, and never store it in a script.
  • Ship logs off the host, and alert if sshd’s log level drops below INFO.
  • Watch for setuid or unowned library files with AIDE or a similar integrity tool. My Host-Based Detection series covers the Wazuh side of that.
  • On Debian, systemctl disable tor does not stop a multi-instance relay. Mask the tor@ units.
  • Compare your published exit policy against what you deployed, from outside the host.

For the SSH hardening side, my posts on SSH certificate authorities and bastion patterns and SSH keys and getting connected cover the key-only setup in full. The tunnelling side of this story overlaps with Pivoting and Tunneling Part 6, which looks at catching tunnels on the network.

What this changes about Tor

Tor protects who you are from the site you visit. It does not protect the content from the exit. For encrypted protocols with verified identities, a hostile exit can drop or delay your traffic and learn where it is going, but nothing more. For protocols that rely on trust on first use, or that carry passwords, a hostile exit can do real damage. SSH with password authentication is exactly that case.

The rest of the series

  1. This post, the incident
  2. How Tor works and what you are trusting
  3. Running a Tor client on Debian 13
  4. SSH over Tor, host keys, and why an onion service beats an exit
  5. What a hostile exit can do to an SSH connection
  6. Ebury, and how to check your own Debian hosts
  7. Finding bad exit relays with a Python scanner
  8. Running a Tor relay securely on Debian
  9. Other ways Tor can fail you
  10. Tor, VPN, Tailscale or Cloudflare Tunnel: a decision table

Sources: