Network Scanning with Nmap

Cyber Security · Lesson 12 of 31 7 min read Updated

Nmap ("Network Mapper") is the most widely used network scanner in the world. Defenders use it to check what their own networks expose; testers use it to map an authorised target; auditors use it to prove that firewall rules really work. It is free, open source and installed on Kali.

In this lesson you will scan your own lab network with Nmap: find live hosts, find open ports, identify services and versions, run safe scripts, and save the results. Then you will parse Nmap's XML output with Python and compare two scans to spot new open ports, which is one of the most useful defensive habits you can build.

Where you may scan

Port scanning a network you do not own, without permission, can break the law and the provider's terms, and CERT-In lists "targeted scanning/probing" as a reportable incident. You have two legal targets:

  • Your lab: 192.168.56.0/24, with the target VM at 192.168.56.101.
  • scanme.nmap.org: the Nmap Project gives everyone permission to scan this one host with Nmap. The permission does not cover exploit testing or denial-of-service attacks, and the project asks you not to run more than about a dozen scans against it per day.

All examples below use the lab. Run Nmap with sudo on Kali so it can use raw packets.

Step 1: find live hosts

A ping scan (-sn) checks which addresses respond, without scanning ports:

Terminal
sudo nmap -sn 192.168.56.0/24

On a local network Nmap uses ARP requests, which are very reliable. If a host blocks discovery probes but you know it exists, -Pn tells Nmap to skip discovery and scan it anyway.

Step 2: scan ports

Terminal
sudo nmap 192.168.56.101                  # top 1000 TCP ports, SYN scan
sudo nmap -p 22,80,443,3000,4280 192.168.56.101
sudo nmap -p- 192.168.56.101              # all 65535 TCP ports
sudo nmap -sU --top-ports 20 192.168.56.101   # 20 most common UDP ports

With root privileges the default is a SYN scan (-sS): Nmap sends a SYN and reads the reply without completing the handshake. Without root it falls back to a connect scan (-sT), which completes the handshake using the operating system. UDP scans (-sU) are slow because closed and filtered UDP ports are hard to tell apart.

Nmap reports each port in one of these states:

StateMeaning
openA program accepts connections on this port
closedReachable, but nothing is listening (the host replied with RST)
filteredNo useful reply, usually because a firewall dropped the probe
unfilteredReachable, but Nmap cannot tell if it is open or closed (seen with -sA)
open|filteredNo reply to a UDP or similar probe: could be either

Step 3: identify services and versions

Knowing that port 4280 is open is not enough; you need to know what runs there and which version, because versions map to known vulnerabilities.

Terminal
sudo nmap -sV -sC -O -p 22,3000,4280 192.168.56.101 -oA lab-target
  • -sV talks to each open port to detect the service and version.
  • -sC runs Nmap's default scripts (the same as --script default).
  • -O guesses the operating system from how its network stack behaves.
  • -oA lab-target saves normal (.nmap), XML (.xml) and grepable (.gnmap) output at once.

-A is a shortcut that turns on -sV, -sC, -O and traceroute together. Example output from a lab target (your versions and details will differ):

Output
Nmap scan report for 192.168.56.101
Host is up (0.00041s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 9.6p1 Ubuntu 3ubuntu13.5 (Ubuntu Linux; protocol 2.0)
3000/tcp open  http    Node.js Express framework
4280/tcp open  http    Apache httpd 2.4.65 ((Debian))
|_http-title: Login :: Damn Vulnerable Web Application (DVWA)
MAC Address: 08:00:27:4A:90:11 (Oracle VirtualBox virtual NIC)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Lines starting with | come from scripts. Here http-title printed the page title, which confirms DVWA runs on port 4280.

Nmap Scripting Engine (NSE)

NSE scripts extend Nmap. Each belongs to categories such as safe, default, discovery, version, vuln, intrusive, brute, dos and exploit. The category tells you how risky a script is. In your lab you can try:

Terminal
sudo nmap -p 4280 --script http-title,http-headers 192.168.56.101
sudo nmap -p 443 --script ssl-enum-ciphers 192.168.56.101
sudo nmap -p 4280 --script "safe and http-*" 192.168.56.101

ssl-enum-ciphers lists the TLS versions and cipher suites a server accepts, which is how auditors find servers still allowing old protocols. Scripts in the intrusive, brute, dos and exploit categories can crash services or lock accounts; on a real engagement use them only if the rules of engagement explicitly allow it.

Timing and politeness

-T0 (slowest) to -T5 (fastest) set timing templates; the default is -T3. -T4 is fine inside your lab. On a real network, agree the scanning speed and time window with the owner, and remember that every scan appears in their firewall and IDS logs.

Parsing Nmap XML with Python

XML output (-oX or -oA) is designed for programs. Python's built-in xml.etree.ElementTree can turn it into a report:

PythonRun it yourself
import xml.etree.ElementTree as ET

xml_text = """<nmaprun>
<host><status state="up"/><address addr="192.168.56.101" addrtype="ipv4"/>
<ports>
<port protocol="tcp" portid="22"><state state="open" reason="syn-ack"/><service name="ssh" product="OpenSSH" version="9.6p1"/></port>
<port protocol="tcp" portid="3000"><state state="open" reason="syn-ack"/><service name="http" product="Node.js Express framework"/></port>
<port protocol="tcp" portid="3306"><state state="filtered" reason="no-response"/><service name="mysql"/></port>
<port protocol="tcp" portid="4280"><state state="open" reason="syn-ack"/><service name="http" product="Apache httpd" version="2.4.65"/></port>
</ports></host>
</nmaprun>"""

root = ET.fromstring(xml_text)
for host in root.iter("host"):
    ip = host.find("address").get("addr")
    print("Host", ip)
    for port in host.iter("port"):
        state = port.find("state").get("state")
        svc = port.find("service")
        product = " ".join(filter(None, [svc.get("product"), svc.get("version")]))
        label = port.get("portid") + "/" + port.get("protocol")
        print(f'  {label:<9} {state:<9} {svc.get("name"):<6} {product}'.rstrip())
Output
Host 192.168.56.101
  22/tcp    open      ssh    OpenSSH 9.6p1
  3000/tcp  open      http   Node.js Express framework
  3306/tcp  filtered  mysql
  4280/tcp  open      http   Apache httpd 2.4.65

Defensive use: spotting new open ports

Defenders scan their own servers on a schedule and compare results with a known-good baseline. A port that appears overnight could be a forgotten test service, a misconfiguration or a backdoor.

PythonRun it yourself
baseline = {"192.168.56.101": {22, 3000, 4280}, "192.168.56.103": {3389}}
today = {"192.168.56.101": {22, 3000, 4280, 8080}, "192.168.56.103": set(), "192.168.56.110": {21, 23}}

for host in sorted(set(baseline) | set(today)):
    before, now = baseline.get(host, set()), today.get(host, set())
    if host not in baseline:
        print(f"{host}: NEW HOST with ports {sorted(now)}")
        continue
    for port in sorted(now - before):
        print(f"{host}: port {port} newly OPEN, investigate")
    for port in sorted(before - now):
        print(f"{host}: port {port} now closed, confirm this was planned")
Output
192.168.56.101: port 8080 newly OPEN, investigate
192.168.56.103: port 3389 now closed, confirm this was planned
192.168.56.110: NEW HOST with ports [21, 23]

A new host offering FTP and Telnet, both clear-text protocols, deserves attention straight away.

Common mistakes

  • Scanning outside your scope, even "just one quick scan". Check every target against your authorisation first.
  • Trusting only the default 1000 ports. Services often run on high ports; use -p- when you need certainty.
  • Reading "filtered" as "safe". It means Nmap could not tell; the service may still be reachable from elsewhere.
  • Forgetting UDP. DNS, SNMP and other UDP services are often missed.
  • Not saving output. Use -oA so you have evidence and can compare scans later.

Key points

  • Scan only your lab or scanme.nmap.org, which permits Nmap scans only, about a dozen per day at most.
  • -sn finds live hosts; a SYN scan (-sS) is the default as root; -p- scans all ports; -sU scans UDP.
  • Port states include open, closed, filtered and open|filtered.
  • -sV, -sC, -O (or -A) identify services, run default scripts and guess the OS.
  • NSE categories show how risky a script is; avoid intrusive, brute, dos and exploit scripts unless explicitly allowed.
  • Save XML output and compare scans with a baseline to catch unexpected changes.

Exercise

Modify the XML parser so that it prints only open ports and adds a warning (clear-text protocol) for the services ftp, telnet and plain http. Test it by adding a port 21 with service name ftp and state open to the sample XML.

Show solution
Python
import xml.etree.ElementTree as ET

xml_text = """<nmaprun><host><address addr="192.168.56.101"/><ports>
<port protocol="tcp" portid="21"><state state="open"/><service name="ftp"/></port>
<port protocol="tcp" portid="22"><state state="open"/><service name="ssh"/></port>
<port protocol="tcp" portid="3306"><state state="filtered"/><service name="mysql"/></port>
<port protocol="tcp" portid="4280"><state state="open"/><service name="http"/></port>
</ports></host></nmaprun>"""

CLEAR_TEXT = {"ftp", "telnet", "http"}
for port in ET.fromstring(xml_text).iter("port"):
    if port.find("state").get("state") != "open":
        continue
    name = port.find("service").get("name")
    warning = " (clear-text protocol)" if name in CLEAR_TEXT else ""
    print(port.get("portid"), name + warning)

It prints ports 21, 22 and 4280; FTP and HTTP get the warning, and the filtered MySQL port is skipped.

Quick Quiz

1. What is the Nmap Project's permission for scanme.nmap.org?

2. Which option scans all 65535 TCP ports?

3. A port is reported as "filtered". What does this usually mean?

4. Why should defenders compare today's scan with a baseline?

Learn Cyber Security with a mentor

Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call