forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchannel.rb
479 lines (396 loc) · 10.6 KB
/
channel.rb
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
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
# -*- coding: binary -*-
require 'rex/post/meterpreter/inbound_packet_handler'
module Rex
module Post
module Meterpreter
#
# The various types of channels
#
CHANNEL_CLASS_STREAM = 1
CHANNEL_CLASS_DATAGRAM = 2
CHANNEL_CLASS_POOL = 3
#
# The various flags that can affect how the channel operates
#
# CHANNEL_FLAG_SYNCHRONOUS
# Specifies that I/O requests on the channel are blocking.
#
# CHANNEL_FLAG_COMPRESS
# Specifies that I/O requests on the channel have their data zlib compressed.
#
CHANNEL_FLAG_SYNCHRONOUS = (1 << 0)
CHANNEL_FLAG_COMPRESS = (1 << 1)
#
# The core types of direct I/O requests
#
CHANNEL_DIO_READ = 'read'
CHANNEL_DIO_WRITE = 'write'
CHANNEL_DIO_CLOSE = 'close'
###
#
# The channel class represents a logical data pipe that exists between the
# client and the server. The purpose and behavior of the channel depends on
# which type it is. The three basic types of channels are streams, datagrams,
# and pools. Streams are basically equivalent to a TCP connection.
# Bidirectional, connection-oriented streams. Datagrams are basically
# equivalent to a UDP session. Bidirectional, connectionless. Pools are
# basically equivalent to a uni-directional connection, like a file handle.
# Pools denote channels that only have requests flowing in one direction.
#
###
class Channel
# Class modifications to support global channel message
# dispatching without having to register a per-instance handler
class << self
include Rex::Post::Meterpreter::InboundPacketHandler
# Class request handler for all channels that dispatches requests
# to the appropriate class instance's DIO handler
def request_handler(client, packet)
cid = packet.get_tlv_value(TLV_TYPE_CHANNEL_ID)
# No channel identifier, then drop it
if cid.nil?
return false
end
channel = client.find_channel(cid)
# No valid channel context? The channel may not be registered yet
if channel.nil?
return false
end
dio = channel.dio_map(packet.method)
# Supported DIO request? Dump it.
if dio.nil?
return true
end
# Call the channel's dio handler and return success or fail
# based on what happens
channel.dio_handler(dio, packet)
end
end
##
#
# Factory
#
##
#
# Creates a logical channel between the client and the server
# based on a given type.
#
def Channel.create(client, type = nil, klass = nil,
flags = CHANNEL_FLAG_SYNCHRONOUS, addends = nil, **klass_kwargs)
request = Packet.create_request(COMMAND_ID_CORE_CHANNEL_OPEN)
# Set the type of channel that we're allocating
if !type.nil?
request.add_tlv(TLV_TYPE_CHANNEL_TYPE, type)
end
# If no factory class was provided, use the default native class
if klass.nil?
klass = self
end
request.add_tlv(TLV_TYPE_CHANNEL_CLASS, klass.cls)
request.add_tlv(TLV_TYPE_FLAGS, flags)
request.add_tlvs(addends)
# Transmit the request and wait for the response
cid = nil
begin
response = client.send_request(request)
cid = response.get_tlv_value(TLV_TYPE_CHANNEL_ID)
if cid.nil?
raise Rex::Post::Meterpreter::RequestError
end
end
# Create the channel instance
klass.new(client, cid, type, flags, response, **klass_kwargs)
end
##
#
# Constructor
#
##
#
# Initializes the instance's attributes, such as client context,
# class identifier, type, and flags.
#
def initialize(client, cid, type, flags, packet, **_)
self.client = client
self.cid = cid
self.type = type
self.flags = flags
@mutex = Mutex.new
# Add this instance to the list
if (cid and client)
client.add_channel(self)
end
# Ensure the remote object is closed when all references are removed
ObjectSpace.define_finalizer(self, self.class.finalize(client, cid))
end
def self.finalize(client, cid)
proc {
unless cid.nil?
deferred_close_proc = proc do
begin
self._close(client, cid)
rescue => e
elog("finalize method for Channel failed", error: e)
end
end
# Schedule the finalizing logic out-of-band; as this logic might be called in the context of a Signal.trap, which can't synchronize mutexes
client.framework.sessions.schedule(deferred_close_proc)
end
}
end
##
#
# Channel interaction
#
##
#
# Wrapper around the low-level channel read operation.
#
def read(length = nil, addends = nil)
return _read(length, addends)
end
#
# Reads data from the remote half of the channel.
#
def _read(length = nil, addends = nil)
if self.cid.nil?
raise IOError, "Channel has been closed.", caller
end
request = Packet.create_request(COMMAND_ID_CORE_CHANNEL_READ)
if length.nil?
# Default block size to a higher amount for passive dispatcher
length = self.client.passive_service ? (1024*1024) : 65536
end
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
request.add_tlv(TLV_TYPE_LENGTH, length)
request.add_tlvs(addends)
begin
response = self.client.send_request(request)
rescue
return nil
end
# If the channel is in synchronous mode, the response should contain
# data that was read from the remote side of the channel
if (flag?(CHANNEL_FLAG_SYNCHRONOUS))
data = response.get_tlv(TLV_TYPE_CHANNEL_DATA);
if (data != nil)
return data.value
end
else
raise NotImplementedError, "Asynchronous channel mode is not implemented", caller
end
return nil
end
#
# Wrapper around the low-level write.
#
def write(buf, length = nil, addends = nil)
return _write(buf, length, addends)
end
#
# Writes data to the remote half of the channel.
#
def _write(buf, length = nil, addends = nil)
if self.cid.nil?
raise IOError, "Channel has been closed.", caller
end
request = Packet.create_request(COMMAND_ID_CORE_CHANNEL_WRITE)
# Truncation and celebration
if ((length != nil) &&
(buf.length >= length))
buf = buf[0..length]
else
length = buf.length
end
# Populate the request
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
cdata = request.add_tlv(TLV_TYPE_CHANNEL_DATA, buf)
if( ( self.flags & CHANNEL_FLAG_COMPRESS ) == CHANNEL_FLAG_COMPRESS )
cdata.compress = true
end
request.add_tlv(TLV_TYPE_LENGTH, length)
request.add_tlvs(addends)
response = self.client.send_request(request)
written = response.get_tlv(TLV_TYPE_LENGTH)
written.nil? ? 0 : written.value
end
#
# Wrapper around check for self.cid
#
def closed?
self.cid.nil?
end
#
# Wrapper around the low-level close.
#
def close(addends = nil)
return _close(addends)
end
#
# Close the channel for future writes.
#
def close_write
return _close
end
#
# Close the channel for future reads.
#
def close_read
return _close
end
#
# Closes the channel.
#
def self._close(client, cid, addends=nil)
if cid.nil?
raise IOError, "Channel has been closed.", caller
end
request = Packet.create_request(COMMAND_ID_CORE_CHANNEL_CLOSE)
# Populate the request
request.add_tlv(TLV_TYPE_CHANNEL_ID, cid)
request.add_tlvs(addends)
client.send_request(request, nil)
# Disassociate this channel instance
client.remove_channel(cid)
return true
end
def _close(addends = nil)
# let the finalizer do the work behind the scenes
@mutex.synchronize {
unless self.cid.nil?
ObjectSpace.undefine_finalizer(self)
self.class._close(self.client, self.cid, addends)
self.cid = nil
end
}
end
#
# Enables or disables interactive mode.
#
def interactive(tf = true, addends = nil)
if self.cid.nil?
raise IOError, "Channel has been closed.", caller
end
request = Packet.create_request(COMMAND_ID_CORE_CHANNEL_INTERACT)
# Populate the request
request.add_tlv(TLV_TYPE_CHANNEL_ID, self.cid)
request.add_tlv(TLV_TYPE_BOOL, tf)
request.add_tlvs(addends)
self.client.send_request(request)
return true
end
##
#
# Direct I/O
#
##
#
# Handles dispatching I/O requests based on the request packet.
# The default implementation does nothing with direct I/O requests.
#
def dio_handler(dio, packet)
if (dio == CHANNEL_DIO_READ)
length = packet.get_tlv_value(TLV_TYPE_LENGTH)
return dio_read_handler(packet, length)
elsif (dio == CHANNEL_DIO_WRITE)
data = packet.get_tlv_value(TLV_TYPE_CHANNEL_DATA)
return dio_write_handler(packet, data)
elsif (dio == CHANNEL_DIO_CLOSE)
return dio_close_handler(packet)
end
return false;
end
#
# Stub read handler.
#
def dio_read_handler(packet, length)
return true
end
#
# Stub write handler.
#
def dio_write_handler(packet, data)
return true
end
#
# Stub close handler.
#
def dio_close_handler(packet)
temp_cid = nil
@mutex.synchronize {
temp_cid = self.cid
self.cid = nil
}
client.remove_channel(temp_cid)
# Trap IOErrors as parts of the channel may have already been closed
begin
self.cleanup
rescue IOError
end
return true
end
#
# Maps packet request methods to DIO request identifiers on a
# per-instance basis as other instances may add custom dio
# handlers.
#
def dio_map(command_id)
if command_id == COMMAND_ID_CORE_CHANNEL_READ
return CHANNEL_DIO_READ
elsif command_id == COMMAND_ID_CORE_CHANNEL_WRITE
return CHANNEL_DIO_WRITE
elsif command_id == COMMAND_ID_CORE_CHANNEL_CLOSE
return CHANNEL_DIO_CLOSE
end
return nil
end
##
#
# Conditionals
#
##
#
# Checks to see if a flag is set on the instance's flags attribute.
#
def flag?(flag)
return ((self.flags & flag) == flag)
end
#
# Returns whether or not the channel is operating synchronously.
#
def synchronous?
return (self.flags & CHANNEL_FLAG_SYNCHRONOUS)
end
#
# The unique channel identifier.
#
attr_reader :cid
#
# The type of channel.
#
attr_reader :type
#
# The class of channel (stream, datagram, pool).
#
attr_reader :cls
#
# Any channel-specific flag, like synchronous IO.
#
attr_reader :flags
#
# Any channel-specific parameters.
#
attr_accessor :params
#
# The associated meterpreter client instance
#
attr_accessor :client
protected
attr_writer :cid, :type, :cls, :flags # :nodoc:
#
# Cleans up any lingering resources
#
def cleanup
end
end
end; end; end