Hacking Like Trinity: Using Nmap as an Engineering Tool
We all wish we could hack like Trinity, the hacker who cracked the IRS database. Alas, hacking isn’t quite as simple as in the Matrix world.
Still, The Matrix Reloaded deserves some credit for getting one hacking scene surprisingly right. During the attack on the power plant near the end of the movie, if you look closely at Trinity’s terminal, we see a tool almost every hacker, sysadmin, or network engineer eventually comes across: nmap.
And it wasn’t random terminal decoration either. Trinity runs Nmap 2.54BETA25 against the target, identifies an SSH service, and then uses the fictional sshnuke tool to exploit a real SSHv1 CRC32 vulnerability that existed at the time.
The actual Nmap command shown in the movie was:
1
nmap -v -sS -O 10.2.2.2
Pretty reasonable, actually. Although today I would probably want -sV in there too.
I already touched on this scene in my Matrix saga review, but Nmap deserves a post of its own because, outside Hollywood, it is an extremely useful engineering tool.
nmap, or Network Mapper, is commonly described as a port scanner. That is correct, but also undersells it considerably. It can help answer questions such as:
- What devices are actually alive on this network?
- Which ports can I reach from here?
- What services are listening behind those ports?
- Which versions appear to be running?
- What operating system or type of device am I probably looking at?
- Is a firewall dropping my traffic or is nothing listening?
- Did a deployment accidentally expose something?
- Does my network segmentation actually work?
- Has the externally visible surface of this server changed since last week?
That last group of questions is where Nmap becomes much more interesting to me.
This isn’t really about “hacking.” It is about comparing what we think our infrastructure looks like with what the network actually exposes.
A small but important disclaimer: Nmap is an active network scanner. Only scan systems you own or networks where you have explicit authorization to do so. Even perfectly legitimate scans create traffic, may trigger IDS/IPS systems, and can occasionally upset particularly fragile equipment. Don’t point random scan commands at infrastructure that isn’t yours.
And I mean that. Broad or aggressive scans can still upset particularly fragile consumer routers, legacy appliances, or stateful firewalls. Scope and scan timing matter, especially in sensitive environments.
Understanding the Target First
Before scanning anything, it is worth understanding how Nmap specifies targets.
A single machine is straightforward:
1
nmap 10.20.30.15
But Nmap also understands CIDR notation, so we can target an entire subnet:
1
nmap 10.20.30.0/24
This tells Nmap to consider every address in that /24.
For larger environments, targets can come from a file:
1
nmap -iL targets.txt
And exclusions can be useful when a range contains equipment we intentionally do not want to touch:
1
nmap 10.20.30.0/24 --exclude 10.20.30.1,10.20.30.254
One lesser-known command I like here is:
1
nmap -sL 10.20.30.0/24
-sL is a list scan.
It does not actually discover which machines are online. It simply expands the targets Nmap believes you asked it to scan and, depending on DNS settings, may resolve their names. That might sound useless, but it is actually a very good sanity check before starting a larger scan.
Before sending packets to hundreds or thousands of addresses, make sure you typed the correct network. Typos in CIDR notation become considerably less funny when they involve networks you don’t own.
Finding What Is Alive
If what we actually want is a list of active nodes, then we want host discovery:
1
nmap -sn 10.20.30.0/24
-sn performs host discovery without following it with a port scan.
On a local Ethernet network, a privileged Nmap process normally uses ARP discovery automatically, which is particularly effective for finding neighboring devices.
We can make that explicit with:
1
sudo nmap -sn -PR 10.20.30.0/24
This is extremely useful when connecting to an unfamiliar LAN.
Maybe you inherited a network without proper documentation. Maybe DHCP says twenty addresses are allocated but you suspect some devices have static IPs. Maybe somebody installed a printer in 2019 and nobody knows where it lives anymore. A quick discovery scan gives us a starting inventory.
On a local network, Nmap may also give us MAC addresses and vendor information, which can provide surprisingly useful hints about what those unknown devices actually are. Of course, “host not discovered” does not necessarily mean “host does not exist.” Firewalls can block discovery probes.
That brings us to:
1
nmap -Pn 10.20.30.15
-Pn tells Nmap to skip host discovery and simply assume the target is online.
This is useful when a machine does not respond to Nmap’s normal discovery probes but you know it exists.
It is important to understand what -Pn does not do, though. It does not bypass a firewall. It does not make the scan stealthy.
It simply says:
Stop asking whether the host is alive and try scanning it anyway.
This distinction matters surprisingly often when troubleshooting.
Finding Open Ports
The classic Nmap use case is, of course, finding open ports.
Simply running:
1
nmap 10.20.30.15
scans the target’s most common 1,000 TCP ports. That is an important detail.
It does not scan every possible TCP port.
If I want a quick check of only a few services:
1
nmap -p 22,80,443 10.20.30.15
Or perhaps the 100 most common ports:
1
nmap --top-ports 100 10.20.30.15
If I really want to inspect all TCP ports:
1
sudo nmap -sS -p- 10.20.30.15
-p- means ports 1-65535.
-sS performs a TCP SYN scan. Instead of completing an entire TCP connection, Nmap sends the initial SYN and interprets the response.
With sufficient privileges, this is Nmap’s normal and generally preferred TCP scanning technique.
Without raw-packet privileges, Nmap can instead perform a TCP connect scan:
1
nmap -sT 10.20.30.15
That uses the operating system’s normal connect() mechanism and therefore completes more of the normal TCP handshake.
For most ordinary engineering work, the implementation detail is less important than understanding the result.
Nmap does not simply classify ports as open or closed. Among its possible states are:
openclosedfilteredunfilteredopen|filteredclosed|filtered
And this is an important distinction.
A port being filtered does not mean it is closed.
It means Nmap cannot determine whether it is open because something between us and the target is filtering the traffic.
Usually, that something is exactly what we hope it is. A firewall.
Even more importantly, these states are from our current network perspective.
The exact same server might show:
1
2
22/tcp open
443/tcp open
from the management VLAN, while showing:
1
2
22/tcp filtered
443/tcp open
from another subnet.
And from the public internet:
1
2
22/tcp filtered
443/tcp open
That isn’t inconsistent. That is segmentation working.
Asking Nmap Why
One of my favorite troubleshooting options is also an extremely simple one:
1
nmap --reason -p 22,443 10.20.30.15
--reason asks Nmap to explain why it classified a host or port the way it did.
For example, it may tell you that a port was marked closed because it received a TCP RST, or that a host was considered alive because it answered an ARP request.
This is much more useful than staring at:
1
filtered
and trying to guess what happened.
Networking becomes considerably easier once you stop asking “does it work?” and start asking “what packet did I send, and what came back?”
Ports Are Not Services
Finding port 22/tcp open does not necessarily mean we found SSH. I mean, it probably is SSH, but the port number itself is only a convention.
Nothing prevents someone from running SSH on port 2222, HTTP on 8080, or some completely unrelated application on 443. Any decent sysadmin will place SSH access behind a VPN or otherwise restrict it to a management network. Changing the default SSH port can also be useful for hygiene and reducing log noise, as I demonstrate in my Ansible Script For Basic Server Security post, but it should not be mistaken for an access-control measure. Nmap will still find it when scanning all ports with -p-, and -sV can then help identify the service listening there.
By default, Nmap can label ports using its service database based largely on well-known port assignments.
To actually probe the listening service, we can enable version detection:
1
nmap -sV 10.20.30.15
Or target specific ports:
1
nmap -sV -p 22,80,443,8443 10.20.30.15
Now Nmap actively probes the services and attempts to identify what is really listening.
We may get something along the lines of:
1
2
3
4
5
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH
80/tcp open http nginx
443/tcp open ssl/http nginx
8443/tcp open ssl/http Jetty
This is much more useful operational information.
Imagine replacing a web server and expecting the new machine to expose only Nginx on 443. A normal port scan tells us port 443 is open. A version scan can tell us that 8443 is also open and apparently running an application server we never intended to expose.
That is the difference between checking whether the deployment works and checking whether the deployment matches the architecture. Version detection is also useful for patch management and inventory, although its results should be treated as identification rather than absolute truth. Nmap is fingerprinting remote behavior. It is not logging into the machine and reading the package database.
Don’t Forget UDP
TCP gets most of the attention because web servers, SSH, databases, and many other common services use it. But infrastructure also relies heavily on UDP.
Think:
- DNS
- NTP
- SNMP
- IPsec
- various discovery protocols
- plenty of network appliances
A targeted UDP scan looks like:
1
sudo nmap -sU -p 53,123,161,500,4500 10.20.30.15
UDP scanning is slower and more ambiguous than TCP scanning because a perfectly healthy UDP service may simply not reply to an unexpected packet.
That is why UDP results often contain:
1
open|filtered
Nmap genuinely cannot always distinguish between a service silently accepting traffic and a firewall silently dropping it.
Adding version detection can sometimes help:
1
sudo nmap -sU -sV -p 53,123,161 10.20.30.15
Ignoring UDP entirely is an easy way to create a very incomplete network inventory.
Operating System Detection
Then there is the option Trinity uses in Reloaded:
1
sudo nmap -O 10.20.30.15
-O enables operating-system detection.
Nmap sends several carefully selected probes and analyzes characteristics of the responses, comparing them against known TCP/IP stack fingerprints.
Depending on how strong the match is, it can infer things such as:
- operating system family,
- approximate OS generation,
- device vendor,
- device type,
- and sometimes even an approximate uptime.
So yes, OS detection does considerably more than printing “Linux” or “Windows.” It can sometimes tell us that something is probably a router, switch, firewall, printer, general-purpose computer, or another device type.
But again, this is fingerprinting. It is an educated remote inference, not divine revelation.
OS detection also works best when Nmap can find both an open and a closed TCP port to compare behavior.
If I only want Nmap to attempt detection where conditions are reasonable:
1
sudo nmap -O --osscan-limit 10.20.30.0/24
That saves it from wasting effort on targets unlikely to produce a useful result.
The Nmap Scripting Engine
This is where Nmap stops looking like “just a port scanner.” Nmap includes the Nmap Scripting Engine, or NSE. NSE scripts can perform additional discovery and protocol-specific checks against identified services.
For example, retrieving information from a TLS certificate:
1
nmap -p 443 --script ssl-cert 10.20.30.15
Inspecting an SSH host key:
1
nmap -p 22 --script ssh-hostkey 10.20.30.15
Or simply checking the title returned by a web service:
1
nmap -p 80,443 --script http-title 10.20.30.15
These are the sort of NSE uses I find genuinely useful. They allow us to ask a more specific question about a service without bringing in an entirely different tool.
NSE is extremely powerful, though, and that means it deserves some caution. There are scripts for discovery, authentication checks, vulnerability detection, brute force, exploitation, denial of service, and plenty of other purposes. Running arbitrary categories against production infrastructure is not something I would recommend.
Even:
1
nmap -sC 10.20.30.15
which runs Nmap’s default script set, should be understood before being thrown at a production range.
Some default scripts can be somewhat intrusive. I generally prefer explicitly selecting the script that answers the question I am asking. The principle is the same as everywhere else in engineering: don’t deploy the entire toolbox when you only needed a screwdriver.
The Famous -A
There is, of course, the convenient:
1
sudo nmap -A 10.20.30.15
-A enables several features together:
- OS detection,
- version detection,
- default NSE scripts,
- traceroute.
It is very useful in a lab or when investigating a single machine. But I rarely think “enable everything” is the best starting point for production infrastructure. Explicit commands make it clearer what traffic we are generating and what question we are trying to answer. There is also a difference between discovering information because we need it and discovering information simply because we can.
Network Paths
Nmap can also perform traceroute:
1
nmap -sn --traceroute 10.20.30.15
This can be handy when troubleshooting routed environments. If I expect traffic to reach a remote server through a WireGuard tunnel, for example, discovering that the path is instead leaving through another gateway immediately gives me something useful to investigate. This becomes especially interesting in multi-subnet environments where routing, firewall policy, and VPN paths interact.
The port may be perfectly healthy. The problem may simply be that the packet never reached the network we thought it did.
Testing Segmentation
This is probably my favorite real-world Nmap use case. In my posts about WireGuard and OPNsense and limiting attack surface, I repeatedly make the argument that management traffic should exist on a separate and restricted network plane.
And how do we verify that? We test it.
Suppose a server is expected to expose:
1
2
3
4
5
6
7
8
9
Management network:
22/tcp
443/tcp
Application network:
443/tcp
Public internet:
443/tcp
From the management network:
1
nmap -Pn -p 22,443 10.20.30.15
From the application network:
1
nmap -Pn -p 22,443 10.20.30.15
And, where appropriate, from an external machine we control:
1
nmap -Pn -p 22,443 server.example
Since this is a known server and the question is specifically whether those ports are reachable from each network position, -Pn is useful here. We are not trying to rediscover whether the server exists. We want Nmap to attempt the port checks even if host-discovery probes happen to be filtered.
The interesting part is not running the command. It is comparing the results. Your firewall configuration, Terraform, documentation, and architecture diagram describe intent. The scan describes observed reality from that network position. Both should agree.
If SSH is supposed to be management-only and appears reachable from the user network, that is something I want to know immediately. This makes Nmap a very simple validation tool for segmentation.
Checking a Deployment
The same principle applies after deploying a new server.
Suppose I intend to expose only HTTP and HTTPS:
1
sudo nmap -sS -Pn --open -p- 10.20.30.15
--open filters the output so we only see open or potentially open ports.
If the result is:
1
2
80/tcp open
443/tcp open
Excellent.
If I unexpectedly see:
1
3306/tcp open
then I have a database listening where I probably didn’t intend it to be reachable. Maybe the firewall rule is wrong. Maybe the service bound to 0.0.0.0 instead of localhost. Maybe somebody temporarily opened a port while debugging and forgot about it. Whatever the explanation, I would much rather discover it immediately after deployment than three years later during an incident.
This ties directly into attack-surface management. Every unexpected open service is another thing that needs to be understood, maintained, patched, monitored, and defended.
Saving Scans and Finding Drift
Running Nmap interactively is useful. Saving the results is considerably more useful.
One option worth remembering is:
1
sudo nmap -sS -sV -p 22,80,443 -oA scans/server-baseline 10.20.30.15
-oA saves the scan in multiple Nmap output formats, including XML.
That means we can create another scan after a deployment or configuration change:
1
sudo nmap -sS -sV -p 22,80,443 -oA scans/server-after 10.20.30.15
And compare the XML files using ndiff:
1
ndiff scans/server-baseline.xml scans/server-after.xml
This is probably one of the least flashy Nmap features and one of the most useful for actual engineering.
ndiff can show changes such as:
- hosts appearing or disappearing,
- ports opening or closing,
- service versions changing,
- OS detection changing,
- script output changing.
This turns a network scan into a primitive form of configuration-drift detection. And that can easily be automated. Run a controlled scan periodically against infrastructure you own, store the results, compare them against the previous baseline, and alert when something unexpectedly becomes reachable.
Now Nmap is no longer a cool command we run manually. It is part of operational assurance.
Nmap During Migrations
Another practical case is infrastructure migration.
Suppose I am replacing a server. Before cutover, I can scan the old one:
1
sudo nmap -sS -sV -p- old-server
Then scan the new one:
1
sudo nmap -sS -sV -p- new-server
If the new machine is supposed to expose exactly the same network services, comparing both results gives us a very quick sanity check.
This obviously does not prove the application behaves correctly. But it can immediately expose mistakes like:
- forgotten services,
- missing listeners,
- management ports accidentally exposed,
- services bound to the wrong interfaces,
- completely different service versions.
The same logic applies after firewall migrations, VLAN changes, load-balancer moves, cloud migrations, or VPN redesigns.
Nmap is very good at answering:
From here, what can I actually reach now?
That question is useful during almost every infrastructure change.
Don’t Forget IPv6
There is another increasingly important source of false confidence: scanning only IPv4.
If the environment is dual stack, then the attack surface is dual stack too.
Nmap supports IPv6:
1
nmap -6 -sV server.example.internal
A service properly firewalled on IPv4 but unintentionally reachable over IPv6 is still reachable.
The fact that nobody remembered to check the second protocol does not make it secure. If IPv6 is enabled in the environment, include it in the validation process.
What Nmap Does Not Tell You
- An open port is not automatically a vulnerability.
- A closed port is not automatically security.
- A service version identified by Nmap is not guaranteed to be correct.
- An OS fingerprint is not proof of the operating system.
- A filtered port does not tell us exactly which device performed the filtering.
- And a clean Nmap scan absolutely does not prove that an application is secure.
Nmap measures network-visible behavior. It is extremely good at that job. But application security, authorization, identity, dependency security, business logic, secrets management, and dozens of other attack surfaces exist above or outside the network layer.
That is exactly why I prefer treating Nmap as an engineering instrument rather than some magical vulnerability detector. Ask it precise questions and interpret the answers within their limits.
A Practical Nmap Workflow
If I were checking a newly deployed server, my flow would normally look something like this.
First, confirm the host:
1
nmap -sn 10.20.30.15
Then check intended ports:
1
nmap --reason -p 22,80,443 10.20.30.15
Identify the services:
1
nmap -sV -p 22,80,443 10.20.30.15
If I need a complete exposure check:
1
sudo nmap -sS -Pn -p- --open 10.20.30.15
If relevant, inspect UDP services:
1
sudo nmap -sU -p 53,123,161,500,4500 10.20.30.15
If I genuinely care about the operating system:
1
sudo nmap -O 10.20.30.15
Then repeat the relevant port checks from other network zones.
Finally, save a baseline:
1
sudo nmap -sS -sV -Pn -p- -oA scans/server-baseline 10.20.30.15
Simple. No Matrix green rain required.
Back to Trinity
What makes the Matrix Reloaded scene good isn’t that Trinity types some magic hacking command and suddenly owns a power plant.
Nmap does not hack the machine for her. It tells her what is there. She scans the target, sees an exposed SSH service, identifies an opportunity, and only then moves to exploitation. That sequence is far more realistic than most Hollywood hacking.
For us, however, the useful lesson ends before the exploit. A sysadmin looking at the exact same information can ask:
Why is this service exposed in the first place?
Which is arguably the more valuable question.
If the CityPower administrators had regularly scanned their own environment, identified the outdated SSH service, patched it, restricted its reachability, or placed management access behind a private network, Trinity’s very cinematic evening would have become considerably less interesting. Perhaps Neo would have never made it to the Architect’s room.
So one can only hope AI sysadmins won’t patch everything. For humanity’s sake.
Final Thoughts
Nmap is one of those tools that deserves to be in almost every sysadmin and network engineer’s toolbox. Not because it makes us hackers, but simply because networks become surprisingly difficult to reason about once they grow.
Firewall rules accumulate. Services move. Temporary exceptions become permanent. Machines gain additional interfaces. Developers bind things to 0.0.0.0. Old systems remain online. IPv6 quietly exists. Documentation drifts away from reality.
Nmap gives us a simple way to look at that reality from the network itself. Not the documentation, which is often nonexistent, as we all know, but the hard, observable, measurable reality. It can discover systems, inspect exposure, identify services, fingerprint devices, validate segmentation, troubleshoot filtering, inspect specific protocols, and provide repeatable scan data that can be compared over time.
Most importantly, it lets us validate one of the fundamental questions behind attack-surface management:
What can actually reach this system, and what does the system expose back?
That is much more useful than pretending to be Trinity. The black terminal and green text are still optional, although we can all agree it’s the best terminal theme.