Digital Bond recently released two Nmap Scripting Engine (NSE) scripts under our Project Redpoint. The second NSE was an attempt to convert S7 enumeration scripts written in Python by SCADA Strange Love into an Nmap NSE. Over the course of development of this script and the BACnet script a lot was learned about development in Lua, especially when it comes working with existing Python code.
Lua is a scripting language that I had not previously developed in, and the only places we knew it of it being widely used, outside of Nmap, was in the gaming industry (e.g. World of Warcraft). Based off some research into Lua and some sample code exercises, we learned that there are many reasons why Nmap had chosen Lua as its scripting language choice. According to the Lua Website, Lua is meant to be fast, portable and light weight. Lua has a great community around it with lots of documentation on how to develop inside the language.
Nmap has also developed its own Lua libraries that is included with the Nmap builds. These libraries help implement a lot of the missing functionality in Lua as compared to other languages such as Python and Ruby. Within the Nmap documentation there is a specific Nmap library that includes how to create sockets and also how to perform error handling within a try/catch for an example.
Nmap has 110 Libraries that were developed to help extend Lua for nmap for basic functions to protocol specific applications. These applications include as an example http, dns, eigrp and snmp. This helps when trying to interface with well known protocols and can make simple queries against the protocol to gather information. Below is an example of both creating a new socket, and using a try/catch to validate that if the socket was created successfully or not.
local result, socket, try, catch
result = “”
socket = nmap.new_socket()
catch = function()
try = nmap.new_try(catch)
result = try(socket:receive_lines(1))
Lua is a classless language, and it proved to be a challenge when trying to convert existing Python code into Lua. In some cases, classes had been defined and multiple functions were created within each class. One solution to this was to create a function name with the class name inside of it. As an example, for a class named Protocol and a function to parse that specific protocol, you could write a function named Protocol:parse() as a basic way to achieve some basics of classes. More advanced classes that require initialization as well as other actions upon setup can be achieved in more complex ways. We do have some working examples of NSE scripts that utilize multiple mock classes.
Another lesson learned was Lua does not have fully developed methods and functionality. In some cases it was necessary to write a basic function that would be found in most other languages. In the An example in Project Redpoint is a function to determine if a string starts with a specific character.
With the S7 Enumeration NSE it proved to be easier to start with a clean slate rather than port the Python script. However the Python script was very helpful in identifying desired output, generating packet captures, and testing to make sure the NSE was working correctly. The clean slate LUA approach also makes it more likely the code will be accepted by the Nmap project for distribution with the other NSE. The Nmap project team has been very helpful in getting the Redpoint NSE scripts ready for submission and has provided great feedback on writing scripts to meet the Nmap NSE code standards.
- NSE Documentation
- Lua Users Wiki
- Lua Homepage
- Infosec Institute Nmap Scripting Example
- Nmap Code Standards
Image by Roblox