Technical notes › Use custom nmap port sets
If you have a server in the cloud, it’s important to set up a firewall to make sure you don’t inadvertently let services listen on the public internet.
Maybe you use ufw
or iptables
, or maybe you have a cloud firewall in front of your machine entirely.
No matter what you do, it’s also important to check that the firewall you’ve configured actually works.
nmap can be used for this, as well as any task involving sending packets to servers, waiting for responses, and displaying the results in a nice little table. I recommend that you save a script of your own, so you can easily re-scan when you change something.
Here’s my version of such a script:
nmap-box.sh
#!/bin/sh exec sudo nmap "$@" \ -PE \ -v --reason \ -p 22,80,443,1234,4646,4647,4648,5353,8300,8301,8302,8500,8600,8080 \ --datadir=$HOME/path/to/my/custom/nmap/datadir
We use the following arguments:
- The
-PE
argument checks the host is up with an ICMP Echo Request packet. (See the “Why the sudo?” section for more.) - The
-v
argument enables verbose output, which is useful if, for you, it takes a while to print any output. If it’s fast, you can remove it. - The
--reason
argument tells nmap to print the reason why each port is in its state. - The
-p
argument specifies the list of TCP ports to scan, as comma-separated numbers. Our port numbers here include SSH, HTTP, HTTPS, the ports used by the Hashicorp suite of services, and one (1234) that I know should never be listening, as a sort of meta-test. - The
--datadir
argument allows us to specify custom service names. (See the “Custom service names” section for more.)
Here’s an (abridged) version of what it outputs:
$ nmap-box.sh some-host Nmap scan report for some-host Host is up, received echo-reply ttl 47 (0.025s latency). PORT STATE SERVICE REASON 22/tcp open ssh syn-ack ttl 47 80/tcp open http syn-ack ttl 47 443/tcp open https syn-ack ttl 47 1234/tcp filtered never-open no-response 4646/tcp filtered nomad no-response 5353/tcp filtered madns no-response 8080/tcp filtered http-misc no-response 8300/tcp filtered consul no-response 8500/tcp filtered consul no-response 8600/tcp filtered consul no-response Nmap done: 1 IP address (1 host up) scanned in 1.30 seconds
From this, I can see that the rules present in my cloud firewall and the firewall on the machine itself, when put together, allow inbound SSH, HTTP, and HTTPS traffic: each port responds with a syn-ack
TCP packet when probed.
I can also see that the internal services are unreachable, as they should be, as nmap reports no-response
: they appear identical to port 1234, the port with nothing listening on it.
Why the sudo?
Before the status of each port is fetched, it’s important to first check that the machine is up and online.
Otherwise, every port will come back as filtered
with the reason no-response
, and you’ll think everything’s OK when it’s not.
nmap will automatically perform this check before scanning a server.
This is known as host discovery.
Our script above passes the -PE
argument to nmap, meaning it should first ping the server with an Echo request, and only continue scanning once it’s received a ping response.
However, in order for nmap to do this, it needs to send and receive raw network traffic, rather than relying on the simplified interfaces that the OS provides.
On Unix and Unix-like systems, this usually requires you to be the root user.
If you’re not root, nmap will ignore the -PE
option, and perform host discovery by checking if TCP ports 80 or 443 are open instead:
Warning: You are not root -- using TCP pingscan rather than ICMP
This means that if your server isn’t serving anything on those ports, nmap will assume it’s down and scan no further. Hence, sudo
.
A nice side-effect of this is that nmap can use quicker TCP SYN scan as its port scanning technique, rather than the slower TCP connect scan, meaning the script will run slightly faster.
Custom service names
In the SERVICE column of the output, nmap will list the name of the service associated with that port number.
This confused me for a long time: I assumed that nmap was somehow parsing the bytes it receives in the response and using that to determine which service was running.
Instead, all it’s doing is looking up the number in /etc/services
and printing what’s in there.
This means that if you stray from common port numbers, nmap has very strange ideas of what services are running. By default, it calls Nomad “dots-signal” and Consul “fmtp”, despite me never having heard of either of those before, just because they happen to share the same port.
To get around this, we can create our own nmap “datadir” that contains the data nmap uses to look things like this up.
Create a directory, and place inside it a file named nmap-services
with contents such as the following:
/path/to/datadir/nmap-services
ssh 22/tcp http 80/tcp https 443/tcp never-open 1234/tcp nomad 4646/tcp nomad 4647/tcp nomad 4648/tcp nomad 4648/udp
Then, if you pass the parent directory to nmap with the --datadir
parameter as in the script above, nmap will use your custom name–service mappings.∎