mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
Use SOA request to get domain name in dns-zone-transfer. Fixes #3014
This commit is contained in:
@@ -164,9 +164,10 @@ end
|
|||||||
-- @param port Port to connect to.
|
-- @param port Port to connect to.
|
||||||
-- @param timeout Number of ms to wait for a response.
|
-- @param timeout Number of ms to wait for a response.
|
||||||
-- @param cnt Number of tries.
|
-- @param cnt Number of tries.
|
||||||
-- @param multiple If true, keep reading multiple responses until timeout.
|
-- @param multiple If true, keep reading multiple responses until timeout (ignored for TCP)
|
||||||
-- @return Status (true or false).
|
-- @return Status (true or false).
|
||||||
local function sendPackets(data, host, port, timeout, cnt, multiple, proto)
|
-- @return Response (if status is true).
|
||||||
|
function sendPackets(data, host, port, timeout, cnt, multiple, proto)
|
||||||
if proto == nil or proto == 'udp' then
|
if proto == nil or proto == 'udp' then
|
||||||
return sendPacketsUDP(data, host, port, timeout, cnt, multiple)
|
return sendPacketsUDP(data, host, port, timeout, cnt, multiple)
|
||||||
else
|
else
|
||||||
@@ -590,10 +591,10 @@ function findNiceAdditional(dtype, dec, retAll)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--
|
--
|
||||||
-- Encodes a FQDN
|
-- Encodes a FQDN as a string of length-prefixed labels
|
||||||
-- @param fqdn containing the fully qualified domain name
|
-- @param fqdn containing the fully qualified domain name
|
||||||
-- @return encQ containing the encoded value
|
-- @return encQ containing the encoded value
|
||||||
local function encodeFQDN(fqdn)
|
function encodeFQDN(fqdn)
|
||||||
if ( not(fqdn) or #fqdn == 0 ) then return "\0" end
|
if ( not(fqdn) or #fqdn == 0 ) then return "\0" end
|
||||||
|
|
||||||
local encQ = {}
|
local encQ = {}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
local dns = require "dns"
|
local dns = require "dns"
|
||||||
local ipOps = require "ipOps"
|
local ipOps = require "ipOps"
|
||||||
local listop = require "listop"
|
local listop = require "listop"
|
||||||
|
local math = require "math"
|
||||||
local nmap = require "nmap"
|
local nmap = require "nmap"
|
||||||
local shortport = require "shortport"
|
local shortport = require "shortport"
|
||||||
local stdnse = require "stdnse"
|
local stdnse = require "stdnse"
|
||||||
@@ -105,12 +106,12 @@ prerule = function()
|
|||||||
)
|
)
|
||||||
|
|
||||||
if not dns_opts.domain then
|
if not dns_opts.domain then
|
||||||
stdnse.debug3("Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.", SCRIPT_NAME, SCRIPT_TYPE)
|
stdnse.verbose1("Skipping '%s' %s, 'dnszonetransfer.domain' argument is missing.", SCRIPT_NAME, SCRIPT_TYPE)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
if not dns_opts.server then
|
if not dns_opts.server then
|
||||||
stdnse.debug3("Skipping '%s' %s, 'dnszonetransfer.server' argument is missing.", SCRIPT_NAME, SCRIPT_TYPE)
|
stdnse.verbose1("Skipping '%s' %s, 'dnszonetransfer.server' argument is missing.", SCRIPT_NAME, SCRIPT_TYPE)
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -158,30 +159,6 @@ local typetab = { 'A', 'NS', 'MD', 'MF', 'CNAME', 'SOA', 'MB', 'MG', 'MR',
|
|||||||
[32768]='TA', [32769]='DLV',
|
[32768]='TA', [32769]='DLV',
|
||||||
}
|
}
|
||||||
|
|
||||||
--- Whitelist of TLDs. Only way to reliably determine the root of a domain
|
|
||||||
--@class table
|
|
||||||
--@name tld
|
|
||||||
local tld = {
|
|
||||||
'aero', 'asia', 'biz', 'cat', 'com', 'coop', 'info', 'jobs', 'mobi', 'museum',
|
|
||||||
'name', 'net', 'org', 'pro', 'tel', 'travel', 'gov', 'edu', 'mil', 'int',
|
|
||||||
'ac','ad','ae','af','ag','ai','al','am','an','ao','aq','ar','as','at','au','aw',
|
|
||||||
'ax','az','ba','bb','bd','be','bf','bg','bh','bi','bj','bm','bn','bo','br','bs',
|
|
||||||
'bt','bv','bw','by','bz','ca','cc','cd','cf','cg','ch','ci','ck','cl','cm','cn',
|
|
||||||
'co','cr','cu','cv','cx','cy','cz','de','dj','dk','dm','do','dz','ec','ee','eg',
|
|
||||||
'eh','er','es','et','eu','fi','fj','fk','fm','fo','fr','ga','gb','gd','ge','gf',
|
|
||||||
'gg','gh','gi','gl','gm','gn','gp','gq','gr','gs','gt','gu','gw','gy','hk','hm',
|
|
||||||
'hn','hr','ht','hu','id','ie','il','im','in','io','iq','ir','is','it','je','jm',
|
|
||||||
'jo','jp','ke','kg','kh','ki','km','kn','kp','kr','kw','ky','kz','la','lb','lc',
|
|
||||||
'li','lk','lr','ls','lt','lu','lv','ly','ma','mc','md','me','mg','mh','mk','ml',
|
|
||||||
'mm','mn','mo','mp','mq','mr','ms','mt','mu','mv','mw','mx','my','mz','na','nc',
|
|
||||||
'ne','nf','ng','ni','nl','no','np','nr','nu','nz','om','pa','pe','pf','pg','ph',
|
|
||||||
'pk','pl','pm','pn','pr','ps','pt','pw','py','qa','re','ro','rs','ru','rw','sa',
|
|
||||||
'sb','sc','sd','se','sg','sh','si','sj','sk','sl','sm','sn','so','sr','st','su',
|
|
||||||
'sv','sy','sz','tc','td','tf','tg','th','tj','tk','tl','tm','tn','to','tp','tr',
|
|
||||||
'tt','tv','tw','tz','ua','ug','uk','um','us','uy','uz','va','vc','ve','vg','vi',
|
|
||||||
'vn','vu','wf','ws','ye','yt','yu','za','zm','zw'
|
|
||||||
}
|
|
||||||
|
|
||||||
--- Convert two bytes into a 16bit number.
|
--- Convert two bytes into a 16bit number.
|
||||||
--@param data String of data.
|
--@param data String of data.
|
||||||
--@param idx Index in the string (first of two consecutive bytes).
|
--@param idx Index in the string (first of two consecutive bytes).
|
||||||
@@ -190,16 +167,6 @@ function bto16(data, idx)
|
|||||||
return (">I2"):unpack(data, idx)
|
return (">I2"):unpack(data, idx)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Check if domain name element is a tld
|
|
||||||
--@param elm Domain name element to check.
|
|
||||||
--@return boolean
|
|
||||||
function valid_tld(elm)
|
|
||||||
for i,v in ipairs(tld) do
|
|
||||||
if elm == v then return true end
|
|
||||||
end
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
--- Parse an RFC 1035 domain name.
|
--- Parse an RFC 1035 domain name.
|
||||||
--@param data String of data.
|
--@param data String of data.
|
||||||
--@param offset Offset in the string to read the domain name.
|
--@param offset Offset in the string to read the domain name.
|
||||||
@@ -209,38 +176,6 @@ function parse_domain(data, offset)
|
|||||||
return offset, string.format("%s.", domain)
|
return offset, string.format("%s.", domain)
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Build RFC 1035 root domain name from the name of the DNS server
|
|
||||||
-- (e.g ns1.website.com.ar -> \007website\003com\002ar\000).
|
|
||||||
--@param host The host.
|
|
||||||
function build_domain(host)
|
|
||||||
local names, buf, x
|
|
||||||
local abs_name, i, tmp
|
|
||||||
|
|
||||||
buf = strbuf.new()
|
|
||||||
abs_name = {}
|
|
||||||
|
|
||||||
names = stringaux.strsplit('%.', host)
|
|
||||||
if names == nil then names = {host} end
|
|
||||||
|
|
||||||
-- try to determine root of domain name
|
|
||||||
for i, x in ipairs(listop.reverse(names)) do
|
|
||||||
table.insert(abs_name, x)
|
|
||||||
if not valid_tld(x) then break end
|
|
||||||
end
|
|
||||||
|
|
||||||
i = 1
|
|
||||||
abs_name = listop.reverse(abs_name)
|
|
||||||
|
|
||||||
-- prepend each element with its length
|
|
||||||
while i <= #abs_name do
|
|
||||||
buf = buf .. string.char(#abs_name[i]) .. abs_name[i]
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
buf = buf .. '\000'
|
|
||||||
return strbuf.dump(buf)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function parse_num_domain(data, offset)
|
local function parse_num_domain(data, offset)
|
||||||
local number, domain
|
local number, domain
|
||||||
number = bto16(data, offset)
|
number = bto16(data, offset)
|
||||||
@@ -720,19 +655,40 @@ action = function(host, port)
|
|||||||
string.format("'%s' script needs a dnszonetransfer.domain argument.",
|
string.format("'%s' script needs a dnszonetransfer.domain argument.",
|
||||||
SCRIPT_TYPE))
|
SCRIPT_TYPE))
|
||||||
end
|
end
|
||||||
if not dns_opts.port then
|
if not host then
|
||||||
dns_opts.port = 53
|
host = dns_opts.server
|
||||||
|
end
|
||||||
|
local proto = "tcp"
|
||||||
|
if not port then
|
||||||
|
port = dns_opts.port or 53
|
||||||
|
else
|
||||||
|
proto = port.protocol
|
||||||
end
|
end
|
||||||
|
|
||||||
local soc = nmap.new_socket()
|
local soc = nmap.new_socket()
|
||||||
local catch = function() soc:close() end
|
local catch = function() soc:close() end
|
||||||
local try = nmap.new_try(catch)
|
local try = nmap.new_try(catch)
|
||||||
soc:set_timeout(4000)
|
soc:set_timeout(4000)
|
||||||
try(soc:connect(dns_opts.server, dns_opts.port))
|
try(soc:connect(host, port))
|
||||||
|
|
||||||
|
-- To find the domain portion, we issue a SOA request.
|
||||||
|
local dnspacket = dns.newPacket()
|
||||||
|
dns.addQuestion(dnspacket, dns_opts.domain:lower(), dns.types.SOA, dns.CLASS.IN)
|
||||||
|
dnspacket.id = math.random(0xffff)
|
||||||
|
local response = try(dns.sendPackets(dns.encode(dnspacket), host, port, 4000, 1, nil, proto))
|
||||||
|
local soa_pkt = dns.decode(response[1].data)
|
||||||
|
local name
|
||||||
|
if #soa_pkt.answers > 0 then
|
||||||
|
-- either it's a domain and the domain is in answer,
|
||||||
|
name = soa_pkt.answers[1].dname
|
||||||
|
elseif #soa_pkt.auth > 0 then
|
||||||
|
-- or it's a hostname and the domain is in authority.
|
||||||
|
name = soa_pkt.auth[1].dname
|
||||||
|
end
|
||||||
|
|
||||||
local req_id = '\222\173'
|
local req_id = '\222\173'
|
||||||
local offset = 1
|
local offset = 1
|
||||||
local name = build_domain(string.lower(dns_opts.domain))
|
name = dns.encodeFQDN(name)
|
||||||
local pkt_len = #name + 16
|
local pkt_len = #name + 16
|
||||||
|
|
||||||
-- build axfr request
|
-- build axfr request
|
||||||
|
|||||||
Reference in New Issue
Block a user