From c579d844ba8e663c502dfe71345191ec824153c0 Mon Sep 17 00:00:00 2001 From: patrik Date: Tue, 17 Jan 2012 11:37:19 +0000 Subject: [PATCH] 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. --- CHANGELOG | 6 ++++ nselib/dns.lua | 53 +++++++++++++++++++++++++++------- scripts/dns-nsid.nse | 69 ++++++++++++++++++++++++++++++++++++++++++++ scripts/script.db | 1 + 4 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 scripts/dns-nsid.nse 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", } }