nmap
The standard network scanner. Beyond port discovery, its NSE scripting engine fingerprints and probes IoT and OT services like Modbus, MQTT, DNP3 and BACnet.
Nmap is the standard network scanner, and with its NSE scripting engine it fingerprints and probes IoT and OT services well beyond simple port discovery.
What it is
At its core nmap sends crafted packets and reasons about the replies to answer three questions: which hosts are up, which ports are open, and what is listening behind them. Host discovery finds the devices, a port scan finds the open services, -sV version-detection identifies the software, and the Nmap Scripting Engine runs Lua scripts (in /usr/share/nmap/scripts/) that speak specific protocols to enumerate them in depth. It is the reconnaissance workhorse: fast, scriptable, and packaged everywhere.
Why it matters
Before touching a device you need to know what it exposes. On an IoT or OT network the interesting services are exactly the ones a generic scanner skips: an unauthenticated Modbus unit on 502, an MQTT broker with anonymous access on 1883, a BACnet controller on UDP 47808. Nmap's OT scripts pull the device identity, register maps and topic lists straight out of these protocols, turning a bare IP into a described, prioritised target list, all read-only and quick.
How to use it
# Version scan the common OT/IoT ports across a subnet
nmap -sV -p 502,1883,8883,4840,44818,20000 192.168.1.0/24
# Enumerate a Modbus unit: device id, slave ids, coil/register hints
nmap -sV --script modbus-discover -p 502 192.168.1.100
# Sample MQTT topics from a broker that allows anonymous subscribe
nmap --script mqtt-subscribe -p 1883 192.168.1.100
# BACnet building-automation controllers speak over UDP
nmap -sU -p 47808 --script bacnet-info 192.168.1.0/24
# Broad, quiet sweep first: which hosts are even alive?
nmap -sn 192.168.1.0/24
Ports worth remembering: 502 Modbus, 1883/8883 MQTT, 4840 OPC UA, 44818 EtherNet/IP, 20000 DNP3, 47808 BACnet. Pair -sV with --script to get the service version and the protocol-specific detail in a single pass.
Reading a scan
Open ports are the attack surface; -sV output tells you the exact firmware or broker version, which you cross-reference against known CVEs. A filtered result means a firewall is dropping probes silently, an open port with no version banner is a candidate for an NSE script or a manual tshark capture, and a device that answers a Modbus-discover with a full register map is telling you it has no authentication at all.
Pitfalls
- Fragile OT gear. PLCs and legacy field devices can hang or fault under an aggressive scan. On a live industrial network, throttle hard (
-T2,--scan-delay), scan narrow port lists, and never blast-A/-T5at production controllers. - UDP services are easy to miss. BACnet, some DNP3 and discovery protocols are UDP; a default TCP-only scan skips them entirely, so add
-sUon the right ports. - Version detection is a guess.
-sVinfers from banners and probe responses; confirm anything load-bearing with a protocol-specific script or a real capture. - NSE scripts can be intrusive. Some do more than enumerate (they write or subscribe). Read what a script does before running it against equipment you do not own.
What it pairs with
Nmap is the first, read-only recon pass. Its output feeds wireshark, which watches the services nmap found actually talk; protocol tools like pymodbus or mosquitto then interact with the specific units it enumerated; and on the client-to-cloud side, mitmproxy picks up where an nmap-discovered HTTPS endpoint leaves off.