From ad0a63deef24fc6997603bba460d1bb8b2613d5b Mon Sep 17 00:00:00 2001 From: david Date: Thu, 21 Jan 2010 01:53:46 +0000 Subject: [PATCH] Add dns-service-discovery.nse from Patrik Karlsson. See http://seclists.org/nmap-dev/2010/q1/87 for more information. --- CHANGELOG | 6 + scripts/dns-service-discovery.nse | 182 ++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 scripts/dns-service-discovery.nse diff --git a/CHANGELOG b/CHANGELOG index 4e14084cb..b4057c635 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,11 @@ # Nmap Changelog ($Id$); -*-text-*- +o [NSE] Added the new dns-service-discovery protocol which uses DNS-SD + to identify services. DNS-SD is one part of automatic configuration + technologies known by names such as Bonjour, Rendezvous, and + Zeroconf. This one script can provide as much information as a full + port scan in some cases. [Patrik Karlsson] + o [NSE] Added a new library, afp.lua, and a script that uses it, afp-showmount. The library is for the Apple Filing Protocol and the script shows shares and their permissions. [Patrik Karlsson] diff --git a/scripts/dns-service-discovery.nse b/scripts/dns-service-discovery.nse new file mode 100644 index 000000000..2b870f941 --- /dev/null +++ b/scripts/dns-service-discovery.nse @@ -0,0 +1,182 @@ +description=[[ +Attempts to discover a hosts services using the DNS Service Discovery protocol. + +The script first sends a query for _services._dns-sd._udp.local to get a +list of services. It then sends a followup query for each one to try to +get more information. +]] + + +--- +-- @usage +-- nmap --script=dns-service-discovery -p 5353 +-- +-- @output +-- PORT STATE SERVICE REASON +-- 5353/udp open zeroconf udp-response +-- | dns-service-discovery: +-- | 548/tcp afpovertcp +-- | model=MacBook5,1 +-- | Address=192.168.0.2 fe80:0:0:0:223:6cff:1234:5678 +-- | 3689/tcp daap +-- | txtvers=1 +-- | iTSh Version=196609 +-- | MID=0xFB5338C04123456 +-- | Database ID=6FA9761FE123456 +-- | dmv=131078 +-- | Version=196616 +-- | OSsi=0x1F6 +-- | Machine Name=Patrik Karlsson\xE2\x80\x99s Library +-- | Media Kinds Shared=1 +-- | Machine ID=8945A7123456 +-- | Password=0 +-- |_ Address=192.168.0.2 fe80:0:0:0:223:6cff:1234:5678 + + +-- Version 0.2 +-- Created 01/06/2010 - v0.1 - created by Patrik Karlsson +-- Revised 01/13/2010 - v0.2 - modified to use existing dns library instead of mdns, changed output to be less DNS like + +author = "Patrik Karlsson" +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"default", "discovery", "safe"} + +require 'shortport' +require 'dns' + +portrule = shortport.portnumber(5353, "udp") + +--- Gets a record from both the Answer and Additional section +-- +-- @param dtype DNS resource record type. +-- @param dec Decoded DNS response. +-- @param retAll If true, return all entries, not just the first. +-- @return True if one or more answers of the required type were found - otherwise false. +-- @return Answer according to the answer fetcher for dtype or an Error message. +function getRecordType( dtype, dec, retAll ) + + local result = {} + local status1, answers = dns.findNiceAnswer( dtype, response, retAll ) + + if status1 then + if retAll then + for _, v in ipairs(answers) do + table.insert(result, string.format("%s", v) ) + end + else + return true, answers + end + end + + local status2, answers = dns.findNiceAdditional( dtype, response, retAll ) + + if status2 then + if retAll then + for _, v in ipairs(answers) do + table.insert(result, v) + end + else + return true, answers + end + end + + if not status1 and not status2 then + return false, answers + end + + return true, result + +end + +action = function(host, port) + + -- do some exception handling / cleanup + local catch = function() + socket:close() + end + + local try = nmap.new_try(catch) + + local result = {} + local deviceinfo = {} + + response = try( dns.query( "_services._dns-sd._udp.local", { port = 5353, host = host.ip, dtype="PTR", retAll=true} ) ) + + -- for each service response in answers, send a service query + for _, v in ipairs( response ) do + + stdnse.print_debug( string.format("-> Sending MDNS query for: %s", v ) ) + local service = {} + local txt = {} + local status, ip, ipv6, srv, address, port, proto + + response = try( dns.query( v, { port = 5353, host = host.ip, dtype="PTR", retPkt=true} ) ) + + status, ip = getRecordType( dns.types.A, response, false ) + + if status then + address = ip + end + + status, ipv6 = getRecordType( dns.types.AAAA, response, false ) + + if status then + address = address .. " " .. ipv6 + end + + status, txt = getRecordType( dns.types.TXT, response, true ) + + if status then + for _, v in ipairs(txt) do + if v:len() > 0 then + table.insert(service, v) + end + end + end + + status, srv = getRecordType( dns.types.SRV, response, false ) + + if status then + local srvparams = stdnse.strsplit( ":", srv ) + + if #srvparams > 3 then + port = srvparams[3] + end + end + + if address then + table.insert( service, ("Address=%s"):format( address ) ) + end + + if v == "_device-info._tcp.local" then + service.name = "Device Information" + deviceinfo = service + else + local serviceparams = stdnse.strsplit("[.]", v) + + if #serviceparams > 2 then + local servicename = serviceparams[1]:sub(2) + local proto = serviceparams[2]:sub(2) + + if port == nil or proto == nil or servicename == nil then + service.name = v + else + service.name = string.format( "%s/%s %s", port, proto, servicename) + end + end + + table.insert( result, service ) + + end + + end + + -- we want the device information at the end + table.insert( result, deviceinfo ) + + -- set port to open + nmap.set_port_state(host, port, "open") + + return stdnse.format_output(true, result ) + +end