Version Detection Using NSE
The version detection system built into Nmap was designed to efficiently recognize the vast majority of protocols with a simple probe and pattern matching syntax. Some protocols require more complex communication than version detection can handle. A generalized scripting language as provided by NSE is perfect for these tough cases.
NSE's version
category contains scripts that enhance standard version
detection. Scripts in this category are run whenever you request
version detection with -sV
; you don't need to use
-sC
to run these. This cuts
the other way too: if you use -sC
, you won't get
version
scripts unless you also use
-sV
.
One protocol which we were unable to detect with normal version detection is Skype version 2. The protocol was likely designed to frustrate detection out of a fear that telecom-affiliated Internet service providers might consider Skype competition and interfere with the traffic. Yet we did find one way to detect it. If Skype receives an HTTP GET request, it pretends to be a web server and returns a 404 error. But for other requests, it sends back a chunk of random-looking data. Proper identification requires sending two probes and comparing the two responses—an ideal task for NSE. The simple NSE script which accomplishes this is shown in Example 9.14.
description = [[ Detects the Skype version 2 service. ]] --- -- @output -- PORT STATE SERVICE VERSION -- 80/tcp open skype2 Skype author = "Brandon Enright" license = "Same as Nmap--See https://nmap.org/book/man-legal.html" categories = {"version"} require "comm" require "shortport" portrule = function(host, port) return (port.number == 80 or port.number == 443 or port.service == nil or port.service == "" or port.service == "unknown") and port.protocol == "tcp" and port.state == "open" and port.service ~= "http" and port.service ~= "ssl/http" and not(shortport.port_is_excluded(port.number,port.protocol)) end action = function(host, port) local status, result = comm.exchange(host, port, "GET / HTTP/1.0\r\n\r\n", {bytes=26, proto=port.protocol}) if (not status) then return end if (result ~= "HTTP/1.0 404 Not Found\r\n\r\n") then return end -- So far so good, now see if we get random data for another request status, result = comm.exchange(host, port, "random data\r\n\r\n", {bytes=15, proto=port.protocol}) if (not status) then return end if string.match(result, "[^%s!-~].*[^%s!-~].*[^%s!-~]") then -- Detected port.version.name = "skype2" port.version.product = "Skype" nmap.set_port_version(host, port) return end return end
If the script detects Skype, it augments its port
table with now-known name
and
product
fields. It then sends this new
information to Nmap by calling
nmap.set_port_version
. Several other version
fields are available to be set if they are known, but in this case
we only have the name and product. For the full list of version
fields, refer to the nmap.set_port_version
documentation.
Notice that this script does nothing unless it detects the protocol. A script shouldn't produce output (other than debug output) just to say it didn't learn anything.