mirror of
https://github.com/nmap/nmap.git
synced 2025-12-09 06:01:28 +00:00
201 lines
4.9 KiB
Lua
201 lines
4.9 KiB
Lua
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 <target>
|
|
--
|
|
-- @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.3
|
|
-- Created 01/06/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
|
-- Revised 01/13/2010 - v0.2 - modified to use existing dns library instead of mdns, changed output to be less DNS like
|
|
-- Revised 02/01/2010 - v0.3 - removed incorrect try/catch statements
|
|
|
|
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 response 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 <code>dtype</code> or an Error message.
|
|
function getRecordType( dtype, response, 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
|
|
|
|
--- Function used to compare discovered DNS services so they can be sorted
|
|
--
|
|
-- @param a table containing first item
|
|
-- @param b table containing second item
|
|
-- @return true if the port of a is less than the port of b
|
|
local function serviceCompare(a, b)
|
|
local port_a = a.name:match("^(%d+)") or 0
|
|
local port_b = b.name:match("^(%d+)") or 0
|
|
|
|
if ( tonumber(port_a) < tonumber(port_b) ) then
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
action = function(host, port)
|
|
|
|
local result = {}
|
|
local deviceinfo = {}
|
|
local status, response = dns.query( "_services._dns-sd._udp.local", { port = 5353, host = host.ip, dtype="PTR", retAll=true} )
|
|
|
|
if not status then
|
|
return
|
|
end
|
|
|
|
-- for each service response in answers, send a service query
|
|
for _, v in ipairs( response ) do
|
|
|
|
local service = {}
|
|
local txt = {}
|
|
local ip, ipv6, srv, address, port, proto
|
|
|
|
status, response = dns.query( v, { port = 5353, host = host.ip, dtype="PTR", retPkt=true} )
|
|
|
|
if not status then
|
|
return
|
|
end
|
|
|
|
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
|
|
|
|
-- sort the tables per port
|
|
table.sort( result, serviceCompare )
|
|
|
|
-- 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
|