description = [[
Gets variables from an NTP server by sending a "read variables" (opcode 2)
control message. Without verbosity, the script shows the value of the
version, processor, system,
refid, and stratum 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.
]]
---
-- @output
-- PORT STATE SERVICE VERSION
-- 123/udp open ntp NTP v4
-- | ntp-info:
-- | 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
author = "Richard Sammet"
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
categories = {"default", "discovery", "safe"}
require "bin"
require "nmap"
require "stdnse"
require "comm"
require "shortport"
portrule = shortport.port_or_service(123, "ntp", {"udp", "tcp"})
-- Transform an array into a table where the array's values all map to true.
local function make_set(a)
local i, v, result
result = {}
for i, v in ipairs(a) do
result[v] = true
end
return result
end
-- Only these fields from the response are displayed with default verbosity.
local DEFAULT_FIELDS = make_set({"version", "processor", "system", "refid", "stratum"})
action = function(host, port)
local status
local bufrlres
local output = {}
-- 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, bufrlres = comm.exchange(host, port, rlreq, {proto=port.protocol})
if status then
local _, data, k, q, v
-- Skip the first 10 bytes of the header, then get the data which is
-- preceded by a 2-byte length.
_, p, data = bin.unpack("A10>P", bufrlres)
-- This parsing is not quite right with respect to quoted strings.
-- Backslash escapes should be interpreted inside strings and commas should
-- be allowed inside them.
for k, q, v in string.gmatch(data, "%s*(%w+)=(\"?)([^,\"]*)%2,?") do
if DEFAULT_FIELDS[k] or nmap.verbosity() then
table.insert(output, string.format("%s: %s", k, v))
end
end
end
if(#output > 0) then
nmap.set_port_state(host, port, "open")
return stdnse.format_output(true, output)
else
return nil
end
end