< All posts | Fediverse | RSS | GitHub | Talks

Apr 4 2018

Giving every Tor Hidden Service a IPv6 address

Tor has a neat feature called Onion Services (A) that allow you to host and access a TCP port with anonymity, the only thing being required is access to the Tor network.

This is a handy feature for many reasons. It allows people to access facebook (facebookcorewwwi.onion) in countries where it is blocked, it allows people to leak documents to the press in a safer way (nyttips4bmquxfzw.onion)

One of the bigger things to note is that it’s only requirement to both host and access onion services is a connection to the Tor network. This means you can host things behind NAT or other firewalls when you normally could not, making it handy for remote access.

Because this makes Tor a nice NAT/firewall evader it would be nice if a Tor client was not needed to access a onion service. For HTTP/HTTPS services there are already services that do this, tor2web being one of them (amusingly facebook detects this and advices users not to log in via a proxy)

However tor2web has the downside in that it will only work for HTTP/HTTPS, even though onion services can be wrapped with any TCP port service!

To get around this, we can use IPv6 and its very large amount of IP addresses that are standard with a IPv6 deployment.

A Tor onion address is a 8 byte base32 encoded string (80 bits). Conveniently the minimum routable IPv6 block size is a /48 (80 bits of address space) meaning we can have a 1-1 mapping of .onion addresses to IPv6 addresses in a /48!

In this case you can fit it into a IP address that looks like this:

2a07:1500:fed5:2804:40b9:ca13:a24b:5ac8

First we need to write a DNS server that can take these onion addresses and serve back these IPv6 addresses.

I wrote a small DNS server (100 lines) to automatically translate these DNS entries to their IPv6 addresses using the very useful miekg/dns libary

ben@eshwil:~$ dig -p 553 @127.0.0.1 facebookcorewwwi.tor6.flm.me.uk.

; <<>> DiG 9.10.3-P4-Ubuntu <<>> -p 553 @127.0.0.1 facebookcorewwwi.tor6.flm.me.uk.
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 10108
;; flags: qr ad; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;facebookcorewwwi.tor6.flm.me.uk. IN	A

;; ANSWER SECTION:
facebookcorewwwi.tor6.flm.me.uk. 2147483646 IN AAAA 2a07:1500:fed5:2804:40b9:ca13:a24b:5ac8

;; Query time: 0 msec
;; SERVER: 127.0.0.1#553(127.0.0.1)
;; WHEN: Tue Apr 03 09:01:57 EDT 2018
;; MSG SIZE  rcvd: 119

Now we have the address translation in place, we can build the proxy. This is a slightly abnormal task for a proxy, as it has to listen on all ports on 1,208,925,819,614,629,174,706,176 addresses. This means that we are not able to use conventional ways to accept connections.

Using iptables to listen on ports

iptables is commonly thought of as only a firewall. However beyond it’s ability to filter is the ability to setup NAT, run bytecode programs and blink LEDs. In our case, we want to send all TCP traffic from a IP range to a single program.

The REDIRECT target is generally used to catch outbound traffic and put it into a proxy (for example, corporate/educational filtering proxies) however you can also use it in the opposite direction and use it for traffic coming inbound to the system.

In our case we have:

ip6tables -t nat -A PREROUTING -d 2a07:1500:fed5::/48 -i eth0 -p tcp -j REDIRECT --to-ports 1337

This takes matches the any TCP connection coming into 2a07:1500:fed5::/48 (even if we are not binding on it) and it redirects it locally to a program listening on port 1337.

Or, better explained:

However there is a catch, when the connection is redirected, the final destination is lost as it has been changed to be the local application. However there is a syscall you can use to request the original address.

From that point onwards it is a case of reversing the dns encoding of the onion address, then proxying the connection to Tor!

After a bit of fiddling, I had a proof of concept that worked! (minus facebook not being able to handle the Host: header I requested with)

The issue with running this as a service

Tor onion services are great because they are live inside a censorship resilient network, however those exact same protections mean that some very unfavorable content can sometimes live inside tor onion services.

To try and avoid any issues such content may cause, I have decided to not accept connections from any port that a browser can make connections on.

You can still use the following ports:

port original protocol use
1 tcpmux
7 echo
9 discard
11 systat
13 daytime
15 netstat
17 qotd
19 chargen
20 ftp data
21 ftp access
22 ssh
23 telnet
25 smtp
37 time
42 name
43 nicname
53 domain
77 priv-rjs
79 finger
87 ttylink
95 supdup
101 hostriame
102 iso-tsap
103 gppitnp
104 acr-nema
109 pop2
110 pop3
111 sunrpc
113 auth
115 sftp
117 uucp-path
119 nntp
123 NTP
135 loc-srv /epmap
139 netbios
143 imap2
179 BGP
389 ldap
465 smtp+ssl
512 print / exec
513 login
514 shell
515 printer
526 tempo
530 courier
531 chat
532 netnews
540 uucp
556 remotefs
563 nntp+ssl
587 stmp
601 n/a
636 ldap+ssl
993 ldap+ssl
995 pop3+ssl
2049 nfs
3659 apple-sasl
4045 lockd
6000 X11
6665 IRC
6666 IRC
6667 IRC
6668 IRC
6669 IRC

Example usage

Let’s say you wanted to always have access to a raspberry pi, even if you don’t have ports forwarded.

root@shelf-pi:~# apt install tor
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  libevent-2.0-5 logrotate tor-geoipdb torsocks
Suggested packages:
  mixmaster xul-ext-torbutton socat tor-arm polipo privoxy apparmor-utils obfsproxy
The following NEW packages will be installed:
  libevent-2.0-5 logrotate tor tor-geoipdb torsocks
0 upgraded, 5 newly installed, 0 to remove and 68 not upgraded.
Need to get 107 kB/2,355 kB of archives.
After this operation, 9,842 kB of additional disk space will be used.
Do you want to continue? [Y/n] 
…

# setup a hidden (aka onion) service and point it to SSH
root@shelf-pi:~# echo "HiddenServiceDir /var/lib/tor/ssh_hidden_service/" >> /etc/tor/torrc 
root@shelf-pi:~# echo "HiddenServicePort 22 127.0.0.1:22" >> /etc/tor/torrc 

# restart tor to apply the change
root@shelf-pi:~# systemctl restart tor

# confirm the service was provisioned, obtain the onion domain
root@shelf-pi:~# ls /var/lib/tor/ssh_hidden_service/
hostname  private_key
root@shelf-pi:~# cat /var/lib/tor/ssh_hidden_service/hostname
s7wgpiadcsilb3dd.onion

At this point, You can now SSH from the clear internet to this onion service!

ben@eshwil:~$ ssh root@s7wgpiadcsilb3dd.tor6.flm.me.uk
The authenticity of host 's7wgpiadcsilb3dd.tor6.flm.me.uk (2a07:1500:fed5:97ec:67a0:314:90b0:ec63)' can't be established.
ECDSA key fingerprint is SHA256:sB2143z+Ls85q4QOs6hAPrP0A50WhelrAy2Xo8Th5ig.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 's7wgpiadcsilb3dd.tor6.flm.me.uk,2a07:1500:fed5:97ec:67a0:314:90b0:ec63' (ECDSA) to the list of known hosts.

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Apr  3 20:40:48 2018 from 
root@shelf-pi:~# 

Or! You can CNAME directly to this domain!

Then use it as a subdomain:

ben@eshwil:~$ ssh root@shelf-pi.benjojo.co.uk
The authenticity of host 'shelf-pi.benjojo.co.uk (2a07:1500:fed5:97ec:67a0:314:90b0:ec63)' can't be established.
ECDSA key fingerprint is SHA256:sB2143z+Ls85q4QOs6hAPrP0A50WhelrAy2Xo8Th5ig.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'shelf-pi.benjojo.co.uk' (ECDSA) to the list of known hosts.

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
Last login: Tue Apr  3 21:12:44 2018 from localhost
root@shelf-pi:~# 

Closing

As always, you can find the source code to this here: https://github.com/benjojo/six-onions

If you liked this, I’m currently at the Recurse Center and spending the next 12 weeks working on things like this and writing them up, so follow me on twitter or RSS to know when the next one of these come out!