Version Detection using NSE
The version detection system built into Nmap was designed to
efficiently recognize the vast majority of protocols with a
simple pattern matching syntax. Some protocols require a more
complex approach, and a generalized scripting language is
perfect for this. Skype2 is one such protocol. It pretends to
be an http server, requiring multiple queries to determine its
true nature. NSE has been integrated into Nmap's version
detection framework to handle these cases. The scripts which
extend the version scanner belong to the reserved category
version
. This category cannot be run from
the command line. It is only executed if the user has required a
version scan. The following listing shows a simple script which
demonstrates the use of the NSE version detection API. If either
the TCP port 80 is open or the service has been determined to be
http, the script is triggered. Although it could be extended to
recognize different http servers, its only purpose is to show off
the version detection API. It is not advisable to use NSE for
version detection in the simple case of http servers. The
version detection variables have been filled with dummy entries
to illustrate their effect on the Nmap output.
description = "Demonstration of a version detection NSE script. It checks \
and reports the version of a remote web server. For real life purposes it is \
better to use Nmap version detection (-sV)."
author = "Diman Todorov <diman.todorov@gmail.at>
license = "See Nmap's COPYING for license"
id = "HTTP version"
categories = {"version"}
runlevel = 1.0
portrule = function(host, port)
if (port.number == 80
or port.service == "http" )
and port.protocol == "tcp"
then
return true
else
return false
end
end
action = function(host, port)
local query = "GET / HTTP/2.1\r\n"
query = query .. "Accept: */*\r\n"
query = query .. "Accept-Language: en\r\n"
query = query .. "User-Agent: Nmap NSE\r\n"
query = query .. "Host: " .. host.ip .. ":" .. port.number .. "\r\n\r\n"
local socket = nmap.new_socket()
local catch = function()
socket:close()
end
local try = nmap.new_try(catch)
try(socket:connect(host.ip, port.number))
try(socket:send(query))
local response = ""
local lines
local status
local value
while true do
status, lines = socket:receive_lines(1)
if not status or value then
break
end
response = response .. lines
value = string.match(response, "Server: (.-)\n")
end
try(socket:close())
if value then
port.version.name = "[Name]"
port.version.name_confidence = 10
port.version.product = "[Product]"
port.version.version = "[Version]"
port.version.extrainfo = "[ExtraInfo]"
port.version.hostname = "[HostName]"
port.version.ostype = "[OSType]"
port.version.devicetype = "[DeviceType]"
port.version.service_tunnel = "none"
port.version.fingerprint = nil
nmap.setPortVersion(host, port, "hardmatched")
end
end
This is what the output of this script looks like:
$ ./nmap -sV localhost -p 80
Starting Nmap ( http://insecure.org )
Interesting ports on localhost (127.0.0.1):
PORT STATE SERVICE VERSION
80/tcp open [Name] [Product] [Version] ([ExtraInfo])
Service Info: Host: [HostName]; OS: [OSType]; Device: [DeviceType]
Nmap finished: 1 IP address (1 host up) scanned in 9.317 seconds
The name variable denotes the detected protocol name.
The product, version and extrainfo variables are used
to produce a human readable description of the server
version. The remaining variables provide information deduced
from the output of the server concerning the target host.