Skip to content

Commit 601131c

Browse files
author
Brent Cook
committed
Land rapid7#8250, Fix packet ordering issue with reverse_tcp sessions
2 parents 53e6fa8 + 67047cf commit 601131c

File tree

4 files changed

+28
-69
lines changed

4 files changed

+28
-69
lines changed

lib/rex/post/meterpreter/packet_dispatcher.rb

Lines changed: 13 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -284,20 +284,6 @@ def keepalive
284284
# Reception
285285
#
286286
##
287-
288-
#
289-
# Simple class to track packets and if they are in-progress or complete.
290-
#
291-
class QueuedPacket
292-
attr_reader :packet
293-
attr_reader :in_progress
294-
295-
def initialize(packet, in_progress)
296-
@packet = packet
297-
@in_progress = in_progress
298-
end
299-
end
300-
301287
#
302288
# Monitors the PacketDispatcher's sock for data in its own
303289
# thread context and parsers all inbound packets.
@@ -320,8 +306,8 @@ def monitor_socket
320306
begin
321307
rv = Rex::ThreadSafe.select([ self.sock.fd ], nil, nil, PING_TIME)
322308
if rv
323-
packet, in_progress = receive_packet
324-
@pqueue << QueuedPacket.new(packet, in_progress)
309+
packet = receive_packet
310+
@pqueue << packet if packet
325311
elsif self.send_keepalives && @pqueue.empty?
326312
keepalive
327313
end
@@ -356,11 +342,11 @@ def monitor_socket
356342
tmp_channel = []
357343
tmp_close = []
358344
backlog.each do |pkt|
359-
if(pkt.packet.response?)
345+
if(pkt.response?)
360346
tmp_command << pkt
361347
next
362348
end
363-
if(pkt.packet.method == "core_channel_close")
349+
if(pkt.method == "core_channel_close")
364350
tmp_close << pkt
365351
next
366352
end
@@ -379,23 +365,21 @@ def monitor_socket
379365
backlog.each do |pkt|
380366

381367
begin
382-
if ! dispatch_inbound_packet(pkt.packet, pkt.in_progress)
368+
if ! dispatch_inbound_packet(pkt)
383369
# Keep Packets in the receive queue until a handler is registered
384370
# for them. Packets will live in the receive queue for up to
385371
# PACKET_TIMEOUT seconds, after which they will be dropped.
386372
#
387373
# A common reason why there would not immediately be a handler for
388374
# a received Packet is in channels, where a connection may
389375
# open and receive data before anything has asked to read.
390-
#
391-
# Also, don't bother saving incomplete packets if we have no handler.
392-
if (!pkt.in_progress and ::Time.now.to_i - pkt.packet.created_at.to_i < PACKET_TIMEOUT)
376+
if (::Time.now.to_i - pkt.created_at.to_i < PACKET_TIMEOUT)
393377
incomplete << pkt
394378
end
395379
end
396380

397381
rescue ::Exception => e
398-
dlog("Dispatching exception with packet #{pkt.packet}: #{e} #{e.backtrace}", 'meterpreter', LEV_1)
382+
dlog("Dispatching exception with packet #{pkt}: #{e} #{e.backtrace}", 'meterpreter', LEV_1)
399383
end
400384
end
401385

@@ -475,16 +459,12 @@ def add_response_waiter(request, completion_routine = nil, completion_param = ni
475459
# Notifies a whomever is waiting for a the supplied response,
476460
# if anyone.
477461
#
478-
# For not-yet-complete responses, we might not be able to determine
479-
# the response ID, in that case just let all waiters know that some
480-
# responses are trickling in.
481-
#
482-
def notify_response_waiter(response, in_progress=false)
462+
def notify_response_waiter(response)
483463
handled = false
484464
self.waiters.each() { |waiter|
485-
if (in_progress || waiter.waiting_for?(response))
486-
waiter.notify(response, in_progress)
487-
remove_response_waiter(waiter) unless in_progress
465+
if (waiter.waiting_for?(response))
466+
waiter.notify(response)
467+
remove_response_waiter(waiter)
488468
handled = true
489469
break
490470
end
@@ -518,7 +498,7 @@ def initialize_inbound_handlers
518498
# Otherwise, the packet is passed onto any registered dispatch
519499
# handlers until one returns success.
520500
#
521-
def dispatch_inbound_packet(packet, in_progress=false)
501+
def dispatch_inbound_packet(packet)
522502
handled = false
523503

524504
# Update our last reply time
@@ -527,7 +507,7 @@ def dispatch_inbound_packet(packet, in_progress=false)
527507
# If the packet is a response, try to notify any potential
528508
# waiters
529509
if packet.response?
530-
if (notify_response_waiter(packet, in_progress))
510+
if (notify_response_waiter(packet))
531511
return true
532512
end
533513
end

lib/rex/post/meterpreter/packet_parser.rb

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,27 +75,22 @@ def recv(sock)
7575
end
7676
end
7777

78-
in_progress = true
79-
80-
# TODO: cipher decryption
81-
if (cipher)
82-
end
83-
84-
# Deserialize the packet from the raw buffer
85-
packet.from_r(self.raw)
86-
8778
# If we've finished reading the entire packet
8879
if ((self.hdr_length_left == 0) &&
8980
(self.payload_length_left == 0))
9081

82+
# TODO: cipher decryption
83+
if (cipher)
84+
end
85+
86+
# Deserialize the packet from the raw buffer
87+
packet.from_r(self.raw)
88+
9189
# Reset our state
9290
reset
9391

94-
# packet is complete!
95-
in_progress = false
92+
return packet
9693
end
97-
98-
return packet, in_progress
9994
end
10095

10196
protected

lib/rex/post/meterpreter/packet_response_waiter.rb

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -39,17 +39,13 @@ class PacketResponseWaiter
3939
# @return [Integer] request ID to wait for
4040
attr_accessor :rid
4141

42-
# @return [Boolean] indicates if part of the response has been received
43-
attr_accessor :in_progress
44-
4542
#
4643
# Initializes a response waiter instance for the supplied request
4744
# identifier.
4845
#
4946
def initialize(rid, completion_routine = nil, completion_param = nil)
5047
self.rid = rid.dup
5148
self.response = nil
52-
self.in_progress = false
5349

5450
if (completion_routine)
5551
self.completion_routine = completion_routine
@@ -73,21 +69,14 @@ def waiting_for?(packet)
7369
#
7470
# @param response [Packet]
7571
# @return [void]
76-
def notify(response, in_progress = false)
72+
def notify(response)
7773
if (self.completion_routine)
78-
self.in_progress = in_progress
79-
unless in_progress
80-
self.response = response
81-
self.completion_routine.call(response, self.completion_param)
82-
end
74+
self.response = response
75+
self.completion_routine.call(response, self.completion_param)
8376
else
8477
self.mutex.synchronize do
85-
self.in_progress = in_progress
86-
unless in_progress
87-
# complete packet, ready for processing...
88-
self.response = response
89-
self.cond.signal
90-
end
78+
self.response = response
79+
self.cond.signal
9180
end
9281
end
9382
end
@@ -103,11 +92,7 @@ def wait(interval)
10392
interval = nil if interval and interval == -1
10493
self.mutex.synchronize do
10594
if self.response.nil?
106-
loop do
107-
self.cond.wait(self.mutex, interval)
108-
break unless self.in_progress
109-
self.in_progress = false
110-
end
95+
self.cond.wait(self.mutex, interval)
11196
end
11297
end
11398
return self.response

spec/lib/rex/post/meterpreter/packet_parser_spec.rb

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,11 @@
2626

2727
it "should parse valid raw data into a packet object" do
2828
while @raw.length >0
29-
parsed_packet, in_progress = parser.recv(@sock)
29+
parsed_packet = parser.recv(@sock)
3030
end
3131
expect(parsed_packet).to be_a Rex::Post::Meterpreter::Packet
3232
expect(parsed_packet.type).to eq Rex::Post::Meterpreter::PACKET_TYPE_REQUEST
3333
expect(parsed_packet.method?("test_method")).to eq true
34-
expect(in_progress).to eq false
3534
end
3635

3736
end

0 commit comments

Comments
 (0)