diff --git a/CHANGELOG b/CHANGELOG index b2ca63563..0e19ddad8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added script dns-srv-enum that enumerates DNS service records for a + given domain. [Patrik] + o [NSE] Added script nessus-xmlrpc-brute that performs brute force password guessing against the Nessus web GUI. [Patrik] diff --git a/scripts/dns-srv-enum.nse b/scripts/dns-srv-enum.nse new file mode 100644 index 000000000..565582689 --- /dev/null +++ b/scripts/dns-srv-enum.nse @@ -0,0 +1,169 @@ +description = [[ +Enumerates various common service (SRV) records for a given domain name. +The service records contain the hostname, port and priority of servers for a given service. +The following services are enumerated by the script: + - Active Directory Global Catalog + - Exchange Autodiscovery + - Kerberos KDC Service + - Kerberos Passwd Change Service + - LDAP Servers + - SIP Servers + - XMPP Servers +]] + +--- +-- @usage +-- nmap --script dns-srv-enum --script-args "dns-srv-enum.domain='example.com'" +-- +-- @output +-- | dns-srv-enum: +-- | Active Directory Global Catalog +-- | service prio weight host +-- | 3268/tcp 0 100 stodc01.example.com +-- | Kerberos KDC Service +-- | service prio weight host +-- | 88/tcp 0 100 stodc01.example.com +-- | 88/udp 0 100 stodc01.example.com +-- | Kerberos Password Change Service +-- | service prio weight host +-- | 464/tcp 0 100 stodc01.example.com +-- | 464/udp 0 100 stodc01.example.com +-- | LDAP +-- | service prio weight host +-- | 389/tcp 0 100 stodc01.example.com +-- | SIP +-- | service prio weight host +-- | 5060/udp 10 50 vclux2.example.com +-- | 5070/udp 10 50 vcbxl2.example.com +-- | 5060/tcp 10 50 vclux2.example.com +-- | 5060/tcp 10 50 vcbxl2.example.com +-- | XMPP +-- | service prio weight host +-- | 5269/tcp 5 0 xmpp-server.l.example.com +-- | 5269/tcp 20 0 alt2.xmpp-server.l.example.com +-- | 5269/tcp 20 0 alt4.xmpp-server.l.example.com +-- | 5269/tcp 20 0 alt3.xmpp-server.l.example.com +-- |_ 5269/tcp 20 0 alt1.xmpp-server.l.example.com +-- +-- @args dns-srv-enum.domain string containing the domain to query +-- @args dns-srv-enum.filter string containing the service to query +-- (default: all) + +author = "Patrik Karlsson" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"discovery", "safe"} + +require 'dns' +require 'tab' + +prerule = function() return true end +hostrule = function() return true end + +local arg_domain = stdnse.get_script_args(SCRIPT_NAME .. ".domain") +local arg_filter = stdnse.get_script_args(SCRIPT_NAME .. ".filter") + +local function parseSvcList(services) + local i = 1 + return function() + local svc = services[i] + if ( svc ) then + i=i + 1 + else + return + end + return svc.name, svc.query + end +end + +local function fail(err) return ("\n ERROR: %s"):format(err or "") end + +local function parseSrvResponse(resp) + local i = 1 + if ( resp.answers ) then + table.sort(resp.answers, + function(a, b) + if ( a.SRV and b.SRV and a.SRV.prio and b.SRV.prio ) then + return a.SRV.prio < b.SRV.prio + end + end + ) + end + return function() + if ( not(resp.answers) or 0 == #resp.answers ) then return end + if ( not(resp.answers[i]) ) then + return + elseif ( resp.answers[i].SRV ) then + local srv = resp.answers[i].SRV + i = i + 1 + return srv.target, srv.port, srv.prio, srv.weight + end + end +end + +local function checkFilter(services) + if ( not(arg_filter) or "" == arg_filter or "all" == arg_filter ) then + return true + end + for name, queries in parseSvcList(services) do + if ( name == arg_filter ) then + return true + end + end + return false +end + +local function doQuery(name, queries, result) + local condvar = nmap.condvar(result) + local svc_result = tab.new(4) + tab.addrow(svc_result, "service", "prio", "weight", "host") + for _, query in ipairs(queries) do + local fqdn = ("%s.%s"):format(query, arg_domain) + local status, resp = dns.query(fqdn, { dtype="SRV", retAll=true, retPkt=true } ) + for host, port, prio, weight in parseSrvResponse(resp) do + local proto = query:sub(-3) + tab.addrow(svc_result, ("%d/%s"):format(port, proto), prio, weight, host) + end + end + if ( #svc_result ~= 1 ) then + table.insert(result, { name = name, tab.dump(svc_result) }) + end + condvar "signal" +end + +action = function(host) + + local services = { + { name = "Active Directory Global Catalog", query = {"_gc._tcp"} }, + { name = "Exchange Autodiscovery", query = {"_autodiscover._tcp"} }, + { name = "Kerberos KDC Service", query = {"_kerberos._tcp", "_kerberos._udp"} }, + { name = "Kerberos Password Change Service", query = {"_kpasswd._tcp", "_kpasswd._udp"} }, + { name = "LDAP", query = {"_ldap._tcp"} }, + { name = "SIP", query = {"_sip._udp", "_sip._tcp"} }, + { name = "XMPP", query = {"_xmpp-server._tcp"} }, + } + + if ( not(checkFilter(services)) ) then + return fail(("Invalid filter (%s) was supplied"):format(arg_filter)) + end + + local threads, result = {}, {} + for name, queries in parseSvcList(services) do + if ( not(arg_filter) or 0 == #arg_filter or + "all" == arg_filter or arg_filter == name ) then + local co = stdnse.new_thread(doQuery, name, queries, result) + threads[co] = true + end + end + + local condvar = nmap.condvar(result) + repeat + condvar "wait" + for t in pairs(threads) do + if ( coroutine.status(t) == "dead" ) then threads[t] = nil end + end + until( next(threads) == nil ) + + table.sort(result, function(a,b) return a.name < b.name end) + + return stdnse.format_output(true, result) +end \ No newline at end of file diff --git a/scripts/script.db b/scripts/script.db index 88b23f857..db050f160 100644 --- a/scripts/script.db +++ b/scripts/script.db @@ -60,6 +60,7 @@ Entry { filename = "dns-random-srcport.nse", categories = { "external", "intrusi Entry { filename = "dns-random-txid.nse", categories = { "external", "intrusive", } } Entry { filename = "dns-recursion.nse", categories = { "default", "safe", } } Entry { filename = "dns-service-discovery.nse", categories = { "default", "discovery", "safe", } } +Entry { filename = "dns-srv-enum.nse", categories = { "discovery", "safe", } } Entry { filename = "dns-update.nse", categories = { "discovery", "safe", } } Entry { filename = "dns-zeustracker.nse", categories = { "discovery", "external", "malware", "safe", } } Entry { filename = "dns-zone-transfer.nse", categories = { "discovery", "intrusive", } }