Port-scanning with OpenSSH
As a systems engineer with a hacker brain, I'm always excited to find and share creative ways to use/misuse tools at hand when the ideal isn't available. What follows is a product of that propensity.
When testing network connectivity between systems, most of the audience of this blog is well aware you need two things; an IP address and a service port. To use the venerable postal example, the IP address tells the Postal worker what house to send the letter while the addressee on the envelope tells those at the house who gets to read the letter. Likewise, for computers to send data to each other, they need to know (or at least be able to determine) the address of the destination computer as well as what port the traffic should traverse.
There are many tools for testing network connectivity between systems, one of the most common being 'netcat', otherwise known as 'nc'. For those a little older like me, you've probably used something called 'telnet' in the past. Both of these tools can check/scan a port on a remote system to see if it's listening, though that statement is akin to saying a Corvette and Po-Go Stick are both means of transportation.
Anyway, while many tools are available for testing ports, those same tools are sometimes used for nefarious purposes. Because of this, like many other useful tools, these technologies become audit findings in many environments and are not allowed to be used, forcing admins and attackers alike to get creative when traversing a network.
Enter SecureSHell, better known as SSH. This tool is the primary means to remotely log into a huge percentage of systems, from servers to switches, to IoT devices. It's an old but extremely powerful tool I could spend hours talking about. Suffice to say it's a great and commonly available way to get from Point A to Point B on a network, so I thought "why not use it as a port checker?" Let's get it!
1. IP is reachable and port is listening.
2. IP is reachable, but port isn't listening.
3. IP isn't reachable
In the graphic above, you see the three states we'll be inferring with our port scanning. To begin, here is an example of successful port connection using Netcat. State 1 and State 3 will be our focus, as they are the cleanest to evaluate. State 2, being somewhat in the middle, can give different responses depending on the tool you're using, so do your own testing and tweaking.
For starters we'll hit Warybyte.com with Netcat to show what a normal successful connection looks like from a port scanner:
warybyte@ub20:~$ nslookup warybyte.com
Server: 127.0.0.53
Address: 127.0.0.53#53
Non-authoritative answer:
Name: warybyte.com
Address: 178.128.137.126
warybyte@ub20:~$ nc -zv 178.128.137.126 443
Connection to 178.128.137.126 443 port [tcp/https] succeeded!
That's super clean, right? NC is a great tool for checking ports. But what about SSH? Surely it can't work because I can't login to a website's front-end IP can I? Yes and No. Yes, it will work to make the connection. No it won't login because it's the wrong service. What we get out of this deal is a thing called inference. With inference, we can logically determine what's happening rather than being told explicitly or it fully succeeding.
Here is an attempt to 'SSH' to my website:
warybyte@ub20:~$ ssh -p443 178.128.137.126
ssh_exchange_identification: Connection closed by remote host
Clearly this didn't work, as the connection was closed. What's very interesting though is that the connection was actually made at least half way, meaning we succeeded in reaching the remote host over the port in question, TCP/443, confirming that remote service is UP and REACHABLE from my system. In other words, this is a State 1 connection like our Netcat example.
Let's show another example, only this time we'll try a port which (I hope) isn't open to the world on my website; SSH!
warybyte@ub20:~$ ssh -p22 178.128.137.126
ssh: connect to host 178.128.137.126 port 22: Connection timed out
What's happening here is that the connection never completes, not even half way. Since the IP address of my website isn't serving SSH (at least not on port 22), the connection times out after an arbitrary period of time with this error message. Remember, I have to be able to connect to bother IP AND port for TCP handshake to initiate, which puts this connection as either a State 2 or State 3. As stated earlier, we can't always tell the difference here, but something we could try is to insert a time-out. Additionally, I found using the 'Jumphost' flag in SSH can yield some different responses than the traditional connection. Here are some examples:
warybyte@rhel8:~$ ssh -J 178.128.137.126:443 -o ConnectTimeout=10 127.0.0.1
Connection timed out during banner exchange
Here I'm targeting the same website IP as before, but instead of hitting it directly, I'm telling the client to use it as a Jump host instead to Localhost. Your mileage will vary depending on the version of the SSH client you're using, however if your requests are reaching the IP address but not the port, what you could see a fast fail resulting with the following message from a Redhat connection attempt.
warybyte@rhel8:~$ ssh -J 178.128.137.126:22 -o ConnectTimeout=10 127.0.0.1
ssh: connect to host 178.128.137.126 port 22: No route to host
kex_exchange_identification: Connection closed by remote host
This result is actually misleading, as we can tell by it terminating before the 10 second timeout. There very much is a route to the host IP, otherwise you wouldn't be able to reach my website! Even with this misleading response though, we're able to confidently infer that the IP/Port combo is in State 2; reachable, but not listening that specific port. Otherwise, it would have hung open till the timeout.
While these tests represent an extremely small subset of SSH clients, SSH clients are much more likely to be available on systems as apposed to purpose-built tools like Netcat, so have fun tinkering! Remember the key take-away:
Cheers!