PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/nselib/giop.lua

https://gitlab.com/g10h4ck/nmap-gsoc2015
Lua | 625 lines | 341 code | 108 blank | 176 comment | 32 complexity | 6ceb0675b96e83c23c59c4515933c05c MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, Apache-2.0, LGPL-2.0, LGPL-2.1, MIT
  1. ---
  2. -- GIOP Library supporting a very limited subset of operations
  3. --
  4. -- Summary
  5. -- -------
  6. -- The library currently provides functionality to connect and query the
  7. -- CORBA naming service for a list of available objects.
  8. --
  9. --
  10. -- Overview
  11. -- --------
  12. -- The library contains the following classes:
  13. --
  14. -- o Packet.*
  15. -- - The Packet classes contain specific packets and function to serialize
  16. -- them to strings that can be sent over the wire. Each class may also
  17. -- contain a function to parse the servers response.
  18. --
  19. -- o Comm
  20. -- - Implements a number of functions to handle communication over the
  21. -- the socket.
  22. --
  23. -- o Helper
  24. -- - A helper class that provides easy access to the rest of the library
  25. --
  26. --
  27. -- Example
  28. -- -------
  29. -- The following sample code illustrates how scripts can use the Helper class
  30. -- to interface the library:
  31. --
  32. -- <code>
  33. -- helper = giop.Helper:new(host, port)
  34. -- status, err = helper:Connect()
  35. -- status, ctx = helper:GetNamingContext()
  36. -- status, objs = helper:ListObjects(ctx)
  37. -- </code>
  38. --
  39. -- Additional information
  40. -- ----------------------
  41. -- The implementation is based on packet dumps and the decoding Wireshark
  42. -- provides.
  43. --
  44. -- This implementation is tested and known to work against:
  45. -- x Sun's JAVA orbd
  46. --
  47. -- @copyright Same as Nmap--See http://nmap.org/book/man-legal.html
  48. -- @author "Patrik Karlsson <patrik@cqure.net>"
  49. --
  50. --
  51. -- Version 0.1
  52. -- Created 08/07/2010 - v0.1 - created by Patrik Karlsson <patrik@cqure.net>
  53. --
  54. local bin = require "bin"
  55. local match = require "match"
  56. local nmap = require "nmap"
  57. local stdnse = require "stdnse"
  58. local table = require "table"
  59. _ENV = stdnse.module("giop", stdnse.seeall)
  60. -- A bunch of constants
  61. Constants = {
  62. SyncScope = {
  63. WITH_TARGET = 3,
  64. },
  65. ServiceContext = {
  66. CODESETS = 1,
  67. SENDING_CONTEXT_RUNTIME = 6,
  68. NEO_FIRST_SERVICE_CONTEXT = 1313165056,
  69. },
  70. ReplyStatus = {
  71. SYSTEM_EXCEPTION = 2,
  72. },
  73. VERSION_1_0 = 1,
  74. VERSION_1_2 = 0x0201,
  75. NAMESERVICE = "NameService\0",
  76. }
  77. Packet = {}
  78. Packet.GIOP = {
  79. magic = "GIOP",
  80. version = 0x0001,
  81. byte_order = 0,
  82. --- Creates a Packet.GIOP
  83. --
  84. -- @param msgtype number containing the message type
  85. -- @param data string containing the message data
  86. -- @return obj a new Packet.GIOP instance
  87. new = function( self, msgtype, data )
  88. local o = {}
  89. setmetatable(o, self)
  90. self.__index = self
  91. o.type = msgtype
  92. o.data = data
  93. o.size = data and #data or 0
  94. return o
  95. end,
  96. --- Converts the class to a string suitable to send over the socket
  97. --
  98. -- @return string containing the instance data
  99. __tostring = function( self )
  100. return bin.pack("<ASCC>IA", self.magic, self.version, self.byte_order, self.type, self.size, self.data )
  101. end,
  102. --- Sets the packet version
  103. --
  104. -- @param version number containing the version to use
  105. setVersion = function( self, version ) self.version = version end,
  106. --- Receives the packet over the socket
  107. --
  108. -- @param socket containing the already connected socket
  109. -- @return status true on success, false on failure
  110. -- @return err containing the error message if status is false
  111. recv = function( self, socket )
  112. local status, data = socket:receive_buf(match.numbytes(12), true)
  113. local pos
  114. if ( not(status) ) then return false, "Failed to read Packet.GIOP" end
  115. pos, self.magic, self.version, self.byte_order,
  116. self.type = bin.unpack("<A4SCC", data )
  117. pos, self.size = bin.unpack( ( self.byte_order == 0 and ">" or "<") .. "I", data, pos )
  118. status, data = socket:receive_buf(match.numbytes(self.size), true)
  119. if ( not(status) ) then return false, "Failed to read Packet.GIOP" end
  120. self.data = data
  121. return true
  122. end,
  123. }
  124. ServiceContext = {
  125. --- Creates a ServiceContext
  126. --
  127. -- @param id number containing the context id
  128. -- @param data the service context data
  129. -- @param pad [optional] number used to pad after the service context
  130. -- @return obj a new ServiceContext instance
  131. new = function( self, id, data, pad )
  132. local o = {}
  133. setmetatable(o, self)
  134. self.__index = self
  135. o.id = id
  136. o.data = data or ""
  137. o.pad = pad
  138. return o
  139. end,
  140. --- Converts the class to a string suitable to send over the socket
  141. --
  142. -- @return string containing the instance data
  143. __tostring = function( self )
  144. if ( self.pad ) then
  145. return bin.pack(">IIAS", self.id, #self.data, self.data, self.pad)
  146. else
  147. return bin.pack(">IIA", self.id, #self.data, self.data)
  148. end
  149. end,
  150. }
  151. --- Creates a SendingContextRuntime
  152. SendingContextRuntime =
  153. {
  154. --- Creates a SendingContextRuntime
  155. --
  156. -- @param lhost string containing the source ip address
  157. -- @return obj a new SendingContextRuntime instance
  158. new = function(self, lhost )
  159. local o = {}
  160. setmetatable(o, self)
  161. self.__index = self
  162. o.data = bin.pack(">HIAH",
  163. [[
  164. 000000000000002849444c3a6f6d672e6f72672f53656e64696e67436f6e746
  165. 578742f436f6465426173653a312e300000000001000000000000006e000102
  166. 00
  167. ]], #lhost + 1, lhost .. "\0",
  168. [[
  169. 00ec5100000019afabcb000000000249765d6900000008000000000000000014
  170. 0000000000000200000001000000200000000000010001000000020501000100
  171. 01002000010109000000010001010000000026000000020002
  172. ]] )
  173. return o
  174. end,
  175. --- Converts the class to a string suitable to send over the socket
  176. --
  177. -- @return string containing the instance data
  178. __tostring = function( self ) return self.data end,
  179. }
  180. Packet.GIOP.reply = {
  181. --- Creates a new Packet.GIOP.reply instance
  182. --
  183. -- @return obj a new Packet.GIOP.get instance
  184. new = function( self )
  185. local o = {}
  186. setmetatable(o, self)
  187. self.__index = self
  188. self.sc = {}
  189. self.GIOP = Packet.GIOP:new()
  190. return o
  191. end,
  192. --- Receives a Packet.GIOP.reply from the socket
  193. --
  194. -- @param socket already connected to the server
  195. -- @return status true on success, false on failure
  196. -- @return err error message if status is false
  197. recv = function( self, socket )
  198. local status, err = self.GIOP:recv( socket )
  199. local pos, tmp
  200. local bo = ( self.GIOP.byte_order == 0 and ">" or "<")
  201. if( not(status) ) then return false, err end
  202. if ( self.GIOP.version == Constants.VERSION_1_2 ) then
  203. pos, self.request_id, self.reply_status = bin.unpack(bo .. "II", self.GIOP.data, pos )
  204. pos, tmp = bin.unpack( bo .. "I", self.GIOP.data, pos )
  205. elseif ( self.GIOP.version == Constants.VERSION_1_0 ) then
  206. pos, tmp = bin.unpack( bo .. "I", self.GIOP.data )
  207. end
  208. for i=1, tmp do
  209. local ctx_id, ctx_len, ctx_data
  210. pos, ctx_id, ctx_len = bin.unpack( bo .. "II", self.GIOP.data, pos )
  211. pos, ctx_data = bin.unpack("A" .. ctx_len, self.GIOP.data, pos )
  212. if ( i ~= tmp ) then pos = pos + 2 end
  213. table.insert( self.sc, ServiceContext:new( ctx_id, ctx_data ) )
  214. end
  215. if ( self.GIOP.version == Constants.VERSION_1_0 ) then
  216. pos, self.request_id, self.reply_status, self.stub_data = bin.unpack( bo .. "IIA" .. ( #self.GIOP.data - pos - 8 ), self.GIOP.data, pos )
  217. elseif ( pos < #self.GIOP.data ) then
  218. pos, self.data = bin.unpack("A" .. (#self.GIOP.data - pos), self.GIOP.data, pos )
  219. end
  220. return true
  221. end,
  222. }
  223. Packet.GIOP.get = {
  224. resp_expected = 1,
  225. key_length = 4,
  226. princ_len = 0,
  227. --- Creates a new Packet.GIOP._is_a instance
  228. --
  229. -- @param id the packet identifier
  230. -- @param key number containing the object key
  231. -- @param data string containing the stub data
  232. -- @return obj a new Packet.GIOP.get instance
  233. new = function( self, id, key, data )
  234. local o = {}
  235. setmetatable(o, self)
  236. self.__index = self
  237. o.op = "get\0"
  238. o.id = id
  239. o.key = key
  240. o.data = data
  241. o.sc = {}
  242. return o
  243. end,
  244. --- Creates and adds a service context to the packet
  245. --
  246. -- @param id number containing the context id
  247. -- @param data the service context data
  248. -- @param pad [optional] number used to pad after the service context
  249. addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end,
  250. --- Converts the class to a string suitable to send over the socket
  251. --
  252. -- @return string containing the packet data
  253. __tostring = function( self )
  254. local data = bin.pack(">I", #self.sc)
  255. local pad = 0
  256. for i=1, #self.sc do
  257. data = data .. tostring( self.sc[i])
  258. end
  259. data = data .. bin.pack( ">ICCCCIIIAIA", self.id, self.resp_expected, pad, pad, pad,
  260. self.key_length, self.key, #self.op, self.op, self.princ_len, self.data )
  261. return tostring( Packet.GIOP:new( 0, data ) )
  262. end,
  263. }
  264. Packet.GIOP._is_a =
  265. {
  266. --- Creates a new Packet.GIOP._is_a instance
  267. --
  268. -- @param id the packet identifier
  269. -- @param flags [optional]
  270. -- @param keyaddr string containing the keyaddr data
  271. -- @return obj a new Packet.GIOP._is_a instance
  272. new = function( self, id, flags, key_addr )
  273. local o = {}
  274. setmetatable(o, self)
  275. self.__index = self
  276. o.op = "_is_a\0"
  277. o.id = id
  278. o.target_addr = 0 -- KeyAddr
  279. o.key_addr = key_addr
  280. o.flags = flags or Constants.SyncScope.WITH_TARGET -- SyncScope WITH_TARGET
  281. o.sc = {}
  282. return o
  283. end,
  284. --- Creates and adds a service context to the packet
  285. --
  286. -- @param id number containing the context id
  287. -- @param data the service context data
  288. -- @param pad [optional] number used to pad after the service context
  289. addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end,
  290. --- Converts the class to a string suitable to send over the socket
  291. --
  292. -- @return string containing the packet data
  293. __tostring = function( self )
  294. local TYPE_ID = "IDL:omg.org/CosNaming/NamingContextExt:1.0\0"
  295. local RESERVED = 0
  296. local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 0
  297. local data = bin.pack(">ICCCCSSIAIASI", self.id, self.flags, RESERVED, RESERVED, RESERVED, self.target_addr,
  298. UNKNOWN, #self.key_addr, self.key_addr, #self.op, self.op, UNKNOWN2, #self.sc )
  299. for i=1, #self.sc do
  300. data = data .. tostring( self.sc[i])
  301. end
  302. data = data .. bin.pack(">IA", #TYPE_ID, TYPE_ID)
  303. local packet = Packet.GIOP:new( 0, data )
  304. packet:setVersion( Constants.VERSION_1_2 )
  305. return tostring( packet )
  306. end,
  307. }
  308. Packet.GIOP.list =
  309. {
  310. --- Creates a new Packet.GIOP.list instance
  311. --
  312. -- @param id the packet identifier
  313. -- @param flags [optional]
  314. -- @param keyaddr string containing the keyaddr data
  315. -- @param how_many string containing the value to retrieve
  316. -- @return obj a new Packet.GIOP.list instance
  317. new = function( self, id, flags, keyaddr, how_many )
  318. local o = {}
  319. setmetatable(o, self)
  320. self.__index = self
  321. o.op = "list\0"
  322. o.id = id
  323. o.flags = flags or Constants.SyncScope.WITH_TARGET
  324. o.target_addr = 0 -- KeyAddr
  325. o.key_addr = keyaddr
  326. o.how_many = how_many or 1000
  327. o.sc = {}
  328. return o
  329. end,
  330. --- Creates and adds a service context to the packet
  331. --
  332. -- @param id number containing the context id
  333. -- @param data the service context data
  334. -- @param pad [optional] number used to pad after the service context
  335. addServiceContext = function( self, id, data, pad ) table.insert( self.sc, ServiceContext:new(id, data, pad) ) end,
  336. --- Converts the class to a string suitable to send over the socket
  337. --
  338. -- @return string containing the packet data
  339. __tostring = function( self )
  340. local RESERVED = 0
  341. local UNKNOWN, UNKNOWN2, UNKNOWN3 = 2, 1, 6
  342. local data = bin.pack(">ICCCCSSIAIACCCI", self.id, self.flags, RESERVED, RESERVED,
  343. RESERVED, self.target_addr, UNKNOWN, #self.key_addr, self.key_addr,
  344. #self.op, self.op, RESERVED, RESERVED, UNKNOWN2, #self.sc )
  345. for i=1, #self.sc do
  346. data = data .. tostring( self.sc[i])
  347. end
  348. data = data .. bin.pack(">II", UNKNOWN3, self.how_many )
  349. local packet = Packet.GIOP:new( 0, data )
  350. packet:setVersion( Constants.VERSION_1_2 )
  351. return tostring( packet )
  352. end,
  353. }
  354. -- Static class containing various message decoders
  355. MessageDecoder = {
  356. --- Decodes a get response
  357. --
  358. -- @param packet the GIOP packet as received by the comm
  359. -- <code>exchGIOPPacket</code> function
  360. -- @return status true on success, false on failure
  361. -- @return table containing <code>ip</code> and <code>ctx</code>
  362. ["get"] = function( packet )
  363. local bo = ( packet.GIOP.byte_order == 0 and ">" or "<")
  364. local pos, len = bin.unpack(bo .. "I", packet.stub_data)
  365. local ip, ctx
  366. pos = pos + len + 16
  367. pos, len = bin.unpack(bo .. "I", packet.stub_data, pos)
  368. pos, ip = bin.unpack( bo .. "A" .. len, packet.stub_data, pos)
  369. pos = pos + 3
  370. pos, len = bin.unpack(bo .. "I", packet.stub_data, pos)
  371. pos, ctx = bin.unpack( bo .. "A" .. len, packet.stub_data, pos)
  372. return true, { ip = ip, ctx = ctx}
  373. end,
  374. --- Decodes a _is_a response (not implemented)
  375. --
  376. -- @param packet the GIOP packet as received by the comm
  377. -- <code>exchGIOPPacket</code> function
  378. -- @return status, always true
  379. ["_is_a"] = function( packet )
  380. return true
  381. end,
  382. --- Decodes a list response
  383. --
  384. -- @param packet the GIOP packet as received by the comm
  385. -- <code>exchGIOPPacket</code> function
  386. -- @return status true on success, false on failure
  387. -- @return table containing <code>id</code>, <code>kind</code> and
  388. -- <code>enum</code> or error message if status is false
  389. ["list"] = function( packet )
  390. local bo = ( packet.GIOP.byte_order == 0 and ">" or "<")
  391. local pos, seq_len = bin.unpack( bo .. "I", packet.data, 7)
  392. local objs = {}
  393. for i=1, seq_len do
  394. local seq_len_of_bind_name
  395. local len, name
  396. local obj = {}
  397. pos, seq_len_of_bind_name = bin.unpack( bo .. "I", packet.data, pos)
  398. if ( seq_len_of_bind_name ~= 1 ) then return false, "Sequence length of Binding_binding_name was greater than 1" end
  399. pos, len = bin.unpack( bo .. "I", packet.data, pos )
  400. pos, obj.id = bin.unpack( "A" .. len - 1, packet.data, pos )
  401. -- Account for terminating zero
  402. pos = pos + 1
  403. -- Account for undecoded data
  404. pos = pos + ( ( len % 4 > 0 ) and ( 4 - ( len % 4 ) ) or 0 )
  405. pos = pos + 3
  406. pos, obj.kind = bin.unpack("C", packet.data, pos)
  407. -- Account for undecoded data
  408. pos = pos + 4
  409. pos, obj.enum = bin.unpack( bo .. "I", packet.data, pos )
  410. table.insert( objs, obj )
  411. end
  412. return true, objs
  413. end,
  414. }
  415. Comm = {
  416. --- Creates a new Comm instance
  417. --
  418. -- @param socket containing a buffered socket connected to the server
  419. -- @return a new Comm instance
  420. new = function(self, socket)
  421. local o = {}
  422. setmetatable(o, self)
  423. self.__index = self
  424. o.socket = socket
  425. return o
  426. end,
  427. --- Sends and receives a GIOP packet
  428. --
  429. -- @param packet containing a Packet.* object, the object must
  430. -- implement the __tostring meta method
  431. -- @return status true on success, false on failure
  432. -- @return data decoder specific data, see the corresponding
  433. -- MessageDecoder for more information.
  434. exchGIOPPacket = function( self, packet )
  435. local status, err = self.socket:send( tostring(packet) )
  436. local op = packet.op:sub(1, -2)
  437. local data
  438. if( not(status) ) then return false, err end
  439. packet = Packet.GIOP.reply:new()
  440. status, err = packet:recv( self.socket )
  441. if( not(status) ) then return false, err end
  442. if ( MessageDecoder[op] ) then
  443. status, data = MessageDecoder[op]( packet )
  444. else
  445. return false, ("No message decoder for op (%s)"):format(op)
  446. end
  447. return status, data
  448. end,
  449. }
  450. Helper = {
  451. new = function(self, host, port )
  452. local o = {}
  453. setmetatable(o, self)
  454. self.__index = self
  455. o.host = host
  456. o.port = port
  457. o.socket = nmap.new_socket()
  458. return o
  459. end,
  460. GetNamingContext = function( self )
  461. local packet = Packet.GIOP.get:new( 5, 0x494e4954, bin.pack(">IA", #Constants.NAMESERVICE, Constants.NAMESERVICE) )
  462. local status, ctx, lhost, pos, len, bo, tmp
  463. packet:addServiceContext( 17, "\0\x02", 0)
  464. packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, "\0\x14", 0)
  465. packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 )
  466. status, packet = self.comm:exchGIOPPacket( packet )
  467. if( not(status) ) then return status, packet end
  468. return true, packet.ctx
  469. end,
  470. ListObjects = function( self, keyaddr )
  471. -- SyncScope WITH_TARGET
  472. local packet = Packet.GIOP._is_a:new( 5, Constants.SyncScope.WITH_TARGET, keyaddr )
  473. local status, err, lhost
  474. status, err = self:Reconnect()
  475. if( not(status) ) then return false, err end
  476. packet:addServiceContext( 17, "\0\2", 0x000d)
  477. packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" )
  478. packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, "\0\x14", 0x5d69)
  479. packet:addServiceContext( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, tostring(SendingContextRuntime:new( self.lhost )), 0 )
  480. status, packet = self.comm:exchGIOPPacket( packet )
  481. if( not(status) ) then return status, packet end
  482. packet = Packet.GIOP.list:new( Constants.ServiceContext.SENDING_CONTEXT_RUNTIME, Constants.SyncScope.WITH_TARGET, keyaddr, 1000 )
  483. packet:addServiceContext( 17, "\0\2", 0x000d)
  484. packet:addServiceContext( Constants.ServiceContext.CODESETS, "\0\0\0\0\0\1\0\1\0\1\1\9" )
  485. packet:addServiceContext( Constants.ServiceContext.NEO_FIRST_SERVICE_CONTEXT, "\0\x14", 0x9c9b)
  486. status, packet = self.comm:exchGIOPPacket( packet )
  487. if( not(status) ) then return status, packet end
  488. return true, packet
  489. end,
  490. --- Connects and performs protocol negotiation with the Oracle server
  491. --
  492. -- @return true on success, false on failure
  493. -- @return err containing error message when status is false
  494. Connect = function( self )
  495. self.socket:set_timeout(10000)
  496. local status, data = self.socket:connect( self.host.ip, self.port.number, "tcp" )
  497. if( not(status) ) then return status, data end
  498. self.comm = Comm:new( self.socket )
  499. status, self.lhost = self.socket:get_info()
  500. if ( not(status) ) then
  501. self.socket:close()
  502. return false, "Error failed to get socket information"
  503. end
  504. return true
  505. end,
  506. Close = function( self )
  507. return self.socket:close()
  508. end,
  509. Reconnect = function( self )
  510. local status = self:Close()
  511. if( not(status) ) then return false, "Failed to close socket" end
  512. status = self:Connect()
  513. if( not(status) ) then return false, "Failed to re-connect socket" end
  514. return true
  515. end,
  516. }
  517. return _ENV;