165 lines
4.5 KiB
Lua
165 lines
4.5 KiB
Lua
|
--- A minimalistic library to support Domino RPC
|
||
|
--
|
||
|
-- Summary
|
||
|
-- -------
|
||
|
-- The library currently only supports user enumeration and uses chunks of
|
||
|
-- captured data to do so.
|
||
|
--
|
||
|
-- Overview
|
||
|
-- --------
|
||
|
-- The library contains the following classes:
|
||
|
--
|
||
|
-- o DominoPacket
|
||
|
-- - The packet class holding the packets sent between the client and the
|
||
|
-- IBM Lotus Domino server
|
||
|
--
|
||
|
-- o Helper
|
||
|
-- - A helper class that provides easy access to the rest of the library
|
||
|
--
|
||
|
-- Example
|
||
|
-- -------
|
||
|
-- The following sample code illustrates how scripts can use the Helper class
|
||
|
-- to interface the library:
|
||
|
--
|
||
|
-- <code>
|
||
|
-- helper = nrpc.Helper:new(host, port)
|
||
|
-- status, err = nrpc:Connect()
|
||
|
-- status, res = nrpc:isValidUser("Patrik Karlsson")
|
||
|
-- status, err = nrpc:Close()
|
||
|
-- </code>
|
||
|
--
|
||
|
-- @copyright Same as Nmap--See https://nmap.org/book/man-legal.html
|
||
|
-- @author Patrik Karlsson <patrik@cqure.net>
|
||
|
--
|
||
|
|
||
|
--
|
||
|
-- Version 0.1
|
||
|
-- Created 07/23/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
|
||
|
--
|
||
|
|
||
|
|
||
|
local match = require "match"
|
||
|
local nmap = require "nmap"
|
||
|
local stdnse = require "stdnse"
|
||
|
local string = require "string"
|
||
|
_ENV = stdnse.module("nrpc", stdnse.seeall)
|
||
|
|
||
|
-- The Domino Packet
|
||
|
DominoPacket = {
|
||
|
|
||
|
--- Creates a new DominoPacket instance
|
||
|
--
|
||
|
-- @param data string containing the packet data
|
||
|
-- @return a new DominoPacket instance
|
||
|
new = function( self, data )
|
||
|
local o = {}
|
||
|
setmetatable(o, self)
|
||
|
self.__index = self
|
||
|
o.data = data
|
||
|
return o
|
||
|
end,
|
||
|
|
||
|
--- Reads a packet from the socket
|
||
|
--
|
||
|
-- @param domsock socket connected to the server
|
||
|
-- @return Status (true or false).
|
||
|
-- @return Error code (if status is false).
|
||
|
read = function( self, domsock )
|
||
|
local status, data = domsock:receive_buf(match.numbytes(2), true)
|
||
|
local len = string.unpack( "<I2", data )
|
||
|
|
||
|
return domsock:receive_buf(match.numbytes(len), true)
|
||
|
end,
|
||
|
|
||
|
--- converts the packet to a string
|
||
|
__tostring = function(self)
|
||
|
return string.pack("<s2", self.data )
|
||
|
end,
|
||
|
|
||
|
}
|
||
|
|
||
|
Helper = {
|
||
|
|
||
|
--- Creates a new Helper instance
|
||
|
--
|
||
|
-- @param host table as received by the script action method
|
||
|
-- @param port table as received by the script action method
|
||
|
new = function(self, host, port)
|
||
|
local o = {}
|
||
|
setmetatable(o, self)
|
||
|
self.__index = self
|
||
|
o.host = host
|
||
|
o.port = port
|
||
|
o.domsock = nmap.new_socket()
|
||
|
return o
|
||
|
end,
|
||
|
|
||
|
--- Connects the socket to the Domino server
|
||
|
--
|
||
|
-- @return status true on success, false on failure
|
||
|
-- @return err error message if status is false
|
||
|
connect = function( self )
|
||
|
self.domsock:set_timeout(5000)
|
||
|
if( not( self.domsock:connect( self.host.ip, self.port.number, "tcp" ) ) ) then
|
||
|
return false, ("ERROR: Failed to connect to Domino server %s:%d\n"):format(self.host, self.port)
|
||
|
end
|
||
|
return true
|
||
|
end,
|
||
|
|
||
|
--- Disconnects from the Lotus Domino Server
|
||
|
--
|
||
|
-- @return status true on success, false on failure
|
||
|
-- @return err error message if status is false
|
||
|
disconnect = function( self )
|
||
|
return self.domsock:close()
|
||
|
end,
|
||
|
|
||
|
--- Attempt to check whether the user exists in Domino or not
|
||
|
--
|
||
|
-- @param username string containing the user name to guess
|
||
|
-- @return status true on success false on failure
|
||
|
-- @return domino_id if it exists and status is true
|
||
|
-- err if status is false
|
||
|
isValidUser = function( self, username )
|
||
|
local data = stdnse.fromhex("00001e00000001000080000007320000700104020000fb2b2d00281f1e000000124c010000000000")
|
||
|
local status, id_data
|
||
|
local data_len, total_len, pkt_type, valid_user
|
||
|
|
||
|
self.domsock:send( tostring(DominoPacket:new( data )) )
|
||
|
data = DominoPacket:new():read( self.domsock )
|
||
|
|
||
|
data = stdnse.fromhex("0100320002004f000100000500000900")
|
||
|
.. string.char(#username + 1)
|
||
|
.. stdnse.fromhex("000000000000000000000000000000000028245573657273290000")
|
||
|
.. string.pack("z", username)
|
||
|
|
||
|
self.domsock:send( tostring(DominoPacket:new( data ) ) )
|
||
|
status, id_data = DominoPacket:new():read( self.domsock )
|
||
|
|
||
|
pkt_type = string.unpack("B", id_data, 3)
|
||
|
valid_user = string.unpack("B", id_data, 11)
|
||
|
total_len = string.unpack("<I2", id_data, 13)
|
||
|
|
||
|
if ( pkt_type == 0x16 ) then
|
||
|
if ( valid_user == 0x19 ) then
|
||
|
return true
|
||
|
else
|
||
|
return false
|
||
|
end
|
||
|
end
|
||
|
|
||
|
if ( pkt_type ~= 0x7e ) then
|
||
|
return false, "Failed to retrieve ID file"
|
||
|
end
|
||
|
|
||
|
status, data = DominoPacket:new():read( self.domsock )
|
||
|
|
||
|
id_data = id_data:sub(33) .. data:sub(11, total_len - #id_data + 11)
|
||
|
|
||
|
return true, id_data
|
||
|
end,
|
||
|
|
||
|
}
|
||
|
|
||
|
return _ENV;
|