1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
|
local dns = require "dns"
local nmap = require "nmap"
local shortport = require "shortport"
local stdnse = require "stdnse"
local string = require "string"
description = [[
Retrieves information from a DNS nameserver by requesting
its nameserver ID (nsid) and asking for its id.server and
version.bind values. This script performs the same queries as the following
two dig commands:
- dig CH TXT bind.version @target
- dig +nsid CH TXT id.server @target
References:
[1]http://www.ietf.org/rfc/rfc5001.txt
[2]http://www.ietf.org/rfc/rfc4892.txt
]]
---
-- @usage
-- nmap -sSU -p 53 --script dns-nsid <target>
--
-- @output
-- 53/udp open domain udp-response
-- | dns-nsid:
-- | NSID dns.example.com (646E732E6578616D706C652E636F6D)
-- | id.server: dns.example.com
-- |_ bind.version: 9.7.3-P3
--
-- @xmloutput
-- <table key="NSID">
-- <elem key="raw">mia01.l.root-servers.org</elem>
-- <elem key="hex">6d696130312e6c2e726f6f742d736572766572732e6f7267</elem>
-- </table>
-- <elem key="id.server">mia01.l.root-servers.org</elem>
-- <elem key="bind.version">NSD 3.2.15</elem>
author = "John R. Bond"
license = "Simplified (2-clause) BSD license--See https://nmap.org/svn/docs/licenses/BSD-simplified"
categories = {"discovery", "default", "safe"}
portrule = function (host, port)
if not shortport.port_or_service(53, "domain", {"tcp", "udp"})(host, port) then
return false
end
-- only check tcp if udp is not open or open|filtered
if port.protocol == 'tcp' then
local tmp_port = nmap.get_port_state(host, {number=port.number, protocol="udp"})
if tmp_port then
return not string.match(tmp_port.state, '^open')
end
end
return true
end
local function rr_filter(pktRR, label)
for _, rec in ipairs(pktRR, label) do
if ( rec[label] and 0 < #rec.data ) then
if ( dns.types.OPT == rec.dtype ) then
if #rec.data < 4 then
return false, "Failed to decode NSID"
end
local _, len, pos = string.unpack(">I2 I2", rec.data)
if ( len ~= #rec.data - pos + 1 ) then
return false, "Failed to decode NSID"
end
return true, string.unpack("c" .. len, rec.data, pos)
else
return true, string.unpack(">s1", rec.data)
end
end
end
end
action = function(host, port)
local result = stdnse.output_table()
local flag = false
local status, resp = dns.query("id.server", {host = host.ip, port=port.number, proto=port.protocol, dtype='TXT', class=dns.CLASS.CH, retAll=true, retPkt=true, nsid=true, dnssec=true})
if ( status ) then
local status, nsid = rr_filter(resp.add,'OPT')
if ( status ) then
flag = true
-- RFC 5001 says NSID can be any arbitrary bytes, and should be displayed
-- as hex, but often it is a readable string. Store both.
result["NSID"] = { raw = nsid, hex = stdnse.tohex(nsid) }
setmetatable(result["NSID"], {
__tostring = function(t)
return ("%s (%s)"):format(t.raw, t.hex)
end
})
end
local status, id_server = rr_filter(resp.answers,'TXT')
if ( status ) then
flag = true
result["id.server"] = id_server
end
end
local status, bind_version = dns.query("version.bind", {host = host.ip, port=port.number, proto=port.protocol, dtype='TXT', class=dns.CLASS.CH})
if ( status ) then
flag = true
result["bind.version"] = bind_version
end
if flag then
return result
end
end
|