mirror of
https://github.com/nmap/nmap.git
synced 2025-12-06 04:31:29 +00:00
286 lines
8.6 KiB
Lua
286 lines
8.6 KiB
Lua
---
|
|
-- A tiny library allowing some basic information enumeration from
|
|
-- Versant object database software (see
|
|
-- http://en.wikipedia.org/wiki/Versant_Corporation). The code is
|
|
-- entirely based on packet dumps captured when using the Versant
|
|
-- Management Center administration application.
|
|
--
|
|
-- @author Patrik Karlsson <patrik@cqure.net>
|
|
--
|
|
|
|
local stdnse = require "stdnse"
|
|
local bin = require "bin"
|
|
local match = require "match"
|
|
local nmap = require "nmap"
|
|
local string = require "string"
|
|
local table = require "table"
|
|
_ENV = stdnse.module("versant", stdnse.seeall)
|
|
|
|
Versant = {
|
|
|
|
-- fallback to these constants when version and user are not given
|
|
USER = "nmap",
|
|
VERSION = "8.0.2",
|
|
|
|
-- Creates an instance of the Versant class
|
|
-- @param host table
|
|
-- @param port table
|
|
-- @return o new instance of Versant
|
|
new = function(self, host, port)
|
|
local o = { host = host, port = port, socket = nmap.new_socket() }
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end,
|
|
|
|
-- Connects a socket to the Versant server
|
|
-- @return status true on success, false on failure
|
|
-- @return err string containing the error message if status is false
|
|
connect = function(self)
|
|
return self.socket:connect(self.host, self.port)
|
|
end,
|
|
|
|
-- Closes the socket
|
|
-- @return status true on success, false on failure
|
|
-- @return err string containing the error message if status is false
|
|
close = function(self)
|
|
return self.socket:close()
|
|
end,
|
|
|
|
-- Sends command to the server
|
|
-- @param cmd string containing the command to run
|
|
-- @param arg string containing any arguments
|
|
-- @param user [optional] string containing the user name
|
|
-- @param ver [optional] string containing the version number
|
|
-- @return status true on success, false on failure
|
|
-- @return data opaque string containing the response
|
|
sendCommand = function(self, cmd, arg, user, ver)
|
|
|
|
user = user or Versant.USER
|
|
ver = ver or Versant.VERSION
|
|
arg = arg or ""
|
|
|
|
local data = bin.pack("Hzzz",
|
|
"000100000000000000020002000000010000000000000000000000000000000000010000",
|
|
cmd,
|
|
user,
|
|
ver
|
|
)
|
|
-- align to even 4 bytes
|
|
data = data .. string.rep("\0", 4 - ((#data % 4) or 0))
|
|
|
|
data = data .. bin.pack("Hzxxxxxxxxxxz",
|
|
"0000000b000001000000000000000000",
|
|
("%s:%d"):format(self.host.ip, self.port.number),
|
|
arg
|
|
)
|
|
|
|
data = data .. string.rep("\0", 2048 - #data)
|
|
|
|
local status, err = self.socket:send(data)
|
|
if ( not(status) ) then
|
|
return false, "Failed to send request to server"
|
|
end
|
|
|
|
local status, data = self.socket:receive_buf(match.numbytes(2048), true)
|
|
if ( not(status) ) then
|
|
return false, "Failed to read response from server"
|
|
end
|
|
|
|
return status, data
|
|
end,
|
|
|
|
-- Get database node information
|
|
-- @return status true on success, false on failure
|
|
-- @return result table containing an entry for each database. Each entry
|
|
-- contains a table with the following fields:
|
|
-- <code>name</code> - the database name
|
|
-- <code>owner</code> - the database owner
|
|
-- <code>created</code> - the date when the database was created
|
|
-- <code>version</code> - the database version
|
|
getNodeInfo = function(self)
|
|
local status, data = self:sendCommand("o_getnodeinfo", "-nodeinfo")
|
|
if ( not(status) ) then
|
|
return false, data
|
|
end
|
|
|
|
status, data = self.socket:receive_buf(match.numbytes(4), true)
|
|
if ( not(status) ) then
|
|
return false, "Failed to read response from server"
|
|
end
|
|
|
|
local _, db_count = bin.unpack(">I", data)
|
|
if ( db_count == 0 ) then
|
|
return false, "Database count was zero"
|
|
end
|
|
|
|
status, data = self.socket:receive_buf(match.numbytes(4), true)
|
|
if ( not(status) ) then
|
|
return false, "Failed to read response from server"
|
|
end
|
|
|
|
local _, buf_size = bin.unpack(">I", data)
|
|
local dbs = {}
|
|
|
|
for i=1, db_count do
|
|
status, data = self.socket:receive_buf(match.numbytes(buf_size), true)
|
|
local _, db = nil, {}
|
|
|
|
_, db.name = bin.unpack("z", data, 23)
|
|
_, db.owner = bin.unpack("z", data, 599)
|
|
_, db.created= bin.unpack("z", data, 631)
|
|
_, db.version= bin.unpack("z", data, 663)
|
|
|
|
-- remove trailing line-feed
|
|
db.created = db.created:match("^(.-)\n*$")
|
|
|
|
table.insert(dbs, db)
|
|
end
|
|
return true, dbs
|
|
end,
|
|
|
|
-- Gets the database OBE port, this port is dynamically allocated once this
|
|
-- command completes.
|
|
--
|
|
-- @return status true on success, false on failure
|
|
-- @return port table containing the OBE port
|
|
getObePort = function(self)
|
|
|
|
local status, data = self:sendCommand("o_oscp", "-utility")
|
|
if ( not(status) ) then
|
|
return false, data
|
|
end
|
|
|
|
status, data = self.socket:receive_buf(match.numbytes(256), true)
|
|
if ( not(status) ) then
|
|
return false, "Failed to read response from server"
|
|
end
|
|
|
|
local pos, success = bin.unpack(">I", data)
|
|
if ( success ~= 0 ) then
|
|
return false, "Response contained invalid data"
|
|
end
|
|
|
|
local port = { protocol = "tcp" }
|
|
pos, port.number = bin.unpack(">S", data, pos)
|
|
|
|
return true, port
|
|
end,
|
|
|
|
|
|
-- Gets the XML license file from the database
|
|
-- @return status true on success, false on failure
|
|
-- @return data string containing the XML license file
|
|
getLicense = function(self)
|
|
|
|
local status, data = self:sendCommand("o_licfile", "-license")
|
|
if ( not(status) ) then
|
|
return false, data
|
|
end
|
|
|
|
status, data = self.socket:receive_buf(match.numbytes(4), true)
|
|
if ( not(status) ) then
|
|
return false, "Failed to read response from server"
|
|
end
|
|
|
|
local _, len = bin.unpack(">I", data)
|
|
if ( len == 0 ) then
|
|
return false, "Failed to retrieve license file"
|
|
end
|
|
|
|
status, data = self.socket:receive_buf(match.numbytes(len), true)
|
|
if ( not(status) ) then
|
|
return false, "Failed to read response from server"
|
|
end
|
|
|
|
return true, data
|
|
end,
|
|
|
|
-- Gets the TCP port for a given database
|
|
-- @param db string containing the database name
|
|
-- @return status true on success, false on failure
|
|
-- @return port table containing the database port
|
|
getDbPort = function(self, db)
|
|
local status, data = self:sendCommand(db, "")
|
|
if ( not(status) ) then
|
|
return false, data
|
|
end
|
|
|
|
if ( not(status) ) then
|
|
return false, "Failed to connect to database"
|
|
end
|
|
|
|
local _, port = nil, { protocol = "tcp" }
|
|
_, port.number = bin.unpack(">I", data, 27)
|
|
if ( port == 0 ) then
|
|
return false, "Failed to determine database port"
|
|
end
|
|
return true, port
|
|
end,
|
|
}
|
|
|
|
Versant.OBE = {
|
|
|
|
-- Creates a new versant OBE instance
|
|
-- @param host table
|
|
-- @param port table
|
|
-- @return o new instance of Versant OBE
|
|
new = function(self, host, port)
|
|
local o = { host = host, port = port, socket = nmap.new_socket() }
|
|
setmetatable(o, self)
|
|
self.__index = self
|
|
return o
|
|
end,
|
|
|
|
-- Connects a socket to the Versant server
|
|
-- @return status true on success, false on failure
|
|
-- @return err string containing the error message if status is false
|
|
connect = function(self)
|
|
return self.socket:connect(self.host, self.port)
|
|
end,
|
|
|
|
-- Closes the socket
|
|
-- @return status true on success, false on failure
|
|
-- @return err string containing the error message if status is false
|
|
close = function(self)
|
|
return self.socket:close()
|
|
end,
|
|
|
|
-- Get database information including file paths and hostname
|
|
-- @return status true on success false on failure
|
|
-- @return result table containing the fields:
|
|
-- <code>root_path</code> - the database root directory
|
|
-- <code>db_path</code> - the database directory
|
|
-- <code>lib_path</code> - the library directory
|
|
-- <code>hostname</code> - the database host name
|
|
getVODInfo = function(self)
|
|
local data = bin.pack("Hz",
|
|
"1002005d00000000000100000000000d000000000000000000000000", --28
|
|
"-noprint -i " --12 + 1 (for null)
|
|
) .. string.rep("\0", 215) -- 256 - (28 + 12 + 1)
|
|
|
|
self.socket:send(data)
|
|
local status, data = self.socket:receive_buf(match.numbytes(256), true)
|
|
if ( not(status) ) then
|
|
return false, "Failed to read response from server"
|
|
end
|
|
|
|
local pos, len = bin.unpack(">I", data, 13)
|
|
status, data = self.socket:receive_buf(match.numbytes(len), true)
|
|
if ( not(status) ) then
|
|
return false, "Failed to read response from server"
|
|
end
|
|
|
|
local result, pos, offset = {}, 1, 13
|
|
pos, result.version = bin.unpack("z", data)
|
|
|
|
for _, item in ipairs({"root_path", "db_path", "lib_path", "hostname"}) do
|
|
pos, result[item] = bin.unpack("z", data, offset)
|
|
offset = offset + 256
|
|
end
|
|
return true, result
|
|
end,
|
|
}
|
|
|
|
return _ENV;
|