# Proxying Application Traffic Through Tor

A quick look at how to proxy any application’s traffic through the Tor network.

# Introduction

While working on another blog post, I touched lightly on using Tor as a SOCKS proxy and realized that it probably deserved its own blog post. While many are aware of using the Tor Browser, fewer are aware of using Tor to proxy your application traffic and a subset understand the limitations / issues you may run into. This blog post will cover configuring Tor as a SOCKS proxy for your applications including:

• how to configure applications that support SOCKS proxying
• how to proxy applications that only support HTTP proxies
• how to support other applications that do not support either of the above
• how to connect to SSH and OpenVPN servers through Tor
• limitations of some of the wrappers used to proxy traffic (such as proxychains, proxify, etc)
• how to proxy only certain sites through Tor
• how to proxy all your traffic through Tor

For more on how Tor works, I’d recommend reading the Tor: Overview. Also, you should keep in mind that Tor’s SOCKS proxy implementation only supports TCP connections and can not be used to proxy UDP traffic.

Enterprises should definetely block Tor traffic (although allowing it to access your public websites is a business decision). I added a small section on detecting Tor.

DISCLAIMER Tor is an important service for many so please don’t abuse it to perform anything illegal or to bypass security policies.

# Installation

Out of the box, installing the Tor Browser and launching it on any operating system will automatically start a SOCKS proxy on port 9150. To verify this, we can run:

ubuntu@linux:~$lsof -i :9150 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME tor 2901 ubuntu 11u IPv4 42609 0t0 TCP localhost:9150 (LISTEN)  However, in many cases, we would prefer to run it without having to run the Tor Browser. This is useful if we want to run it on headless devices or even just run it in the background as a service. To do so, we have to download and install the standalone Tor executable. ## Linux On Linux, we can simply: sudo apt install tor  The exact command needed may be different on your distribution. Hopping over to: https://command-not-found.com/tor shows us the required command to install the package: This provides us with just the tor binary in addition to other useful tools such as torify which allow us to run an application through Tor without having to explicitly configure it to use a SOCKS proxy. To enable it to run as a service on system boot: systemctl start tor systemctl enable tor  ## Windows On Windows, we would download the tor executable from: https://dist.torproject.org/torbrowser/11.0/tor-win64-0.4.6.8.zip. If desired, we could also run it as a System Service by running: tor -service install  and placing the torrc file (more on this in the Configuration section) so that the account it runs under will find it. A simpler alternative is to have it run on user login by following the instructions here. ## Mac This is as simple as using Homebrew or Macports to install it: $ brew install tor


## Docker

Another option is to install and run the dperson/torproxy Docker container. This has the advantage of exposing both a Tor SOCKS proxy in addition to a traditional HTTP proxy that forwards requests to the Tor SOCKS proxy (for applications that don’t support SOCKS proxying).

For example:

docker run -p 8118:8118 -p 9050:9050 -d dperson/torproxy


and we can then direct our applications to use port 8118 as an HTTP proxy.

# Configuring and Running Tor

Configuration is straight-forward but first, we have to find our configuration file torrc if we plan on using the Tor Browser:

• on Mac OS, this file is typically located at ~/Library/Application\ Support/TorBrowser-Data/Tor/torrc
• on Linux, this file is located at /etc/tor/torrc
• on Windows, the torrc file is typically located under the path \Browser\TorBrowser\Data\Tor\torrc under the directory where the Tor browser was installed

If, on the other hand, we plan on launching it from the tor binary instead, we would just pass it in as a parameter:

$tor -f <config_file>  Although there are a number of configuration options, the only one we really care about is SocksPort. This defines: • the port to listen on • the IP address to listen on (by default, it listens only on localhost) To have it listen only on the localhost interface port (this is the default if we don’t specify a configuration file), we specify only the port number: SocksPort 9050  while we have to specify the IP address and port number if we want it to listen on a specific interface: SocksPort 192.168.0.1:9050  We can use 0.0.0.0 if we want it to listen on all interfaces: SocksPort 0.0.0.0:9050  Finally, we can specify that it binds to multiple IPs and ports. For example, this tells it to bind to port 9050 locally and bind to port 19050 on all interfaces: SocksPort 9050 SocksPort 0.0.0.0:19050  If we configure it to listen on a non-localhost interface, we may want to specify which IP(s) can connect to it. We can do so through the use of SocksPolicy. In the below example, we specify that it accept requests only from the given network: SocksPolicy accept 192.168.1.0/24 SocksPolicy reject *  This last option is useful if we are in a situation where we can’t install software on our current machine but have a machine within our network that we control. # Configuring Our Apps When configuring our applications to use the Tor proxy, we have two options: • configuring individual applications • configuring all applications to go through the proxy ## Configuring Individual Applications Once our Tor proxy is up and running, we can then point our applications and tools to start using it. To do so, we would either: • explicitly configure our application / tool to use it • configure an environment variable that our application / tool looks at to determine if it should proxy requests or not Unfortunately, there is no standard and some applications look at environment variables while others don’t. Some applications have support while others don’t, so it basically requires one to check the application’s documention on how to enable this. Another option is to use a wrapper that hooks underlying network functions (on Linux, this would typically be GLIBC) and proxies them for us. On Linux and Mac, we have a number of tools including: proxychains and torsocks while we have Torifier for Windows. ### torify/torsocks for Linux/Mac On Linux and Mac, the tor package gives us torify which can torify our applications. Other options that provide similar functionality include torsocks and proxychains (proxychains deserves its own blog post). By invoking it before our command, we can force connections through the Tor proxy. For example: $ torify curl http://httpbin.org/ip
{
"origin": "5.183.209.217"
}


A quick visit to https://metrics.torproject.org/exonerator.html verifies that this is indeed a Tor exit node IP:

#### Warning / Limitations

WARNING Tools such as torify and torsocks and proxychains only work if your application is dynamically compiled against GLIBC. This is the case for most applications but means that statically compiled applications or applications that don’t make use of GLIBC (like Go binaries) can’t be proxied using these tools. It is therefore better to explicitly specify the SOCKS proxy when possible when running our tools.

For example, bat, a curl-like tool written in Go isn’t impacted by torify’s hooking. So the below two commands behave identically:

$bat http://httpbin.org/get$ torify bat http://httpbin.org/get


We get no warning and our request goes directly to the target without going through Tor. In this case, we would check to see if the application itself has supports for using a SOCKS proxy. If not, we would check to see if it has support for HTTP(S) proxy and combine it with something like Privoxy to forward this traffic to Tor. In our case, bat only supports HTTP(S) proxies and we can quickly start a Docker image (refer to the Installation section) and proxy our requests successfully:

$bat -proxy=http://192.168.1.200:8118 http://httpbin.org/ip { "origin": "185.220.100.253" }  As a final option, we could forward all of our traffic transparently through Tor and this would torify these applications (more on this later). ### Torifier for Windows If you’re still stuck in the 90s and still running Windows as your desktop environment, there is still hope. You may one day see the light and finally use a real operating system. In the meantime, we can use Torifier to easily proxify most applications. Once installed, we simply add the application we want to torify. Here is an example of SSH’ing using Putty which was torified: NOTE Unlike torify on Linux, Torifier is able to successfully proxy Go binaries such as bat. ### SSHing Through Tor In environments where outgoing SSH access is blocked, you can proxy your SSH traffic through Tor. Out of the box, SSH does not support proxying through a SOCKS proxy. However, we can use torsocks to our traffic: $ torsocks ssh root@A.B.C.D -i ~/.ssh/id_rsa_digitalocean


or, as shown previously, use Torifier for Windows.

Another option is to use the ProxyCommand option in SSH. This option lets us hook standard input and output from the SSH client and forward them on its behalf. Using netcat (we could also have just used socat or corkscrew or any other application), we could run:

$ssh -o 'ProxyCommand nc -x 127.0.0.1:9050 %h %p' root@A.B.C.D -i ~/.ssh/id_rsa_digitalocean  and even save it in our ~/.ssh/config: Host digitalocean_socks HostName A.B.C.D IdentityFile ~/.ssh/id_rsa_digitalocean User root Port 22 ProxyCommand nc -x 127.0.0.1:19050 %h %p  To verify that we are indeed going through the Tor network, we can inspect the SSH_CLIENT environment variable: root@me:~# echo "$SSH_CLIENT"
185.220.101.46 21476 22


### Running OpenVPN Over Tor

If we are in an environment where OpenVPN traffic is blocked, we can run our OpenVPN client over Tor. NOTE This will only work if our OpenVPN server runs in TCP mode (I’ll cover how to handle UDP mode in a future post). To verify this, we can simply inspect our ovpn configuration file:

$cat client1.ovpn | grep -i "remote " remote 138.68.156.184 1194 tcp  Once confirmed, all we have to is to add a single line to our ovpn file: socks-proxy 127.0.0.1 9050  and restart our OpenVPN client. Obviously, this is less than ideal due to the overhead added by the Tor network but in most cases, this is acceptable (unless we have to transfer large amounts of data). An alternative would be to use torify or a similar application but I prefer the above as it is platform agnostic. ### Using Tor For Specific Sites Only If we are in an environment where access to only specific domains is restricted and we would prefer not to have to proxy all our web traffic, we can configure our main web browser to proxy only traffic to these domains while bypassing it for all other websites for better performance. Two downsides to using Tor for browsing include: • slower performance • some sites block Tor traffic or demanding that you prove that you are human One popular browser plugin that supports selectively proxying only certain traffic is SwitchyOmega which has plugins for both Firefox and Chrome. After installation, we then define our proxy/proxies and then define which domains should be routed by which proxy in addition to not proxying certain requests: For example, if the httpbin.org domain was blocked in our environment, we can configure SwitchyOmega to use our proxy for this domain (it is recommended that you add a *. before the domain to cover all subdomains as shown below) while allowing all other traffic to bypass the proxy: NOTE Make sure to click on the Apply Changes button after every change. We can then verify that it works by visiting a site that displays our IP address and we can verify that it is no longer our real IP: NOTE One interesting feature of SwitchyOmega is that you can export your configuration and share it with others. ## Proxying All Our Traffic One option is to proxy all our traffic through the Tor SOCKS proxy. However, this will not apply to UDP traffic as the SOCKS proxy that comes with Tor only supports TCP traffic. To do this, we have to configure what is known as Transparent Proxying. This typically uses a combination of: • an application that takes outgoing traffic and modifies it to go through our SOCKS proxy • firewall rules to transparently redirect outgoing traffic to this application On Linux, redsocks fits the bill. Mac and Windows users can use a tool such as Proxifier or win2socks for Windows users. Another option for for Windows users is Tallow which allows us to proxy all our traffic (it drops UDP traffic) through Tor. It doesn’t provide the same anonymity features that the Tor Browser provides us with but does allow us to torify applications that don’t natively support SOCKS proxies. However, one caveat is that it torifies all traffic, including LAN traffic, which means we can’t connect to LAN resources while using it. # Verification As mentioned previously, we should always verify if our traffic is actually being proxied or not. In some cases, proxifiers may fail (as seen previously when trying to run bat through torify). ### Mac / Linux For Mac and Linux, we can use the versatile lsof command. If we had a curl command that was running and wanted to verify if it was using our proxy or not, we could run the watch command below: $ watch 'lsof -w -c curl -a -iTCP'
Every 2.0s: lsof -w -c curl -a -iTCP                                                                           dell: Fri Nov 12 15:48:33 2021

COMMAND     PID   USER   FD   TYPE  DEVICE SIZE/OFF NODE NAME
curl    1019390 ubuntu    5u  IPv4 2372612      0t0  TCP localhost:44946->localhost:9050 (ESTABLISHED)


The above switches tell lsof to:

• -w - disable warnings
• -c - we are only interested in the curl command
• -iTCP - we are only interested in TCP connections (lsof can list files, sockets and other details)

Another option on Linux is the ss command. For example, if we are interested in seeing what curl is currently connect to, we could run:

$watch 'ss -uptown | grep -i "process\|curl"' Every 2.0s: ss -uptown | grep -i "process\|curl" dell: Fri Nov 12 15:25:34 2021 Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process tcp ESTAB 0 0 127.0.0.1:35042 127.0.0.1:9050 users:(("curl",pid=965197,fd=5)) timer:(keepalive,58sec,0)  We added the process\| in the grep to get the output header. Personally, I prefer using lsof as it is more powerful and cross-platform. ## Windows To do so on Windows, we can use Nirsoft’s CurrPorts. In this example, I configured Firefox to use a SOCKS proxy whereas Edge was configured to not use one: Interestingly, I noticed that some connections in Firefox aren’t using the SOCKS proxy. After further investigation, it appears that Firefox’s telemetry service bypasses your proxy settings. This thread validates this finding. # Using it Programmatically Now that we have our TOR SOCKS proxy up and running, we can use it in our scripts and applications. We just have to make sure that we use a library with SOCKS support. On Python, the very popular Requests library supports this but requires that we install the PySocks module. Here is a simplified example of it in action: import requests URL = "http://httpbin.org/get" proxy = "socks5://127.0.0.1:9050" proxies = {"http": proxy, "https": proxy} TIMEOUT = 15 resp = requests.get(URL, proxies=proxies, timeout=TIMEOUT) print(resp.text)  When using it, we have to add a little extra time for our request due to the overhead in latency introduced by the Tor network. We can also tell it to tunnel our DNS request via the SOCKS proxy by using socks5h instead of socks5: proxy = "socks5h://127.0.0.1:9050"  # Detecting Tor on Your Network Detecting and blocking Tor traffic in your enterprise environment is definitely something you want to do as it is used extensively by malicious actors. To block it, most network filtering devices either: • block the IPs used by Tor • inspect the SSL handshake and look for anomalies On the wire, Tor traffic is basically TLS-encrypted traffic over TCP. However, there are some unique features of the SSL/TLS handshake that allow network devices to accurately detect it. For example, the client requests a non-existent domain as shown below: and we can confirm that this domain does not exist: $ host www.exqzmp2flm46ug.com