antiTree | posts and projects
posted on Oct 14, 2017

I noticed this bug ticket from Tor Project last week: Make exit flag depend on ports 80 and 443, not 6667 and it reminded me about a short talk I gave regarding how the port you connect to a service on, directly affects the anonymity you’re able to achieve. In short, visiting services on non-standard ports such as https://www.antitree.com:64201 increases the risk of you choosing a compromised circuit compared to visiting https://www.antitree.com:443.

Calculating Probability of Anonymity

Tor’s original threat model simply said that in order to compromise an individual, you would have to compromise 2 of the 3 nodes in a given circuit. Specifically the Guard and Exit. And since the nodes are run by anyone, there’s a risk that some of them could be compromised. So not factoring in anything else (path selection, guards, latency), your probability of choosing a compromised circuit comes out to be:

P=(M/N)2

Where the probability of a compromise(P) would be the number of malicious nodes that are on the network(M) divided by the total number of nodes available(N), squared.

You can plug some numbers into that to think about it yourself:

Latest tor metrics

So if N is 7000, and the NSA owned 1000 nodes (M), there’s a 2% chance that the next circuit you used was compromised.

This is overly simplistic for the modern threat model because it doesn’t take into account that Guard nodes are relatively static, that the path selection algorithm specifically tries to reduce this probability, and that Exit node selection is based the port that you’re connecting to. The later is the point I’m trying to emphasize because it’s something users can control themselves.

Path Selection Review

As a reminder, when you tor client needs to visit https://www.antitree.com, it does a bunch of work before it starts deciding what path to select. It downloades the consensus from the Directory Authorities and it uses that to find an available Exit node that will allow you to connect to servcies on port 443.

If you were going to https://www.antitree.com:64201, it would have to find available Exit nodes that allow outbound on port 64201.

So the question is, how many services allow you to connect to ports 64201? The answer is: Not as many as you would like.

Minimums for Exit Nodes

In the above image, you can see there’s just under 1000 exit nodes available on the network. They’re confirmed as functioning Exit nodes if they have ports 80 and 443 outbound on them. If you were to setup an Exit node yourself, and only allow outbound on ports 22, it wouldn’t register as an exit node on the network.

Stats

If you download the consensus doc and you crunch the numbers about available nodes that allow outbound on certain ports you’ll get something like this:

  • Total number of exits found in consensus: 1099
  • Total number of exits that can access port 20: 765
  • Total number of exits that can access port 21: 761
  • Total number of exits that can access port 22: 659
  • Total number of exits that can access port 23: 638
  • Total number of exits that can access port 53: 960
  • Total number of exits that can access port 79: 723
  • Total number of exits that can access port 80: 1044
  • Total number of exits that can access port 81: 751
  • Total number of exits that can access port 100: 288
  • Total number of exits that can access port 143: 817
  • Total number of exits that can access port 389: 645
  • Total number of exits that can access port 194: 668
  • Total number of exits that can access port 443: 1016
  • Total number of exits that can access port 636: 652
  • Total number of exits that can access port 587: 517
  • Total number of exits that can access port 993: 865
  • Total number of exits that can access port 995: 863
  • Total number of exits that can access port 1194: 817
  • Total number of exits that can access port 1723: 726
  • Total number of exits that can access port 5900: 661
  • Total number of exits that can access port 6697: 702
  • Total number of exits that can access port 9999: 654
  • Total number of exits that can access port 64201: 285

NOTE: Don’t forget that when you download the consensus document now, you are only getting a part of it which is why the total exits is so low compared to what the Tor Metrics website says

Threat

If P=(M/N)2 and N is a factor of the port you’re connecting outbound on, then visiting https://www.antitree.com:64201 has a higher probability of compromise compared to ports 443 or 80. This also means that connecting to ports 22 provides less anonymity than ports 443.

But more concerning are the services that could induce you into visiting one of these malicious services. For example, how hard would it be for me to include <img src="https://www.antitree.com:64201"> into a web page?

What would tor do in that case? It would make a separate, less anonymous circuit just to connect and download that image.

Defense

Luckily, Tor Project’s tor network is big enough that this isn’t likely to change anything but if this concerns you, you could

  • stop connecting to anything that isn’t over ports 443. And maybe you’re doing that already.
  • You could also configure your browser to never connect to any service that isn’t ports 443. That would protect you from accidentally visiting a service on another port and it would prevent any web page you visit from forcing you into making connections to other services.
  • Run full open Exit nodes instead of a reduced exit policy. More services that allow you to connect to ports 64201, the better it is for everyone.

Source

You can download the latest consensus and crunch the numbers yourself:

import stem.descriptor.remote
import matplotlib.pyplot as plt
import pandas as pd


data = []

print("Downloading latest descriptors...")
try:
  exits = [desc for desc in stem.descriptor.remote.get_server_descriptors().run() if desc.exit_policy.is_exiting_allowed()]
  for desc in exits:
      for rule in desc.exit_policy._get_rules():
                  if rule.is_accept:
                    if rule.__str__() == "accept *:*":
                      data += [int(p) for p in range(1,65536)]
                    elif not rule.is_port_wildcard():
                      data += [p for p in range(int(rule.min_port),int(rule.max_port)+1)]

except Exception as exc:
  print("Unable to retrieve the consensus: %s" % exc)

randport = 64201
rpcount = data.count(randport)

reduced_exitpolicy = [
        20, 21, 22, 23,53,79,80,81,100,143,389,194,443,636,587,993,
        995,1194,1723,5900,6697,9999,
        ]

print()
print("Question: are there any ports not covered?")
print("Total number of exits found in consensus: %s" % len(exits))
print("Total number of exits that can access port %s: %s" % (randport, rpcount))
for port in reduced_exitpolicy:
    print("Total number of exits that can access port %s: %s" % (port, data.count(port)))
tor_ports   = set(data)
avail_ports = set(range(1,65536))
unused_ports = set(avail_ports - tor_ports)
if len(unused_ports) == 0:
  print("Answer: All ports were covered")
else:
  print("Ports missing:")
  _ = [print(x) for x in unused_ports]

Slides

A few of the slides from my talk:

Download PDF version