From 0d47952611198ef6b1163f366dc03922d20b1475 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:42:04 +0200 Subject: Adding upstream version 7.94+git20230807.3be01efb1+dfsg. Signed-off-by: Daniel Baumann --- scripts/dns-srv-enum.nse | 179 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 179 insertions(+) create mode 100644 scripts/dns-srv-enum.nse (limited to 'scripts/dns-srv-enum.nse') diff --git a/scripts/dns-srv-enum.nse b/scripts/dns-srv-enum.nse new file mode 100644 index 0000000..86dd042 --- /dev/null +++ b/scripts/dns-srv-enum.nse @@ -0,0 +1,179 @@ +local coroutine = require "coroutine" +local dns = require "dns" +local nmap = require "nmap" +local stdnse = require "stdnse" +local tab = require "tab" +local table = require "table" +local target = require "target" + +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 S2S + - XMPP C2S +]] + +--- +-- @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 server-to-server +-- | 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 https://nmap.org/book/man-legal.html" +categories = {"discovery", "safe"} + + +local arg_domain = stdnse.get_script_args(SCRIPT_NAME .. ".domain") +local arg_filter = stdnse.get_script_args(SCRIPT_NAME .. ".filter") + +prerule = function() return not(not(arg_domain)) end + +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 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 + if target.ALLOW_NEW_TARGETS then + target.add(host) + end + 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 server-to-server", query = {"_xmpp-server._tcp"} }, + { name = "XMPP client-to-server", query = {"_xmpp-client._tcp"} }, + } + + if ( not(checkFilter(services)) ) then + return stdnse.format_output(false, ("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 + for t in pairs(threads) do + if ( coroutine.status(t) == "dead" ) then threads[t] = nil end + end + if ( next(threads) ) then + condvar "wait" + end + until( next(threads) == nil ) + + table.sort(result, function(a,b) return a.name < b.name end) + + return stdnse.format_output(true, result) +end -- cgit v1.2.3