summaryrefslogtreecommitdiffstats
path: root/scripts/ntp-info.nse
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/ntp-info.nse')
-rw-r--r--scripts/ntp-info.nse172
1 files changed, 172 insertions, 0 deletions
diff --git a/scripts/ntp-info.nse b/scripts/ntp-info.nse
new file mode 100644
index 0000000..d72f5b5
--- /dev/null
+++ b/scripts/ntp-info.nse
@@ -0,0 +1,172 @@
+local comm = require "comm"
+local datetime = require "datetime"
+local os = require "os"
+local nmap = require "nmap"
+local shortport = require "shortport"
+local stdnse = require "stdnse"
+local string = require "string"
+local table = require "table"
+local lpeg = require "lpeg"
+local U = require "lpeg-utility"
+
+description = [[
+Gets the time and configuration variables from an NTP server. We send two
+requests: a time request and a "read variables" (opcode 2) control message.
+Without verbosity, the script shows the time and the value of the
+<code>version</code>, <code>processor</code>, <code>system</code>,
+<code>refid</code>, and <code>stratum</code> variables. With verbosity, all
+variables are shown.
+
+See RFC 1035 and the Network Time Protocol Version 4 Reference and
+Implementation Guide
+(http://www.eecis.udel.edu/~mills/database/reports/ntp4/ntp4.pdf) for
+documentation of the protocol.
+]]
+
+---
+-- @usage
+-- nmap -sU -p 123 --script ntp-info <target>
+-- @output
+-- PORT STATE SERVICE VERSION
+-- 123/udp open ntp NTP v4.2.4p4@1.1520-o
+-- | ntp-info:
+-- | receive time stamp: Sat Dec 12 16:22:41 2009
+-- | version: ntpd 4.2.4p4@1.1520-o Wed May 13 21:06:31 UTC 2009 (1)
+-- | processor: x86_64
+-- | system: Linux/2.6.24-24-server
+-- | stratum: 2
+-- |_ refid: 195.145.119.188
+--
+-- @xmloutput
+-- <elem key="receive time stamp">2013-10-18T18:03:05</elem>
+-- <elem key="version">ntpd 4.2.6p3@1.2290-o Tue Jun 5 20:12:11 UTC 2012 (1)</elem>
+-- <elem key="processor">i686</elem>
+-- <elem key="system">Linux/3.9.3-24</elem>
+-- <elem key="leap">3</elem>
+-- <elem key="stratum">16</elem>
+-- <elem key="precision">-20</elem>
+-- <elem key="rootdelay">0.000</elem>
+-- <elem key="rootdisp">2502.720</elem>
+-- <elem key="refid">INIT</elem>
+-- <elem key="reftime">0x00000000.00000000</elem>
+-- <elem key="clock">0xd60bf655.4cc0ba51</elem>
+-- <elem key="peer">0</elem>
+-- <elem key="tc">3</elem>
+-- <elem key="mintc">3</elem>
+-- <elem key="offset">0.000</elem>
+-- <elem key="frequency">-46.015</elem>
+-- <elem key="jitter">0.001</elem>
+-- <elem key="wander">0.000</elem>
+
+author = "Richard Sammet"
+license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
+categories = {"default", "discovery", "safe"}
+
+
+
+portrule = shortport.port_or_service(123, "ntp", {"udp", "tcp"})
+
+-- This script run against open|filtered ports, so don't wait too long if
+-- there's no response.
+local TIMEOUT = 5000
+
+-- Only these fields from the response are displayed with default verbosity.
+local DEFAULT_FIELDS = {"version", "processor", "system", "refid", "stratum"}
+
+-- comma-space-separated key=value pairs with optional quotes
+local kvmatch = U.localize( {
+ lpeg.V "space"^0 * lpeg.V "kv" * lpeg.P ","^-1,
+ kv = lpeg.V "key" * lpeg.P "="^-1 * lpeg.V "value",
+ key = lpeg.C( (lpeg.V "alnum" + lpeg.S "_-.")^1 ),
+ value = U.escaped_quote() + lpeg.C((lpeg.P(1) - ",")^0),
+ } )
+
+action = function(host, port)
+ local status
+ local buftres, bufrlres
+ local output = stdnse.output_table()
+
+ -- This is a ntp v2 mode3 (client) date/time request.
+ local treq = string.char(0xd3, 0x00, 0x04, 0xfa, 0x00, 0x01, 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00)
+
+ -- This is a ntp v2 mode6 (control) rl (readlist/READVAR(2)) request. See
+ -- appendix B of RFC 1305.
+ local rlreq = string.char(0x16, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00)
+
+ status, buftres = comm.exchange(host, port, treq, {timeout=TIMEOUT})
+ if status then
+ local recvtime = os.time()
+
+ local sec, frac = string.unpack(">I4I4", buftres, 33)
+ -- The NTP epoch is 1900-01-01, so subtract 70 years to bring the date into
+ -- the range Lua expects. The number of seconds at 1970-01-01 is taken from
+ -- the NTP4 reference above.
+ local tstamp = sec - 2208988800 + frac / 0x10000000
+
+ datetime.record_skew(host, tstamp, recvtime)
+
+ output["receive time stamp"] = datetime.format_timestamp(tstamp)
+ end
+
+ status, bufrlres = comm.exchange(host, port, rlreq, {timeout=TIMEOUT})
+
+ if status then
+ -- This only looks at the first fragment of what can possibly be several
+ -- fragments in the response.
+
+ -- Skip the first 10 bytes of the header, then get the data which is
+ -- preceded by a 2-byte length.
+ local data = string.unpack(">s2", bufrlres, 11)
+
+ -- loop over capture pairs which represent (key, value)
+ local function accumulate_output (...)
+ local k, v = ...
+ if k == nil then return end
+ output[k] = v
+ return accumulate_output(select(3, ...))
+ end
+
+ -- do the match and accumulate the captures
+ local list = kvmatch^0 / accumulate_output
+ list:match(data)
+ end
+
+ if(#output > 0) then
+ stdnse.debug1("Test len: %d", #output)
+ nmap.set_port_state(host, port, "open")
+ if output['version'] then
+ -- Look for the version string from the official ntpd and format it
+ -- in a manner similar to the output of the standard Nmap version detection
+ local version_num = string.match(output['version'],"^ntpd ([^ ]+)")
+ if version_num then
+ port.version.version = "v" .. version_num
+ nmap.set_port_version(host, port, "hardmatched")
+ end
+ end
+ if output['system'] then
+ port.version.ostype = output['system']
+ nmap.set_port_version(host, port, "hardmatched")
+ end
+ if nmap.verbosity() < 1 then
+ local mt = getmetatable(output)
+ mt["__tostring"] = function(t)
+ local out = {}
+ for _,k in ipairs(DEFAULT_FIELDS) do
+ if output[k] ~= nil then
+ table.insert(out, ("%s: %s"):format(k, output[k]))
+ end
+ end
+ return "\n " .. table.concat(out, "\n ")
+ end
+ end
+ return output
+ else
+ return nil
+ end
+end