General security

Achieving anonymity with Tor Part 5: Tor bridges and hidden services

Dejan Lukan
August 20, 2012 by
Dejan Lukan

Running a Tor bridge is essentially the same as running a Tor relay, as far as configuration is concerned. We already mentioned that the only difference is that Tor bridges are not listed in the Tor directory. We also said that using a bridge is helpful when we want to connect to the Tor network, even if our ISP is blocking connections to known Tor relays. Since bridges are not listed in the Tor directory, the ISP doesn't know their IPs and can't block them.

Using Tor bridge relay

If our ISP is blocking access to the Tor network, we can still bypass ISP's filters by using a Tor bridge. First we need to find Tor bridges in order to get the IP address to which we'll connect. There are two ways of finding the Tor bridge IP addresses and they are described below:

  • Find a Tor bridge by visiting the Tor Bridges in a web browser, which should give us two bridge relays as shown in the picture below:

We can see that we got two bridge relays, one on port 80 and the other on port 443:

    • bridge
    • bridge
  • The second option is not hard to guess, since the previous picture mentions it. We can get a list of bridges by sending an email message from a Google or Yahoo account to and setting the Subject and Body of an email message to "get bridges". Soon after the message will be sent, we'll receive the IP of one of the available bridges. This can be seen in the picture below:

To use those IP addresses we can open up Vidalia and go to Network and check "My ISP blocks connections to the Tor network". This will open "Bridge Settings" where we can add the bridges. Let's add the first two bridges in the Vidalia and click Ok. The configuration settings can be seen in the picture below:

In the Message Log we can see logging information presented below:

[Notice] Learned fingerprint 873A7B69F6E9ABB5DF770B4073F6D2AA188AB0EF for bridge

[Notice] Learned fingerprint 9D7F4EFB7F0DFB0A12D7FF3323B7054DA97288AE for bridge

[Notice] new bridge descriptor 'derby' (fresh)

[Notice] We now have enough directory information to build circuits.

[Notice] new bridge descriptor 'Unnamed' (fresh)

[Notice] Tor has successfully opened a circuit. Looks like client functionality is working.

We can see that Tor successfully learned the fingerprint of both bridges. The first bridge is named derby, while the second one is Unnamed, but both are working just fine.

What if we want to configure the use of bridges manually? We can do that by editing /etc/tor/torrc configuration file and adding the following configuration variables:

UseBridges 1

UpdateBridgesFromAuthority 1



Let's describe those configuration variables a little bit – summarized from [1]:

  • UseBridges

This option specifies that Tor will use the bridges specified with the bridge configuration variable to be used as entry points to the Tor network.

  • UpdateBridgesFromAuthority

When set, Tor will first try to fetch bridge descriptors from the configured bridge authorities. If that fails, it will request them directly from the bridges themselves.

  • Bridge

Specifies the bridge's IP address and port number to be used when the ISP is blocking the use of the Tor network. Tor usually verifies that the bridge has the right fingerprint before using it.

After our configuration is done, we should save the torrc configuration file and restart Tor for our changes to take effect. Afterwards we should be able to connect to the Tor network through one of the bridges that represent an entry node into the Tor network. Since the bridges most probably use some IP addresses that the ISP doesn't know about, we can successfully connect to the Tor network without being blocked.

Running a Tor bridge relay

To configure our own bridge relay, we need to start Vidalia and click on the Settings – Sharing - "Help censored users reach the Tor network", which is presented in the picture below:

We can see that the configuration is practically the same as with Tor relays. The only difference in Vidalia is the presence of the last setting "Let others access your bridge by giving them this line", which specifies the IP, port and fingerprint of our bridge server. There's also an option to "Automatically distribute our bridge address", which specifies that Tor will automatically use this bridge descriptor and send it to clients wishing to connect to a bridge. We can of course disable this feature and only send the string to our friends, so only they will be able to connect through our bridge.

Hidden services

With Tor hidden services, we can set-up various services anonymously on the Internet. Only Tor users can access those services, as they are not accessible by everybody with an Internet connection. Therefore, the services can remain hidden, without revealing their external IP address, while still providing the promised service to the Tor users.

Hidden services can be almost any service, like SSH server, HTTP server, FTP server, etc. An example of hidden services are web pages that use the .onion TLD (Top Level Domain) as their domain name.

Setting-up a hidden service

Before setting up a hidden service, we must set-up Tor and run it. In this article we'll set-up an HTTP Apache server, which will act as a hidden service. First we need to install and configure the Apache locally and run it. We need to configure Apache to not disclose any sensitive information. The hidden service should be running on localhost only, because otherwise an attacker can connect to our IP directly and confirm that the hidden service is present. When Apache is running as a global service we'll see it binded to an IP address of as follows:

# netstat -lntup

tcp 0 0* LISTEN 2302/apache2

The IP address means all IP addresses on a machine, which exposes the service to the outside world, as opposed to localhost only. In order to run Apache on localhost only, we need to configure the httpd.conf Apache configuration file and modify the following configuration variable to the following:


Then the Apache should be restarted, which should force it to be binded to the IP address as can be seen here:

# /etc/init.d/apache2 restart

# netstat -lntup

tcp 0* LISTEN 2302/apache2

We also need to configure the /etc/tor/torrc Tor configuration file and add the following two configuration variables:

HiddenServiceDir /home/user/tor/Data/Hidden/Apache/

HiddenServicePort 80

The description of the above configuration variables is the presented here and summarized after [1]:

  • HiddenServiceDir

Specifies an existing directory that is used by the hidden service to store data files. If we are running multiple services, this option has to be specified more than once, since every service should have its own data directory. This directory should also be readable and writable by the user running Tor.

  • HiddenServicePort

Specifies the virtual port of a hidden service. When we are running multiple services, this option has to be specified more than once, because every service needs its own port. Every HiddenServicePort uses the most recent HiddenServiceDir data directory. This option maps the virtual port to the same port on the localhost, where our hidden service should be running. We can change that setting if we specify multiple arguments to the configuration variable, as we did in our example.

After the /etc/tor/torrc configuration file is modified, we should restart Tor. The following commands should set-up all required features to configure Apache hidden service:

# mkdir -p /home/user/tor/Data/Hidden/Apache/

# chown debian-tor:debian-tor /home/user/tor/Data/Hidden/Apache

# echo "HiddenServiceDir /home/user/tor/Data/Hidden/Apache/" >> /etc/tor/torrc

# echo "HiddenServicePort 80" >> /etc/tor/torrc

# /etc/init.d/tor restart

If Tor starts up successfully, it will create two files in the HiddenServiceDir, the hostname and private key as can be seen in the next picture:

The file private_key contains the public/private keypair that is used for encrypting/decrypting the data that flows between the client and our hidden service. The contents of that file can be seen in the next picture:

The file hostname contains the public key for our service. This is the URI address that the clients need to use to connect to our hidden service. The contents of the hostname file are presented in the next picture:

When we restart Tor, it needs to generate a hidden service descriptor, which contains a description of our hidden service as well as our public key. The descriptor is then anonymously published in the Tor's directory. Afterwards we can use the presented URI to get access to our hidden service. If we paste the URI into our web browser, the default Apache web page should pop-up as we can see in the picture below:

We can see that we've successfully hidden our external IP while still keeping the service online and accessible by Tor users.

Hidden service protocol

Tor provides a functionality called Rendezvous Points that allows Tor users to connect to hidden services without knowing the server's identity. A Tor user client can't connect to a Tor's hidden service if the network doesn't know about the hidden service. Therefore the hidden service has to advertise its existence. It does that by randomly choosing a few relays, building a circuit to them and politely asking them to act as introduction points. Basically we're establishing a circuit to a couple of introduction points. We're establishing a circuit to each introduction point, because we don't want to be connected right to the introduction point, which increases the risk of discovering the real IP of the hidden service: because each Tor relay knows where the connection came from and where the connection is going – thus revealing the source and destination IP address of its neighboring relays. If we build a circuit, which by default consists of three relays, we're successfully hiding the presence of the hidden service, since we can't determine where the connection came from or is going.

The Tor sends the hidden service's public key to the introduction points, which in turn sends it to other interested nodes, so the external IP of the hidden service remains hidden. Then the hidden service creates a hidden service descriptor (contains public key), signs it with its private key and sends it to a distributed hash table. That descriptor can then be found by the clients requesting the appropriate .onion domain, which consists of unique 16 characters that are used to verify that clients are talking to the right service – since that name can be derived from the service's public key.

A client that wants to connect to a hidden service needs to know the .onion address. It uses it to download the descriptor from the distributed hash table, which contains a set of introduction points and public key to use to initiate a connection. A client creates a circuit to random relay and politely asks it to act as a rendezvous point by telling it a one-time secret.

After the rendezvous point is ready, the client creates an introduction message and sends it to one of the introduction points. An introduction point takes the message and delivers it to a hidden service. This is required so that the client can stay anonymous, since it's the introduction point that's doing the communication instead of the client. When the hidden service gets the message, it decrypts it, and finds the address of the rendezvous point and one-time secret. The service creates a circuit to the rendezvous point and sends the one-time secret to it.

The rendezvous point is then connected to both the client and the hidden service and can communicate with both of them. So the the rendezvous point is used to relay messages from the client to the server and the other way around. We should also note that the rendezvous point doesn't know the hidden service's identity. At the end, the complete connection between a client and a hidden server is using six relays: three of them were picked by the client with the third being the rendezvous point and three of them were picked by the hidden service.

If you're interested in rendezvous points, you can read more in Tor Rendezvous Specification.

FREE role-guided training plans

FREE role-guided training plans

Get 12 cybersecurity training plans — one for each of the most common roles requested by employers.


Dejan Lukan
Dejan Lukan

Dejan Lukan is a security researcher for InfoSec Institute and penetration tester from Slovenia. He is very interested in finding new bugs in real world software products with source code analysis, fuzzing and reverse engineering. He also has a great passion for developing his own simple scripts for security related problems and learning about new hacking techniques. He knows a great deal about programming languages, as he can write in couple of dozen of them. His passion is also Antivirus bypassing techniques, malware research and operating systems, mainly Linux, Windows and BSD. He also has his own blog available here: