summaryrefslogtreecommitdiffstats
path: root/nselib/nrpc.lua
blob: d5af8a63c79659a9aa02ee43a663ab15bf0f3429 (plain)
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
--- 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;