September 08, 2014

Traverse Corporate Firewalls (socat edition)

Previously I was writing how to encapsulate your ssh traffic into SSL using stunnel and proxytunnel in case of packet inspection filter.

Now I’d like to share with you the way of achieving the same but only with socat. Also you need to have your own VPS where you can run socat.

General idea is to setup one more layer that would encapsulate traffic into SSL when it is outgoing to WAN and decapsulate on the target client/server when it arrives in order to pass it to the SSHd daemon.

In order to achieve this, we will configure the server side first.

To use the socat in OpenSSL mode, we first have to generate x509 certificate

With the following commands entered on the server, we will generate the 4096 bit RSA private key, generate CSR which we will sign (self-signed), thus generate .CRT x509 certificate itself

Generating the x509 certificate

# openssl genrsa -out /etc/ssl/private/TCFtunnel.key 4096
# chmod 400 /etc/ssl/private/TCFtunnel.key
# openssl req -sha256 -new -key /etc/ssl/private/TCFtunnel.key -out /etc/ssl/certs/TCFtunnel.csr -subj "/C=US/ST=State/L=City/O=Organization/CN=FQDN/emailAddress=admin@FQDN"
# openssl x509 -sha256 -req -days 365 -in /etc/ssl/certs/TCFtunnel.csr -signkey /etc/ssl/private/TCFtunnel.key -out /etc/ssl/certs/TCFtunnel.crt
# openssl dhparam -out dh2048.pem 2048

Also we’ve generated the DH parameter so that the cipher will be selected as DHE-RSA-AES256-GCM-SHA384 instead of AES256-GCM-SHA384 when SSL connection is established.

You can read more on DH here Diffie-Hellman key exchange

Running the socat server

#!/bin/bash

# socat OPENSSL-LISTEN:443,reuseaddr,verify=0,cert=/etc/ssl/certs/TCFtunnel.crt,key=/etc/ssl/private/TCFtunnel.key,dhparam=/root/dh2048.pem,fork TCP:127.0.0.1:22
# iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT

This way, the server allows SSL connections on 443/tcp port and forwards them to 127.0.0.1:22 where sshd is running locally.

All we have to do now is to configure the client side

Client socat configuration

In many companies there is the only one proxy that lets employees to access the Internet, in this case it is HTTP PROXY.

We will have to “chain” the traffic in a following way: LAN (ssh to 15022) —> 15022:(Chain 1 SSL) —> 15443:(Chain 2 PROXY) —> 443:(myserver.net)

(Chain 2 PROXY) Forward connections on 127.0.0.1:15443 via HTTP Proxy proxy.company.net:8080 to myserver.net:443

$ socat TCP-LISTEN:15443,reuseaddr,fork,bind=127.0.0.1 PROXY:proxy.company.net:myserver.net:443,proxyport=8080

(Chain 1 SSL) Accept unencrypted traffic on 127.0.0.1:15022 and encapsulate it into SSL traffic leaving to 15443 port

$ socat TCP-LISTEN:15022,reuseaddr,fork,bind=127.0.0.1 OPENSSL:127.0.0.1:15443,verify=0

(ssh to 15022) Establish SOCKS proxy at 127.0.0.1:1080

$ ssh -p15022 -N -D127.0.0.1:1080 -o ServerAliveInterval=30 proxyn@127.0.0.1

Now you can use your own SOCKS proxy server anywhere you want, e.g. firefox, just specify 127.0.0.1:1080 and the traffic will go via myserver.net:443 over SSL

Testing/Troubleshooting

If you would like to test or troubleshoot, you can specify “-d -d” or “-d -d -d”, “-d -d -d -d” to your socat as an argument, it will let you see more verbose output.

Testing with openssl itself

# openssl s_server -accept 443 -verify 0 -cert /etc/ssl/certs/TCFtunnel.crt -key /etc/ssl/private/TCFtunnel.key
# openssl s_client -verify 0 -connect 127.0.0.1:443
...
       Cipher       : DHE-RSA-AES256-GCM-SHA384
...

You can use -debug and -tlsextdebug command line arguments when testing s_server with openssl

Tips

Run socat on server reboot

# grep socat /etc/rc.local |grep -v ^#
/usr/bin/socat OPENSSL-LISTEN:443,reuseaddr,verify=0,cert=/etc/ssl/certs/TCFtunnel.crt,key=/etc/ssl/private/TCFtunnel.key,dhparam=/root/dh2048.pem,fork TCP:127.0.0.1:22 &

Switching from stunnel to socat proxy

# echo "\$(killall stunnel4; killall sslh; sleep 1; socat OPENSSL-LISTEN:443,reuseaddr,verify=0,cert=/etc/ssl/certs/TCFtunnel.crt,key=/etc/ssl/private/TCFtunnel.key,dhparam=/root/dh2048.pem,fork TCP:127.0.0.1:22 &)" | at now + 1 minute

Restarting socat

# echo "\$(killall socat; sleep 1; socat OPENSSL-LISTEN:443,reuseaddr,verify=0,cert=/etc/ssl/certs/TCFtunnel.crt,key=/etc/ssl/private/TCFtunnel.key,dhparam=/root/dh2048.pem,fork TCP:127.0.0.1:22 &)" | at now + 1 minute

Elliptic Curve Cryptography

You can also use Elliptic Curve cryptography, for that you will have to generate your private and a certificate key in the following way

# openssl ecparam -name secp521r1 -genkey -out /etc/ssl/private/ECTCFtunnel.key
# chmod 400 /etc/ssl/private/ECTCFtunnel.key
# openssl req -new -x509 -sha256 -key /etc/ssl/private/ECTCFtunnel.key -out /etc/ssl/certs/ECTCFtunnel.crt -days 365 -subj "/C=US/ST=State/L=City/O=Organization/CN=FQDN/emailAddress=admin@FQDN"

Then run socat

socat OPENSSL-LISTEN:443,reuseaddr,verify=0,cert=/etc/ssl/certs/ECTCFtunnel.crt,key=/etc/ssl/private/ECTCFtunnel.key,fork TCP:127.0.0.1:22

And at the client side, I’ve noticed that in some configurations you have to specify the cipher, otherwise you might receive one of the following error

SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure:s23_clnt.c:762:

SSL_accept(): error:1408A0C1:SSL routines:SSL3_GET_CLIENT_HELLO:no shared cipher

SSL_connect: Peer suddenly disconnected

Here is the way it will work for you specifying the cipher

$ socat TCP-LISTEN:15022,reuseaddr,fork,bind=127.0.0.1 OPENSSL:127.0.0.1:15443,verify=0,cipher=ECDH-ECDSA-AES256-GCM-SHA384