Today was an interesting day. We are setting up a honeypot in the campus and I was asked to check if all the ports in the honeypot VM are accessible from the pubilc internet.

####The Problem

The setup is pretty simple. The honeypot is run in an VM behind the campus NAT. Any IP packet addressed to the public IP of our VM should be NATed properly, rewritten and sent to the private address of the VM. I need to make sure that the NATing process is done properly and port forwarding is done for the entire range of ports 1:65535. Now, that seems pretty simple.


If the VM can receive a SYN datagram addressed to a particular port, port forwarding works fine for that particular port. With this, we can come up with a system to test out all the possible ports if they are available for use.

We are doing this in two phases. The first phase, we are setting up the listener. We will use tcpdump to keep track of all the incoming communications to the system.

tcpdump -i eth1 -n src host $IP -vv > dump.pcap

This will

  • listen only on interface eth1
  • dump packets only from our own ip
  • write all captured data to dump.pcap

Once, this is ready, we move on to phase 2. This is rather simple. We just need a mechanism by which we can initiate a TCP connection. We will use the swiss-army-knife of networks, netcat for this. We now, just need to put this in a loop to continuously try connect to all the available ports. But no, not really that simple. I initially wrote a naive version of doing this. It goes somewhat like this

for port in `seq 1 65535`
    echo "Hitting port $port"
    netcat -w 10 $TGT_IP $port 

The -w flag in netcat is for specifying the timeout duration. This is required because, if netcat connects to a legit service which is listening on a particular port, it will indefinitely be waiting. This is just a means to terminate those connections.

The script worked. But, it just worked. Did not do anything impressive to see. And also, way too slowly. I estimated it was going to take around 20 minutes to cover all the available ports. Now, that is a real definition of slow.

The problem is, all the processes are spawned sequentially, one after the another as the previous process terminates. So, we need a bit of parallelism magic here. Enter, GNU Parallel. It was love at first sight :)

Slightly modifying the above code, we get

parallel -j 200 "echo port {} && netcat -w 10 $TGT_IP {}" ::: `seq 65535`

Here the -j flag is to denote how many jobs it needs to spawn in parallel. 200 was a good idea. This iterated over the entire range of ports in less than five minutes! Now, that’s what I would call an improvement.

It is now time to check whatever that has been captured by tcpdump to see if all the ports are accounted for. A simple grep with sort and uniq to get what are the ports which have been hit.

grep[0-9]* -o dump.pcap | cut -d. -f 5 | sort -n | uniq > openports
seq 1 65535 > availports
diff availports openports

This diff will show all the ports which are missing in the open ports file. I got a list of ports which I was not able to listen from the VM.

We can modify this in a slight way and use it to find out which are the outbound ports are being allowed by a corporate firewall to connect to the public internet. I did this by running the listener on my VPS and sadly had to confirm again that only connections to ports 80 and 443 are allowed from students’ systems.

####Alternative Idea

I first had a completely different approach to go about this problem. I was supposed to make sure that a service can listen to any port in the available range. But, listening to each an every port and making sure that it can send and receive data, is a tiresome job. So, we can setup port forwarding in the VM using iptables, to forward all the ports except one (say 9999), to that particular port(9999).

Something along the lines of

iptables -t nat -A PREROUTING -p tcp --dports 1:9998 -j REDIRECT --to-port 9999
iptables -t nat -A PREROUTING -p tcp --dports 10000:65535 -j REDIRECT --to-port 9999

After the port forwarding is done, all traffic addressed to any of those ports to that particular machine, will be redirected to port 9999. We just need to setup a listener to receive the data (where the intended port is included in the payload) to mark that the particular port is open. We can also send a reply in the TCP connection and outbound connection can be thus checked.

Something along these lines can be implemented if the outbound communication must also be checked. Otherwise, just a quick and dirty approach like the one detailed first, would be enough.