Jun 16 2010

Serve an anonymous shell via Tor

I've been an avid user of Tor since about 2005. Retaining the right of anonymity and individual privacy grows increasingly important as different communication technologies emerge and become ubiquitous, as the recent Facebook heat has reminded us. If you don't think it matters, you need to stop what you're doing right now and read Phil Zimmerman's excellent PGP justification. (I am particularity fond of the postcard metaphor.)

Done? Okay. Lets move on!

While most people use Tor simply for anonymous web browsing, Tor also provides a slick way to host a service (web site, IRC chat server, etc) called a Hidden Service. These services are "hidden" because they are only accessible via the Tor network, and are under the same anonymity umbrella as Tor clients -- unless they accidently expose information about themselves, it is essentially impossible to determine the source location of the service.

As it turns out, not exposing information about yourself is a lot more difficult than you might imagine; even for a single process. Hosting a simple web page involves configuring your server signature and potentially customizing error messages. Are you sure any images on that web page don't have embedded EXIF data? What about documents that support author metadata (PDF, DOC, many many many more?) META tags if you used an HTML editor to construct your pages?

This got me thinking about how to offer other services to anonymous users. Wouldn't an encrypted backup service be neat? That wouldn't really be practical for a hobbyist (storage needs, Tor network speeds, etc) -- but a generic anonymous shell would certainly be a nice communications tool. You could use it for file storage, chat, message passing, or even as a digital dead drop. If I (or anyone) wanted to offer that as a hidden service, how best to do so while 1) remaining anonymous as a provider and 2) providing default settings that retain Tor ideals (trusted privacy, resistance to traffic analysis, and accessibility for blocked users) to the shell accounts? We're talking about exposing an entire OS, after all.

Here's what I came up with. Steel thyself for diagram madness!!

Diagram-o-rama!

There are a lot of advantages to this setup. Entire environment snapshot/rollback, complete machine mobility, FreeBSD 'secure levels' for added security, and network obfuscation via VirtualBox NAT and OpenBSD's PF.

And the biggest disadvantage? Speed. Tor is designed for privacy, not for performance. It's really, really slow. I'm talking 2400 baud modem slow, at times. Again, this isn't a deal breaker if you've got your expectations set. You can help the overall speed of the network, too. More on this later.

So, here's a step-by-step for the setup I'm using.

Tor Setup

In your torrc file, you can enable the hidden service via two lines of config.

HiddenServiceDir /path/to/service_dir/
HiddenServicePort 22 127.0.0.1:50022

This automatically creates two files wherever your service_dir points to; the unique *.onion hostname that your Tor client is now advertising to relays, and the private key for it. Don't lose this key if you care about hanging on to your *.onion hostname.

The HiddenServicePort directive tells Tor that incoming connections to port 22 (ssh) that are coming in via Tor should be redirected to localhost, port 50022. We'll now tell VirtualBox to listen on that port, and do its own forwarding.

VirtualBox Host

One of the biggest reasons to use VirtualBox for this server is mobility. As long as you carry the generated Tor hidden service key alongside your VDI file, you can essentially move this server to any VirtualBox supported host OS, anywhere in the world, and it'll still be accessible via Tor at the same .onion hostname.

It reminds me of the inter-dimensional Black Fortress from the old movie Krull, or the Dark Tower from Stephen King's Gunslinger series. Some "physical" construct that ethereally shifts locations. Neat-o. Let's call it "Tower" for kicks.

VirtualBox also gives us the ability to easily hide behind NAT. The guest operating system receives network information from the host environment, providing an immediate sandbox, and IANA private IP ranges. (More on those specifics here.)

First off, lets forward traffic from port 50022 to the virtual machine at port 22, so the Tor network can eventually reach the ssh daemon.

% VBoxManage modifyvm "Tower" --natpf1 "ssh,tcp,,50022,,22"

Unfortunately, VirtualBox NAT exposes resolvers and search domains via DHCP from the host environment by default, so we require some additional guest configuration.

VirtualBox Guest (aka FreeBSD Host)

The VirtualBox guest OS runs a recent FreeBSD image, in itself acting as a host for a jail underneath it. The single largest potential for identifying machine location is the internet itself. The local IP address will be 10.0.2.15 (by default), so that doesn't give any location sensitive info away... but what about a simple traceroute? Any outbound connection will reveal the server's gateway IP, and it's simply not feasible to try and subvert this without forwarding all traffic back across Tor.

So the first thing we do is block all outbound traffic via PF. I renamed the interface to net0 so we can reference it everywhere, and if we decide to change the underlying virtual device, we only need do so in one location. Here's an example /etc/pf.conf. We're not running ssh on this host, only on the jail inside of it -- so ssh traffic that hits the host is redirected inside to 127.0.0.2. We blindly accept all other traffic -- this is safe as no traffic can reach this machine that is not explicitly port forwarded from VirtualBox.

net_if="net0"
set optimization normal
set block-policy return
set loginterface $net_if
set skip on lo0
scrub in on $net_if all fragment reassemble
rdr pass on $net_if proto tcp to port 22 -> 127.0.0.2
block out on $net_if
pass in on $net_if

We should also override the dhclient options file at /etc/dhclient.conf, so host information remains private.

supersede domain-name "tower";
supersede domain-name-servers 8.8.8.8;

Also -- resist the urge to set the timezone! UTC time doesn't give away what part of the world you're in. Here's the /etc/rc.conf that loads up PF, and starts up the jail. (Check the jail(8) man page if you are looking for information on how to create a new one -- that's outside the scope of this already very long post.)

hostname="tower-host"
ifconfig_le0_name="net0"
ifconfig_net0="DHCP"
pf_enable="YES"
pf_rules="/etc/pf.conf"
pflog_enable="YES"
sendmail_enable="NONE"
tmpmfs="YES"
tmpsize="128m"
fsck_y_enable="YES"
kern_securelevel_enable="YES"
kern_securelevel="3"
jail_enable="YES"
jail_list="tower"
jail_set_hostname_allow="NO"
jail_socket_unixiproute_only="YES"
jail_sysvipc_allow="YES"
jail_tower_rootdir="/jails/tower"
jail_tower_hostname="tower"
jail_tower_interface="net0"
jail_tower_ip="127.0.0.2"
jail_tower_exec_start="/bin/sh /etc/rc"
jail_tower_exec_stop="/bin/sh /etc/rc.shutdown"
jail_tower_devfs_enable="YES"
jail_tower_fdescfs_enable="NO"

What else? We should ensure users can't see what other users are doing via sysctl variables. (These behaviors are carried to the jail.) Also, I set a cron that removes login histories every minute.

% cat /etc/sysctl.conf
security.bsd.see_other_uids=0
security.bsd.see_other_gids=0
security.bsd.unprivileged_read_msgbuf=0

% echo '* * * * * root rm /var/log/wtmp \ 
    /jails/tower/var/log/wtmp' >> /etc/crontab

After all of this stuff is set up to our content, lets lock it down via chflags.

% chflags schg /etc/pf.conf /etc/rc.conf \ 
    /etc/dhclient.conf /etc/sysctl.conf /etc/crontab

Doing this, in combination with securelevel, will protect the files from modification. Even if an attacker somehow gains root access in this FreeBSD host, they can't disable the firewall or change these files without rebooting to single user mode -- and there's no network in single user mode. This also means: if you want to make changes, you need to be sitting at the VirtualBox console.

FreeBSD Jail

This is finally the environment that an anonymous Tor user will be exposed to. Why the additional layer here? Couldn't you just have people log in to the FreeBSD host?

Well, sure. However, good hardening practices include the assumption that the box can be compromised (if not already so), and configure it as such -- just like we assumed someone having root in the host (and chflag'ed accordingly.) The chances of "leaking" identifying information are greater from the guest OS to VirtualBox than they are from a jail to the host that is properly locked down. In addition, it adds an extra obfuscation layer that an attacker would have to break through. Root access in a jail is fairly useless. They can't make any permanent system modifications, nor read any additional system data that a normal user couldn't. They can't even see the current firewall ruleset. They can read other user's homedir data -- but just like the real operator of this service, an attacker would have no information available to determine identity.

Some other things to set up:

Here's the entirety of the system rc.conf for the jail:

sshd_enable="YES"
inetd_enable="YES"
syslogd_enable="NO"
cron_enable="NO"
update_motd="NO"
sendmail_enable="NONE"

And here is what you'd see if others are logged in. Note that the remote IP will appear to be the VirtualBox host.

 3:51PM  up 14:53, 1 user, load averages: 0.03, 0.01, 0.00
USER             TTY      FROM              LOGIN@  IDLE WHAT
q44r4            pts/0    10.0.2.2          3:51PM     - w
achgung          pts/2    10.0.2.2          2:21AM     - -
lenny            pts/1    10.0.2.2         10:51AM     - -

The Easy Way

Instead of re-doing all of that above, you could also just grab my VirtualBox machine that already includes all of it. Fire it up, login as guest, enjoy. The host password is root/root, if you wanted to change it.

Get it here -- it's about 325M, and should import right into VirtualBox.

Configuring your SSH client

SSH needs to be told to use Tor for connectivity, and for DNS lookups (so it knows how to find a .onion addresses.) The connect proxy forwarder can do this really easily. It's available in macports, FreeBSD ports, and Ubuntu's package management. You can also compile it yourself, it doesn't have any funky dependencies.

After it's installed, add this configuration chunk into your ~/.ssh/config file:

Host *.onion
    PubKeyAuthentication no
    VisualHostKey yes
    Compression yes
    ForwardAgent no
    ForwardX11 no
    PreferredAuthentications password
    ProxyCommand /path/to/connect -S 127.0.0.1:9050 %h %p

... and that's it. You'll be able to ssh as normal, and if you're connected to Tor, ssh will know what to do with hosts that end in .onion. Make sure that if you're connecting to a .onion host, that you explicitly pass your username -- otherwise, ssh defaults to using the username of the currently logged in user.

What else?

If you have even a little bit of bandwidth to spare, please consider being a Tor relay. It can arguably increase your own privacy while using Tor, and it improves the overall speed of the Tor network. It's easy to limit what kind of traffic you'll allow your relay to pass, and you can even bandwidth limit it.

Have I forgotten anything? Could privacy be improved in this environment for end users and/or the server operator? Let me know -- I'll update the VirtualBox export!

How would you go about doing this?