How to Get Started Writing Your Own NSE Scripts for Nmap

Sep 14, 2018 10:35 PM
Feb 16, 2019 12:48 AM
636724321288240839.jpg

The road to becoming a skilled white hat is paved with many milestones, one of those being learning how to perform a simple Nmap scan. A little further down that road lies more advanced scanning, along with utilizing a powerful feature of Nmap called the Nmap Scripting Engine. Even further down the road is learning how to modify and write scripts for NSE, which is what we'll be doing today.

Overview of NSE

The Nmap Scripting Engine is one of the most robust, yet underutilized features of Nmap, containing a huge variety of scripts to automate tasks beyond port scanning and basic network discovery. Some of these tasks include scripts for vulnerability detection, advanced version detection, malware detection, fuzzing, and even active exploitation.

Nmap can take the command line argument -sC to execute a default set of scripts, but doing so can be noisy as some of these scripts are considered pretty intrusive. Users also have the option to specify which script or scripts to run by using the --script argument; Individual scripts can be passed as well as whole categories of scripts.

One of the greatest aspects of NSE, though, is the ability for users to tweak and modify scripts to better fit their needs, as well as the power to write custom scripts from scratch. All scripts used by NSE are written in an embedded scripting language called Lua.

Introduction to Lua

Lua is an efficient, dynamically typed scripting language that is both powerful and lightweight. It is very flexible in that it supports procedural, functional, and object-oriented programming. Lua is meant to be portable, so it can easily be embedded into other applications using a C API, and it can run on nearly all platforms, including Unix, Windows, mobile devices, and embedded microprocessors such as ARM. Lua has been used in many popular applications, including Adobe Photoshop Lightroom and Garry's Mod.

Let's go over some basics in order to get a feel for the language. Comments begin with a double-hyphen and run to the end of the line:

-- this is a comment in Lua

In the tradition of learning a new programming language, the classic "Hello world!" program can be written like so:

print("Hello world!")

Global variables do not need to be declared, and unlike some languages, there is no error for accessing a variable that isn't initialized — instead, the value nil will be returned. Local variables, on the other hand, are assigned using the local keyword:

print(a) -- returns nil
local b = 5
print(b) -- returns 5

Control flow in Lua consists of if and if...else statements for conditionals, and while, repeat, and for loops for iteration. Some examples follow:

if condition then
	statement
else
	statement
end
while condition do
	statement
end
repeat
	statement
until condition
for i = x, y, z do
	statement
end

Functions in Lua operate similarly to other languages, requiring a function name, return statement, and end statement. The following is an example of a simple function that returns the bigger of two numbers:

function max(a, b)
	if (a > b) then
		bigger = a
	else
		bigger  = b
	end
	return bigger
end

A complete tutorial on Lua is beyond the scope of this article, but more detailed information can be found on Lua's site. Now that we have the basics of the language down, we can begin to interact with some NSE scripts.

Writing a Basic Script

NSE scripts are made up of three main sections: head, rule, and action.

  • The head section contains mostly meta-data, such as the author, description, category, and other relevant information about the script.
  • The rule section is where conditions are defined that allow the script to execute; This section must contain at least one of the following functions that will determine when the script will run: prerule, hostrule, portrule, or postrule.
  • The action section is where the main functionality of the script takes place. All of the logic and instructions will go in this section.

For demonstration purposes, we will compose a simple script that determines if a given port is open and outputs a relevant message. Let's begin with the head. For now, we can put in a short description and leave it at that:

-- HEAD --

description = [[
This is a simple script example that determines if a port is open.
]]

author = "Null Byte"

Next, we can move on to the rule section. Let's define a portrule to determine whether any given port is open or not:

-- RULE --

portrule = function(host, port)
	return port.protocol == "tcp"
		and port.state == "open"
end

Now, we can define the actual script logic in the action section. Let's have the script display a message for any give port that is open:

-- ACTION --

action = function(host, port)
	return "This port is open!"
end

The entire script should now look something like this:

-- HEAD --

description = [[
This is a simple script example that determines if a port is open.
]]

author = "Null Byte"

-- RULE --

portrule = function(host, port)
	return port.protocol == "tcp"
		and port.state == "open"
end

-- ACTION --

action = function(host, port)
	return "This port is open!"
end

From here, we should be able to run our script to see if it works. Save the file as example-script.nse and copy it to the /usr/share/nmap/scripts directory. We can keep it simple and just use localhost to test our script against. We also need to pass in a port, so run service apache2 restart in the terminal to ensure the web server is running, and we can now use port 80. Finally, let's run the script:

nmap --script example-script localhost -p 80

Starting Nmap 7.70 ( https://nmap.org ) at 2018-09-12 13:08 CDT
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000087s latency).
Other addresses for localhost (not scanned): ::1

PORT   STATE SERVICE
80/tcp open  http
|_example-script: This port is open!

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

We can see that port 80 is open on localhost and the script displays our message. This is merely a rudimentary example of what NSE scripts can actually do, but with the basics down, it makes it easier to modify existing scripts for custom needs or write even more robust scripts from scratch.

More NSE Scripting

The Nmap Scripting Engine is a powerful feature of Nmap used to expand the functionality of the core tool. In this guide, we learned a bit about Lua, the programming language that drives NSE, as well as how to structure and write a simple script for our own use. We have barely scratched the surface of what Nmap scripts can truly do, so I encourage you to dive in and get your hands dirty so that you may be able to take full advantage of what NSE has to offer.

Cover image by Comfreak/Pixabay

Comments

No Comments Exist

Be the first, drop a comment!