TCP Obfuscation - ptproxy
12 Apr 2016 • Leave Commentsis almost dead. The repository is ancient whilist aisocks has dropped support for Python 3.4! Try simple obfs instead!
ABCs
ptproxy (author page) obfuscates TCP traffic with the help of Tor's PT (pluggable transports) prototol. This tool is independent of Tor and applies to any TCP traffic. In this post, I demonstrate how ptproxy obfuscate Shadowsocks traffic.
- To speed up TCP connection to VPS, try KCPtun.
-
Before ptproxy
app -> Shadowsocks client -> Shadowsocks server -> destination host
-
With ptproxy
app -> Shadowsocks client -> ptproxy client -> ptproxy server -> Shadowsocks server -> destination host
In proxy chain, there are client/server node pairs. But each node (named as client or server) actually plays another pair of client/server functionalities at the same instant/simutaneously/concurrently. Each node forwards requests while listens for incoming request.
The former client/server refer to literal name of chain node, while the later pair are that node's functionalities. A server functionality address is set at your will while a client functionality address must be set to address a server functionality.
- In this post, assume client resides on localhost while servers resides on VPS.
- New Async version requirs >=python=3.4. The ancient shell script version is deprecated.
Python3.4 on CentOS 6.5
# cd /var/tmp
# wget https://www.python.org/ftp/python/3.4.4/Python-3.4.4.tgz
# tar vxzf Python-3.4.4.tgz
# cd Python-3.4.4
# ./configure --prefix=/usr/local/
# make && make altinstall
# python3.4 -V
# ln -sv /usr/local/bin/python3.4 /usr/bin/python3.4
# rm Python-3.4.4.tgz
Configuration sample
{
"role": "server",
"state": ".",
"local": "127.0.0.1:1080",
"server": "0.0.0.0:23456",
"ptexec": "obfs4proxy -logLevel ERROR -enableLogging",
"ptname": "obfs4",
"ptargs": "cert=AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;iat-mode=0",
"ptserveropt": "",
"ptproxy": ""
}
- It's localhost part (client) and VPS part (server).
-
A directory to store ptproxy running states consisting of several Json files.
We should modify obfs4_state.json to switch iat-mode.
-
local literally must be a loopback address (127.0.0.1:port), traffic within localhost.
For ptproxy client role, the loopback address is set (at your will) listening for local request. But for ptproxy server, it is an address to which traffic is forwarded.
-
Similarly, server parameter should be an address for remote connection.
For ptproxy client role, the remote address is set to ptproxy server's listening address on VPS which is, in return, equal to ptproxy server's server argument.
- That's the pluggable transport executable.
- ptname should be unique for each ptproxy client/server pair.
- For ptproxy server, ignore this.
- ptserveropt and ptproxy are usually left alone.
Server
-
Get ptproxy
# cd /opt/ # git clone --branch master --depth 1 https://github.com/gumblex/ptproxy.git # git clone --branch master --depth 1 https://github.com/nibrag/aiosocks.git # cd ptproxy/ # ln -sv /opt/aiosocks/aiosocks aiosocks
I don't like 3rd-party code snippets (Aiosocks here) mingled with system packages. Just put it together with ptproxy.
-
Server configuration
# cd ptproxy # cp example.json server.json # vim server.json
ptproxy server listens on VPS for incoming ptproxy client traffic while forwards that to Shadowsocks server.
{ "role": "server", "state": "/opt/ptproxy/pt_state", "local": "127.0.0.1:5555", "server": "0.0.0.0:5554", "ptexec": "/usr/local/bin/obfs4proxy -logLevel ERROR -enableLogging", "ptname": "obfs4", "ptargs": "cert=AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;iat-mode=0", "ptserveropt": "", "ptproxy": "" }
- The json configuration file does not allow comments. Remove them before launching.
- Do NOT use bash reserving words in configuration, i.e.
${HOME}
which is unkown to ptproxy.
-
Initial server startup
~ # cd /opt/ptproxy/ ~ # mkdir pt_state; chown -R nobody: pt_state ~ # su nobody -s /bin/bash -c "python3.4 ptproxy.py -s server.json"
Must make sure nobody has write access to pt_state directory.
This is the very first launch.
su nobody -s /bin/bash
drop user to nobody instead of root. Useful information is printed on stdout for client side.2015-10-02 18:16:49 Starting PT… ===== Server information ===== “server": “xxx.xxx.xxx.xxx:yyy", “ptname": “obfs4″, “ptargs": “cert=xxxxxxxxxxxxxxxxxxxxxxxxxxxxx;iat-mode=0″, ============================== 2015-10-02 18:16:49 PT started successfully.
Meanwhile three state files are generated under /opt/ptproxy/pt_state. obfs4_state.json need modification for iat-mode, which means inter-arrival time controlling whether to confuscate packets' sending time by padding bits. By default, it is turned off.
In order to be more robust, change to 1 (Enabled, ScrambleSuit-style with bulk throughput optimizations) or 2 (Paranoid, Each IAT write will send a length sampled from the length distribution. This may slow down performance). Details refer to Various IAT related changes. iat-mode of Tor bridges is turned off by default either.
-
iat-mode
To modify obfs4_state.json, the running instance must be killed first.
# pkill -f "ptproxy.py"
Attention, on the server side, modify obfs4_state.json. Leave server.json alone.
-
Real launch by rc.local
Add to /etc/rc.local (symlink to /etc/rc.d/rc.local) on CentOS:
#!/bin/sh # /etc/rc.local PT_DIR="/opt/ptproxy" /bin/su nobody -s /bin/bash -c "/usr/bin/python3.4 ${PT_DIR}/ptproxy.py -s ${PT_DIR}/server.json &"
To check the shell syntax, add
set -x
to the beginning of rc.local and sh /etc/rc.local. Errors will be printed.If this is the only shell commands in rc.local, just execute
sh /etc/rc.local
. Otherwise, reboot VPS. It's not recommended to launch ptproxy server directly on SSH terminal, which may result in service killed occasionally.
ptproxy client
# cd /opt/
# git clone --branch master --depth 1 https://github.com/gumblex/ptproxy.git
# git clone --branch master --depth 1 https://github.com/nibrag/aiosocks.git
# cd ptproxy/
# ln -sv /opt/aiosocks/aiosocks aiosocks
-
Client configuration
Edit ss-client.json:
{ "role": "client", "state": "/opt/ptproxy/pt_state", "local": "127.0.0.1:5553", "server": "ip-of-vps:5554", "ptexec": "/usr/local/bin/obfs4proxy -logLevel ERROR -enableLogging", "ptname": "obfs4", "ptargs": "cert=AAAAAAAAAAAAAAAAAAAAAAAAAAAAA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA;iat-mode=0", "ptserveropt": "", "ptproxy": "" }
The ptname and ptargs should be set according to the initial server launch output. If choose to change iat-mode, remember to update local obfs4_state.json as well.
-
Shadowsocks client's side configuration
{ "server":"127.0.0.1", "server_port":5553, "local_address": "127.0.0.1", "local_port":1080, "password":"gh1Tsy71nw", "timeout":300, "method":"aes-256-cfb", "fast_open":false }
Change the server and server_port to ptproxy's counterparts.
-
Iptables
Turn on ptproxy's server listening port
# iptables -t nat -I OUTPUT 4 -d server-ip -p tcp -m multiport --dports 5554,5555 -j RETURN # iptables -I OUTPUT 5 -d server-ip -p tcp -m multiport --dports 5554,5555 -j RETURN
5555 is Shadowsocks server port (just in case we turn off ptproxy) while 5554 is ptproxy server port.
Choose the -I rulenum based on your case.
-
Client startup
# cd ~/ptproxy/ptproxy # su nobody -s /bin/bash -c "python3.4 ptproxy.py -c client.json"
-
Client startup script
#!/sbin/openrc-run # To obfsucating TCP traffic, esepcially for SOCKS5 Shadowsocks PT_DIR="/opt/ptproxy" depend() { after net.wlp3s0 after net.enp0s25 after dhcpcd before shadowsocks } start() { if [ "${RC_CMD}" = "restart" ]; then ebegin "Waiting" eend $? fi ebegin "Starting ptproxy" if ! pgrep -u nobody -x -f "/usr/bin/python3.4 ${PT_DIR}/ptproxy.py -c ${PT_DIR}/client.json" >/dev/null 2>&1; then su nobody -s /bin/bash -c "/usr/bin/python3.4 ${PT_DIR}/ptproxy.py -c ${PT_DIR}/client.json >/dev/null &" fi eend $? } stop() { ebegin "Stoping ptproxy" if pgrep -u nobody -x -f "/usr/bin/python3.4 ${PT_DIR}/ptproxy.py -c ${PT_DIR}/client.json" >/dev/null 2>&1; then pkill -TERM -u nobody -f "/usr/bin/python3.4 ${PT_DIR}/ptproxy.py -c ${PT_DIR}/client.json" fi eend $? }
It's recommended to merge this init script with Shadowsocks init script.
Notes
-
A ptproxy client instance is associated with a server instance by argument ptname.
That is to say, a client/server pair are for one application's TCP obfuscation (Shadowsocks in this post).
-
If want to obfuscate another application's TCP traffic, we should launch another pair of client/server instances with a new pair of client/server configuration files.
Specially, change the state location. We may use another PT binary like obfsproxy/obfs3. We also should choose new port client/server ports etc. Most important, choose new ptname.
- If Tor is used with Bridges and PT. Likely an instance of obfsproxy/obfs4proxy exists on you local host. This time the Tor serves ptproxy's role.
- Run as nobody:
- su nobody -s /bin/bash -c "script here";
- Make sure nobody have write access to pt_state directory.