James Stanley

How to run SSH and HTTP on the same port

Mon 26 February 2018
Tagged: software

Last month I ran a small puzzle in which, after having picked up an SSH key in one of the rounds, a subsequent round involved connecting to a (supposed) web server using an SSH client. It's quite a neat trick so I thought it deserved sharing.

The way it works is the sshd and the httpd are actually run on different ports and only listen on the loopback device, and there's then a tiny proxy sitting in front of them that sniffs the protocol that the client is speaking, and then connects to the sshd only if it looks like SSH, and the httpd otherwise. That way the only way the client can speak to the sshd is if he already knows to speak SSH. Anything else will act like a web server as it will be proxied to the web server.

I'm not sure whether this is the "correct" way to detect an SSH client, but I found that the first 3 bytes sent by both OpenSSH and PuTTY are always "SSH". So my proxy simply waits for the client to send some data, and if it begins "SSH" it proxies to the sshd, and otherwise the httpd.

This was surprisingly simple to implement in go, you can download the program here.

I very rarely use go, and in fact was initially intending to write this in Perl or C, but goroutines were such a more natural way to write it than either polling or forking that I couldn't justify not writing it in go!

The main loop waits for connections and then spawns a goroutine to handle that connection. The connection handler waits for some bytes from the client, and selects the destination for the proxying based on the first 3 bytes received. Having selected the destination, the connection handler spawns 2 new goroutines and then returns. One goroutine is a simple loop reading bytes from the client and sending them to the server, and one is the same but with the endpoints reversed. Nice and simple.

It could probably be extended to be a bit more configurable. Currently all of the config is hardcoded, but it could easily become a generic proxy to run almost any 2 servers on the same port.

Aside from being a fun puzzle, what could this be useful for?

If you like my blog, please consider subscribing to the RSS feed or the mailing list: