How to hide (obfuscate) any traffic using obfs4

Overview

In this post, I’m going to give you the information necessary to be able to use obfs4 protocol not just for wrapping Tor traffic, but for virtually any other TCP traffic as well.

Doing so for the previous Pluggable Transports of this kind (namely obfs2,obfs3 and ScrambleSuit) was rather simple. obfsproxy program written python, already supports being run in standalone (or so called unmanaged) mode and there are dozens of guides available on how to do so.

However, obfs4proxy which implements obfs4 protocol in Go, is primarily designed to work in conjunction with Tor (managed mode). This is not a protocol limitation but rather an implementation one.

If you ever try running obfs4proxy by itself, you’d get an error like this:

[ERROR]: obfs4proxy - must be run as a managed transport

But Hey! It is not the end of the world. There are still couple of ways to make use of obfs4 outside of Tor:

  • Adding unmanaged mode of operation to obfs4proxy
    This is probably the best way, but I’m not a Go programmer so I’ll leave that to those who are.

  • Using external wrappers
    PTProxy and ptadapter in Python, along with Shapeshifter in Go, are just some of the options available.

  • Make your own script to bootstrap the launch of obfs4proxy
    This is the main interest of this article: showing you how to do so without relying on third party apps.

While I will primarily target Linux, it should be possible to apply the same procedures to other operating systems (e.g, Windows) as well.

Overall setup

Before going into details, it’s good to have a basic understanding of the process involved. Let’s take a look at the architecture:1

obfs4proxy diagram

  • Client App
    Is the program on the client side that you want to wrap its traffic with obfs4.

  • obfs4proxy Client
    obfs4proxy exposes a SOCKS5 proxy for the client app to connect to. The client app should be set to use the local obfs4proxy SOCKS5 capability to connect to the obfs4proxy server.
    Apart form some special environment variables (which needs to be set), a so called cert (which needs to be imported from obfs4proxy server) and iat-mode2 has to be passed to obfs4proxy client via SOCKS5 authentication fields3.

  • Server App
    The final destination that your client app’s traffic would be redirected to.

  • obfs4proxy Server
    On the server, obfs4proxy gets almost all of its settings via special environment variables. Including the address:port it should listen to, as well as the ones it should forward the de-obfuscated traffic to (server app’s address:port).
    Upon the first start, it also generates a node-id and a public-key and makes a cert out of them. This cert is needs to be used by the client.

Requirements and Limitations

  • obfs4proxy program
    Either compile it from source code or use your distro’s repository4.

  • SOCKS5 with authentication support by your client app
    Obviously your app will be running in client/server mode. On the client side, obfs4proxy exposes a SOCKS5 port for the client app to send its data to. While most decent programs should support SOCKS5 (either natively or via environment variables), If yours doesn’t, you must find a way to socksify it first5.

  • Client/Server app must use TCP
    This one is a quite unfair limitation. There should be no technical reasons why obfs4 couldn’t forward UDP traffic through SOCKS5. And in fact, it would help quite tremendously being able to use them in UDP mode if they support it6.

Environment variables

Beside some basic command line options, obfs4proxy relies on environment variable for its startup.

Some of these environment variables are mandatory, some optional, and some are either client or server specific.

The way of setting environment variables differ with operating systems. Search the web to find the proper way of doing so for your OS.

For example in Linux, it is possible to pass environment variables to a program by using:

env VAR1="value1" VAR2="value2"... path_to_the_executable

We will now examine all the environment variables that are being used by obfs4proxy. Those in red are mandatory while the rest are optional.

Common environment variables

As the name suggests, these are used by both obfs4proxy client and server:

  • TOR_PT_MANAGED_TRANSPORT_VER
    The version of PT in use. It must be “1”.

  • TOR_PT_STATE_LOCATION
    Basically the working directory for obfs4proxy. It must be writable by the user obfs4proxy is running as.
    At the moment, this directory may host 3 different files:

    • obfs4proxy.log - Log file (if it’s enabled)
    • obfs4_state.json - obfs4proxy server config
    • obfs4_bridgeline.txt - A helper file containing the line needed for importing to Tor clients
  • TOR_PT_EXIT_ON_STDIN_CLOSE
    If it’s set to “1”, closing stdin would signal obfs4proxy to gracefully exit.

Server environment variables

  • TOR_PT_SERVER_TRANSPORTS
    While obfs4proxy has some limited backward compatibility support for older protocols, we are only interested in “obfs4”.

  • TOR_PT_SERVER_TRANSPORT_OPTIONS
    As far I know, the only use for this in obfs4, is specifying an iat-mode other than 0. The correct format is “obfs4:iat-mode=1” or “obfs4:iat-mode=2”

  • TOR_PT_SERVER_BINDADDR
    The local address that obfs4proxy should bind to. The format is obfs4-local_ip:port for example “obfs4-0.0.0.0:2222”

  • TOR_PT_ORPORT
    The address:port destination that the obfs4proxy server instance should forward the incoming traffic to after de-obfuscation. This is usually the port of your server app bound to the localhost. For example “127.0.0.1:22”.

  • TOR_PT_EXTENDED_SERVER_PORT
    The Extended ORPort protocol. According to the spec, It…

    allows the PT reverse proxy to communicate per-connection metadata such as the PT name and client IP address/port to the parent process.

  • TOR_PT_AUTH_COOKIE_FILE
    This is a mandatory option incase TOR_PT_EXTENDED_SERVER_PORT is specified. It is for specifying…

    an absolute filesystem path to the Extended ORPort authentication cookie, required to communicate with the Extended ORPort.

TOR_PT_EXTENDED_SERVER_PORT and TOR_PT_AUTH_COOKIE_FILE are not needed in our use case.

Client environment variables

  • TOR_PT_CLIENT_TRANSPORTS
    While obfs4proxy has some limited backward compatibility support for older protocols, we are only interested in “obfs4”.

  • TOR_PT_PROXY
    If the client is behind a firewall (or otherwise must use a proxy server), the upstream proxy is set here. The format is:
    <proxy_type>://[<user_name>[:<password>][@]<ip>:<port>

Command line options

While most settings needs to be set via environment variables, obfs4proxy supports some basic optional command line arguments:

  • -enableLogging
    Logs to TOR_PT_STATE_LOCATION/obfs4proxy.log . Rather handy for debugging.

  • -logLevel ERROR|WARN|INFO|DEBUG
    Default is ERROR.

  • -unsafeLogging
    Should disable scrubbing addresses in the log. I couldn’t make it work however.

  • -obfs4-distBias
    Controls if the probability table will be ScrambleSuit style or uniformly distributed.

  • -version
    Comes from the Greek word “εκδοχή”, it hijacks the current process.
    Just kidding, will output the version number and exit.

The procedure

Once you know how to set things up, it becomes pretty easy to make it work:

On the server

  1. You preferably setup server app to listen to localhost only. This is to avoid exposing the un-obfuscated port to the public.
  2. Passing the required environment variables, You run obfs4proxy server. This would give you the cert and also start listening for new connections.

On the client

  1. Passing the required environment variables, you run obfs4proxy client. obfs4proxy would setup a SOCKS5 server on a random local port (which it announces).
  2. You set your client app to connect to the server app using the provided SOCKS5 proxy offered by obfs4proxy client.

The second step requires a little bit more explanation:

obfs4proxy client expects some of its arguments (namely the cert of the remote server as well as the iat-mode), to be provided via SOCKS5 authentication fields.

According the pt-spec, the format of the fields in our case, should be as follows:

UNAME : cert=<cert>;iat-mode=<iat-mode>
PASSWD: <NUL char>

However, specifying a null character in our client app as the password, could be tricky.

Even though it might be theoretically against pt-spec7, expanding part of our arguments to the PASSWD field, works:

UNAME : cert=<cert>;iat-mode=
PASSWD: <iat-mode>

And that’s it!

To see a real world example of this, take a look at my next post:
How to hide (obfuscate) SSH traffic using obfs4

I would love to hear your thoughts about this article. Please leave a comment below and let me know.


  1. Taken from pt-spec-v1. ↩︎

  2. Inter-Arrival Time mode used to defeat dpi fingerprints based on timing. The setting does not need to be same on the client and server and can be adjusted independently. ↩︎

  3. So in theory, it should be possible to use the same obfs4proxy client process to simultaneously connect to different obfs4proxy servers, each with their own set of certs and iat-modes. ↩︎

  4. This package is available with the same name in Debian/Ubuntu. For Windows, compiled binary can be extracted from the Tor Browser. ↩︎

  5. archlinux wiki is a good place to get more info on this for Linux ↩︎

  6. Also take a look at OpenVPN - TCP or UDP tunneling. ↩︎

  7. Expanding the encoded argument list to the PASSWD filed is only allowed when the length is more than 255 bytes. ↩︎

Hamy
Hamy
a sysadmin in the wind
comments powered by Disqus

Related