mirror of
https://github.com/nmap/nmap.git
synced 2025-12-12 10:49:02 +00:00
o [NSE] Added a new Web Service Dynamic Discovery library (wsdd) and the two
scripts broadcast-wsdd-discover and wsdd-discover. [Patrik]
This commit is contained in:
@@ -1,5 +1,8 @@
|
|||||||
# Nmap Changelog ($Id$); -*-text-*-
|
# Nmap Changelog ($Id$); -*-text-*-
|
||||||
|
|
||||||
|
o [NSE] Added a new Web Service Dynamic Discovery library (wsdd) and the two
|
||||||
|
scripts broadcast-wsdd-discover and wsdd-discover. [Patrik]
|
||||||
|
|
||||||
o [Zenmap] Upgraded to the newer gtk.Tooltip API to avoid deprecation
|
o [Zenmap] Upgraded to the newer gtk.Tooltip API to avoid deprecation
|
||||||
messages about gtk.Tooltip. [Rob Nicholls]
|
messages about gtk.Tooltip. [Rob Nicholls]
|
||||||
|
|
||||||
|
|||||||
373
nselib/wsdd.lua
Normal file
373
nselib/wsdd.lua
Normal file
@@ -0,0 +1,373 @@
|
|||||||
|
--- A library that enables scripts to send Web Service Dynamic Discovery probes
|
||||||
|
-- and perform some very basic decoding of responses. The library is in no way
|
||||||
|
-- a full WSDD implementation it's rather the result of some packet captures
|
||||||
|
-- and some creative coding.
|
||||||
|
--
|
||||||
|
-- The "general" probe was captured of the wire of a Windows 7 box while
|
||||||
|
-- connecting to the network. The "wcf" probe was captured from a custom tool
|
||||||
|
-- tool performing WCF discovery in .NET 4.0.
|
||||||
|
--
|
||||||
|
-- More information about the protocol can be found here:
|
||||||
|
-- * http://docs.oasis-open.org/ws-dd/discovery/1.1/os/wsdd-discovery-1.1-spec-os.pdf
|
||||||
|
-- * http://specs.xmlsoap.org/ws/2005/04/discovery/ws-discovery.pdf
|
||||||
|
--
|
||||||
|
-- The library contains the following classes
|
||||||
|
-- * <code>Comm</code>
|
||||||
|
-- ** A class that handles most communication
|
||||||
|
-- * <code>Helper</code>
|
||||||
|
-- ** The helper class wraps the <code>Comm</code> class using functions with a more descriptive name.
|
||||||
|
-- * <code>Util</code>
|
||||||
|
-- ** The Util class contains a number of static functions mainly used to convert data.
|
||||||
|
-- * <code>Decoders</code>
|
||||||
|
-- ** The Decoders class contains static functions used for decoding probe matches
|
||||||
|
--
|
||||||
|
-- The following code snipplet shows how the library can be used:
|
||||||
|
-- <code>
|
||||||
|
-- local helper = wsdd.Helper:new()
|
||||||
|
-- helper:setMulticast(true)
|
||||||
|
-- return stdnse.format_output( helper:discoverDevices() )
|
||||||
|
-- </code>
|
||||||
|
--
|
||||||
|
-- @author "Patrik Karlsson <patrik@cqure.net>"
|
||||||
|
-- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
|
||||||
|
--
|
||||||
|
|
||||||
|
module(... or "wsdd", package.seeall)
|
||||||
|
|
||||||
|
require 'openssl'
|
||||||
|
require 'target'
|
||||||
|
|
||||||
|
-- The different probes
|
||||||
|
local probes = {
|
||||||
|
|
||||||
|
-- Detects devices supporting the WSDD protocol
|
||||||
|
{
|
||||||
|
name = 'general',
|
||||||
|
desc = 'Devices',
|
||||||
|
data = '<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" ' ..
|
||||||
|
'xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" ' ..
|
||||||
|
'xmlns:wsd="http://schemas.xmlsoap.org/ws/2005/04/discovery">' ..
|
||||||
|
'<env:Header>' ..
|
||||||
|
'<wsd:AppSequence InstanceId="1285624958737" MessageNumber="1" ' ..
|
||||||
|
'SequenceId="urn:uuid:#uuid#"/>' ..
|
||||||
|
'<wsa:To>urn:schemas-xmlsoap-org:ws:2005:04:discovery</wsa:To>' ..
|
||||||
|
'<wsa:Action>' ..
|
||||||
|
'http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe' ..
|
||||||
|
'</wsa:Action><wsa:MessageID>urn:uuid:#uuid#</wsa:MessageID>' ..
|
||||||
|
'</env:Header><env:Body><wsd:Probe/></env:Body></env:Envelope>'
|
||||||
|
},
|
||||||
|
|
||||||
|
-- Detects Windows Communication Framework (WCF) web services
|
||||||
|
{
|
||||||
|
name = 'wcf',
|
||||||
|
desc = 'WCF Services',
|
||||||
|
data = '<s:Envelope xmlns:s="http://www.w3.org/2003/05/soap-envelope" ' ..
|
||||||
|
'xmlns:a="http://www.w3.org/2005/08/addressing">' ..
|
||||||
|
'<s:Header>' ..
|
||||||
|
'<a:Action s:mustUnderstand="1">' ..
|
||||||
|
'http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01/Probe' ..
|
||||||
|
'</a:Action>' ..
|
||||||
|
'<a:MessageID>urn:uuid:#uuid#</a:MessageID>' ..
|
||||||
|
'<a:To s:mustUnderstand="1">' ..
|
||||||
|
'urn:docs-oasis-open-org:ws-dd:ns:discovery:2009:01' ..
|
||||||
|
'</a:To>' ..
|
||||||
|
'</s:Header>' ..
|
||||||
|
'<s:Body>' ..
|
||||||
|
'<Probe xmlns="http://docs.oasis-open.org/ws-dd/ns/discovery/2009/01">' ..
|
||||||
|
'<Duration xmlns="http://schemas.microsoft.com/ws/2008/06/discovery">' ..
|
||||||
|
'PT20S' ..
|
||||||
|
'</Duration>' ..
|
||||||
|
'</Probe>' ..
|
||||||
|
'</s:Body>' ..
|
||||||
|
'</s:Envelope>',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- A table that keeps track of received probe matches
|
||||||
|
local probe_matches = {}
|
||||||
|
|
||||||
|
Util = {
|
||||||
|
|
||||||
|
--- Creates a UUID
|
||||||
|
--
|
||||||
|
-- @return uuid string containing a uuid
|
||||||
|
generateUUID = function()
|
||||||
|
local rnd_bytes = select(2, bin.unpack( "H16", openssl.rand_bytes( 16 ) ) ):lower()
|
||||||
|
|
||||||
|
return ("%s-%s-%s-%s-%s"):format( rnd_bytes:sub(1, 8),
|
||||||
|
rnd_bytes:sub(9, 12), rnd_bytes:sub( 13, 16 ), rnd_bytes:sub( 17, 20 ),
|
||||||
|
rnd_bytes:sub(21, 32) )
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Retrieves a probe from the probes table by name
|
||||||
|
--
|
||||||
|
-- @param name string containing the name of the probe to retrieve
|
||||||
|
-- @return probe table containing the probe or nil if not found
|
||||||
|
getProbeByName = function( name )
|
||||||
|
for _, probe in ipairs(probes) do
|
||||||
|
if ( probe.name == name ) then
|
||||||
|
return probe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return
|
||||||
|
end,
|
||||||
|
|
||||||
|
getProbes = function() return probes end,
|
||||||
|
|
||||||
|
sha1sum = function(data) return openssl.sha1(data) end
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Decoders = {
|
||||||
|
|
||||||
|
--- Decodes a wcf probe response
|
||||||
|
--
|
||||||
|
-- @param data string containing the response as received over the wire
|
||||||
|
-- @return status true on success, false on failure
|
||||||
|
-- @return response table containing the following fields
|
||||||
|
-- <code>msgid</code>, <code>xaddrs</code>, <code>types</code>
|
||||||
|
-- err string containing the error message
|
||||||
|
['wcf'] = function( data )
|
||||||
|
local response = {}
|
||||||
|
|
||||||
|
-- extracts the messagid, so we can check if we already got a response
|
||||||
|
response.msgid = data:match("\<.*:MessageID\>urn:uuid:(.*)\</.*:MessageID\>")
|
||||||
|
|
||||||
|
-- if unable to parse msgid return nil
|
||||||
|
if ( not(response.msgid) ) then
|
||||||
|
return false, "No message id was found"
|
||||||
|
end
|
||||||
|
|
||||||
|
response.xaddrs = data:match("\<.*:*XAddrs\>(.*)\</.*:*XAddrs\>")
|
||||||
|
response.types = data:match("\<.*:Types\>[wsdp:]*(.*)\</.*:Types\>")
|
||||||
|
|
||||||
|
return true, response
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Decodes a general probe response
|
||||||
|
--
|
||||||
|
-- @param data string containing the response as received over the wire
|
||||||
|
-- @return status true on success, false on failure
|
||||||
|
-- @return response table containing the following fields
|
||||||
|
-- <code>msgid</code>, <code>xaddrs</code>, <code>types</code>
|
||||||
|
-- err string containing the error message
|
||||||
|
['general'] = function( data )
|
||||||
|
return Decoders['wcf'](data)
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Decodes an error message received from the service
|
||||||
|
--
|
||||||
|
-- @param data string containing the response as received over the wire
|
||||||
|
-- @return status true on success, false on failure
|
||||||
|
-- @return err string containing the error message
|
||||||
|
['error'] = function( data )
|
||||||
|
local response = "Failed to decode response from device: "
|
||||||
|
local err = data:match("\<SOAP.-ENV:Reason\>\<SOAP.-ENV:Text\>(.-)\<")
|
||||||
|
response = response .. (err or "Unknown error")
|
||||||
|
|
||||||
|
return true, response
|
||||||
|
end,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Comm = {
|
||||||
|
|
||||||
|
--- Creates a new Comm instance
|
||||||
|
--
|
||||||
|
-- @param host string containing the host name or ip
|
||||||
|
-- @param port number containing the port to connect to
|
||||||
|
-- @return o a new instance of Comm
|
||||||
|
new = function( self, host, port, mcast )
|
||||||
|
local o = {}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
o.host = host
|
||||||
|
o.port = port
|
||||||
|
o.mcast = mcast or false
|
||||||
|
o.sendcount = 2
|
||||||
|
o.timeout = 5000
|
||||||
|
return o
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Sets the timeout for socket reads
|
||||||
|
setTimeout = function( self, timeout ) self.timeout = timeout end,
|
||||||
|
|
||||||
|
--- Sends a probe over the wire
|
||||||
|
--
|
||||||
|
-- @return status true on success, false on failure
|
||||||
|
sendProbe = function( self )
|
||||||
|
local status, err
|
||||||
|
|
||||||
|
-- replace all instances of #uuid# in the probe
|
||||||
|
local probedata = self.probe.data:gsub("#uuid#", Util.generateUUID())
|
||||||
|
|
||||||
|
if ( self.mcast ) then
|
||||||
|
self.socket = nmap.new_socket("udp")
|
||||||
|
self.socket:set_timeout(self.timeout)
|
||||||
|
else
|
||||||
|
self.socket = nmap.new_socket()
|
||||||
|
self.socket:set_timeout(self.timeout)
|
||||||
|
status, err = self.socket:connect( self.host, self.port, "udp" )
|
||||||
|
if ( not(status) ) then return err end
|
||||||
|
end
|
||||||
|
|
||||||
|
for i=1, self.sendcount do
|
||||||
|
if ( self.mcast ) then
|
||||||
|
status, err = self.socket:sendto( self.host, self.port, probedata )
|
||||||
|
else
|
||||||
|
status, err = self.socket:send( probedata )
|
||||||
|
end
|
||||||
|
if ( not(status) ) then return err end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Sets a probe from the <code>probes</code> table to send
|
||||||
|
--
|
||||||
|
-- @param probe table containing a probe from <code>probes</code>
|
||||||
|
setProbe = function( self, probe )
|
||||||
|
self.probe = probe
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Receives one or more responses for a Probe
|
||||||
|
--
|
||||||
|
-- @return table containing decoded responses suitable for
|
||||||
|
-- <code>stdnse.format_output</code>
|
||||||
|
recvProbeMatches = function( self )
|
||||||
|
local responses = {}
|
||||||
|
repeat
|
||||||
|
local data
|
||||||
|
|
||||||
|
local status, data = self.socket:receive()
|
||||||
|
if ( not(status) ) then
|
||||||
|
if ( data == "TIMEOUT" ) then
|
||||||
|
break
|
||||||
|
else
|
||||||
|
return false, data
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, ip
|
||||||
|
status, _, _, ip, _ = self.socket:get_info()
|
||||||
|
if( not(status) ) then
|
||||||
|
stdnse.print_debug( 3, "wsdd.recvProbeMatches: ERROR: Failed to get socket info" )
|
||||||
|
return false, "ERROR: Failed to get socket info"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- push the unparsed response to the response table
|
||||||
|
local status, response = Decoders[self.probe.name]( data )
|
||||||
|
local id, output
|
||||||
|
-- if we failed to decode the response indicate this
|
||||||
|
if ( status ) then
|
||||||
|
output = {}
|
||||||
|
table.insert(output, "Message id: " .. response.msgid)
|
||||||
|
if ( response.xaddrs ) then
|
||||||
|
table.insert(output, "Address: " .. response.xaddrs)
|
||||||
|
end
|
||||||
|
if ( response.types ) then
|
||||||
|
table.insert(output, "Type: " .. response.types)
|
||||||
|
end
|
||||||
|
id = response.msgid
|
||||||
|
else
|
||||||
|
status, response = Decoders["error"](data)
|
||||||
|
output = response
|
||||||
|
id = Util.sha1sum(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( self.mcast and not(probe_matches[id]) ) then
|
||||||
|
if target.ALLOW_NEW_TARGETS then target.add(ip) end
|
||||||
|
table.insert( responses, { name=ip, output } )
|
||||||
|
elseif ( not(probe_matches[id]) ) then
|
||||||
|
responses = output
|
||||||
|
end
|
||||||
|
|
||||||
|
-- avoid duplicates
|
||||||
|
probe_matches[id] = true
|
||||||
|
until( not(self.mcast) )
|
||||||
|
|
||||||
|
-- we're done with the socket
|
||||||
|
self.socket:close()
|
||||||
|
|
||||||
|
return true, responses
|
||||||
|
end
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Helper = {
|
||||||
|
|
||||||
|
--- Creates a new helper instance
|
||||||
|
--
|
||||||
|
-- @param host string containing the host name or ip
|
||||||
|
-- @param port number containing the port to connect to
|
||||||
|
-- @return o a new instance of Helper
|
||||||
|
new = function( self, host, port )
|
||||||
|
local o = {}
|
||||||
|
setmetatable(o, self)
|
||||||
|
self.__index = self
|
||||||
|
o.host = host
|
||||||
|
o.port = port
|
||||||
|
o.mcast = false
|
||||||
|
o.timeout = 5000
|
||||||
|
return o
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Instructs the helper to use unconnected sockets supporting multicast
|
||||||
|
--
|
||||||
|
-- @param mcast boolean true if multicast is to be used, false otherwise
|
||||||
|
setMulticast = function( self, mcast )
|
||||||
|
assert( type(mcast)=="boolean", "mcast has to be either true or false")
|
||||||
|
self.mcast = mcast
|
||||||
|
self.host, self.port = "239.255.255.250", 3702
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Sets the timeout for socket reads
|
||||||
|
setTimeout = function( self, timeout ) self.timeout = timeout end,
|
||||||
|
|
||||||
|
--- Sends a probe, receives and decodes a probematch
|
||||||
|
--
|
||||||
|
-- @param probename string containing the name of the probe to send
|
||||||
|
-- check <code>probes</code> for available probes
|
||||||
|
-- @return status true on success, false on failure
|
||||||
|
-- @return matches table containing responses, suitable for printing using
|
||||||
|
-- the <code>stdnse.format_output</code> function
|
||||||
|
discoverServices = function( self, probename )
|
||||||
|
local comm = Comm:new(self.host, self.port, self.mcast)
|
||||||
|
local probe = Util.getProbeByName(probename)
|
||||||
|
comm:setProbe( probe )
|
||||||
|
comm:setTimeout( self.timeout )
|
||||||
|
|
||||||
|
local status = comm:sendProbe()
|
||||||
|
if ( not(status) ) then
|
||||||
|
return false, "ERROR: wcf.discoverServices failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
local status, matches = comm:recvProbeMatches()
|
||||||
|
if ( not(status) ) then
|
||||||
|
return false, "ERROR: wcf.recvProbeMatches failed"
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( #matches > 0 ) then matches.name = probe.desc end
|
||||||
|
return true, matches
|
||||||
|
end,
|
||||||
|
|
||||||
|
--- Sends a general probe to attempt to discover WSDD supporting devices
|
||||||
|
--
|
||||||
|
-- @return status true on success, false on failure
|
||||||
|
-- @return matches table containing responses, suitable for printing using
|
||||||
|
-- the <code>stdnse.format_output</code> function
|
||||||
|
discoverDevices = function( self )
|
||||||
|
return self:discoverServices('general')
|
||||||
|
end,
|
||||||
|
|
||||||
|
|
||||||
|
--- Sends a probe that attempts to discover WCF web services
|
||||||
|
--
|
||||||
|
-- @return status true on success, false on failure
|
||||||
|
-- @return matches table containing responses, suitable for printing using
|
||||||
|
-- the <code>stdnse.format_output</code> function
|
||||||
|
discoverWCFServices = function( self )
|
||||||
|
return self:discoverServices('wcf')
|
||||||
|
end,
|
||||||
|
|
||||||
|
}
|
||||||
102
scripts/broadcast-wsdd-discover.nse
Normal file
102
scripts/broadcast-wsdd-discover.nse
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
description = [[
|
||||||
|
Discovers devices supporting the Web Services Dynamic Discovery (WS-Discovery)
|
||||||
|
protocol. It also attempts to locate any published Windows Communication
|
||||||
|
Framework (WCF) web services (.NET 4.0 or later).
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- sudo ./nmap --script broadcast-wsdd-discover
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- | broadcast-wsdd-discover:
|
||||||
|
-- | Devices
|
||||||
|
-- | 1.2.3.116
|
||||||
|
-- | Message id: 9ea97e41-e874-faa7-fe28-deadbeefceb3
|
||||||
|
-- | Address: http://1.2.3.116:50000
|
||||||
|
-- | Type: Device wprt:PrintDeviceType
|
||||||
|
-- | 1.2.3.131
|
||||||
|
-- | Message id: 4d971368-291c-1218-30f1-deadbeefceb3
|
||||||
|
-- | Address: http://1.2.3.131:5357/deadbeef-ea5c-4b9a-a68d-deadbeefceb3/
|
||||||
|
-- | Type: Device pub:Computer
|
||||||
|
-- | 1.2.3.110
|
||||||
|
-- | Message id: f5a25a38-d61c-49e5-96c4-deadbeefceb3
|
||||||
|
-- | Address: http://1.2.3.110:5357/deadbeef-469b-4da4-b413-deadbeefee90/
|
||||||
|
-- | Type: Device pub:Computer
|
||||||
|
-- | WCF Services
|
||||||
|
-- | 1.2.3.131
|
||||||
|
-- | Message id: c1767df8-43e5-4440-9e26--deadbeefceb3
|
||||||
|
-- |_ Address: http://win-7:8090/discovery/scenarios/service2/deadbeef-3382-4668-86e7-deadbeefb935/
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 10/31/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"safe", "discovery"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'wsdd'
|
||||||
|
|
||||||
|
prerule = function() return true end
|
||||||
|
|
||||||
|
-- function used for running several discovery threads in parallell
|
||||||
|
--
|
||||||
|
-- @param funcname string containing the name of the function to run
|
||||||
|
-- the name should be one of the discovery functions in wsdd.Helper
|
||||||
|
-- @param result table into which the results are stored
|
||||||
|
discoverThread = function( funcname, results )
|
||||||
|
-- calculates a timeout based on the timing template (default: 5s)
|
||||||
|
local timeout = ( 20000 / ( nmap.timing_level() + 1 ) )
|
||||||
|
local condvar = nmap.condvar( results )
|
||||||
|
local helper = wsdd.Helper:new()
|
||||||
|
helper:setMulticast(true)
|
||||||
|
helper:setTimeout(timeout)
|
||||||
|
|
||||||
|
local func = loadstring( "return helper:" .. funcname .. "()" )
|
||||||
|
setfenv(func, setmetatable({ helper=helper; }, {__index = _G}))
|
||||||
|
|
||||||
|
if ( func ) then
|
||||||
|
local status, result = func()
|
||||||
|
if ( status ) then table.insert(results, result) end
|
||||||
|
else
|
||||||
|
stdnse.print_debug("ERROR: Failed to call function: %s", funcname)
|
||||||
|
end
|
||||||
|
condvar("broadcast")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sortfunc(a,b)
|
||||||
|
if ( a and b and a.name and b.name ) and ( a.name < b.name ) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
action = function()
|
||||||
|
|
||||||
|
local threads, results = {}, {}
|
||||||
|
local condvar = nmap.condvar( results )
|
||||||
|
|
||||||
|
-- Attempt to discover both devices and WCF web services
|
||||||
|
for _, f in ipairs( {"discoverDevices", "discoverWCFServices"} ) do
|
||||||
|
threads[stdnse.new_thread( discoverThread, f, results )] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local done
|
||||||
|
-- wait for all threads to finish
|
||||||
|
while( not(done) ) do
|
||||||
|
condvar("wait")
|
||||||
|
done = true
|
||||||
|
for thread in pairs(threads) do
|
||||||
|
if (coroutine.status(thread) ~= "dead") then done = false end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( results ) then
|
||||||
|
table.sort( results, sortfunc )
|
||||||
|
return stdnse.format_output(true, results)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -8,6 +8,7 @@ Entry { filename = "auth-spoof.nse", categories = { "malware", "safe", } }
|
|||||||
Entry { filename = "banner.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "banner.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "broadcast-dns-service-discovery.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "broadcast-dns-service-discovery.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "broadcast-upnp-info.nse", categories = { "default", "discovery", "safe", } }
|
Entry { filename = "broadcast-upnp-info.nse", categories = { "default", "discovery", "safe", } }
|
||||||
|
Entry { filename = "broadcast-wsdd-discover.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "citrix-brute-xml.nse", categories = { "auth", "intrusive", } }
|
Entry { filename = "citrix-brute-xml.nse", categories = { "auth", "intrusive", } }
|
||||||
Entry { filename = "citrix-enum-apps-xml.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "citrix-enum-apps-xml.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "citrix-enum-apps.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "citrix-enum-apps.nse", categories = { "discovery", "safe", } }
|
||||||
@@ -156,4 +157,5 @@ Entry { filename = "vnc-brute.nse", categories = { "auth", "intrusive", } }
|
|||||||
Entry { filename = "vnc-info.nse", categories = { "discovery", "safe", } }
|
Entry { filename = "vnc-info.nse", categories = { "discovery", "safe", } }
|
||||||
Entry { filename = "wdb-version.nse", categories = { "default", "discovery", "version", } }
|
Entry { filename = "wdb-version.nse", categories = { "default", "discovery", "version", } }
|
||||||
Entry { filename = "whois.nse", categories = { "discovery", "external", "safe", } }
|
Entry { filename = "whois.nse", categories = { "discovery", "external", "safe", } }
|
||||||
|
Entry { filename = "wsdd-discover.nse", categories = { "default", "discovery", "safe", } }
|
||||||
Entry { filename = "x11-access.nse", categories = { "auth", "default", "safe", } }
|
Entry { filename = "x11-access.nse", categories = { "auth", "default", "safe", } }
|
||||||
|
|||||||
90
scripts/wsdd-discover.nse
Normal file
90
scripts/wsdd-discover.nse
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
description = [[
|
||||||
|
Discovers devices supporting the Web Services Dynamic Discovery (WS-Discovery)
|
||||||
|
protocol. It also attempts to locate any published Windows Communication
|
||||||
|
Framework (WCF) web services (.NET 4.0 or later).
|
||||||
|
]]
|
||||||
|
|
||||||
|
---
|
||||||
|
-- @usage
|
||||||
|
-- sudo ./nmap --script broadcast-wsdd-discover
|
||||||
|
--
|
||||||
|
-- @output
|
||||||
|
-- PORT STATE SERVICE
|
||||||
|
-- 3702/udp open|filtered unknown
|
||||||
|
-- | wsdd-discover:
|
||||||
|
-- | Devices
|
||||||
|
-- | Message id: 39a2b7f2-fdbd-690c-c7c9-deadbeefceb3
|
||||||
|
-- | Address: http://10.0.200.116:50000
|
||||||
|
-- |_ Type: Device wprt:PrintDeviceType
|
||||||
|
--
|
||||||
|
--
|
||||||
|
|
||||||
|
--
|
||||||
|
-- Version 0.1
|
||||||
|
-- Created 10/31/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||||||
|
|
||||||
|
author = "Patrik Karlsson"
|
||||||
|
license = "Same as Nmap--See http://nmap.org/book/man-legal.html"
|
||||||
|
categories = {"safe", "discovery", "default"}
|
||||||
|
|
||||||
|
require 'shortport'
|
||||||
|
require 'wsdd'
|
||||||
|
|
||||||
|
portrule = shortport.portnumber(3702, "udp", {"open", "open|filtered"})
|
||||||
|
|
||||||
|
-- function used for running several discovery threads in parallell
|
||||||
|
--
|
||||||
|
-- @param funcname string containing the name of the function to run
|
||||||
|
-- the name should be one of the discovery functions in wsdd.Helper
|
||||||
|
-- @param result table into which the results are stored
|
||||||
|
discoverThread = function( funcname, host, port, results )
|
||||||
|
-- calculates a timeout based on the timing template (default: 5s)
|
||||||
|
local timeout = ( 20000 / ( nmap.timing_level() + 1 ) )
|
||||||
|
local condvar = nmap.condvar( results )
|
||||||
|
local helper = wsdd.Helper:new(host, port)
|
||||||
|
helper:setTimeout(timeout)
|
||||||
|
|
||||||
|
local func = loadstring( "return helper:" .. funcname .. "()" )
|
||||||
|
setfenv(func, setmetatable({ helper=helper; }, {__index = _G}))
|
||||||
|
|
||||||
|
if ( func ) then
|
||||||
|
local status, result = func()
|
||||||
|
if ( status ) then table.insert(results, result) end
|
||||||
|
else
|
||||||
|
stdnse.print_debug("ERROR: Failed to call function: %s", funcname)
|
||||||
|
end
|
||||||
|
condvar("broadcast")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function sortfunc(a,b)
|
||||||
|
if ( a and b and a.name and b.name ) and ( a.name < b.name ) then
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
action = function(host, port)
|
||||||
|
|
||||||
|
local threads, results = {}, {}
|
||||||
|
local condvar = nmap.condvar( results )
|
||||||
|
|
||||||
|
-- Attempt to discover both devices and WCF web services
|
||||||
|
for _, f in ipairs( {"discoverDevices", "discoverWCFServices"} ) do
|
||||||
|
threads[stdnse.new_thread( discoverThread, f, host, port, results )] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
local done
|
||||||
|
-- wait for all threads to finish
|
||||||
|
while( not(done) ) do
|
||||||
|
condvar("wait")
|
||||||
|
done = true
|
||||||
|
for thread in pairs(threads) do
|
||||||
|
if (coroutine.status(thread) ~= "dead") then done = false end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if ( results ) then
|
||||||
|
table.sort( results, sortfunc )
|
||||||
|
return stdnse.format_output(true, results)
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user