Berkner Tech

Nmap for Embedded and IoT Pen Testing: A Field Guide

Nmap reconnaissance pipeline for embedded and IoT penetration testing, from host discovery to NSE scripts

If you cannot see what a device is exposing, you cannot secure it. Nmap is the first tool I reach for on almost every embedded engagement. It settles the question you have to answer before anything else: what is this thing actually listening on? Here is how I use it against connected products, along with the things that catch people who learned nmap on cloud servers and then bring those habits to hardware.

Why Nmap Still Matters on Embedded Targets

A connected product is just a host on a network: a thermostat, a vehicle gateway, a smart lock controller, a PLC. It has an IP stack, it has services, and every one of those services is attack surface. Nmap maps that surface quickly. The difference with embedded is that the host on the other end is often a microcontroller with a few hundred kilobytes of RAM and a TCP stack that was never stress tested. Scan it the way you would scan a Linux server and you can knock it over. That is its own kind of finding, but rarely the one you set out to produce.

Step 0: Install and Confirm Scope

Nmap ships in every package manager:

# Debian, Ubuntu, Kali
sudo apt install nmap

# macOS
brew install nmap

# confirm the install
nmap --version
Example output
Nmap version 7.95 ( https://nmap.org )
Platform: x86_64-pc-linux-gnu
Compiled with: liblua-5.4.6 openssl-3.0.13 libssh2-1.11.0 zlib-1.3
Available nsock engines: epoll poll select

Before a single packet goes out, get the scope in writing. Which IPs, which subnets, what hours, and a clear acknowledgement that a fragile device might reboot or hang. On embedded work that last point is not boilerplate. It is the difference between a finding and an incident.

Step 1: Host Discovery

Start by finding what is alive. A ping sweep across the subnet with no port scan:

# -sn = ping scan, no ports
nmap -sn 192.168.1.0/24
Example output
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-01 09:14 EDT
Nmap scan report for 192.168.1.1
Host is up (0.0012s latency).
Nmap scan report for 192.168.1.50
Host is up (0.0089s latency).
Nmap done: 256 IP addresses (2 hosts up) scanned in 2.74 seconds

Here is the embedded gotcha. Plenty of devices do not answer ICMP echo. A lean stack may ignore ping entirely, or a firewall in front of it drops the request, and nmap then writes the host off as down and never scans it. If you know the device is there, skip discovery and force the scan with -Pn:

# treat the host as online; scan it regardless of ping
nmap -Pn 192.168.1.50
Example output
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-01 09:16 EDT
Nmap scan report for 192.168.1.50
Host is up (0.0091s latency).
Not shown: 997 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
8443/tcp open  https-alt

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

Step 2: Port Scanning With the SYN Scan

The default workhorse is the TCP SYN scan (-sS). It sends a SYN, watches the reply, and never completes the handshake, which makes it fast and relatively gentle. It needs root:

# SYN scan, all 65,535 TCP ports
sudo nmap -sS -p- 192.168.1.50
Example output
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-01 09:20 EDT
Nmap scan report for 192.168.1.50
Host is up (0.0090s latency).
Not shown: 65531 closed tcp ports (reset)
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
8443/tcp  open  https-alt
9999/tcp  open  abyss

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

That -p- matters more on embedded than almost anywhere else. Note the port the full sweep caught that a default scan would miss: 9999. Vendors like to park services on non-standard ports, such as a debug shell on 9999, a proprietary management protocol on 50000, or a forgotten web interface on 8443. The default scan only checks the top 1,000 ports and walks right past them. Scan the full range first, then narrow.

Nmap port states explained: open, closed, and filtered ports on an embedded device

Reading the States Correctly

An open port is live attack surface, and that is where the work is. A closed port means the device is up but nothing is listening on that number. The state that trips people up is filtered. Nmap got no usable reply, which usually means something is dropping the probe rather than that the port is dead. On embedded gear, filtered often just means the device’s thin stack did not answer the way nmap expected, so the right move is a different probe or slower timing rather than a shrug.

Step 3: Service and Version Detection

Knowing port 8080 is open tells you almost nothing. Knowing it is a GoAhead 2.5 embedded web server tells you which CVEs to pull. That is what -sV does:

# probe open ports to fingerprint the service and version
nmap -sV 192.168.1.50

# push harder when banners are stubborn (0-9, 9 is most aggressive)
nmap -sV --version-intensity 9 192.168.1.50
Example output
PORT      STATE SERVICE   VERSION
22/tcp    open  ssh       Dropbear sshd 2020.81 (protocol 2.0)
80/tcp    open  http      GoAhead WebServer 2.5.0
8443/tcp  open  ssl/https GoAhead WebServer 2.5.0
9999/tcp  open  telnet    BusyBox telnetd
Service Info: Device: embedded

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.47 seconds

Embedded services often hand back terse or non-standard banners, so version detection will not always land cleanly. Even a partial fingerprint combined with the open-port pattern is frequently enough to identify the firmware family.

Anatomy of a careful nmap scan command for fragile embedded device TCP stacks

Step 4: The Nmap Scripting Engine (NSE)

NSE is where nmap stops describing and starts probing. The -sC flag runs the default script set, which covers safe, useful enumeration such as TLS configuration, HTTP titles, and default-credential checks:

# default scripts plus version detection in one pass
nmap -sC -sV 192.168.1.50

# point a specific category at the target
nmap --script vuln 192.168.1.50

# or run a single script, such as enumerating SNMP, common on IoT gear
nmap -sU -p 161 --script snmp-info 192.168.1.50
Example output
# nmap -sC -sV
PORT     STATE SERVICE   VERSION
80/tcp   open  http      GoAhead WebServer 2.5.0
|_http-title: Device Login
| http-auth:
|   HTTP/1.1 401 Unauthorized
|_  Basic realm=Embedded WebServer
8443/tcp open  ssl/https GoAhead WebServer 2.5.0
| ssl-cert: Subject: commonName=192.168.1.50
|_Not valid after:  2049-01-01T00:00:00
|_ssl-date: TLS randomness does not represent time

# nmap --script vuln
| http-slowloris-check:
|   VULNERABLE: Slowloris DoS attack
|_    State: LIKELY VULNERABLE

# nmap -sU -p 161 --script snmp-info
| snmp-info:
|   snmpEngineTime: 4d23h11m
|_  sysDescr: Linux gateway 4.9.0 #1 SMP armv7l

One caution. Not every NSE script is gentle. The vuln and brute categories can hammer a device hard enough to crash it. Read what a script does before you run it against something fragile, and skip the aggressive -A flag on constrained hardware, since it bundles OS detection, version detection, scripts, and traceroute into one heavy burst.

Step 5: Do Not Forget UDP

IoT lives on UDP: CoAP, SNMP, mDNS, DTLS, and proprietary discovery protocols. TCP-only scanning misses half the picture. UDP scanning is slow, so scope it to the ports that matter:

# top 50 UDP ports, a sane starting point
sudo nmap -sU --top-ports 50 192.168.1.50

# or target known IoT UDP services directly
sudo nmap -sU -p 53,123,161,5353,5683 192.168.1.50
Example output
PORT     STATE         SERVICE
53/udp   open          domain
123/udp  open          ntp
161/udp  open          snmp
1900/udp open|filtered upnp
5353/udp open          mdns

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

Step 6: Scan Like the Stack Is Fragile

This is the embedded-specific skill. A desktop scan fires probes as fast as the network allows. A microcontroller with one socket buffer and a cooperative scheduler cannot keep up, so it drops packets, which shows up as false filtered results, or it watchdog-reboots in the middle of your scan. Slow down on purpose:

# -T2 = polite timing; pace the probes; cap retries
sudo nmap -sS -p- -T2 --scan-delay 100ms --max-retries 1 192.168.1.50

# cap the packet rate explicitly for the most delicate targets
sudo nmap -sS --max-rate 50 192.168.1.50
Example output
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-01 10:02 EDT
Nmap scan report for 192.168.1.50
Host is up (0.011s latency).
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
8443/tcp  open  https-alt
9999/tcp  open  abyss

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

Same four open ports as the fast scan, but it took 642 seconds instead of 78. That trade is almost always worth it: a scan that returns clean, reproducible results and leaves the device running beats a fast scan that crashes the target. Timing templates run from -T0 (paranoid) through -T5 (insane), and -T2 is a sensible default for hardware you do not fully trust to survive.

Step 7: Save Everything for the Report

Capture output in all formats up front so you never have to re-scan to recover a result:

# -oA writes .nmap (human), .xml (tooling), and .gnmap (grep) at once
sudo nmap -sS -sV -p- -T2 -oA embedded_scan_192.168.1.50 192.168.1.50
Example output
Nmap done: 1 IP address (1 host up) scanned in 95.44 seconds

$ ls embedded_scan_192.168.1.50.*
embedded_scan_192.168.1.50.gnmap
embedded_scan_192.168.1.50.nmap
embedded_scan_192.168.1.50.xml

Putting It Together

A realistic first-contact sequence against a single device on a bench:

# 1. full TCP sweep, gently, skip ping, save output
sudo nmap -Pn -sS -p- -T2 --scan-delay 100ms -oA tcp_full 192.168.1.50

# 2. enumerate just the open ports it found
sudo nmap -Pn -sV -sC -p 22,80,8443,9999 -oA tcp_enum 192.168.1.50

# 3. check the usual IoT UDP suspects
sudo nmap -Pn -sU --top-ports 50 -oA udp_top 192.168.1.50
Example output
# 1. tcp_full   ->  full SYN sweep
Discovered open ports: 22, 80, 8443, 9999/tcp

# 2. tcp_enum   ->  service and default scripts on the open ports
22/tcp   open  ssh       Dropbear sshd 2020.81
80/tcp   open  http      GoAhead WebServer 2.5.0
8443/tcp open  ssl/https GoAhead WebServer 2.5.0
9999/tcp open  telnet    BusyBox telnetd

# 3. udp_top    ->  common IoT UDP services
161/udp  open  snmp
5353/udp open  mdns

$ ls *.nmap *.xml *.gnmap    # nine result files, three per scan

With those three commands you move from knowing there is a device at .50 to having a documented inventory of every service it exposes. That inventory is the foundation for everything that follows in the assessment.

Where This Fits

Nmap is reconnaissance, not the whole assessment. It tells you where the doors are, not whether the locks hold. Mapping the network surface is the first move in a product security assessment or a penetration test. What comes next is firmware analysis, fault injection, protocol fuzzing, and tracing each finding back to the design decision that allowed it. If you are shipping a connected product and want a clear picture of what it exposes, and what to do about it, that is the kind of work we do at Berkner Tech.


References and Further Reading