diff --git a/CHANGELOG b/CHANGELOG
index 95c70950b..02ac01417 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,5 +1,11 @@
# Nmap Changelog ($Id$); -*-text-*-
+o [NSE] Added script dns-nsid by John Bond, that retrieves name server ID and
+ version information.
+
+o [NSE] Applied patch to DNS library by John Bond that adds support for the
+ CHAOS class and NSID requests.
+
o [NSE] Changed the dnsbl library to take a threaded approach into querying
DNSBL provides drastically improving performance. [Patrik]
diff --git a/nselib/dns.lua b/nselib/dns.lua
index 51957a7ce..fb2bd5d65 100644
--- a/nselib/dns.lua
+++ b/nselib/dns.lua
@@ -62,6 +62,7 @@ types = {
CLASS = {
IN = 1,
+ CH = 3,
ANY = 255
}
@@ -252,6 +253,7 @@ function query(dname, options)
local dtype, host, port = options.dtype, options.host, options.port
+ local class = options.class or CLASS.IN
if not options.tries then options.tries = 10 end -- don't get into an infinite loop
if not options.sendCount then options.sendCount = 2 end
@@ -279,12 +281,18 @@ function query(dname, options)
end
local pkt = newPacket()
- addQuestion(pkt, dname, dtype)
+ addQuestion(pkt, dname, dtype, class)
if options.norecurse then pkt.flags.RD = false end
- if options.dnssec then
- addOPT(pkt, {DO = true})
- end
+ if ( options.dnssec ) then
+ if ( options.nsid ) then
+ addNSID(pkt, {DO = true})
+ else
+ addOPT(pkt, {DO = true})
+ end
+ elseif ( options.nsid ) then
+ addNSID(pkt, {})
+ end
if ( options.flags ) then pkt.flags.raw = options.flags end
if ( options.id ) then pkt.id = options.id end
@@ -1076,6 +1084,25 @@ decoder[types.TXT] =
end
+---
+-- Decodes OPT record, puts it in entry.OPT.
+--
+-- entry.OPT has the fields mname, rname,
+-- serial, refresh, retry,
+-- expire, and minimum.
+-- @param entry RR in packet.
+-- @param data Complete encoded DNS packet.
+-- @param pos Position in packet after RR.
+decoder[types.OPT] =
+ function(entry, data, pos)
+ local np = pos - #entry.data - 6
+ local opt = { bufsize = entry.class }
+ np, opt.rcode, opt.version, opt.zflags, opt.rdlen = bin.unpack(">CCSS", data, np)
+ np, opt.data = bin.unpack("A" .. opt.rdlen, data, np)
+ entry.OPT = opt
+ end
+
+
-- Decodes MX record, puts it in entry.MX.
--
-- entry.MX has the fields pref and
@@ -1109,7 +1136,6 @@ decoder[types.SRV] =
np, entry.SRV.target = decStr(data, np)
end
----
-- Decodes returned resource records (answer, authority, or additional part).
-- @param data Complete encoded DNS packet.
-- @param count Value of according counter in header.
@@ -1226,13 +1252,14 @@ end
-- @param pkt Table representing DNS packet.
-- @param dname Domain name to be asked.
-- @param dtype RR to be asked.
-function addQuestion(pkt, dname, dtype)
+function addQuestion(pkt, dname, dtype, class)
if type(pkt) ~= "table" then return nil end
if type(pkt.questions) ~= "table" then return nil end
+ local class = class or CLASS.IN
local q = {}
q.dname = dname
q.dtype = dtype
- q.class = CLASS.IN
+ q.class = class
table.insert(pkt.questions, q)
return pkt
end
@@ -1269,12 +1296,18 @@ local function encodeOPT_Z(flags)
return table.concat(bits)
end
+function addNSID (pkt,Z)
+ local udp_payload_size = 4096
+ local opt = bin.pack(">SS",3, 0) -- nsid data
+ addOPT(pkt,Z,opt)
+end
---
-- Adds an OPT RR to a DNS packet's additional section. Only the table of Z
-- flags is supported (i.e., not RDATA). See RFC 2671 section 4.3.
-- @param pkt Table representing DNS packet.
-- @param Z Table of Z flags. Only DO is supported.
-function addOPT(pkt, Z)
+function addOPT(pkt, Z, opt)
+ local rdata = opt or ""
if type(pkt) ~= "table" then return nil end
if type(pkt.additional) ~= "table" then return nil end
local _, Z_int = bin.unpack(">S", bin.pack("B", encodeOPT_Z(Z)))
@@ -1282,8 +1315,8 @@ function addOPT(pkt, Z)
type = types.OPT,
class = 4096, -- Actually the sender UDP payload size.
ttl = 0 * (0x01000000) + 0 * (0x00010000) + Z_int,
- rdlen = 0,
- rdata = "",
+ rdlen = #rdata,
+ rdata = rdata,
}
table.insert(pkt.additional, opt)
return pkt
diff --git a/scripts/dns-nsid.nse b/scripts/dns-nsid.nse
new file mode 100644
index 000000000..86b443e22
--- /dev/null
+++ b/scripts/dns-nsid.nse
@@ -0,0 +1,69 @@
+description = [[
+Ateemps to get more information from a server by requesting the server nsid[1],
+and asking for id.server[2] and version.bind. This script dose the same as the
+following two dig commands:
+ - dig CH TXT bind.version @target
+ - dig +nsid CH TXT id.server @target
+
+[1]http://www.ietf.org/rfc/rfc5001.txt
+[2]http://www.ietf.org/rfc/rfc4892.txt
+]]
+
+---
+-- @usage
+-- nmap -sSU -p 53 --script dns-nsid
+--
+-- @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
+---
+
+author = "John Bond"
+license = "Simplified (2-clause) BSD license--See http://nmap.org/svn/docs/licenses/BSD-simplified"
+
+categories = {"discovery", "default"}
+
+require "stdnse"
+require "shortport"
+require "dns"
+
+portrule = shortport.port_or_service(53, "domain", {"tcp", "udp"})
+
+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
+ local pos, _, len = bin.unpack(">SS", rec.data)
+ if ( len ~= #rec.data - pos + 1 ) then
+ return false, "Failed to decode NSID"
+ end
+ return true, select(2, bin.unpack("A" .. len, rec.data, pos))
+ else
+ return true, select(2, bin.unpack("p", rec.data))
+ end
+ end
+ end
+end
+
+action = function(host, port)
+ local result = {}
+ local status, resp = dns.query("id.server", {host = host.ip, 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
+ table.insert(result, ("NSID: %s (%s)"):format(nsid, stdnse.tohex(nsid)))
+ end
+ local status, id_server = rr_filter(resp.answers,'TXT')
+ if ( status ) then
+ table.insert(result, ("id.server: %s"):format(id_server))
+ end
+ end
+ local status, bind_version = dns.query("version.bind", {host = host.ip, dtype='TXT', class=dns.CLASS.CH})
+ if ( status ) then
+ table.insert(result, ("bind.version: %s"):format(bind_version))
+ end
+ return stdnse.format_output(true, result)
+end
diff --git a/scripts/script.db b/scripts/script.db
index 66b4d954b..5c707fd77 100644
--- a/scripts/script.db
+++ b/scripts/script.db
@@ -55,6 +55,7 @@ Entry { filename = "dns-brute.nse", categories = { "discovery", "intrusive", } }
Entry { filename = "dns-cache-snoop.nse", categories = { "discovery", "intrusive", } }
Entry { filename = "dns-fuzz.nse", categories = { "fuzzer", "intrusive", } }
Entry { filename = "dns-nsec-enum.nse", categories = { "discovery", "intrusive", } }
+Entry { filename = "dns-nsid.nse", categories = { "default", "discovery", } }
Entry { filename = "dns-random-srcport.nse", categories = { "external", "intrusive", } }
Entry { filename = "dns-random-txid.nse", categories = { "external", "intrusive", } }
Entry { filename = "dns-recursion.nse", categories = { "default", "safe", } }