mirror of
https://github.com/nmap/nmap.git
synced 2025-12-08 05:31:31 +00:00
Added llmnr-resolve.nse script.
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE] Added llmnr-resolve script which resolves a hostname by using the LLMNR
|
||||||
|
(Link-Local Multicast Name Resolution) protocol. [Hani Benhabiles]
|
||||||
|
|
||||||
o [NSE] Added broadcast-igmp-discovery script which discovers and outputs
|
o [NSE] Added broadcast-igmp-discovery script which discovers and outputs
|
||||||
interesting information from targets that have multicast groups memberships.
|
interesting information from targets that have multicast groups memberships.
|
||||||
[Hani Benhabiles]
|
[Hani Benhabiles]
|
||||||
|
|||||||
211
scripts/llmnr-resolve.nse
Normal file
211
scripts/llmnr-resolve.nse
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
local nmap = require "nmap"
|
||||||
|
local stdnse = require "stdnse"
|
||||||
|
local table = require "table"
|
||||||
|
local bin = require "bin"
|
||||||
|
local bit = require "bit"
|
||||||
|
local packet = require "packet"
|
||||||
|
local ipOps = require "ipOps"
|
||||||
|
local target = require "target"
|
||||||
|
|
||||||
|
description = [[
|
||||||
|
Resolves a hostname by using the LLMNR (Link-Local Multicast Name Resolution) protocol.
|
||||||
|
|
||||||
|
The script works by sending a LLMNR Standard Query containing the hostname to
|
||||||
|
the 5355 UDP port on the 224.0.0.252 multicast address. It listens for any
|
||||||
|
LLMNR responses that are sent to the local machine with a 5355 UDP source port.
|
||||||
|
A hostname to resolve must be provided.
|
||||||
|
|
||||||
|
For more information, see:
|
||||||
|
* http://technet.microsoft.com/en-us/library/bb878128.aspx
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
--@args llmnr-resolve.hostname Hostname to resolve.
|
||||||
|
--
|
||||||
|
--@args llmnr-resolve.timeout Max time to wait for a response. Defaults to
|
||||||
|
-- <code>3</code> seconds.
|
||||||
|
--
|
||||||
|
--@usage
|
||||||
|
-- nmap --script llmnr-resolve --script-args 'llmnr-resolve.hostname=examplename' -e wlan0
|
||||||
|
--
|
||||||
|
--@output
|
||||||
|
-- Pre-scan script results:
|
||||||
|
-- | llmnr-query:
|
||||||
|
-- |_ acer-PC : 192.168.1.4
|
||||||
|
--
|
||||||
|
|
||||||
|
prerule = function()
|
||||||
|
if not nmap.is_privileged() then
|
||||||
|
stdnse.print_verbose("%s not running due to lack of privileges.", SCRIPT_NAME)
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
author = "Hani Benhabiles"
|
||||||
|
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
|
||||||
|
categories = {"discovery", "safe", "broadcast"}
|
||||||
|
|
||||||
|
|
||||||
|
--- Returns a raw llmnr query
|
||||||
|
-- @param hostname Hostname to query for.
|
||||||
|
-- @return query Raw llmnr query.
|
||||||
|
local llmnrQuery = function(hostname)
|
||||||
|
local query = bin.pack(">S", math.random(0,65535)) -- transaction ID
|
||||||
|
query = query .. bin.pack(">S", 0x0000) -- Flags: Standard Query
|
||||||
|
query = query .. bin.pack(">S", 0x0001) -- Questions = 1
|
||||||
|
query = query .. bin.pack(">S", 0x0000) -- Answer RRs = 0
|
||||||
|
query = query .. bin.pack(">S", 0x0000) -- Authority RRs = 0
|
||||||
|
query = query .. bin.pack(">S", 0x0000) -- Additional RRs = 0
|
||||||
|
query = query .. bin.pack(">CAC", #hostname, hostname, 0x00) -- Hostname
|
||||||
|
query = query .. bin.pack(">S", 0x0001) -- Type: Host Address
|
||||||
|
query = query .. bin.pack(">S", 0x0001) -- Class: IN
|
||||||
|
return query
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Sends a llmnr query.
|
||||||
|
-- @param query Query to send.
|
||||||
|
local llmnrSend = function(query, mcast, mport)
|
||||||
|
-- Multicast IP and UDP port
|
||||||
|
local sock = nmap.new_socket()
|
||||||
|
local status, err = sock:connect(mcast, mport, "udp")
|
||||||
|
if not status then
|
||||||
|
stdnse.print_debug("%s: %s", SCRIPT_NAME, err)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
sock:send(query)
|
||||||
|
sock:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Listens for llmnr responses
|
||||||
|
-- @interface Network interface to listen on.
|
||||||
|
-- @timeout Maximum time to listen.
|
||||||
|
-- @result table to put responses into.
|
||||||
|
local llmnrListen = function(interface, timeout, result)
|
||||||
|
local condvar = nmap.condvar(result)
|
||||||
|
local start = nmap.clock_ms()
|
||||||
|
local listener = nmap.new_socket()
|
||||||
|
local status, l3data
|
||||||
|
|
||||||
|
-- packets that are sent to our UDP port number 5355
|
||||||
|
local filter = 'dst host ' .. interface.address .. ' and udp src port 5355'
|
||||||
|
listener:set_timeout(100)
|
||||||
|
listener:pcap_open(interface.device, 1024, true, filter)
|
||||||
|
|
||||||
|
while (nmap.clock_ms() - start) < timeout do
|
||||||
|
status, _, _, l3data = listener:pcap_receive()
|
||||||
|
if status then
|
||||||
|
local p = packet.Packet:new(l3data, #l3data)
|
||||||
|
-- Skip IP and UDP headers
|
||||||
|
local llmnr = string.sub(l3data, p.ip_hl*4 + 8 + 1)
|
||||||
|
-- Flags
|
||||||
|
local _, trans = bin.unpack(">S", llmnr)
|
||||||
|
local _, flags = bin.unpack(">S", llmnr, 3)
|
||||||
|
-- Questions number
|
||||||
|
local _, questions = bin.unpack(">S", llmnr, 5)
|
||||||
|
|
||||||
|
-- Make verifications
|
||||||
|
-- Message == Response bit
|
||||||
|
-- and 1 Question (hostname we requested) and
|
||||||
|
if (bit.rshift(flags, 15) == 1) and questions == 0x01 then
|
||||||
|
stdnse.print_debug("%s got response from %s", SCRIPT_NAME, p.ip_src)
|
||||||
|
-- Skip header's 12 bytes
|
||||||
|
-- extract host length
|
||||||
|
local index, qlen = bin.unpack(">C", llmnr, 13)
|
||||||
|
-- Skip hostname, null byte, type field and class field
|
||||||
|
index = index + qlen + 1 + 2 + 2
|
||||||
|
|
||||||
|
-- Now, answer record
|
||||||
|
local response, alen = {}
|
||||||
|
index, alen = bin.unpack(">C", llmnr, index)
|
||||||
|
-- Extract hostname with the correct case sensivity.
|
||||||
|
index, response.hostname = bin.unpack(">A".. alen, llmnr, index)
|
||||||
|
|
||||||
|
-- skip null byte, type, class, ttl, dlen
|
||||||
|
index = index + 1 + 2 + 2 + 4 + 2
|
||||||
|
index, response.address = bin.unpack("<I", llmnr, index)
|
||||||
|
response.address = ipOps.fromdword(response.address)
|
||||||
|
table.insert(result, response)
|
||||||
|
else
|
||||||
|
stdnse.print_debug("%s skipped llmnr response.", SCRIPT_NAME)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
condvar("signal")
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Returns the network interface used to send packets to a target host.
|
||||||
|
--@param target host to which the interface is used.
|
||||||
|
--@return interface Network interface used for target host.
|
||||||
|
local getInterface = function(target)
|
||||||
|
-- First, create dummy UDP connection to get interface
|
||||||
|
local sock = nmap.new_socket()
|
||||||
|
local status, err = sock:connect(target, "12345", "udp")
|
||||||
|
if not status then
|
||||||
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local status, address, _, _, _ = sock:get_info()
|
||||||
|
if not status then
|
||||||
|
stdnse.print_verbose("%s: %s", SCRIPT_NAME, err)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for _, interface in pairs(nmap.list_interfaces()) do
|
||||||
|
if interface.address == address then
|
||||||
|
return interface
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
action = function()
|
||||||
|
local timeout = tonumber(stdnse.get_script_args(SCRIPT_NAME .. ".timeout")) or 3
|
||||||
|
local hostname = stdnse.get_script_args(SCRIPT_NAME .. ".hostname")
|
||||||
|
timeout = timeout * 1000
|
||||||
|
local result, output = {}, {}
|
||||||
|
local mcast = "224.0.0.252"
|
||||||
|
local mport = 5355
|
||||||
|
|
||||||
|
-- Check if a valid hostname was provided
|
||||||
|
if not hostname or #hostname == 0 then
|
||||||
|
stdnse.print_debug("%s no hostname was provided.", SCRIPT_NAME)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Check if a valid interface was provided
|
||||||
|
local interface = nmap.get_interface()
|
||||||
|
if interface then
|
||||||
|
interface = nmap.get_interface_info(interface)
|
||||||
|
else
|
||||||
|
interface = getInterface(mcast)
|
||||||
|
end
|
||||||
|
if not interface then
|
||||||
|
return ("\n ERROR: Couldn't get interface for %s"):format(mcast)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Launch listener thread
|
||||||
|
stdnse.new_thread(llmnrListen, interface, timeout, result)
|
||||||
|
-- Craft raw query
|
||||||
|
local query = llmnrQuery(hostname)
|
||||||
|
-- Small sleep so the listener doesn't miss the response
|
||||||
|
stdnse.sleep(0.5)
|
||||||
|
-- Send query
|
||||||
|
llmnrSend(query, mcast, mport)
|
||||||
|
-- Wait for listener thread to finish
|
||||||
|
local condvar = nmap.condvar(result)
|
||||||
|
condvar("wait")
|
||||||
|
|
||||||
|
-- Check responses
|
||||||
|
if #result > 0 then
|
||||||
|
for _, response in pairs(result) do
|
||||||
|
if target.ALLOW_NEW_TARGETS then target.add(response.address) end
|
||||||
|
table.insert(output, response.hostname.. " : " .. response.address)
|
||||||
|
end
|
||||||
|
if not target.ALLOW_NEW_TARGETS then
|
||||||
|
table.insert(result,"Use the newtargets script-arg to add the results as targets")
|
||||||
|
end
|
||||||
|
return stdnse.format_output(true, output)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -233,6 +233,7 @@ Entry { filename = "ldap-novell-getpass.nse", categories = { "discovery", "safe"
|
|||||||
Entry { filename = "ldap-rootdse.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "ldap-rootdse.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "ldap-search.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "ldap-search.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "lexmark-config.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "lexmark-config.nse", categories = { "discovery", "safe", } }
|
||||||
|
Entry { filename = "llmnr-resolve.nse", categories = { "broadcast", "discovery", "safe", } }
|
||||||
Entry { filename = "lltd-discovery.nse", categories = { "broadcast", "discovery", "safe", } }
|
Entry { filename = "lltd-discovery.nse", categories = { "broadcast", "discovery", "safe", } }
|
||||||
Entry { filename = "maxdb-info.nse", categories = { "default", "version", } }
|
Entry { filename = "maxdb-info.nse", categories = { "default", "version", } }
|
||||||
Entry { filename = "mcafee-epo-agent.nse", categories = { "safe", "version", } }
|
Entry { filename = "mcafee-epo-agent.nse", categories = { "safe", "version", } }
|
||||||
|
|||||||
Reference in New Issue
Block a user