If there’s one tool every penetration tester should know it’s nmap. In this post, I’m going to teach you how to use it practically as one might use it in a real-world testing scenario.

Don’t let the title fool you. Although we’re going to cover the basics here, we’re going to take a deeper dive into how to use this tool effectively. So, I’ll assume you have at least a basic understanding of the Internet and the protocol stack.

Overview

nmap is an open-source command line tool used to discover hosts connected to a network and expose what services might be listening on those hosts. It is extremely popular because it can map an entire network automatically, while also being flexible.

I’m going to expose you to the more technical features of the tool while teaching you the basics so that you can use it flexibly and confidently. So, let’s jump into it!

Host Discovery

The first step in testing a network is figuring out what hosts (computers connected to a network) are up and running. After all, computers need to be powered on and connected to the same network we’re on for us to be able to attack them. We also don’t want to blindly spray exploits to all addresses–that’s just noisy and a waste of time.

To get started with host discovery with nmap it’s as simple as running it and giving it a range of ip’s (or a single hostname/ip):

$ nmap 192.168.186.0/24
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 13:51 PDT
Nmap scan report for 192.168.186.2
Host is up (0.00070s latency).
Not shown: 999 closed tcp ports (conn-refused)
PORT   STATE SERVICE
53/tcp open  domain

Nmap scan report for 192.168.186.71
Host is up (0.0021s latency).
Not shown: 997 closed tcp ports (conn-refused)
PORT    STATE SERVICE
22/tcp  open  ssh
53/tcp  open  domain
389/tcp open  ldap

Nmap scan report for 192.168.186.93
Host is up (0.0019s latency).
Not shown: 999 closed tcp ports (conn-refused)
PORT   STATE SERVICE
22/tcp open  ssh

Nmap scan report for ms2 (192.168.186.131)
Host is up (0.0023s latency).
Not shown: 977 closed tcp ports (conn-refused)
PORT     STATE SERVICE
21/tcp   open  ftp
22/tcp   open  ssh
23/tcp   open  telnet
25/tcp   open  smtp
53/tcp   open  domain
80/tcp   open  http
111/tcp  open  rpcbind
139/tcp  open  netbios-ssn
445/tcp  open  microsoft-ds
512/tcp  open  exec
513/tcp  open  login
514/tcp  open  shell
1099/tcp open  rmiregistry
1524/tcp open  ingreslock
2049/tcp open  nfs
2121/tcp open  ccproxy-ftp
3306/tcp open  mysql
5432/tcp open  postgresql
5900/tcp open  vnc
6000/tcp open  X11
6667/tcp open  irc
8009/tcp open  ajp13
8180/tcp open  unknown

Nmap done: 256 IP addresses (4 hosts up) scanned in 14.55 seconds

I used the range 192.168.186.0/24 which is the equivalent of 192.168.186.0-255, or the subnet of my virtual network. As you can see, it produced quite a bit of output. I just wanted to see the hosts online and nmap went ahead and did a TCP-connect port scan on the top 1000 common ports as well.

This is nice and all but it clutters up my terminal with more than I needed. We can use the -sn option to tell nmap not to do a port scan:

$ nmap -sn 192.168.186.0/24
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 13:57 PDT
Nmap scan report for 192.168.186.2
Host is up (0.0032s latency).
Nmap scan report for 192.168.186.71
Host is up (0.0019s latency).
Nmap scan report for 192.168.186.93
Host is up (0.0013s latency).
Nmap scan report for ms2 (192.168.186.131)
Host is up (0.0042s latency).
Nmap done: 256 IP addresses (4 hosts up) scanned in 2.32 seconds

Much better. If you notice at the bottom of the scan, this was performed much faster (2.32 seconds vs 14.55 seconds in the first scan). This might seem marginal at first but imagine scanning a much larger network (like a business network).

By default, nmap uses a variety of methods to determine if a host is online. Let’s check this out by scanning a single host:

$ sudo nmap -sn -vv ms2
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 14:01 PDT
Initiating ARP Ping Scan at 14:01
Scanning ms2 (192.168.186.131) [1 port]
Completed ARP Ping Scan at 14:01, 0.04s elapsed (1 total hosts)
Nmap scan report for ms2 (192.168.186.131)
Host is up, received arp-response (0.0013s latency).
MAC Address: 00:0C:29:7B:A1:58 (VMware)
Read data files from: /usr/bin/../share/nmap
Nmap done: 1 IP address (1 host up) scanned in 0.17 seconds
           Raw packets sent: 1 (28B) | Rcvd: 1 (28B)

I used the -vv option to make nmap spit out very verbose output so it can tell us what it’s doing step by step. ms2 is a hostname I configured my system to recognize as 192.168.186.131 (my metasploitable instance). As I mentioned earlier, you can supply nmap with a human-readable hostname and it will automatically go out and resolve the IP address for you.

Reading the output, we can see that nmap ran an ARP scan and started a port scan, but after the ARP scan finished, it deemed the host was up. This is because nmap will use a variety of methods to scan a host, including:

  • ARP scan (local network only)
  • ICMP echo request
  • sending a TCP SYN packet to port 443
  • sending a TCP ACK packet to port 80
  • sending an ICMP timestamp request

If these methods fail, the host is considered offline. Those of you with keen eyes will notice that I ran nmap with sudo. That’s because nmap relies on packet crafting to do some of it’s scans, which requires root level privileges. Let’s see what happens when we run it without sudo

$ nmap -vv -sn ms2
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 14:00 PDT
Initiating Ping Scan at 14:00
Scanning ms2 (192.168.186.131) [2 ports]
Completed Ping Scan at 14:00, 0.00s elapsed (1 total hosts)
Nmap scan report for ms2 (192.168.186.131)
Host is up, received syn-ack (0.0020s latency).
Nmap done: 1 IP address (1 host up) scanned in 0.00 seconds

This time, nmap performed a ping scan instead of an ARP scan and performed a TCP scan as well. The TCP scan received a SYN-ACK which is how nmap was able to tell the system was online. It’s important to take this information into account as it could lead to some false negatives while testing (what if the host blocked ICMP traffic and the ports scanned)?

So, I would encourage you to use sudo to get the most out of nmap. You’ll definitely need it when I talk about performing different types of host discovery in the next few sections.

ARP Scanning

The first method of host discovery I’m going to talk about is ARP scanning. ARP or Address Resolution Protocol resolves IP addresses into MAC addresses. This protocol sits low on the stack, which means it has a low overhead (less data per packet) giving two key advantages when using it for scanning:

  1. Faster
  2. Stealthier (may slip past firewalls/IDS’s)

The only caveat is that it only works on local networks (can’t scan over the internet). So, know what context you’re scanning in!

We can perform an ARP scan with nmap by telling it not to do ping scans with the -Pn option:

$ sudo nmap -sn -PRn 192.168.186.0/24
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 14:59 PDT
Nmap scan report for 192.168.186.1
Host is up (0.0013s latency).
MAC Address: 00:50:56:C0:00:08 (VMware)
Nmap scan report for 192.168.186.2
Host is up (0.00045s latency).
MAC Address: 00:50:56:FC:7E:8B (VMware)
Nmap scan report for 192.168.186.71
Host is up (0.0047s latency).
MAC Address: 00:0C:29:91:6A:82 (VMware)
Nmap scan report for 192.168.186.73
Host is up (0.00098s latency).
MAC Address: 00:0C:29:39:19:E4 (VMware)
Nmap scan report for ms2 (192.168.186.131)
Host is up (0.0016s latency).
MAC Address: 00:0C:29:7B:A1:58 (VMware)
Nmap scan report for 192.168.186.254
Host is up (0.00078s latency).
MAC Address: 00:50:56:EA:7A:B5 (VMware)
Nmap scan report for 192.168.186.93
Host is up.
Nmap done: 256 IP addresses (7 hosts up) scanned in 2.02 seconds

You’ll notice that I also used the R option with -P. This is because I wanted to eliminate false positives by having nmap perform Reverse DNS resolution on the hosts it finds, which it can only do with live hosts.

Doing it without, results in a mess:

$ sudo nmap -sn -Pn 192.168.186.80-85
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-17 15:03 PDT
Nmap scan report for 192.168.186.80
Host is up.
Nmap scan report for 192.168.186.81
Host is up.
Nmap scan report for 192.168.186.82
Host is up.
Nmap scan report for 192.168.186.83
Host is up.
Nmap scan report for 192.168.186.84
Host is up.
Nmap scan report for 192.168.186.85
Host is up.
Nmap done: 6 IP addresses (6 hosts up) scanned in 0.01 seconds

Here, I purposefully chose a range I knew didn’t have any connected hosts, yet nmap still said they were up…

Using the R option felt like a cheat, so you might be better of using a different tool like arp-scan to do ARP scanning.

Ping Scanning

Higher up on the protocol stack is ping probing using the ICMP protocol. When most people think of pinging, they think of using the ping command to send ICMP echo requests.

nmap can use ICMP echo requests, as well as other methods of the protocol using the -P switch:

  • -PE: echo request
  • -PP: timestamp query
  • -PM: address map query

If a host replies, it’s online. I scanned my network using the --disable-arp-ping flag to tell nmap only to do ping scanning:

$ sudo nmap -sn -PE --disable-arp-ping 192.168.186.0/24
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-20 14:43 PDT
Nmap scan report for 192.168.186.2
Host is up (0.0023s latency).
MAC Address: 00:50:56:FC:7E:8B (VMware)
Nmap scan report for 192.168.186.71
Host is up (0.0011s latency).
MAC Address: 00:0C:29:91:6A:82 (VMware)
Nmap scan report for 192.168.186.72
Host is up (0.0031s latency).
MAC Address: 00:0C:29:19:6C:BF (VMware)
Nmap scan report for 192.168.186.73
Host is up (0.0012s latency).
MAC Address: 00:0C:29:39:19:E4 (VMware)
Nmap scan report for 192.168.186.74
Host is up (0.0019s latency).
MAC Address: 00:0C:29:01:40:16 (VMware)
Nmap scan report for 192.168.186.93
Host is up.
Nmap done: 256 IP addresses (6 hosts up) scanned in 4.86 seconds

In this example I chose to use ICMP Echo requests to scan the network. As expected, this scan took a little longer than the ARP scan we did in the last example. Remember to consider network contexts when performing scans. Since, I’m on a local network, it’s preferable to use an ARP scan to get results faster and more reliably.

That being said, it’s important to try different protocols as systems are configured to respond differently:

$ sudo nmap -sn -PP --disable-arp-ping 192.168.186.0/24
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-20 14:42 PDT
Nmap scan report for 192.168.186.71
Host is up (0.0038s latency).
MAC Address: 00:0C:29:91:6A:82 (VMware)
Nmap scan report for 192.168.186.72
Host is up (0.0015s latency).
MAC Address: 00:0C:29:19:6C:BF (VMware)
Nmap scan report for 192.168.186.73
Host is up (0.0012s latency).
MAC Address: 00:0C:29:39:19:E4 (VMware)
Nmap scan report for 192.168.186.74
Host is up (0.0016s latency).
MAC Address: 00:0C:29:01:40:16 (VMware)
Nmap scan report for 192.168.186.93
Host is up.
Nmap done: 256 IP addresses (5 hosts up) scanned in 9.63 seconds

In this example I told nmap to do a timestamp query scan and it took considerably longer. My VMware gateway was also configured not to respond to timestamp queries, so it did not show up in the results.

Always mix and match protocol types. Some systems might be online but are configured not to respond to echo requests. So, you might find something that was missed in a previous scan by running nmap with different options.

TCP/UDP/SCTP Scanning

We’re taking another step higher in the protocol stack using TCP/UDP/SCTP scanning. In this type of scan, we’re using these protocols to probe a single port. While this scan has the highest level of overhead (and will be slower) it an advantage of being able to scan a host over the internet.

As with the previous scans, we’re going to again use the -P option:

  • -PY: SCTP INIT
  • -PS: TCP SYN
  • -PA: TCP ACK
  • -PU: UDP

Let’s try a SYN ping scan:

$ sudo nmap -sn --disable-arp-ping -PS 192.168.186.0/24
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-20 15:04 PDT
Nmap scan report for 192.168.186.2
Host is up (0.0020s latency).
MAC Address: 00:50:56:FC:7E:8B (VMware)
Nmap scan report for 192.168.186.71
Host is up (0.0012s latency).
MAC Address: 00:0C:29:91:6A:82 (VMware)
Nmap scan report for 192.168.186.72
Host is up (0.0060s latency).
MAC Address: 00:0C:29:19:6C:BF (VMware)
Nmap scan report for 192.168.186.74
Host is up (0.0018s latency).
MAC Address: 00:0C:29:01:40:16 (VMware)
Nmap scan report for 192.168.186.93
Host is up.
Nmap done: 256 IP addresses (5 hosts up) scanned in 4.88 seconds

nmap chooses ports to probe by default but you can specify them by putting a number or range next to the scan type:

$ sudo nmap -sn --disable-arp-ping -PU53 192.168.186.0/24
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-20 15:06 PDT
Nmap scan report for 192.168.186.72
Host is up (0.0018s latency).
MAC Address: 00:0C:29:19:6C:BF (VMware)
Nmap scan report for 192.168.186.74
Host is up (0.0017s latency).
MAC Address: 00:0C:29:01:40:16 (VMware)
Nmap scan report for 192.168.186.93
Host is up.
Nmap done: 256 IP addresses (3 hosts up) scanned in 9.61 seconds

I chose port 53 (DNS) to perform my UDP scan because it’s super common. As you can see, I got fewer results with this and it took a lot longer. Again, it’s important to try different ports and protocols with these scans. For example, if I knew an organization had websites up and running, I might try ports 80 and 443 to see what servers they have online. If I’m scanning a business network with a lot of users, they may be using AD to manage their systems, so I might try port 389 or 636 for LDAP.

Context matters!

Port Scanning

Now that we know what hosts are up and running, we can check what services are running on these hosts. Open ports present an opportunity for access, so it’s important to know what can be potentially accessed or exploited.

We saw that nmap scans ports by default using full TCP connections, or just by using TCP SYN packets when running as root.

To specify a scan option, we can use the -s argument with nine different options. We’ll only go over a few but I’ll list some common scans:

  • -sS: SYN Scan (default)
  • -sT: TCP Scan
  • -sU: UDP Scan
  • sY: SCTP Scan

This sort of syntax should be familiar to you as it is very similar to the various ping scan options with -P. I used a SYN scan to scan my metasploitable machine:

$ sudo nmap -sS ms2
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-22 21:16 PDT
Nmap scan report for ms2 (192.168.186.131)
Host is up (0.0029s latency).
Not shown: 977 closed tcp ports (reset)
PORT     STATE SERVICE
21/tcp   open  ftp
22/tcp   open  ssh
23/tcp   open  telnet
25/tcp   open  smtp
53/tcp   open  domain
80/tcp   open  http
111/tcp  open  rpcbind
139/tcp  open  netbios-ssn
445/tcp  open  microsoft-ds
512/tcp  open  exec
513/tcp  open  login
514/tcp  open  shell
1099/tcp open  rmiregistry
1524/tcp open  ingreslock
2049/tcp open  nfs
2121/tcp open  ccproxy-ftp
3306/tcp open  mysql
5432/tcp open  postgresql
5900/tcp open  vnc
6000/tcp open  X11
6667/tcp open  irc
8009/tcp open  ajp13
8180/tcp open  unknown
MAC Address: 00:0C:29:7B:A1:58 (VMware)

Nmap done: 1 IP address (1 host up) scanned in 0.38 seconds

nmap was able to find quite a bit of open ports. I like using the SYN scan because it combines the reliability of a TCP scan without being as slow (this scan only took 0.38 seconds) because it doesn’t establish a full connection.

That being said, it’s important to try other scan types:

$ sudo nmap -sU -p 53,1000-1010 ms2
Starting Nmap 7.93 ( https://nmap.org ) at 2023-03-22 21:20 PDT
Nmap scan report for ms2 (192.168.186.131)
Host is up (0.0018s latency).

PORT     STATE         SERVICE
53/udp   open          domain
1000/udp closed        ock
1001/udp open|filtered unknown
1002/udp closed        unknown
1003/udp closed        unknown
1004/udp closed        unknown
1005/udp open|filtered unknown
1006/udp closed        unknown
1007/udp closed        unknown
1008/udp open|filtered ufsd
1009/udp closed        unknown
1010/udp closed        surf
MAC Address: 00:0C:29:7B:A1:58 (VMware)

Nmap done: 1 IP address (1 host up) scanned in 4.87 seconds

This time, I told nmap to do a UDP scan. You’ll also notice that I specified what ports to scan using the -p option. You can tell nmap to scan a range, comma-separated values, ports specified by name, or every port using -p-.

Even though I scanned only 11 ports, it took a little longer than the SYN scan. Outside of finding ports that were open and closed, nmap claimed some ports were open|filtered. This just mean that nmap couldn’t tell if the port was truly open, or is protected by some sort of firewall.

Like I said when doing host discovery, it’s important to experiment with different scan methods. There are more than just the few types I listed, so feel free to check those out at nmap.org.

Output

As a penetration tester, it’s important to keep track of your scan results. Not only for you to go back over (instead of repeating scans), but for reporting purposes as well.

nmap makes this a snap with the -o option. There are few options that go along with -o:

  • -oN: normal output
  • -oX: XML output
  • -oG: grepable output
  • -oA: output in all formats

I usually stick with outputting my scan results in every format because they’re all useful in their own ways. I like normal output for reading over myself. Grepable output is great because it puts each host on a single line which makes it easier to grep by host (hence the name). Finally, XML output is extremely useful for web-based visualization tools or storing the results in a database. For example, I love feeding my XML scan results to metasploit, which makes it really easy to organize and search through my results.

Conclusion

If you made it this far, you can now add an invaluable tool to your hacking arsenal. This was a lengthy post so give your self a huge pat on the back for reading it all. Your learning is not over yet, though. To really make this stick, download nmap and play around on your own network. It’s fun and is a great way to make sure you retain what you learned. Otherwise (in my own experience) the knowledge won’t stick!

References