# -*- coding: binary -*-
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 !(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 !(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 !(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 !(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 !(resp and resp =~ /VERSION/)
      disconnect
      return false
    end

    # Store the version information
    mver = resp.match(/IVERSION\x00([^\x00]+)/n)
    info['Version'] = mver[1] if mver

    # Store the hostname information
    mver = resp.match(/ISERVNAME\x00([^\x00]+)/n)
    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 !(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 !(resp and resp.length > 0)
      break if resp[0,2] == "\x00\x69"
    }
    if !(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]+)/n)
      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
