1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
|
local nmap = require "nmap"
local os = require "os"
local stdnse = require "stdnse"
local string = require "string"
local table = require "table"
description = [[
Discovers Sybase Anywhere servers on the LAN by sending broadcast discovery messages.
]]
---
-- @usage
-- nmap --script broadcast-sybase-asa-discover
--
-- @output
-- Pre-scan script results:
-- | broadcast-sybase-asa-discover:
-- | ip=192.168.0.1; name=mysqlanywhere1; port=2638
-- |_ ip=192.168.0.2; name=mysqlanywhere2; port=49152
--
author = "Patrik Karlsson"
license = "Same as Nmap--See https://nmap.org/book/man-legal.html"
categories = { "broadcast", "safe" }
prerule = function() return ( nmap.address_family() == "inet") end
--
-- The following code is a bit overkill and is meant to go into a library once
-- more scripts that make use of it are developed.
--
Ping = {
-- The PING request class
Request = {
-- Creates a new Ping request
new = function(self)
local o = {}
setmetatable(o, self)
self.__index = self
return o
end,
-- returns the ping request as a string
__tostring = function(self)
return stdnse.fromhex("1b00003d0000000012")
.. "CONNECTIONLESS_TDS"
.. stdnse.fromhex("000000010000040005000500000102000003010104080000000000000000070204b1")
end
},
-- The Ping Response class
Response = {
-- Creates a new response
-- @param data string containing the raw data as received over the socket
-- @return o instance of Response
new = function(self, data)
local o = { data = data }
setmetatable(o, self)
self.__index = self
o:parse()
if ( o.dbinstance ) then
return o
end
end,
-- Parses the raw response and populates the
-- <code>dbinstance.name</code> and <code>dbinstance.port</code> fields
parse = function(self)
-- do a very basic length check
local len, pos = string.unpack(">I4", self.data)
len = len & 0x0000FFFF
if ( len ~= #self.data ) then
stdnse.debug2("The packet length was reported as %d, expected %d", len, #self.data)
return
end
local connectionless_tds
connectionless_tds, pos = string.unpack("s1", self.data, 9)
if ( connectionless_tds ~= "CONNECTIONLESS_TDS" ) then
stdnse.debug2("Did not find the expected CONNECTIONLESS_TDS header")
return
end
self.dbinstance = {}
self.dbinstance.name, pos = string.unpack("s1", self.data, 40)
pos = pos + 2
self.dbinstance.port, pos = string.unpack(">I2", self.data, pos)
end,
}
}
-- Main script interface
Helper = {
-- Creates a new helper instance
-- @param host table as received by the action method
-- @param port table as received by the action method
-- @param options table containing:
-- <code>timeout</code> - the amount of time to listen for responses
-- @return o instance of Helper
new = function(self, host, port, options)
local o = {
host = host,
port = port,
options = options or {}
}
setmetatable(o, self)
self.__index = self
return o
end,
-- Sends a ping request to the service and processes the response
-- @return status true on success, false on failure
-- @return instances table of instance tables containing
-- <code>name</code> - the instance name
-- <code>ip</code> - the instance ip
-- <code>port</code> - the instance port
-- err string containing error message on failure
ping = function(self)
local socket = nmap.new_socket("udp")
socket:set_timeout(1000)
-- send 2 packets just in case
for i=1, 2 do
local ping_req = Ping.Request:new()
local status, err = socket:sendto(self.host, self.port, tostring(ping_req))
if ( not(status) ) then
return false, "Failed to send broadcast packet"
end
end
local stime = os.time()
local instances = {}
local timeout = self.options.timeout or ( 20 / ( nmap.timing_level() + 1 ) )
repeat
local status, data = socket:receive()
if ( status ) then
local response = Ping.Response:new(data)
if ( response ) then
local status, _, _, rhost, _ = socket:get_info()
if ( not(status) ) then
socket:close()
return false, "Failed to get socket information"
end
response.dbinstance.ip = rhost
-- avoid duplicates
instances[response.dbinstance.name] = response.dbinstance
end
end
until( os.time() - stime > timeout )
socket:close()
return true, instances
end,
}
action = function()
local timeout = ( 20 / ( nmap.timing_level() + 1 ) )
local host = { ip = "255.255.255.255" }
local port = { number = 2638, protocol = "udp" }
local helper = Helper:new(host, port)
local status, instances = helper:ping()
if ( not(status) ) then
return stdnse.format_output(false, instances)
end
-- if we don't have any instances, silently abort
if ( next(instances) == nil ) then
return
end
local result = {}
for _, instance in pairs(instances) do
table.insert(result, ("ip=%s; name=%s; port=%d"):format(instance.ip, instance.name, instance.port))
end
table.sort(result)
return stdnse.format_output(true, result)
end
|