Lesson 2 of 10
The Nmap Scripting Engine and its use cases
How NSE works, what scripts are used for, how to select and run them, and how to read an existing script.
What NSE is
The Nmap Scripting Engine is a Lua interpreter embedded in Nmap. It lets you write small scripts that run during a scan and reuse everything Nmap already provides: fast parallel I/O, target and port lists, service detection results, timing controls and output formats.
Nmap ships with hundreds of scripts and a large library of reusable Lua modules (the nselib). You can use them as they are, copy and adapt them, or write your own.
Use cases
| Use case | Example scripts | What they do |
|---|---|---|
| Service discovery | http-title, ssh-hostkey, ssl-cert | Grab extra information from services already found. |
| Configuration audit | ssl-enum-ciphers, ftp-anon, smb-os-discovery | Check settings against expectations, list weak or exposed configuration. |
| Vulnerability detection | http-vuln-cve2017-5638 and friends | Test whether a specific known flaw is present. |
| Enumeration | http-enum, dns-brute | Find directories, subdomains, users, shares. |
| Credential checks | ssh-brute, ftp-brute | Try password lists (only with permission!). |
| Network recon | broadcast-dhcp-discover | Discover things on the local network without a target. |
| Custom internal checks | your scripts | “Is our internal service still on v1.4?” across a whole estate. |
That last row is the reason to learn to write scripts. Off-the-shelf scripts cover public software. Your organisation’s own protocols, appliances and policies are only covered if you cover them.
Script categories
Every script declares one or more categories. They’re how you select scripts by intent and how you keep a scan safe.
| Category | Meaning |
|---|---|
default | Run by -sC. Should be fast, useful, safe and reliable. |
safe | Won’t crash services, use lots of bandwidth or exploit anything. |
intrusive | Might crash a service, use lots of resources, or be noticed. |
discovery | Learns more about the network (registries, directories, and so on). |
version | Extends version detection. Runs only with -sV. |
vuln | Checks for specific vulnerabilities. |
auth | Deals with authentication credentials (or bypassing them). |
brute | Brute-force credential guessing. |
exploit | Actively exploits a vulnerability. |
dos | May cause denial of service. |
fuzzer | Sends unexpected or randomised input. |
malware | Checks for backdoors and malware infections. |
broadcast | Discovers hosts by broadcasting on the local network. |
external | Sends data to a third party (for example a whois database). |
Choosing categories honestly matters. Anyone running --script safe is trusting yours.
Running scripts
nmap -sC 10.0.0.5 # default category
nmap --script vuln 10.0.0.5 # a whole category
nmap --script http-title,ssl-cert 10.0.0.5 # by name
nmap --script "http-*" -p 80,443 10.0.0.5 # wildcard
nmap --script "default and safe" 10.0.0.5 # boolean expression
nmap --script "not intrusive" 10.0.0.5
nmap --script ./my-script.nse 10.0.0.5 # a file path
nmap --script-args 'http.useragent=MyScanner' --script http-title 10.0.0.5
Useful companions:
| Option | Purpose |
|---|---|
--script-help <name> | Print a script’s documentation (description, usage, arguments). |
--script-args / --script-args-file | Pass arguments to scripts. |
--script-updatedb | Rebuild script.db, the index of names and categories. |
--script-timeout <time> | Cap how long a single script may run. |
--script-trace | Show all network traffic sent and received by scripts. |
The four script types
A script’s rule decides when it runs. There are four types:
| Rule | Runs | Receives |
|---|---|---|
prerule | Once, before scanning starts. | nothing |
hostrule | Once per host that matches. | host |
portrule | Once per matching host and port. | host, port |
postrule | Once, after all scanning finishes. | nothing |
Most scripts you’ll write use a portrule: “if this port looks like a web server, run my code.”
How NSE executes scripts
Two properties shape how you write scripts:
- Scripts run in parallel, cooperatively. Each script instance (one per host or port) runs in its own Lua coroutine. When a script waits on the network, NSE switches to another one. That’s why scanning thousands of ports with scripts is fast.
- There is no pre-emption. A script that spins in a long CPU-bound loop blocks every other script. Keep loops small and let network calls do the waiting. (More in lesson 10.)
Where scripts live
| OS | Default scripts directory |
|---|---|
| Linux | /usr/share/nmap/scripts/ |
| macOS (Homebrew) | /opt/homebrew/share/nmap/scripts/ (Apple silicon) or /usr/local/share/nmap/scripts/ |
| Windows | C:\Program Files (x86)\Nmap\scripts\ |
The libraries sit beside it in nselib/. Reading them is the fastest way to learn what’s available.
To use your own script by name, put it in a scripts/ directory Nmap searches (for example ~/.nmap/scripts/, or anywhere via --datadir) and run nmap --script-updatedb so it lands in script.db. Passing an explicit path (--script ./my-script.nse) always works and is what we’ll use in the labs.
Lab: read a real script
- The
description,author,licenseandcategoriesfields at the top. - The
@usage,@argsand@outputdocumentation comments. - The
portruleline. Whichshortportpredicate does it use? - The
actionfunction. What does it return when there’s no title?
Then run it against the lab server from lesson 1:
nmap -p 8000 --script http-title 127.0.0.1
nmap -p 8000 --script-help http-title
nmap -p 8000 --script http-title -d 127.0.0.1 2>&1 | grep -i "NSE"
Checkpoint
You want a script to run only against SSH services. Which rule type, and which argument does your action receive?
A portrule, and action(host, port) receives both the host and port tables.
Why is a busy while true do loop in an action harmful even if it eventually ends?
NSE scheduling is cooperative. Nothing else runs while the loop holds the CPU, so every other script instance stalls until it yields (typically at a network call).
What does --script "default and safe" select?
Scripts that belong to both categories.