[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]

[tor-dev] Internet-wide scanning for bridges

Hello, while taking a looking at TorCloud I it used static OR and obfs2+obfs3 ports making them trivial to discover by scanning EC2's IP address space. This led me to consider the more general attack of internet-wide scanning to discover Tor bridges. Most Tor relays run their ORPorts on port 443 or 9001 so I began investigating there.

I began by looking at Project Sonar's port 443 SSL certificate database looking for certificates matching the tor certificate pattern. In talking with Eric Wustrow about ZMap, I discovered that he and the other ZMap authors had already published about this attack in their original paper. Eric mentioned that in his discussion with Roger that the response to the attack was going to be the soon-to-be-deployed obfuscated transports which were intended to be run on random high ports; however, the eventual roll out of obfsproxy did not change the fact that bridge's ORPorts continue to be publicly accessible.

While I was analyzing Internet-wide survey-data for port 443 and 9001 (helpfully provided by Project Sonar/H D Moore) I found the Onionoo data set. I was able to quantify the impact of the attack using the Onionoo data and verify it using the ZMap scans. There are 4267 bridges, of them 1819 serve their ORPort on port 443 and 383 serve on port 9001. That's 52% of tor bridges. There are 1926 pluggable-transports enabled bridges, 316 with ORPort 443 and 33 with ORPort 9001. That's 18% of Tor bridges. 203 (64%) of PT-enabled bridges with ORPort 443 were TorCloud. I was able to approximately verify these figures using the Internet-wide scans. By grabbing bridge's certificates and calculating their hashed fingerprint I realized I was also discovering a fair amount of private bridges not included in the Onionoo data set.

The results aren't too awful, just under 20% of PT-enabled bridges are affected. It's unclear to me if the low number of PT-enabled bridges (and corresponding high number of vulnerable non-PT-enabled bridges) is a historical artifact or a consequence of out-of-date documentation like https://www.torproject.org/docs/bridges.html.en not setting up pluggable transports.

Eliminating the ORPort entirely for PT-enabled bridges seems like the ultimate solution to this attack, but the implications of such a change are unclear to me. Another change to mitigate this issue is changing the behavior of ports specified as 'auto' and then changing defaults and documentation to use auto by default. Currently specifying 'auto' in your torrc lets the OS decide the port; however, if tor were to generate a random port on first use that it continued to use across restarts it would it possible to use this for ORPorts and pluggable transports by default (as opposed to the current situation where for bridges you want ORPort auto but constant PT ports and a constant ORPort for relays which is confusing.)

I've attached a patch to warn bridge operators running with ORPort set to 443 or 9001 as a stop-gap measure.

- Vladislav
diff --git a/src/or/config.c b/src/or/config.c
index 28f1df0..4011371 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -523,7 +523,7 @@ static int parse_dir_authority_line(const char *line,
 static int parse_dir_fallback_line(const char *line,
                                    int validate_only);
 static void port_cfg_free(port_cfg_t *port);
-static int parse_ports(or_options_t *options, int validate_only,
+static int parse_ports(or_options_t *options, smartlist_t **ports_out,
                               char **msg_out, int *n_ports_out);
 static int check_server_ports(const smartlist_t *ports,
                               const or_options_t *options);
@@ -1060,7 +1060,13 @@ options_act_reversible(const or_options_t *old_options, char **msg)
     /* Adjust the port configuration so we can launch listeners. */
-    if (parse_ports(options, 0, msg, &n_ports)) {
+    if (configured_ports) {
+      SMARTLIST_FOREACH(configured_ports, port_cfg_t *, p, port_cfg_free(p));
+      smartlist_free(configured_ports);
+      configured_ports = NULL;
+    }
+    if (parse_ports(options, &configured_ports, msg, &n_ports)) {
       if (!*msg)
         *msg = tor_strdup("Unexpected problem parsing port config");
       goto rollback;
@@ -2525,7 +2531,7 @@ options_validate(or_options_t *old_options, or_options_t *options,
         "for details.", uname);
-  if (parse_ports(options, 1, msg, &n_ports) < 0)
+  if (parse_ports(options, NULL, msg, &n_ports) < 0)
     return -1;
   if (parse_outbound_addresses(options, 1, msg) < 0)
@@ -3607,6 +3613,29 @@ options_validate(or_options_t *old_options, or_options_t *options,
       REJECT("BridgeRelay is 1, ORPort is not set. This is an invalid "
+  if (options->BridgeRelay) {
+    smartlist_t *orports = NULL;
+    if (parse_ports(options, &orports, msg, &n_ports) < 0)
+      return -1;
+    if (orports) {
+      SMARTLIST_FOREACH_BEGIN(orports, port_cfg_t *, port) {
+        if (port->type != CONN_TYPE_OR_LISTENER)
+          continue;
+        if (port->port == 443 || port->port == 9001) {
+            COMPLAIN("Your bridge ORPort should be set to a random high "
+                     "port to avoid having it discovered by internet-wide "
+                     "port scanning. Ports 443 and 9001 are common relay "
+                     "ORPorts that should be avoided.");
+        }
+      SMARTLIST_FOREACH(orports, port_cfg_t *, p, port_cfg_free(p));
+      smartlist_free(orports);
+    }
+  }
   return 0;
 #undef REJECT
 #undef COMPLAIN
@@ -5955,11 +5984,11 @@ count_real_listeners(const smartlist_t *ports, int listenertype)
  * <b>options</b>, and return 0.  On failure, set *<b>msg</b> to a
  * description of the problem and return -1.
- * If <b>validate_only</b> is false, set configured_client_ports to the
- * new list of ports parsed from <b>options</b>.
+ * If <b>ports_out</b> is not NULL, set *ports_out to the new list
+ * of ports parsed from <b>options</b>.
 static int
-parse_ports(or_options_t *options, int validate_only,
+parse_ports(or_options_t *options, smartlist_t **ports_out,
             char **msg, int *n_ports_out)
   smartlist_t *ports;
@@ -6082,13 +6111,8 @@ parse_ports(or_options_t *options, int validate_only,
   options->ExtORPort_set =
     !! count_real_listeners(ports, CONN_TYPE_EXT_OR_LISTENER);
-  if (!validate_only) {
-    if (configured_ports) {
-      SMARTLIST_FOREACH(configured_ports,
-                        port_cfg_t *, p, port_cfg_free(p));
-      smartlist_free(configured_ports);
-    }
-    configured_ports = ports;
+  if (ports_out) {
+    *ports_out = ports;
     ports = NULL; /* prevent free below. */
tor-dev mailing list