Back to all notes

Diving into NSE: The Nmap Scripting Engine

After getting comfortable with Nmap's core scanning capabilities, the next layer I wanted to peel back was the Nmap Scripting Engine (NSE) — the piece that turns Nmap from "just a port scanner" into something far more powerful. These notes summarize what NSE is, what it can do, the script categories and execution phases I learned about, and how to actually invoke and combine scripts on the command line.

What is NSE?

NSE stands for Nmap Scripting Engine. It's a feature built into Nmap that allows users to write simple scripts to automate a wide variety of networking tasks. The scripts are written in Lua and are executed in parallel with the rest of an Nmap scan, so the speed of the underlying scanner is preserved while the engine adds depth.

Practically, NSE turns Nmap from a "what ports are open?" tool into a "what's actually running on those ports, and what's wrong with it?" tool. That's why almost every real-world workflow I came across uses NSE in some form.

Why I Use NSE

NSE shows up in five main use cases during reconnaissance and assessment:

Network Discovery
Querying registries for target IP ownership, performing ident lookups on open ports, subdomain enumeration (e.g. dns-brute).
Advanced Version Detection
Goes deeper than default -sV matching by probing services in protocol-specific ways. Note: without -sV, version-related NSE scripts can't fully kick in.
Vulnerability Detection
NSE is powerful enough to handle even demanding vulnerability checks — known CVE detection, misconfigurations, weak ciphers.
Backdoor Detection
Complex worms and backdoors that regex-based version detection misses can be reliably identified through NSE's deeper protocol logic.
Vulnerability Exploitation
NSE can even be used to exploit vulnerabilities — not just find them. Use this category with caution and only with permission.

Activating NSE

NSE is activated with the -sC option (which runs the default script set), or with --script if I want to specify a custom set of scripts. Script scanning is also included as part of the -A aggressive scan option, which is why -A feels so noisy: it's running OS detection, version detection, AND default NSE scripts all at once.

# Run the default script set
nmap -sC 192.168.1.50

# Same thing, made explicit
nmap --script default 192.168.1.50

# Run a specific script
nmap --script smb-os-discovery 192.168.1.50

I can customize how some scripts behave by passing arguments to them via the --script-args and --script-args-file options. The --script-help flag shows a description of what each selected script does — I use it constantly before running anything unfamiliar.

# Read the script's documentation first
nmap --script-help dns-brute

# Pass arguments to a script
nmap --script dns-brute --script-args dns-brute.threads=20 example.com

Critical safety note: Scripts are not run in a sandbox and could accidentally or maliciously damage the system or invade your privacy. Never run scripts from third parties unless you trust the authors or have carefully audited the scripts yourself.

Command-Line Arguments

The flags I keep coming back to when working with NSE:

Flag What it does
-sC Performs a script scan using the default set of scripts.
--script <filename> Runs a script scan using a comma-separated list of filenames, script categories, or directories.
--script-args <args> Provides arguments to the scripts (e.g. script.argname=value).
--script-args-file Same as --script-args, except the arguments are read from a file rather than the command line.
--script-help <name>|<category> Shows the description and usage info for the named script or category.
--script-trace Prints all incoming and outgoing communication performed by the scripts. Great for learning what a script actually does on the wire.
--script-updatedb Updates the script database (scripts/script.db) used by Nmap to determine the available default scripts and categories.

Script Categories

Every NSE script belongs to one or more categories. The categories serve two purposes: they let me select groups of scripts at once (instead of typing dozens of names), and they communicate the risk profile of running a script.

Category Purpose
auth Scripts that deal with authentication credentials (or bypassing them) on the target system. Examples: ftp-anon, http-auth.
broadcast Scripts that typically discover hosts not listed on the command line by broadcasting on the local network. Use the newtargets script argument to allow these to automatically add discovered hosts to Nmap's scanning queue.
brute Scripts that use brute force attacks to guess authentication credentials of a remote server. Nmap ships with brute-force scripts for dozens of protocols, including http-brute, oracle-brute, snmp-brute.
default The default set, run when using -sC or -A instead of listing scripts with --script. Examples: identd-owners (determines the username running remote services using identd), http-auth (obtains the authentication scheme and realm of sites requiring auth), and ftp-anon (tests whether an FTP server allows anonymous access).
discovery Scripts that actively discover more about the network by querying public registries, SNMP-enabled devices, directory services, and so on. Examples: html-title (obtains the title of the root path of web sites), smb-enum-shares (enumerates Windows shares), snmp-sysdescr (extracts system details via SNMP).
dos Scripts that test for denial-of-service conditions — meaning they can actually take the target offline if it's vulnerable. Use only with explicit authorization.
malware Scripts that test whether the target platform is infected by malware or backdoors.
vuln Scripts that check for specific known vulnerabilities and generally only report results if a vulnerable condition is found.

A single script can belong to multiple categories. For example, smb-vuln-ms17-010 is tagged vuln, intrusive, and dos all at once — because checking for that vulnerability can actually crash the target. Always read the script's full category list with --script-help before running it.

Script Types: The Four Execution Phases

NSE scripts don't all run at the same point in a scan. Each script declares when it should fire, and Nmap has four execution phases that map to four script types:

Prerule
──▶
Runs before any of Nmap's scan phases. Nmap has not collected any information about its targets yet. Useful for tasks that don't depend on specific targets — like broadcasting DHCP or DNS-SD queries to discover hosts. Some prerule scripts can even generate new targets (only if you specify the newtargets NSE argument). For example, dns-zone-transfer can obtain a list of IPs in a domain and then automatically add them to Nmap's scan target list.
Host scripts
──▶
Run during Nmap's normal scanning process, after Nmap has performed host discovery, port scanning, version detection, and OS detection against the target. These have full context about the host.
Service scripts
──▶
Run against specific services listening on a target host. For example, Nmap includes more than 15 HTTP service scripts that run against web servers on whichever ports are serving HTTP.
Postrule
──▶
Runs after Nmap has scanned all of its targets. Useful for formatting and presenting Nmap output. For example, ssh-hostkey is best known for its service-script side that connects to SSH servers and discovers their public keys, but it also has a postrule component for printing the collected key set at the end.

Selecting Scripts

The --script flag accepts more than just a single script name — I can pass categories, wildcards, comma-separated lists, and even boolean expressions. This is what makes NSE selection so flexible.

# Categories → nmap --script default,safe
Loads all scripts in the default and safe categories.

# Single script → nmap --script smb-os-discovery
Loads only the smb-os-discovery script. The .nse extension is optional.

# Wildcard → nmap --script "http-*"
Loads all scripts whose name starts with http-.

# Boolean OR → nmap --script "default or safe"
Loads all scripts that are in the default category, the safe category, or both.

# Complex → nmap --script "(default or safe or intrusive) and not http-*"
Loads scripts in default, safe, or intrusive categories, except any whose name starts with http-.

I can also run two (or more) specific scripts together by listing them, comma-separated:

nmap -p 22 --script ssh-auth-methods,ssh-brute 192.168.0.12
# Enumerates SSH auth methods first, then brute-forces credentials on port 22

The script-selector quotes matter: shells will try to expand wildcards like http-* against your local filesystem unless they're inside quotes. Always wrap selectors that contain *, parentheses, or boolean operators in double quotes.

Wrapping Up

These notes cover the foundation I built around NSE: what the engine is, the five core use cases (network discovery, advanced version detection, vulnerability detection, backdoor detection, and exploitation), how to activate scripts with -sC and --script, the eight script categories I worked with, the four execution phases (prerule, host, service, postrule), and the boolean selector syntax that makes script selection so expressive.

The next step for me is moving past using existing scripts into actually reading and writing them — opening up /usr/share/nmap/scripts/, picking simple scripts like http-title.nse apart line by line, and eventually authoring my own. I'll write that up in a follow-up post.

Back to all notes