# -*- coding: binary -*- require 'msf/core' require 'msf/core/exploit/tcp' module Msf ### # # This module provides methods for working with the RealPort protocol # ### module Exploit::Remote::RealPort include Msf::Exploit::Remote::Tcp # # Initializes an instance of an auxiliary module that uses RealPort # def initialize(info = {}) super register_options( [ Opt::RPORT(771) ], Msf::Exploit::Remote::RealPort ) end @@REALPORT_BAUD_MAP = { '2400' => "\x03\x00", '9600' => "\x00\xc0", '19200' => "\x00\x60", '38400' => "\x00\x20", '57600' => "\x00\x30", '76800' => "\x00\x10", '115200' => "\x00\x10", # Yup, same as above '230400' => "\x00\x08", '460800' => "\x00\x04", '921600' => "\x00\x02", } # Connect to the RealPort service and send the initial handshake # This has the benefit of retrieving the port count and product # Returns true if it succeeds and nil otherwise def realport_connect connect sock.put("\xfb\x01\xfb\x02\xfb\x18") res = sock.get_once(12, 5) return unless (res and res.length == 12) unless res[0,2] == "\xfc\x01" vprint_error("#{rhost}:#{rport} Bad reply: #{res.inspect}") return end len = res[2,2].unpack("n").first return unless len > 0 res = sock.get_once(len, 5) unless res.length == len vprint_error("#{rhost}:#{rport} Bad length: #{res.length} wanted #{len}") return end name,info = res.split("\xfc\x02", 2) fields = info.unpack("n*") @realport_port_count = fields[1].to_i @realport_name = name.gsub(/[\r\n]/, '') # The server also sends us an additional four-byte packet we can ignore here # This throws away a \xFC\x18\x00\x04 sequence sock.get_once(-1, 5) return true end def realport_disconnect disconnect end def realport_baud_to_speed(baud) @@REALPORT_BAUD_MAP[baud] end def realport_recv_banner(port=0, timeout=30, max_data=4096) # # Data is received here, header is: # a2 00 01 82 XX # ^ [ counter ] [ length ] [ data ] # # Can also see f0 here (keep alive) banner = "" stime = Time.now.to_f dcnt = 0 pcnt = 0 while banner.length < max_data and (Time.now.to_f - stime) < timeout res = sock.get_once(1, 1) unless res if banner.length == 0 or pcnt < 3 # Send a new line to wake up the remote end realport_send(port, "\r") pcnt += 1 next else # Allow three empty reads *after* we have sent at least one probe and have data dcnt += 1 break if dcnt > 3 next end end bit = res.unpack("C").first case bit when (0xA0 + port) # Read the packet sequence number (two bytes) res = sock.get_once(2, 1) when 0xF0 # Skip this keep-alive response when (0x80 + port) # Read the one-byte length value res = sock.get_once(1, 1) if res len = res.unpack("C").first res = sock.get_once(len, 1) if res banner << res end end end end banner end def realport_send(port=0, data="") sock.put( [port].pack("C") + data ) end def realport_close(port=0) cprt = [ 0xb0 + port ].pack("C") pkt = cprt + "\x28\x00\xc0\x00\xb0\x00\x01\x00\x00\x00\x00" + cprt + "\x0a\x03" # Response # b2 0b 03 00 00 02 # Send a close request sock.put(pkt) res = sock.get_once(-1, 5) vprint_status("#{target_host}:#{rport} Port:#{port} Close:#{ res.inspect }") return end def realport_open(port=0, baud='9600') @realport_banner = '' cprt = [ 0xb0 + port ].pack("C") aprt = [ 0xa0 + port ].pack("C") speed = realport_baud_to_speed(baud) # Open port pkt1 = "\xf0" + cprt + "\x0a"+ "\x00" # Response # b2 0b 00 00 00 02 # ^ ^ <- port number # Open the port sock.put(pkt1) res = sock.get_once(-1, 5) vprint_status("#{target_host}:#{rport} Port:#{port} Baud:#{baud} Open:#{ res.inspect }") # Access the port pkt2 = cprt + "\x0e" + cprt + "\x2a\x02\xc0\xf3" + cprt + "\x10" + cprt + "\x14" + cprt + "\x16" + cprt + "\x2c\x03\x00\x00" # Response (GOOD) # b2 0f 00 00 00 00 b2 15 0f ff 0f ff b2 11 00 00 # 13 b2 17 01 02 00 2f 06 a8 00 1c 20 00 00 00 00 # 00 f3 f3 00 00 00 00 00 00 00 00 00 00 00 00 00 # 00 # Response (BAD) # \xFF \x17 Access to unopened port\x00 # Send negotiate request sock.put(pkt2) res = sock.get_once(-1, 5) if res.to_s =~ /^\xff/n vprint_status("#{target_host}:#{rport} Port:#{port} is closed: #{res.inspect}") return :closed end vprint_status("#{target_host}:#{rport} Port:#{port} Baud:#{baud} Negotiate:#{ res.inspect }") # Terminal settings pkt3 = cprt + "\x30\x03\xff\x00\x64" + cprt + "\x2d\x03\xff\x0b\xff" + cprt + "\x28" + speed + "\x04" + cprt + "\x00\x01\x00\x00\x00\x00" + cprt + "\x2c\x00\x12\x00" + cprt + "\x2e\x11\x13\x16\x00\x00" + cprt + "\x2f\x03\xff\x00\x64" + cprt + "\x40\x37" + aprt + "\x0f\xff" # Response # c2 12 00 00 f0 # ^ # Send terminal settings request sock.put(pkt3) res = sock.get_once(-1, 5) if res.to_s =~ /^\xff/n vprint_status("#{target_host}:#{rport} Port:#{port} is closed: #{res.inspect}") return :closed end vprint_status("#{target_host}:#{rport} Port:#{port} Baud:#{baud} Settings:#{ res.inspect }") return :open end end end