Nuts and bolts NSE: Nmap Scripting Engine 

Exploring Nmap's scripting engine

Security Script

The Nmap security scanner comes with a built-in scripting engine. We'll take a look at Nmap scripting and how the scripts are organized. By Ron McCarty

The Network Mapper, or Nmap [1], is one of the most popular network scanners available. System administrators and security specialists use Nmap for network scanning, discovery, and inventories. Nmap is useful for these security tasks, but it is also quite useful for network troubleshooting, You can use Nmap to determine whether a service is running or to determine whether a service version has changed. Nmap is also very good at identifying application and operating system versions through fingerprinting of protocols and applications. Although Nmap comes with a graphical user interface, Zenmap, many administrators prefer to work from the command line. Nmap lends itself very well to scripting in a shell interpreter scripting language such as the Bourne again shell (Bash) or a text-processing language such as Perl, sed, or awk. However, Nmap also has a native scripting feature. The Nmap scripting engine (NSE) has several advantages over Bash or a text-processing language:

Nmap's scripting language uses the Lua programming language. Lua is an interpreted language that is "powerful, fast, lightweight" [2]. It is probably most well known as the scripting language used by the popular game World of Warcraft (WoW) [3]; however, Lua is also used with Adobe Lightroom and other tools.

A large collection of scripts is available online at the website [4]. Some of these scripts are also bundled with the Nmap application. The ls command will give you a list of locally installed NSE scripts (Listing 1).

Listing 1: Local NSE Scripts

root@dal01:~# ls -l /usr/share/nmap/scripts
total 564
-rw-r--r-- 1 root root 15655 2009-11-06 03:39 asn-query.nse
-rw-r--r-- 1 root root  1864 2009-11-06 03:39 auth-owners.nse
-rw-r--r-- 1 root root   705 2009-11-06 03:39 auth-spoof.nse
-rw-r--r-- 1 root root  5582 2009-11-06 03:39 banner.nse
-rw-r--r-- 1 root root  6636 2009-11-06 03:39 sslv2.nse
-rw-r--r-- 1 root root  5376 2009-11-06 03:39 telnet-brute.nse
-rw-r--r-- 1 root root  5780 2009-11-06 03:39 upnp-info.nse
-rw-r--r-- 1 root root 89999 2009-11-06 03:39 whois.nse

The Nmap Reference Guide [5] provides a breakdown of the scripts that are currently distributed with Nmap.

Understanding Nmap Scripts

Nmap organizes the scanning process into five phases (Figure 1). NSE scripts fall into categories associated with the phases of the scanning process as:

NSE divides the scanning process into five phases.
Figure 1: NSE divides the scanning process into five phases.

NSE scripts are divided into the following sections:

The description section pro-vides a description of the script included inside double square brackets:

description = [[
Example for Admin magazine

The categories section aptly defines the categories the script is executed in (see "Script Categories.") Associating a script with a category allows it to be executed as part of a category scan. A script can belong to multiple categories. The format for the categories section is

categories = {"default", "safe"}

which specifies that the script will run any time the administrator executes scripts in either the default or safe categories.

The phase section identifies the phase when the script will run: hostrule, portrule, postrule, or prerule. (See the discussion of phases earlier in this article.) Note the hostrule and portrule settings require parameters and receive the parameters from Nmap's scanning engine. The Phase section ends with the Lua end command. (The phases are implemented as Lua functions.) Listing 2 shows the portrule code from the html-title.nse script.

Listing 2: Portrule Code

01 portrule = function(host, port)
02     local svc = { std = { ["http"] = 1, ["http-alt"] = 1 },
03                   ssl = { ["https"] = 1, ["https-alt"] = 1 } }
04     if port.protocol ~= 'tcp'
05     or not ( svc.std[port.service] or svc.ssl[port.service] ) then
06         return false
07     end
08     -- Don't bother running on SSL ports if we don't have SSL.
09     if (svc.ssl[port.service] or port.version.service_tunnel == 'ssl')
10     and not nmap.have_ssl() then
11         return false
12     end
13     return true
14 end

The portrule in Listing 2 creates the variables svc.std and svc.ssl as associative arrays (lines 2 and 3) and then checks to see whether the transport protocol is not TCP (line 4) or whether the ports are not running HTTP or HTTP over SSL (line 5). If either of these constraints is not true, the script will simply return false (line 6) and Nmap will not print any results.

On the other hand, if the port is running TCP, the logic continues and the script determines whether Nmap has access to the SSL client libraries. If it does not have the SSL client libraries, it will return false (line 11) and not print anything during execution. If all of these tests pass, the function returns true, and the logic defined in the action section is carried out.

The action section is the algorithm containing the logic of the script once the phase logic is executed. The logic uses standard Lua variables, syntax, and functions, combined with the libraries included with Nmap. The presence of the Nmap NSE libraries save the developer from having to deal with functions such as connecting to the port or reading from a network-connected service. The code in Listing 3, which is from the anonymous FTP Nmap script ftp-anon.nse, shows how easy it is to connect and send data to a socket and check for a status code.

Listing 3: Taking Action

01 socket:set_timeout(5000)
02 try(socket:connect(host.ip, port.number, port.protocol))
03 try(socket:send("USER anonymous\r\n"))
04 try(socket:send("PASS IEUser@\r\n"))
05         while status do
06                 status, result = socket:receive_lines(1);
07                 if string.match(result, "^230") then
08                         isAnon = true
09                         break
10                 end

Using NSE

Many basic NSE scripts are members of the default category. Use the -sC option to run Default scripts (Listing 4).

Listing 4: Running Default Scripts

root@dal01:~# nmap -sC
Starting Nmap 5.00 ( ) at 2011-03-06 19:35 CST
Interesting ports on (
Not shown: 998 filtered ports
80/tcp   open  http
|  robots.txt: has 7 disallowed entries
|_ /t/trackback /t/comments /t/stats /t/app /.m/ / *
|_ html-title: Ron McCarty's Blog
2909/tcp open  unknown
Nmap done: 1 IP address (1 host up) scanned in 12.93 seconds

The -A option runs the Default scripts and executes several other scanning options. To run a specific script, use --script with the name of the script. For example,

nmap --script html-title

runs the Whois script included with NSE (Listing 5). NSE also lets you run several scripts by separating them with commas, or complete categories by including the category name. The command in Listing 6 runs all Malware category scripts.

Listing 5: Running the html-title Script

root@dal01:~# nmap --script html-title
Starting Nmap 5.00 ( ) at 2011-03-06 19:18 CST
Interesting ports on (
Not shown: 998 filtered ports
80/tcp   open  http
|_ html-title: Ron McCarty's Blog
2909/tcp open  unknown
Nmap done: 1 IP address (1 host up) scanned in 8.47 seconds

Listing 6: Running Scripts in a Category

root@dal01:~# nmap --script malware
Starting Nmap 5.00 ( ) at 2011-03-06 19:23 CST
Interesting ports on (
Not shown: 998 filtered ports
80/tcp   open  http
2909/tcp open  unknown
Nmap done: 1 IP address (1 host up) scanned in 11.02 seconds

Wrap Up

In this article, I introduced you to the Nmap scripting engine. I hope this discussion gave you a good basis for determining when you need to use scripts.

Gaining a solid understanding of the various Nmap categories and scripts, as well as some background on the scripting engine itself, will help you use Nmap to its fullest potential. Have fun!